/*
* (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.Threading;
namespace MSNP{
/// <summary>
/// The main helper class which provides access to the underlying MSNP protocol API.
/// </summary>
/// <remarks>
/// This is the main public class that users of the API use to connect and work with
/// the MSNP protocol.
/// </remarks>
public class MSNPHelper
{
/***************************************************************
* Enumeration
***************************************************************/
/// <summary>
/// Enumeration of main contact states.
/// </summary>
public enum ContactState
{
/// <summary>Online, ready to receive messages </summary>
Online,
/// <summary>Offline, not receiving messages</summary>
Offline,
/// <summary>Hidden, online, but appear offline to everyone else</summary>
Hidden,
}
/// <summary>
/// Enumeration of contact substates (busy, idle, etc)
/// </summary>
public enum ContactSubstate
{
/// <summary>Fully online, ready to receive messages</summary>
Online,
/// <summary>Online, but busy, might not respond right away</summary>
Busy,
/// <summary>Online, but idle, doing something else</summary>
Idle,
/// <summary>Online, but doing something else for a short period of time</summary>
BeRightBack,
/// <summary>Away from computer. I have a life</summary>
AwayFromComputer,
/// <summary>On the phone, someone must love me or want my money</summary>
OnThePhone,
/// <summary>Out to lunch, even super heroes have to eat</summary>
OutToLunch,
/// <summary>Offline, no substate necessary</summary>
None,
}
/***************************************************************
* Fields
***************************************************************/
String m_Host;
String m_User;
String m_Pass;
int m_Port;
bool m_SignedIn = false;
AutoResetEvent m_WaitEvent;
String m_DSXFRHost;
int m_DSXFRPort;
NotificationConnectionHandler m_NCH;
ISessionHandler m_SessionHandler;
/***************************************************************
* Delegates and Events
***************************************************************/
/// <summary>
/// The main event delegate used for events thrown by the MSNPHelper.
/// </summary>
public delegate void MSNPEventHandler(object sender, MSNPEventArgs me);
/// <summary>
/// Thrown when a contact in your contact list changes state (online/offline, etc).
/// </summary>
/// <remarks>
/// The <see cref="MSNPEventArgs.Data"/> property for this event will be the <see cref="Contact"/>
/// object representing the user whose state has changed.
/// </remarks>
public event MSNPEventHandler ContactStateChangedEvent;
/***************************************************************
* Constructors
***************************************************************/
/// <summary>
/// Main constructor for the helper
/// </summary>
/// <remarks>
/// No actual connections are made or actions performed in the constructor. Use <see cref="Signin"/> to begin an MSNP connection.
/// </remarks>
/// <param name="host">The DNS name or IP address of the MSN Dispatch Server to which the initial connection should be made.</param>
/// <param name="port">The 32-bit TCP port on which to connect to the MSN Dispatch Server</param>
/// <param name="user">The user handle with which to authenticate to the MSN system.</param>
/// <param name="pass">The password for the user</param>
/// <param name="handler">An implemented <see cref="ISessionHandler"/> for handing new sessions either incoming or outgoing.</param>
public MSNPHelper(String host, int port, String user, String pass, ISessionHandler handler)
{
Debug.AutoFlush = true;
m_Host = host;
m_Port = port;
m_User = user;
m_Pass = pass;
m_WaitEvent = new AutoResetEvent(false);
m_SessionHandler = handler;
}
/// <summary>
/// Convenience constructor which uses the default MSN port.
/// </summary>
/// <remarks>
/// No actual connections are made or actions performed in the constructor. Use <see cref="Signin"/> to begin an MSNP connection.
/// </remarks>
/// <param name="host">The DNS name or IP address of the MSN Dispatch Server to which the initial connection should be made.</param>
/// <param name="user">The user handle with which to authenticate to the MSN system.</param>
/// <param name="pass">The password for the user</param>
/// <param name="handler">An implemented <see cref="MSNP.ISessionHandler"/> for handing new sessions either incoming or outgoing.</param>
public MSNPHelper(String host, String user, String pass, ISessionHandler handler) :
this(host, ConnectionHandler.MSN_PORT, user, pass, handler){}
/***************************************************************
* Properties
***************************************************************/
/// <summary>
/// Returns a collection of contacts in the Forward List (main contact list)
/// </summary>
/// <remarks>
/// Note that this is a clone of the actual list. Modifications to the collection
/// or to the <see cref="Contact"/> objects therein will not be reflected anywhere else.
/// </remarks>
/// <value>A collection of <see cref="MSNP.Contact"/> objects</value>
public ICollection FLContacts
{
get
{
if(! m_SignedIn )
return new ArrayList();
else
return m_NCH.FLContacts;
}
}
/// <summary>
/// The session timeout
/// </summary>
/// <remarks>This determines when a session will close itself after the last person has left.</remarks>
/// <value>The timeout in milliseconds</value>
public int SessionTimeout
{
get{ return SwitchboardConnectionHandler.SessionTimeout; }
set{ SwitchboardConnectionHandler.SessionTimeout = value; }
}
/***************************************************************
* Public Methods
***************************************************************/
/// <summary>
/// Signs into the MSN system.
/// </summary>
/// <remarks>
/// This is a synchronous method and will block while connecting.
/// </remarks>
public void Signin()
{
if( ! m_SignedIn )
{
DispatchConnectionHandler dch = new DispatchConnectionHandler(m_Host, m_Port, m_User, m_Pass);
dch.XFREvent += new EventHandler(this.GotDSXFREvent);
dch.Start();
m_WaitEvent.WaitOne();
m_WaitEvent.Reset();
Debug.WriteLine("Done with DCH Connection");
m_NCH = new NotificationConnectionHandler(m_DSXFRHost, m_DSXFRPort, m_User, m_Pass, m_SessionHandler);
m_NCH.OnlineEvent += new EventHandler(this.GotOnlineEvent);
m_NCH.SignedOutEvent += new EventHandler(this.GotSignedOutEvent);
m_NCH.ContactStateChangedEvent += new NotificationConnectionHandler.NotificationEventHandler(this.GotContactStateChanged);
m_NCH.Start();
m_WaitEvent.WaitOne();
m_WaitEvent.Reset();
Debug.WriteLine("Signed in! Contacts: " + m_NCH.FLContacts);
m_SignedIn = true;
}
else
{
throw new Exception("Already signed in");
}
}
/// <summary>
/// Signs out and ends the connection to the MSN system.
/// </summary>
/// <remarks>
/// This is a synchronous method which will block until the signout is complete.
/// </remarks>
public void Signout()
{
if( m_SignedIn )
{
m_NCH.Stop();
m_WaitEvent.WaitOne();
m_SignedIn = false;
}
else
{
throw new Exception("Not signed in");
}
}
/// <summary>
/// Establishes a new session with the specified user.
/// </summary>
/// <remarks>
/// <para>
/// This method is asynchronous and will submit the request and return immediately.
/// The <see cref="ISessionHandler"/> will be notified when the session is successfully established.
/// The <paramref name="sessionIdentifier"/> will allow the caller to identify this session from others.
/// It is the caller's responsibility to ensure that the <paramref name="sessionIdentifier"/> is unique or useful.
/// The <paramref name="userHandle"/> specified to join the session may not join immediately. The session will be
/// established and the call will be placed to the user. Due to Internet latency and other issues, it's possible
/// this user may take some time to join or may never join at all. It is the <see cref="ISessionHandler"/> 's
/// responsibility to ensure that these cases are dealt with properly.</para>
/// <para>
/// If the invited user does not connect within the specified timeout period (default 30 seconds), the session
/// will terminate automatically.</para>
/// <para>
/// This method is thread-safe.
/// </para>
/// </remarks>
/// <param name="userHandle">The handle (email address) of the user with which to establish the session.</param>
/// <param name="sessionIdentifier">An identifier which will be placed on the session</param>
public void RequestSession(String userHandle, object sessionIdentifier)
{
if( m_SignedIn )
m_NCH.StartSBSession(userHandle, sessionIdentifier);
else
throw new Exception("Not signed in");
}
/// <summary>
/// Changes the way this user appears to other users
/// </summary>
/// <param name="state">The state to set</param>
/// <param name="substate">The substate to set</param>
public void ChangeState(ContactState state, ContactSubstate substate)
{
if( m_SignedIn )
{
switch( (int) state )
{
case (int) ContactState.Online:
switch( (int) substate )
{
case (int) ContactSubstate.Online:
m_NCH.ChangeState(ConnectionHandler.NLN, null);
break;
case (int) ContactSubstate.Busy:
m_NCH.ChangeState(ConnectionHandler.NLN, ConnectionHandler.BSY);
break;
case (int) ContactSubstate.Idle:
m_NCH.ChangeState(ConnectionHandler.NLN, ConnectionHandler.IDL);
break;
case (int) ContactSubstate.BeRightBack:
m_NCH.ChangeState(ConnectionHandler.NLN, ConnectionHandler.BRB);
break;
case (int) ContactSubstate.AwayFromComputer:
m_NCH.ChangeState(ConnectionHandler.NLN, ConnectionHandler.AWY);
break;
case (int) ContactSubstate.OnThePhone:
m_NCH.ChangeState(ConnectionHandler.NLN, ConnectionHandler.PHN);
break;
case (int) ContactSubstate.OutToLunch:
m_NCH.ChangeState(ConnectionHandler.NLN, ConnectionHandler.LUN);
break;
}
break;
case (int) ContactState.Offline:
m_NCH.ChangeState(ConnectionHandler.FLN, null);
break;
case (int) ContactState.Hidden:
m_NCH.ChangeState(ConnectionHandler.HDN, null);
break;
}
}
else
throw new Exception("Not signed in");
}
/***************************************************************
* Private Methods
***************************************************************/
private void GotDSXFREvent(object sender, EventArgs args)
{
DispatchConnectionHandler dch = (DispatchConnectionHandler) sender;
Debug.WriteLine("Got XFR event! XFR To: " + dch.XFRHost + ":" + dch.XFRPort);
m_DSXFRHost = dch.XFRHost;
m_DSXFRPort = dch.XFRPort;
m_WaitEvent.Set();
}
private void GotOnlineEvent(object sender, EventArgs args)
{
m_WaitEvent.Set();
}
private void GotSignedOutEvent(object sender, EventArgs args)
{
m_WaitEvent.Set();
}
private void GotContactStateChanged(object sender, MSNPEventArgs args)
{
if( ContactStateChangedEvent != null )
ContactStateChangedEvent(this, args);
}
}
}
|