SwitchboardConnectionHandler.cs :  » Network-Clients » MSNP-Helper-API » MSNP » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » Network Clients » MSNP Helper API 
MSNP Helper API » MSNP » SwitchboardConnectionHandler.cs
/*
 * (standard BSD license)
 * 
 * Copyright (c) 2002, Chad Myers, et al.
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * 
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer. Redistributions in binary form
 * must reproduce the above copyright notice, this list of conditions and the
 * following disclaimer in the documentation and/or other materials provided with
 * the distribution. Neither the name of SourceForge, nor Microsoft nor the names
 * of its contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission. 
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

using System;
using System.Collections;
using System.Timers;

namespace MSNP{
  /// <summary>
  /// Handler for connections to Switchboard servers
  /// </summary>
  internal class SwitchboardConnectionHandler : ConnectionHandler
  {
    /**********************************************/
    // Enumerations
    /**********************************************/

    /// <summary>
    /// Determines which state the <see cref="ProcessConnection"/> method is in this cycle.
    /// </summary>
    private enum ConnectionState :int {STARTING, SENTAUTH, ONLINE, CLOSING}

    /**********************************************/
    // Fields
    /**********************************************/

    private static  int    m_SessionTimeout = 30000;

    private int        m_State = (int) ConnectionState.STARTING; // Current processing state
    private String      m_FriendlyName;                // This user's friendly name
    private String      m_AuthChallenge;              // Challenge string for initial connection
    private String      m_SessionID;                // Session ID of this session
    private ISessionHandler m_Handler;                  // Implementer provided session handler
    private Session      m_Session;                  // Current Session object for this session
    private  int        m_RosterCount = 0;              // Number of users in this session's user roster
    private Timer      m_Timer;                  // Timer for determining whether or not to terminate the session
    private Queue      m_RequestQueue = Queue.Synchronized(new Queue(10));  // Requests to be sent on next cycle
    private object      m_SessionIdentifier;            // Implementer specified identifier for this session
    
    /**********************************************/
    // Events
    /**********************************************/
    /// <summary>
    /// Thrown when this session is ending
    /// </summary>
    public event EventHandler SessionEndEvent;

    /// <summary>
    /// Thrown when this is a user-requested session and the session ID has finally been determined.
    /// </summary>
    public event EventHandler SessionIDObtained;

    /**********************************************/
    // Constructors
    /**********************************************/
    /// <summary>
    /// Main constructor. Configures this handler.
    /// </summary>
    /// <remarks>
    /// No connections or actual processing is done in the constructor. Use the <see cref="ConnectionHandler.Start"/> method to begin processing.
    /// </remarks>
    /// <param name="host">The DNS name or IP address of the SB to connect to.</param>
    /// <param name="port">The port of the SB server</param>
    /// <param name="user">The username with which to authenticate</param>
    /// <param name="pass">The user's password</param>
    /// <param name="friendlyName">This user's friendly name</param>
    /// <param name="authChallengeInfo">The challenge info for connecting to an existing session</param>
    /// <param name="sessionID">The ID of this session if joining an existing session</param>
    /// <param name="sessionHandler">The implemented session handler for any new sessions</param>
    /// <param name="sessionIdentifier">The implementer specified identifier for this session</param>
    public SwitchboardConnectionHandler(String host, int port, String user, 
                      String pass, String friendlyName, 
                      String authChallengeInfo,
                      String sessionID,
                      ISessionHandler sessionHandler,
                        object sessionIdentifier) :
      base(host, port, user, pass)
    {
      m_FriendlyName = friendlyName;
      m_AuthChallenge = authChallengeInfo;
      m_SessionID = sessionID;
      m_Handler = sessionHandler;
      m_Timer = new Timer(SwitchboardConnectionHandler.SessionTimeout);
      m_Timer.Elapsed += new ElapsedEventHandler(OnTimerElapsed);
      m_Timer.Enabled = true;
      m_SessionIdentifier = sessionIdentifier;
    }

    /**********************************************/
    // Properties
    /**********************************************/
    public String SessionID{ get{ return m_SessionID; } }
    internal static int SessionTimeout 
    { 
      get{ return m_SessionTimeout; }
      set{ m_SessionTimeout = value; } 
    }

    /**********************************************/
    // Protected Methods
    /**********************************************/

    /// <summary>
    /// Implementation of the abstract <see cref="ConnectionHandler.ProcessConnection"/> method.
    /// </summary>
    /// <remarks>
    /// <para>This is the main processing method for this class. This method is called
    /// every cycle in the connection thread.</para>
    /// <para>This thread will process the initial protocol initialization dialogue and then
    /// enter the main switchboard phase and process any new messages or notifications.</para>
    /// </remarks>
    protected override void ProcessConnection()
    {
      Response[] responses = GetResponses();

      switch(m_State)
      {
        // Just starting, send auth or challenge info
        case (int) ConnectionState.STARTING:
          m_Session = new Session(this, m_Handler, m_SessionIdentifier);

          if( m_SessionID != null )
            AddRequest( new Request(ANS, User + " " + m_AuthChallenge + " " + m_SessionID) );
          else
            AddRequest( new Request(USR, User + " " + m_AuthChallenge) );

          m_State = (int) ConnectionState.SENTAUTH;

          break;

        // Check for auth response
        case (int) ConnectionState.SENTAUTH:
          Response response;

          ProcessResponses(responses);            

          if( m_SessionID != null )
            response = LookForSpecificResponse(responses, ANS);
          else
            response = LookForSpecificResponse(responses, USR);

          if( response == null )
            break;

          // Different cases depending on whether we're joining a session or
          // starting a new one
          if( m_SessionID != null && 
            (response.Parameters.Length != 1 || response.Parameters[0] != "OK" ) )
              throw new Exception("Unexpected answer response from switchboard server");
          else if( m_SessionID == null &
            (response.Parameters.Length != 3 || response.Parameters[0] != "OK" ) )
              throw new Exception("Unexpected answer response from switchboard server");
          
          response = null;

          m_Session.Start();

          m_State = (int) ConnectionState.ONLINE;
          break;

        // Online, authenticated, go into main loop
        case (int) ConnectionState.ONLINE:
          ProcessResponses(responses);
          ProcessSessionRequests();
          break;

        // Signout requested, shutdown gracefully
        case (int) ConnectionState.CLOSING:
          CloseConnection();
          break;
      }
    }

    /// <summary>
    /// Called when the <see cref="ConnectionHandler"/> has actually
    /// closed the connection.
    /// </summary>
    protected override void OnConnectionClosed()
    {
      m_State = (int) ConnectionState.CLOSING;
      m_Session.Stop();
      OnSessionEnd();
    }

    /**********************************************/
    // Private Methods
    /**********************************************/

    /// <summary>
    /// Process responses from server.
    /// </summary>
    /// <remarks>
    /// This method handles situations such as a user joining the session,
    /// leaving the session, or responses to commands such as CAL
    /// </remarks>
    /// <param name="responses">The list of responses to process</param>
    private void ProcessResponses(Response[] responses)
    {
      for(int i = 0; i < responses.Length; i++ )
      {
        // Ignore ANS and USR since they're part of the authentication, but process everything else
        if( responses[i].Command != ANS && responses[i].Command != USR )
        {
          // Roster update -- users already in this session
          if( responses[i].Command == IRO )
          {
            System.Threading.Interlocked.Increment(ref m_RosterCount);
            m_Session.AddRosterUser( responses[i].Parameters[2], responses[i].Parameters[3] );            
          }
          // User joined
          else if( responses[i].Command == JOI )
          {
            System.Threading.Interlocked.Increment(ref m_RosterCount);
            m_Session.AddResponseToQueue(responses[i]);
          }
          // User departed
          else if( responses[i].Command == BYE )
          {
            System.Threading.Interlocked.Decrement(ref m_RosterCount);
            m_Session.AddResponseToQueue(responses[i]);
          }
          // Confirmation response to CAL command
          // If this is a new session, the session ID will be passed back
          // so we can remember it.
          else if( responses[i].Command == CAL && m_SessionID == null )
          {
            m_SessionID = responses[i].Parameters[1];
            OnSessionIDObtained();
          }
          // Otherwise, add it for the Session object to deal with
          else
          {
            m_Session.AddResponseToQueue(responses[i]);
          }
        }
      }
    }

    /// <summary>
    /// Adds a request for this SB session to process
    /// </summary>
    /// <remarks>
    /// This method is used by the convience methods to allow
    /// other classes to request things like inviting a user
    /// or sending a message.
    /// </remarks>
    /// <param name="request">The request to add</param>
    private void AddSessionRequest(Request request)
    {
      m_RequestQueue.Enqueue(request);
    }

    /// <summary>
    /// Processes any requests pending in the session queue
    /// </summary>
    private void ProcessSessionRequests()
    {
      ArrayList requests;

      lock(m_RequestQueue.SyncRoot)
      {
        requests = new ArrayList(m_RequestQueue.Count);

        for(int i = 0; i < m_RequestQueue.Count; i++)
        {
          Request request = (Request) m_RequestQueue.Dequeue();
          if( ! (request.Command == ConnectionHandler.MSG && m_RosterCount == 0) )
            requests.Add(request);
        }
      }

      foreach(Request request in requests)
      {
        AddRequest(request);
      }
    }

    /// <summary>
    /// Used to determine is the session should terminate
    /// </summary>
    /// <remarks>
    /// If no one has joined the session after 30 seconds, close it so
    /// it doesn't linger around wasting resources.
    /// </remarks>
    /// <param name="sender">Type who rasied the event</param>
    /// <param name="args">Arguments and data for this event delegate</param>
    private void OnTimerElapsed(object sender, ElapsedEventArgs args)
    {
      if( m_RosterCount == 0 )
        Stop();
    }

    /// <summary>
    /// Throws SessionEndEvent.
    /// </summary>
    private void OnSessionEnd()
    {
      if( SessionEndEvent != null )
        SessionEndEvent(this, new EventArgs());
    }

    /// <summary>
    /// Throws a SessionIDObtained event.
    /// </summary>
    private void OnSessionIDObtained()
    {
      if( SessionIDObtained != null )
        SessionIDObtained(this, new EventArgs());
    }

    /**********************************************/
    // Internal methods
    /**********************************************/

    /// <summary>
    /// Invites a user to this session.
    /// </summary>
    /// <param name="userHandle">The user handle (email address) of the user to invite</param>
    internal void InviteContact(String userHandle)
    {
      AddSessionRequest(new Request(CAL, userHandle));
    }

    /// <summary>
    /// Sends a message to the session.
    /// </summary>
    /// <param name="message">The message body to send</param>
    internal void SendMessage(String message)
    {
      string headers = "MIME-Version: 1.0\r\nContent-Type: text/plain; charset=UTF-8\r\n\r\n";
      int length = headers.Length + message.Length + 2;

      string commandParams = "U " + length.ToString() + "\r\n" + headers + message;

      AddSessionRequest(new Request(MSG, commandParams));
    }

    /// <summary>
    /// Sends a MIME message to the session
    /// </summary>
    /// <param name="message">The message to send</param>
    internal void SendMessage(MimeMessage message)
    {
      string headers = "";
      IDictionary headersDict = message.Headers;

      foreach( DictionaryEntry entry in headersDict )
      {
        headers += entry.Key + ": " + entry.Value + "\r\n";
      }

      headers += "\r\n";

      int length = headers.Length + message.Body.Length + 2;

      string commandParams = "U " + length.ToString() + "\r\n" + headers + message;

      AddSessionRequest(new Request(MSG, commandParams));
    }

    /// <summary>
    /// Stop this connection and close out gracefully.
    /// </summary>
    internal void Stop()
    {
      lock(this)
      {
        AddRequest( new Request(BYE, null) );
        m_State = (int) ConnectionState.CLOSING;
        m_Session.Stop();
      }  
      OnSessionEnd();
    }

    /// <summary>
    /// Converts an error response from the server to a human-readable description
    /// </summary>
    /// <param name="errorCommand">The command for which to get the description</param>
    /// <returns>A description of the error</returns>
    internal string GetErrorDescriptionForCommand(string errorCommand)
    {
      return GetErrorDescription(System.Int32.Parse(errorCommand));
    }
  }
}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.