/* * Galaxium Messenger * * Copyright (C) 2008 Philippe Durand <draekz@gmail.com> * * License: GNU General Public License (GPL) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ using System; using System.Collections.Generic; using System.Reflection; using System.Text; using System.Timers; using System.Net; using System.IO; using Anculus.Core; using Galaxium.Core; using Galaxium.Protocol; namespace Galaxium.Protocol.Msn { internal enum HTTPActions { Open, Poll, None }; internal enum HTTPServerTypes { NS, SB }; public class HTTPConnection : AbstractConnection { private HTTPActions _action = HTTPActions.None; private string _ip = String.Empty; private HTTPServerTypes _serverType = HTTPServerTypes.NS; private string _sessionID = String.Empty; private System.Timers.Timer _pollTimer; private bool _connected = false; private bool _sending = false; internal string IP { get { return _ip; } set { _ip = value; } } internal HTTPActions Action { get { return _action; } set { _action = value; } } internal HTTPServerTypes Type { get { return _serverType; } set { _serverType = value; } } public override bool IsConnected { get { return _connected; } } public override bool IsSending { get { return _sending; } } public HTTPConnection (ISession session, IConnectionInfo connectionInfo) : base (session, connectionInfo) { _pollTimer = new System.Timers.Timer (2000); _pollTimer.Elapsed += PollForData; _pollTimer.AutoReset = true; } public override void Connect () { Log.Debug ("Connect to {0}:{1}", _connectionInfo.HostName, _connectionInfo.Port); _intendedDisconnect = false; OnEstablished (new ConnectionEventArgs (this)); _connected = true; OnAfterConnect (new ConnectionEventArgs (this)); } protected override void Dispose (bool disposing) { if (!_disposed) { if (disposing) { if (IsConnected) { Log.Debug("Disconnecting during disposing of abstract connection."); Disconnect (); } } } _disposed = true; } public override void Reconnect () { Disconnect (); Connect (); } public override void Disconnect () { _intendedDisconnect = true; if (IsConnected) { _connected = false; if (_pollTimer.Enabled) _pollTimer.Stop(); OnClosed (new ConnectionEventArgs (this)); } } private string GenerateURI () { StringBuilder sb = new StringBuilder(); sb.Append ("http://"); sb.Append (ConnectionInfo.HostName); sb.Append ("/gateway/gateway.dll?"); switch (Action) { case HTTPActions.Open: sb.Append ("Action=open&"); sb.Append ("Server="+Type.ToString()+"&"); sb.Append ("IP="+IP); break; case HTTPActions.Poll: sb.Append ("Action=poll&"); sb.Append ("SessionID="+_sessionID); break; case HTTPActions.None: sb.Append ("SessionID="+_sessionID); break; } //Log.Debug ("Using URI: "+sb.ToString()); return (sb.ToString()); } public override void Send (byte[] data) { ThrowUtility.ThrowIfNull ("data", data); lock (_lock) { // We dont want to send the poll while sending other stuff. if (_pollTimer.Enabled) _pollTimer.Stop (); HttpWebRequest request = (HttpWebRequest) HttpWebRequest.Create (GenerateURI ()); if (Configuration.Proxy.Section.GetBool (Configuration.Proxy.UseProxy.Name, Configuration.Proxy.UseProxy.Default)) { // We should use the HTTP proxy. string host = Configuration.Proxy.Section.GetString (Configuration.Proxy.HttpHost.Name, Configuration.Proxy.HttpHost.Default); int port = Configuration.Proxy.Section.GetInt (Configuration.Proxy.HttpPort.Name, Configuration.Proxy.HttpPort.Default); string username = Configuration.Proxy.Section.GetString (Configuration.Proxy.HttpUsername.Name, Configuration.Proxy.HttpUsername.Default); string password = Configuration.Proxy.Section.GetString (Configuration.Proxy.HttpPassword.Name, Configuration.Proxy.HttpPassword.Default); request.Proxy = new WebProxy (host, port); request.Proxy.Credentials = new NetworkCredential (username, password); } request.Method = "POST"; request.Accept = "*/*"; request.AllowAutoRedirect = false; request.AllowWriteStreamBuffering = false; request.KeepAlive = true; request.UserAgent = "MSMSGS"; request.ContentType = "application/x-msn-messenger"; request.ContentLength = data.Length; request.Headers.Add ("Pragma", "no-cache"); Stream stream = request.GetRequestStream (); stream.Write (data, 0, data.Length); stream.Close (); _sending = true; request.BeginGetResponse(BeginGetResponseComplete, request); } } private void BeginGetResponseComplete (IAsyncResult ar) { bool wasSending = false; byte[] responseData = null; int responseLength = 0; lock (_lock) { if (ar != null) { HttpWebRequest request = (HttpWebRequest)ar.AsyncState; HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(ar); //Log.Debug ("Response Header: \n"+response.Headers.ToString()); foreach (string str in response.Headers.AllKeys) { switch (str) { case "Content-Length": responseLength = Int32.Parse(response.Headers.Get (str)); break; case "X-MSN-Messenger": string text = response.Headers.Get(str); string[] parts = text.Split(';'); foreach (string part in parts) { string[] elements = part.Split('='); switch (elements[0].Trim()) { case "SessionID": _sessionID = elements[1]; break; case "GW-IP": ConnectionInfo.HostName = elements[1]; break; case "Session": break; case "Action": break; } } break; } } responseData = new byte[responseLength]; int responseRead = 0; Stream responseStream = response.GetResponseStream(); while (responseRead < responseLength) { byte[] buf = new byte[256]; int read = responseStream.Read (buf, 0, buf.Length); Array.Copy(buf, 0, responseData, responseRead, read); responseRead += read; } responseStream.Close(); response.Close(); // This looks odd I know, but we have to only say we are done sending, once we have received // the data we are looking for. This is because the HTTP method does not like sending 2 // commands at once. wasSending = _sending; _sending = false; } } // Make sure the dispatch isn't within the lock statement ThreadUtility.SyncDispatch (new VoidDelegate (delegate { if (wasSending) OnSendComplete (new EventArgs ()); OnDataReceived (new ConnectionDataEventArgs (this, responseData, responseLength)); })); lock (_lock) { if ((!_sending) && (!_pollTimer.Enabled)) _pollTimer.Start (); } } private void PollForData (object sender, ElapsedEventArgs args) { Action = HTTPActions.Poll; Send(new byte[0]); } } }