using System; using System.IO; using System.Net; using System.Collections; using System.Diagnostics; namespace Lumpio.Net.Msn { public class MsnConversation { private MsnClient client; private MsnConnectionHandler sbconn; public ConnectionState state; private bool isConnected = false; public bool IsConnected { get { return isConnected; } } private UInt32 authtrid; public UInt32 AuthTrID { get { return authtrid; } } private MsnUserCollection users; public MsnUserCollection Users { get { return users; } } private ArrayList p2preceives; private ArrayList chunkedMessages; public event MsnConnectEventHandler Connected; public delegate void JoinEventHandler(object sender, JoinEventArgs args); public event JoinEventHandler UserJoined; public delegate void LeaveEventHandler(object sender, LeaveEventArgs args); public event LeaveEventHandler UserLeft; public delegate void MessageEventHandler(object sender, MessageEventArgs args); public event MessageEventHandler MessageReceived; public delegate void MessageConfirmationEventHandler(object sender, MessageConfirmationEventArgs args); public event MessageConfirmationEventHandler MessageConfirmed; public MsnConversation(MsnClient client, UInt32 authtrid) { this.client = client; this.authtrid = authtrid; this.users = new MsnUserCollection(); this.p2preceives = new ArrayList(); this.chunkedMessages = new ArrayList(); } public void Connect(IPEndPoint ep, bool invited, string authcode, string sessionid) { sbconn = new MsnConnectionHandler(); sbconn.Connect(ep); if (invited) { state = ConnectionState.ANS; sbconn.SendCommand("ANS " + sbconn.GetTrID() + " " + client.Passport + " " + authcode + " " + sessionid); } else { state = ConnectionState.USR; sbconn.SendCommand("USR " + sbconn.GetTrID() + " " + client.Passport + " " + authcode); } } public void Run() { if (state == ConnectionState.Invalid) { return; } MsnCommand cmd = null; MsnUser user = null; MsnMessage msg = null; while ((cmd = sbconn.PollCommand()) != null) { switch (state) { case ConnectionState.USR: switch (cmd.Code) { case "USR": if (cmd.Args[0] == "OK") { state = ConnectionState.Connected; isConnected = true; OnConnected(); } break; } break; case ConnectionState.ANS: switch (cmd.Code) { case "IRO": user = client.ContactList.GetByPassport(cmd.Args[2]); if (user == null) { user = new MsnUser(cmd.Args[2], cmd.Args[3], new Guid()); } Users.Add(user); OnUserJoined(user, true); break; case "ANS": if (cmd.Args[0] == "OK") { state = ConnectionState.Connected; isConnected = true; OnConnected(); } break; } break; case ConnectionState.Connected: switch (cmd.Code) { case "JOI": user = client.ContactList.GetByPassport(MsnClient.URLDecode(cmd.Args[0])); if (user == null) { user = new MsnUser(MsnClient.URLDecode(cmd.Args[0]), MsnClient.URLDecode(cmd.Args[1]), new Guid()); } Users.Add(user); OnUserJoined(user, false); break; case "BYE": user = users.GetByPassport(MsnClient.URLDecode(cmd.Args[0])); if (user != null) { Users.Remove(user); OnUserLeft(user, cmd.Args.Length == 2 && cmd.Args[1] == "1"); } break; case "MSG": msg = new MsnMessage(this, Users.GetByPassport(MsnClient.URLDecode(cmd.Args[0])), System.Text.Encoding.UTF8.GetBytes(cmd.Payload)); if (msg.Headers["Message-ID"] != null) { Console.WriteLine("MESSAGEID FOUND"); bool found = false; msg.MessageID = new Guid(msg.Headers["Message-ID"]); foreach (MsnMessage m in chunkedMessages) { if (m.MessageID.Equals(msg.MessageID)) { m.Body += msg.Body; m.ChunksLeft--; Console.WriteLine("{0} CHUNKS LEFT!!!!", m.ChunksLeft); if (m.ChunksLeft == 0) { //if (msg.Headers["Chunk"] == "1") { if (m.From != null) { Console.WriteLine("CHUUNNDDKKEEEDD MESSSAGEEE:" + m.Body); OnMessageReceived(m.From, m); } //} } found = true; } } if (!found) { msg.ChunksLeft = Int32.Parse(msg.Headers["Chunks"]) - 1; chunkedMessages.Add(msg); Console.WriteLine("NEW CHUNKED:::::"); } } else { if (msg.From != null) { OnMessageReceived(msg.From, msg); } } if (msg.MessageType == MsnMessageType.MsnP2P) { bool found = false; foreach (MsnP2PReceive pr in p2preceives) { byte[] data = null; if (pr.HandleCommand(msg, out data)) { found = true; if (data != null) { Debug.WriteLine(pr.Data.Length + " bytes P2P data received"); OnMessageReceived(null, msg = new MsnMessage(this, msg.From, data)); FileStream f = File.OpenWrite("p2p.gif"); f.Write(data, 0, data.Length); f.Close(); } break; } } if (!found) { byte[] data = null; MsnP2PReceive pr = new MsnP2PReceive(this); p2preceives.Add(pr); pr.HandleCommand(msg, out data); } } break; case "NAK": OnMessageConfirmed(false, cmd.TrID); break; case "ACK": OnMessageConfirmed(true, cmd.TrID); break; } break; } } } private MessageConfirmation defaultMessageConfirmation = MessageConfirmation.OnFailure; public MessageConfirmation DefaultMessageConfirmation { get { return defaultMessageConfirmation; } set { defaultMessageConfirmation = value; } } protected void OnConnected() { if (Connected != null) { Connected(this, EventArgs.Empty); } } /*public void Invite(string passport) { sbconn.SendCommand("CAL " + sbconn.GetTrID() + " " + MsnClient.URLEncode(passport)); }*/ public void Invite(string passport) { sbconn.SendCommand("CAL " + sbconn.GetTrID() + " " + MsnClient.URLEncode(passport)); } public void Invite(MsnUser user) { sbconn.SendCommand("CAL " + sbconn.GetTrID() + " " + MsnClient.URLEncode(user.Passport)); //this.users.Add(user); } protected void OnUserJoined(MsnUser user, bool initial) { if (UserJoined != null) { UserJoined(this, new JoinEventArgs(user, initial)); } } protected void OnUserLeft(MsnUser user, bool timeout) { if (UserLeft != null) { UserLeft(this, new LeaveEventArgs(user, timeout)); } } protected void OnMessageReceived(MsnUser user, MsnMessage msg) { if (MessageReceived != null) { MessageReceived(this, new MessageEventArgs(this, msg)); } } protected void OnMessageConfirmed(bool success, UInt32 trid) { if (MessageConfirmed != null) { MessageConfirmed(this, new MessageConfirmationEventArgs(success, trid)); } } /*public void RawBinaryMessage(string headers, byte[] binary, MessageConfirmation conf) { sbconn. }*/ public UInt32 RawMessage(string msg, MessageConfirmation conf) { UInt32 trid = sbconn.GetTrID(); sbconn.SendPayloadCommand("MSG " + trid + " " + EnumExtraDataAttribute.GetExtraDataFor(conf), msg); return trid; } public UInt32 RawMessage(string msg) { return RawMessage(msg, defaultMessageConfirmation); } public UInt32 PlainTextMessage(string text) { string message; message = "MIME-Version: 1.0\r\n" + "Content-Type: text/plain; charset=UTF-8\r\n\r\n" + text; return RawMessage(message); } public UInt32 NudgeMessage() { string message; message = "MIME-Version: 1.0\r\n" + "Content-Type: text/x-msnmsgr-datacast\r\n\r\n" + "ID: 1\r\n\r\n\r\n"; return RawMessage(message); } public UInt32 TypingNotifyMessage() { string message; message = "MIME-Version: 1.0\r\n" + "Content-Type: text/x-msmsgscontrol\r\n" + "TypingUser: " + MsnClient.URLEncode(client.Passport) + "\r\n\r\n\r\n"; return RawMessage(message); } public void InkMessage(byte[] data) { //const int charsPerChunk = 800; Guid msgid = Guid.NewGuid(); Debug.WriteLine(msgid.ToString()); string message; message = "MIME-Version: 1.0\r\n" + "Content-Type: image/gif\r\n\r\n" + "base64:" + Convert.ToBase64String(data); RawMessage(message); } public void GifMessage(byte[] bytes) { const int charsPerChunk = 1400; Guid msgid = Guid.NewGuid(); Debug.WriteLine(msgid.ToString()); string data = "base64:" + Convert.ToBase64String(bytes); int nchunks = (data.Length / charsPerChunk) + 1; string message; message = "MIME-Version: 1.0\r\n" + "Content-Type: image/gif\r\n"; //+ "base64:" + Convert.ToBase64String(data); if (data.Length < charsPerChunk) { RawMessage(message + "\r\n" + data); } else { string messageid = "{" + Guid.NewGuid().ToString() + "}"; for (int chunk = nchunks; chunk >= 1; --chunk) { if (chunk == nchunks) { message += "Message-ID: " + messageid + "\r\n" + "Chunks: " + nchunks + "\r\n\r\n"; } else { message = "Message-ID: " + messageid + "\r\n" + "Chunk: " + chunk + "\r\n\r\n"; } if (data.Length > charsPerChunk) { message += data.Substring(0, charsPerChunk); data = data.Substring(charsPerChunk); } else { message += data; } RawMessage(message); } } } public void Close() { sbconn.SendCommand("OUT"); sbconn.Close(); state = ConnectionState.Closed; } public enum ConnectionState { Invalid = 0, USR, ANS, Connected, Closed, } public enum MessageConfirmation { [EnumExtraData("U")] Never = 0, [EnumExtraData("N")] OnFailure, [EnumExtraData("A")] Always, } public class JoinEventArgs : EventArgs { private MsnUser user; public MsnUser User { get { return user; } } private bool initial; public bool Initial { get { return initial; } } public JoinEventArgs(MsnUser user, bool initial) { this.user = user; this.initial = initial; } } public class LeaveEventArgs : EventArgs { private MsnUser user; public MsnUser User { get { return user; } } private bool timeout; public bool Timeout { get { return timeout; } } public LeaveEventArgs(MsnUser user, bool timeout) { this.user = user; this.timeout = timeout; } } public class MessageEventArgs : EventArgs { /*private MsnUser user; public MsnUser User { get { return user; } } */ private MsnConversation conversation; public MsnConversation Conversation { get { return conversation; } } private MsnMessage message; public MsnMessage Message { get { return message; } } public MessageEventArgs(MsnConversation conversation, MsnMessage msg) { this.conversation = conversation; this.message = msg; } } public class MessageConfirmationEventArgs : EventArgs { private bool success; public bool Success { get { return success; } } private UInt32 trid; public UInt32 TrID { get { return trid; } } public MessageConfirmationEventArgs(bool success, UInt32 trid) { this.success = success; this.trid = trid; } } } class MsnP2PReceive { private MsnConversation convo; private int identifier; public int Identifier { get { return identifier; } /*set { identifier = value; }*/ } private MemoryStream stream; public byte[] Data { get { return stream.ToArray(); } /*set { data = value; }*/ } private int appID; public int AppID { get { return appID; } } public MsnP2PReceive(MsnConversation convo) { this.convo = convo; this.identifier = -1; this.stream = new MemoryStream(); //new byte[dataLength]; } public bool HandleCommand(MsnMessage msg, out byte[] data) { byte[] headerbytes = new byte[48]; Array.Copy(msg.BinaryBody, headerbytes, 48); MsnP2PHeader header = new MsnP2PHeader(headerbytes); data = null; if (identifier == -1) { identifier = header.identifier; appID = BitConverter.ToInt32(msg.BinaryBody, msg.BinaryBody.Length - 4); } else if (identifier != header.identifier) { return false; } Debug.WriteLine("GOT " + header.messageLength + " BYTES P2P DATA"); stream.Write(msg.BinaryBody, 48, header.messageLength); if (stream.Length == header.totalDataSize) { Debug.WriteLine(stream.Length + " P2P DATA RECEIVED"); data = this.Data; } return true; //return (stream.Length == header.totalDataLength); } } struct MsnP2PHeader { public int sessionID; public int identifier; public long dataOffset; public long totalDataSize; public int messageLength; public int flag; public int ackIdentifier; public int ackUniqueID; public long ackDataSize; public MsnP2PHeader(byte[] header) { if (header.Length != 48) { throw new ArgumentException("Header length different than 48 bytes!"); } MemoryStream stream = new MemoryStream(header); BinaryReader br = new BinaryReader(stream); sessionID = br.ReadInt32(); identifier = br.ReadInt32(); dataOffset = br.ReadInt64(); totalDataSize = br.ReadInt64(); messageLength = br.ReadInt32(); flag = br.ReadInt32(); ackIdentifier = br.ReadInt32(); ackUniqueID = br.ReadInt32(); ackDataSize = br.ReadInt64(); } public byte[] ToByteArray() { MemoryStream stream = new MemoryStream(); BinaryWriter bw = new BinaryWriter(stream); bw.Write(sessionID); bw.Write(identifier); bw.Write(dataOffset); bw.Write(totalDataSize); bw.Write(messageLength); bw.Write(flag); bw.Write(ackIdentifier); bw.Write(ackUniqueID); bw.Write(ackDataSize); return stream.ToArray(); } } }