ConnectionHandler.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 » ConnectionHandler.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.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using MSNP.CommandParsers;

namespace MSNP{
  /// <summary>
  /// Provides a base for classes that deal with the various connections of MSNP.
  /// </summary>
  /// <remarks>
  /// This class provides functionality such as socket handling, reading/writing, and basic
  /// protocol menutia.  The implementer needs only implement the <see cref="ProcessConnection"/> method.
  /// Responses and Requests can be retrieved and sent using <see cref="GetResponses"/> and <see cref="AddRequest"/>
  /// respectively.
  /// </remarks>
  internal abstract class ConnectionHandler
  {

    /**********************************************/
    // Fields
    /**********************************************/
    IPAddress    m_Host;              // The host with whom to establish the connection
    int        m_Port;              // The TCP port on which to connect
    String      m_User;              // The username with which to authenticate
    String      m_Pass;              // The password of the user
    Thread      m_Thread;            // Local reference to the thread for this connection
    String      m_ThreadName;          // The name of this thread. Configurable for debug/trace purposes.
    Queue      m_RequestsQueue;        // The queue for outgoing requests
    Queue      m_ResponsesQueue;        // The queue for incoming responses/commands
    Hashtable    m_PendingRequests;        // Requests that have been issued, but not yet responded to
    Socket      m_Socket;            // The TCP Socket type which is used for basic protocol I/O
    int        m_TransactionID;        // Counter for transaction IDs
    CommandParser  m_Parser = new CommandParser(); // Used for parsing responses from the server
    bool      m_CloseConnection;        // Flag that implementers use to have the connection closed
    IAsyncResult  m_SocketReadResult;        // Used for asynchronous reads from the socket

    /**********************************************/
    // Constructors
    /**********************************************/
    /// <summary>
    /// Creates a new connection handler and initializes internal data.
    /// </summary>
    /// <remarks>
    /// Nothing actually happens yet. To begin processing the connection, call
    /// the start method.
    /// </remarks>
    /// <param name="host">A String that represents the hosts' DNS name or IPv4 (32-bit) IP address in the format of x.y.z.a</param>
    /// <param name="port">An integer representing the port (0-65534) on which to connect</param>
    /// <param name="user">The user handle with which to authenticate to the MSN servers</param>
    /// <param name="pass">The password for the user</param>
    public ConnectionHandler(String host, int port, String user, String pass)
    {
      m_Host = IPAddress.Parse(host);
      m_Port = port;
      m_User = user;
      m_Pass = pass;
      m_RequestsQueue = Queue.Synchronized(new Queue());
      m_ResponsesQueue = Queue.Synchronized(new Queue());
      m_PendingRequests = Hashtable.Synchronized(new Hashtable());
      m_ThreadName = this.GetType().FullName;
      m_CloseConnection = false;    
  
      Debug.AutoFlush=true;
    }

    /**********************************************/
    // Properties
    /**********************************************/
    protected String Host
    {
      get{ return m_Host.ToString(); }
      set{ m_Host = IPAddress.Parse(value); }
    }

    protected int Port
    {
      get{ return m_Port; }
      set{ m_Port = value; }
    }

    protected String User
    {
      get{ return m_User; }
      set{ m_User = value; }
    }

    protected String Pass
    {
      get{ return m_Pass; }
      set{ m_Pass = value; }
    }

    protected Thread Thread{ get{ return m_Thread; } }

    protected String ThreadName
    { 
      get{ return m_ThreadName; }
      set{ m_ThreadName = value; }
    }

    /**********************************************/
    // Internal Methods
    /**********************************************/
    /// <summary>
    /// Starts this connection.
    /// </summary>
    /// <remarks>
    /// The current implementation of ConnectionHandler is single-use.
    /// You cannot stop/disconnect/signout and then restart a connection
    /// handler. You must create a new handler and start it.
    /// </remarks>
    internal virtual void Start()
    {
      if( m_Thread == null || ! m_Thread.IsAlive )
      {
        m_CloseConnection = false;
        ThreadStart start = new ThreadStart(handleConnection);
        m_Thread = new Thread(start);
        m_Thread.Name = m_ThreadName;        
        m_Thread.Start();
      }
      else
      {
        throw new Exception("Connection already started");
      }

    }

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

    /// <summary>
    /// Implementer entry point
    /// </summary>
    /// <remarks>
    /// <para>There is where the derriving class implements all its functionality.
    /// This method is called each thread cycle.</para>
    /// <para>Received responses can get checked using the <see cref="GetResponses"/> method.
    /// Any requests that need to be sent out can be queued using the <see cref="AddRequest"/> method.
    /// </para>
    /// <para>It is important that ProcessConnection finish in a timely manner and not block
    /// for long periods of time. Any lengthy processes should be spawned into sub-threads.</para>
    /// <para>When the connection needs to be closed, call the <see cref="CloseConnection"/>
    /// method to signal to the ConnectionHandler its time to close the connection and bail.</para>
    /// </remarks>
    protected abstract void ProcessConnection();

    /// <summary>
    /// Retrieves any responses that have been received since the last cycle.
    /// </summary>
    /// <remarks>
    /// This method is thread-safe.
    /// </remarks>
    /// <returns>An array of responses to be processed.</returns>
    protected Response[] GetResponses()
    {
      Response[] responses;

      lock(m_ResponsesQueue.SyncRoot)
      {
        responses = new Response[m_ResponsesQueue.Count];

        for( int i = 0; i < responses.Length; i++ )
        {
          responses[i] = (Response)m_ResponsesQueue.Dequeue();
        }
      }

      return responses;
    }

    /// <summary>
    /// Adds a request to the outgoing request queue
    /// </summary>
    /// <remarks>
    /// <para>This method is thread-safe and uses a syncrhonized Queue collection.</para>
    /// <para>Requests will actually be sent after the <see cref="ProcessConnection"/>
    /// method finishes each cycle.</para>
    /// </remarks>
    /// <param name="request">The request to send.</param>
    protected void AddRequest(Request request)
    {
      m_RequestsQueue.Enqueue(request);
    }

    /// <summary>
    /// Called when the connection has been closed.
    /// </summary>
    /// <remarks>
    /// This method is called after the socket has closed and
    /// allows the implementer a chance to gracefully shut down
    /// in the case of a forced disconnect or user-driven signout.
    /// </remarks>
    protected virtual void OnConnectionClosed(){}

    /// <summary>
    /// Called when a connection to a new host is necessary.
    /// </summary>
    /// <remarks>
    /// <para>This is used mainly by Notification Server connections when an
    /// XFR command is issued.</para>
    /// <para>First set the new Host and Port properties and then call this method.</para>
    /// </remarks>
    protected void Reconnect()
    {
      Close();
      Connect();
    }

    /// <summary>
    /// Called by the implementer to signal that a disconnect is requested.
    /// </summary>
    /// <remarks>
    /// The disconnect will not actually occur until the <see cref="ProcessConnection"/> method
    /// has finished. Generally, this method should be called and then the ProcessConnection
    /// method ended.
    /// </remarks>
    protected void CloseConnection()
    {
      m_CloseConnection = true;
    }

    /// <summary>
    /// Convenience method used for sending the VER command
    /// </summary>
    /// <remarks>
    /// <para>Since most MSN-related connections use this command, this saves implementers from having to rewrite this section of code.</para>
    /// <para>Call this method and then use <see cref="LookForProtocolResponse"/> on the next cycle to determine if the proper response was received.</para>
    /// </remarks>
    /// <param name="Protocol">The supported protocol (usually MSNP2)</param>
    protected void SetProtocol(string Protocol)
    {
      Request request = new Request(VER, Protocol);

      AddRequest(request);
    }

    /// <summary>
    /// Determines if a response to a VER command has been received.
    /// </summary>
    /// <param name="responses">Currently received responses. Use the <see cref="GetResponses"/> method to get these.</param>
    /// <returns>The response, if found, or null if not found.</returns>
    protected Response LookForProtocolResponse(Response[] responses)
    {
      return LookForSpecificResponse(responses, VER);
    }

    /// <summary>
    /// Determines if a specific response has been recieved.
    /// </summary>
    /// <param name="responses">The responses in which to look. Use the <see cref="GetResponses"/> method to get these.</param>
    /// <param name="command">The command part of the response to look for.</param>
    /// <returns>The response, if found, or null if not found.</returns>
    protected Response LookForSpecificResponse(Response[] responses, String command)
    {
      Response foundResponse = null;

      for( int i = 0; i < responses.Length; i++ )
      {
        if( responses[i].Command == command )
        {
          foundResponse = responses[i];
          break;
        }      
      }

      return foundResponse;
    }

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

    /// <summary>
    /// Main thread class.
    /// </summary>
    /// <remarks>
    /// <para>This is the method passed to the ThreadStart for the thread.
    /// It connects, runs the main thread loop and disconnects.</para>
    /// <para>The main thread loop gathers any waiting responses/commands from the
    /// server and then calls ProcessConnection() which allows the implementer to 
    /// perform various tasks and process the responses and issue any new requests. After
    /// ProcessConnection finishes, any waiting requests are dispatched at this time.</para>
    /// </remarks>
    private void handleConnection()
    {
      Connect();
      while(!m_CloseConnection)
      {
        CheckResponses();
        ProcessConnection();
        if( !m_CloseConnection )
          SendRequests();
        Thread.Sleep(200);
      }
      Close();
    }


    /// <summary>
    /// Establishes the actual socket connection to the specified host and port.
    /// </summary>
    private void Connect()
    {
      if( m_Socket == null )
      {
        
        m_Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        IPEndPoint endpoint = new IPEndPoint(m_Host, m_Port);
        m_Socket.Connect(endpoint);
      }
      else
        throw new Exception("Already Connected");
    }
    
    /// <summary>
    /// Closes and terminates the socket connection.
    /// </summary>
    private void Close()
    {
      if( m_Socket != null )
      {
        m_Socket.Shutdown(SocketShutdown.Both);
        m_Socket.Close();
        m_Socket = null;
        OnConnectionClosed();
      }
      else
        throw new Exception("Not connected");
    }



    /// <summary>
    /// Dequeues queued requests and sends them using <see cref="SendRequest"/>.
    /// </summary>
    private void SendRequests()
    {
      Request[] requests;
      lock(m_RequestsQueue.SyncRoot)
      {
        requests = new Request[m_RequestsQueue.Count];

        for( int i = 0; i < requests.Length; i++ )
        {
          requests[i] = (Request)m_RequestsQueue.Dequeue();
        }
      }

      for( int i = 0; i < requests.Length; i++ )
      {
        SendRequest(requests[i]);
      }
    }

    /// <summary>
    /// Sends a request over the opened socket.
    /// </summary>
    /// <param name="request">The request to send.</param>
    private void SendRequest(Request request)
    {
      //Debug.WriteLine("Sending request: " + request);
      int transID = GetTransactionID();

      String messageToSend;
      if( request.Parameters != null )
        messageToSend = request.Command + " " + transID.ToString() + " " + request.Parameters + "\r\n";
      else
        messageToSend = request.Command + " " + transID.ToString() + "\r\n";

      byte[] bytesToSend = Encoding.ASCII.GetBytes(messageToSend);

      Debug.WriteLine("Sending request (raw): " + messageToSend);

      m_Socket.Send(bytesToSend);
    }

    /// <summary>
    /// Checks the socket for any awaiting messages
    /// </summary>
    /// <remarks>
    /// <para>Initiates an asynchronous read from the socket which calls <see cref="FinishedRead"/> when finished.</para>
    /// </remarks>
    private void CheckResponses()
    {
      /* This doesn't seem to be working as documented.
       * there doesn't seem to be a good way to determine if the
       * socket is closed or not. It just kind of "is" at any
       * given point in time.
       
      if( m_Socket.Poll(200, SelectMode.SelectRead) )
      {
        // The socket was closed or terminated
        //CloseConnection();
        //return;
      }*/
      try
      {
        // Only start a read if there isn't one pending already.
        if( m_SocketReadResult == null ||
          m_SocketReadResult.IsCompleted )
        {
          byte[] responseBytes = new byte[8024];

          m_SocketReadResult = m_Socket.BeginReceive(
            responseBytes, 0, responseBytes.Length, 
            SocketFlags.None, new AsyncCallback(this.FinishedRead),
            responseBytes);
        }
      }
      catch(Exception e)
      {        
        Debug.WriteLine("Socket exception while checking responses: " + e.Message);
        CloseConnection();
      }
    }

    /// <summary>
    /// Asynchronous callback from the socket read operation.
    /// </summary>
    /// <remarks>
    /// Takes the raw message from the server and parses it using the <see cref="CommandParser"/>.
    /// </remarks>
    /// <param name="ar">The async result from the read operation</param>
    private void FinishedRead(IAsyncResult ar)
    {
      string rawResponse;

      if( m_Socket != null )
      {

        // AsyncState contains the byte array that was passed in to the BeginRead method
        rawResponse = Encoding.ASCII.GetString((byte[]) ar.AsyncState);
        
        Debug.WriteLine("Got response: " + rawResponse);


        // Check for null, because it's possible the socket could've closed in the meantime.
        if( m_Socket != null )
        {
          try{ m_Socket.EndReceive(ar); } 
          catch(Exception e)
          {
            // This usually occurs because the session was forcibly terminated.
            Debug.WriteLine("Socket Exception: " + e.Message);
            CloseConnection();
          }
        }
    
        if( rawResponse.Length > 0 )
        {
          // Have the command parser parse the responses
          Response[] responses = m_Parser.ParseResponses(rawResponse);

          lock(m_ResponsesQueue.SyncRoot)
          {
            for( int i = 0; i < responses.Length; i++ )
            {
              if( responses[i].TransactionID != -1 )
              {
                lock(m_PendingRequests.SyncRoot)
                {
                  if( m_PendingRequests.ContainsKey(responses[i].TransactionID) )
                  {
                    // Determine if this response was to any pending requests and match them up
                    responses[i].SetOriginatingRequest((Request) m_PendingRequests[responses[i].TransactionID]);
                    m_PendingRequests.Remove(responses[i].TransactionID);
                  }
                }
              }
              // Add this to received responses
              m_ResponsesQueue.Enqueue(responses[i]);          
            }
          }
        }
      }
    }    

    /// <summary>
    /// Retrieves the next transaction ID
    /// </summary>
    /// <remarks>
    /// This is a thread-safe atomic operation and garauntees unique transaction IDs across threads
    /// </remarks>
    /// <returns>The next transaction ID</returns>
    private int GetTransactionID()
    {
      return Interlocked.Increment(ref m_TransactionID);
    }

    /// <summary>
    /// Gets a description for an error number
    /// </summary>
    /// <param name="errorNum">The error to describe</param>
    /// <returns>The description for the error</returns>
    protected string GetErrorDescription(int errorNum)
    {
      switch(errorNum)
      {
        case ERR_SYNTAX_ERROR:
          return "Syntax error in request";
        case ERR_INVALID_PARAMETER:
          return "Invalid parameter in request";
        case ERR_INVALID_USER:
          return "Invalid user";
        case ERR_FQDN_MISSING:
          return "Fully qualified domain name missing";
        case ERR_ALREADY_LOGIN:
          return "Already logged in";
        case ERR_INVALID_USERNAME:
          return "Invalid username";
        case ERR_INVALID_FRIENDLY_NAME:
          return "Invalid friendly name";
        case ERR_LIST_FULL:
          return "List full";
        case ERR_ALREADY_THERE:
          return "Already there";
        case ERR_NOT_ON_LIST:
          return "User not on list";
        case ERR_USER_NOT_ONLINE:
          return "User not online";
        case ERR_ALREADY_IN_THE_MODE:
          return "Already in that mode";
        case ERR_ALREADY_IN_OPPOSITE_LIST:
          return "Already in opposite list";
        case ERR_SWITCHBOARD_FAILED:
          return "Switchboard connection failed";
        case ERR_NOTIFY_XFR_FAILED:
          return "Notification transfer failed";
        case ERR_REQUIRED_FIELDS_MISSING:
          return "Required fields missing";
        case ERR_NOT_LOGGED_IN:
          return "Not logged in";
        case ERR_INTERNAL_SERVER:
          return "Internal server error";
        case ERR_DB_SERVER:
          return "DB server error";
        case ERR_FILE_OPERATION:
          return "Invalid file operation";
        case ERR_MEMORY_ALLOC:
          return "Error allocating memory";
        case ERR_SERVER_BUSY:
          return "Server too busy";
        case ERR_SERVER_UNAVAILABLE:
          return "Server unavailable";
        case ERR_PEER_NS_DOWN:
          return "Peer notification server is down";
        case ERR_DB_CONNECT:
          return "Error connecting to DB";
        case ERR_SERVER_GOING_DOWN:
          return "Server is going down";
        case ERR_CREATE_CONNECTION:
          return "Cannot create connection";
        case ERR_BLOCKING_WRITE:
          return "Write operation blockings";
        case ERR_SESSION_OVERLOAD:
          return "Session overload";
        case ERR_USER_TOO_ACTIVE:
          return "User too active";
        case ERR_TOO_MANY_SESSIONS:
          return "Too many active sessions";
        case ERR_NOT_EXPECTED:
          return "Not expected";
        case ERR_BAD_FRIEND_FILE:
          return "Bad friend file";
        case ERR_AUTHENTICATION_FAILED:
          return "Authentication failed";
        case ERR_NOT_ALLOWED_WHEN_OFFLINE:
          return "Not allowed while offline";
        case ERR_NOT_ACCEPTING_NEW_USERS:
          return "Not accepting new users";
        default:
          return "Unknown error";
      }
    }

    /**********************************************/
    // Constants
    /**********************************************/

    // MSN default port
    public const int MSN_PORT = 1863;

    // Protocol Command Constants
    public const string ACK = "ACK";
    public const string ADD = "ADD";
    public const string ANS = "ANS";
    public const string BLP = "BLP";
    public const string BYE = "BYE";
    public const string CAL = "CAL";
    public const string CHG = "CHG";
    public const string FLN = "FLN";
    public const string GTC = "GTC";
    public const string INF = "INF";
    public const string ILN = "ILN";
    public const string IRO = "IRO";
    public const string JOI = "JOI";
    public const string LST = "LST";
    public const string MSG = "MSG";
    public const string NAK = "NAK";
    public const string NLN = "NLN";
    public const string HDN = "HDN";
    public const string OUT = "OUT";
    public const string REM = "REM";
    public const string RNG = "RNG";
    public const string SYN = "SYN";
    public const string USR = "USR";
    public const string VER = "VER";
    public const string XFR = "XFR";

    public const string ERR = "ERR"; // This is not defined in the standard. This is for this application's use only

    // Protocol Error Constants
    public const int ERR_SYNTAX_ERROR                 = 200;
    public const int ERR_INVALID_PARAMETER            = 201;
    public const int ERR_INVALID_USER                 = 205;
    public const int ERR_FQDN_MISSING                 = 206;
    public const int ERR_ALREADY_LOGIN                = 207;
    public const int ERR_INVALID_USERNAME             = 208;
    public const int ERR_INVALID_FRIENDLY_NAME        = 209;
    public const int ERR_LIST_FULL                    = 210;
    public const int ERR_ALREADY_THERE                = 215;
    public const int ERR_NOT_ON_LIST                  = 216;
    public const int ERR_USER_NOT_ONLINE        = 217;
    public const int ERR_ALREADY_IN_THE_MODE          = 218;
    public const int ERR_ALREADY_IN_OPPOSITE_LIST     = 219;
    public const int ERR_SWITCHBOARD_FAILED           = 280;
    public const int ERR_NOTIFY_XFR_FAILED            = 281;
    public const int ERR_REQUIRED_FIELDS_MISSING      = 300;
    public const int ERR_NOT_LOGGED_IN                = 302;
    public const int ERR_INTERNAL_SERVER              = 500;
    public const int ERR_DB_SERVER                    = 501;
    public const int ERR_FILE_OPERATION               = 510;
    public const int ERR_MEMORY_ALLOC                 = 520;
    public const int ERR_SERVER_BUSY                  = 600;
    public const int ERR_SERVER_UNAVAILABLE           = 601;
    public const int ERR_PEER_NS_DOWN                 = 602;
    public const int ERR_DB_CONNECT                   = 603;
    public const int ERR_SERVER_GOING_DOWN            = 604;
    public const int ERR_CREATE_CONNECTION            = 707;
    public const int ERR_BLOCKING_WRITE               = 711;
    public const int ERR_SESSION_OVERLOAD             = 712;
    public const int ERR_USER_TOO_ACTIVE              = 713;
    public const int ERR_TOO_MANY_SESSIONS            = 714;
    public const int ERR_NOT_EXPECTED                 = 715;
    public const int ERR_BAD_FRIEND_FILE              = 717;
    public const int ERR_AUTHENTICATION_FAILED        = 911;
    public const int ERR_NOT_ALLOWED_WHEN_OFFLINE     = 913;
    public const int ERR_NOT_ACCEPTING_NEW_USERS      = 920;

    // Protocol Dialect Constants
    public const string DEFAULT_PROTOCOL = "MSNP2";

    // Policies
    public const string MD5 = "MD5";
    public const string CKI = "CKI";

    // Referral Types
    public const string NS = "NS";
    public const string SB = "SB";

    // Online substates
    public const string BSY = "BSY"; // Busy
    public const string IDL = "IDL"; // Idle
    public const string BRB = "BRB"; // Be right back
    public const string AWY = "AWY"; // Away from computer
    public const string PHN = "PHN"; // On the Phone
    public const string LUN = "LUN"; // Out to lunch

    // List types
    public const string FL = "FL"; // Forward List
    public const string RL = "RL"; // Reverse List
    public const string AL = "AL"; // Access List
    public const string BL = "BL"; // Block List

  }
}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.