PageRenderTime 28ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/MSNPSHARP_DEV/MSNPSharp/P2P/P2PHandler.New.cs

http://msnp-sharp.googlecode.com/
C# | 422 lines | 267 code | 94 blank | 61 comment | 68 complexity | 2640b62d8f0981d2543a86e356f8bad9 MD5 | raw file
  1. #region
  2. /*
  3. Copyright (c) 2002-2012, Bas Geertsema, Xih Solutions
  4. (http://www.xihsolutions.net), Thiago.Sayao, Pang Wu, Ethem Evlice, Andy Phan, Chang Liu.
  5. All rights reserved. http://code.google.com/p/msnp-sharp/
  6. Redistribution and use in source and binary forms, with or without
  7. modification, are permitted provided that the following conditions are met:
  8. * Redistributions of source code must retain the above copyright notice,
  9. this list of conditions and the following disclaimer.
  10. * Redistributions in binary form must reproduce the above copyright notice,
  11. this list of conditions and the following disclaimer in the documentation
  12. and/or other materials provided with the distribution.
  13. * Neither the names of Bas Geertsema or Xih Solutions nor the names of its
  14. contributors may be used to endorse or promote products derived from this
  15. software without specific prior written permission.
  16. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS'
  17. AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  18. IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  19. ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  20. LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  21. CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  22. SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  23. INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  24. CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  25. ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  26. THE POSSIBILITY OF SUCH DAMAGE.
  27. */
  28. #endregion
  29. using System;
  30. using System.IO;
  31. using System.Text;
  32. using System.Diagnostics;
  33. using System.Collections.Generic;
  34. namespace MSNPSharp.P2P
  35. {
  36. using MSNPSharp;
  37. using MSNPSharp.Apps;
  38. using MSNPSharp.Core;
  39. /// <summary>
  40. /// New P2P handler
  41. /// </summary>
  42. public class P2PHandler : IDisposable
  43. {
  44. #region Events
  45. /// <summary>
  46. /// Occurs when interaction with user has required for a file transfer, activity.
  47. /// Emoticons, display pictures and other msn objects are automatically accepted.
  48. /// </summary>
  49. public event EventHandler<P2PSessionEventArgs> InvitationReceived;
  50. protected internal virtual void OnInvitationReceived(P2PSessionEventArgs e)
  51. {
  52. if (InvitationReceived != null)
  53. InvitationReceived(this, e);
  54. }
  55. #endregion
  56. #region Members
  57. private SDGBridge sdgBridge;
  58. private SLPHandler slpHandler;
  59. private NSMessageHandler nsMessageHandler = null;
  60. private P2PMessagePool slpMessagePool = new P2PMessagePool();
  61. private List<P2PBridge> bridges = new List<P2PBridge>();
  62. private List<P2PSession> p2pV1Sessions = new List<P2PSession>();
  63. private List<P2PSession> p2pV2Sessions = new List<P2PSession>();
  64. protected internal P2PHandler(NSMessageHandler nsHandler)
  65. {
  66. this.nsMessageHandler = nsHandler;
  67. this.sdgBridge = new SDGBridge(nsHandler);
  68. this.slpHandler = new SLPHandler(nsHandler);
  69. }
  70. #endregion
  71. #region Properties
  72. public SDGBridge SDGBridge
  73. {
  74. get
  75. {
  76. return sdgBridge;
  77. }
  78. }
  79. #endregion
  80. #region Public
  81. #region RequestMsnObject & SendFile & AddTransfer
  82. public ObjectTransfer RequestMsnObject(Contact remoteContact, MSNObject msnObject)
  83. {
  84. ObjectTransfer objectTransferApp = new ObjectTransfer(msnObject, remoteContact);
  85. AddTransfer(objectTransferApp);
  86. return objectTransferApp;
  87. }
  88. public FileTransfer SendFile(Contact remoteContact, string filename, FileStream fileStream)
  89. {
  90. FileTransfer fileTransferApp = new FileTransfer(remoteContact, fileStream, Path.GetFileName(filename));
  91. AddTransfer(fileTransferApp);
  92. return fileTransferApp;
  93. }
  94. public P2PSession AddTransfer(P2PApplication app)
  95. {
  96. P2PSession session = new P2PSession(app);
  97. session.Closed += P2PSessionClosed;
  98. if (app.P2PVersion == P2PVersion.P2PV2)
  99. p2pV2Sessions.Add(session);
  100. else
  101. p2pV1Sessions.Add(session);
  102. session.Invite();
  103. return session;
  104. }
  105. #endregion
  106. #region ProcessP2PMessage
  107. public bool ProcessP2PMessage(P2PBridge bridge, Contact source, Guid sourceGuid, P2PMessage p2pMessage)
  108. {
  109. // 1) SLP BUFFERING: Combine splitted SLP messages
  110. if (slpMessagePool.BufferMessage(ref p2pMessage))
  111. {
  112. // * Buffering: Not completed yet, we must wait next packets -OR-
  113. // * Invalid packet received: Don't kill me, just ignore it...
  114. return true;
  115. }
  116. Trace.WriteLineIf(Settings.TraceSwitch.TraceVerbose,
  117. String.Format("Received P2PMessage from {0}\r\n{1}", bridge.ToString(), p2pMessage.ToDebugString()), GetType().Name);
  118. // 2) CHECK SLP: Check destination, source, endpoints
  119. SLPMessage slp = p2pMessage.IsSLPData ? p2pMessage.InnerMessage as SLPMessage : null;
  120. if (slp != null)
  121. {
  122. if (!slpHandler.CheckSLPMessage(bridge, source, sourceGuid, p2pMessage, slp))
  123. return true; // HANDLED, This SLP is not for us.
  124. }
  125. // 3) FIRST SLP MESSAGE: Create applications/sessions based on invitation
  126. if (slp != null && slp is SLPRequestMessage &&
  127. (slp as SLPRequestMessage).Method == "INVITE" &&
  128. slp.ContentType == "application/x-msnmsgr-sessionreqbody")
  129. {
  130. uint appId = slp.BodyValues.ContainsKey("AppID") ? uint.Parse(slp.BodyValues["AppID"].Value) : 0;
  131. Guid eufGuid = slp.BodyValues.ContainsKey("EUF-GUID") ? new Guid(slp.BodyValues["EUF-GUID"].Value) : Guid.Empty;
  132. P2PVersion ver = slp.P2PVersion;
  133. if (P2PApplication.IsRegistered(eufGuid, appId))
  134. {
  135. P2PSession newSession = FindSessionByCallId(slp.CallId, ver);
  136. if (newSession == null)
  137. {
  138. newSession = new P2PSession(slp as SLPRequestMessage, p2pMessage, nsMessageHandler, bridge);
  139. newSession.Closed += P2PSessionClosed;
  140. if (newSession.Version == P2PVersion.P2PV2)
  141. p2pV2Sessions.Add(newSession);
  142. else
  143. p2pV1Sessions.Add(newSession);
  144. }
  145. else
  146. {
  147. // P2PSession exists, bridge changed...
  148. if (newSession.Bridge != bridge)
  149. {
  150. // BRIDGETODO
  151. }
  152. }
  153. return true;
  154. }
  155. // Not registered application. Decline it without create a new session...
  156. slpHandler.SendSLPStatus(bridge, p2pMessage, source, sourceGuid, 603, "Decline");
  157. return true;
  158. }
  159. // 4) FIND SESSION: Search session by SessionId/ExpectedIdentifier
  160. P2PSession session = FindSession(p2pMessage, slp);
  161. if (session != null)
  162. {
  163. // ResetTimeoutTimer();
  164. // Keep track of theremoteIdentifier
  165. // Keep track of the remote identifier
  166. session.remoteIdentifier = (p2pMessage.Version == P2PVersion.P2PV2) ?
  167. p2pMessage.Header.Identifier + p2pMessage.Header.MessageSize :
  168. p2pMessage.Header.Identifier;
  169. // Session SLP
  170. if (slp != null && slpHandler.HandleP2PSessionSignal(bridge, p2pMessage, slp, session))
  171. return true;
  172. // Session Data
  173. if (slp == null && session.ProcessP2PData(bridge, p2pMessage))
  174. return true;
  175. }
  176. return false;
  177. }
  178. #endregion
  179. #region Dispose
  180. public void Dispose()
  181. {
  182. lock (slpMessagePool)
  183. slpMessagePool.Clear();
  184. lock (p2pV1Sessions)
  185. p2pV1Sessions.Clear();
  186. lock (p2pV2Sessions)
  187. p2pV2Sessions.Clear();
  188. lock (bridges)
  189. bridges.Clear();
  190. sdgBridge.Dispose();
  191. slpHandler.Dispose();
  192. }
  193. #endregion
  194. #endregion
  195. #region Internal & Protected
  196. #region GetBridge & BridgeClosed
  197. internal P2PBridge GetBridge(P2PSession session)
  198. {
  199. foreach (P2PBridge existing in bridges)
  200. if (existing.SuitableFor(session))
  201. return existing;
  202. return nsMessageHandler.SDGBridge;
  203. /*MSNP21TODO
  204. P2PBridge bridge = new SBBridge(session);
  205. bridge.BridgeClosed += BridgeClosed;
  206. bridges.Add(bridge);
  207. return bridge;
  208. * */
  209. }
  210. private void BridgeClosed(object sender, EventArgs args)
  211. {
  212. P2PBridge bridge = sender as P2PBridge;
  213. if (!bridges.Contains(bridge))
  214. {
  215. Trace.WriteLineIf(Settings.TraceSwitch.TraceWarning, "Closed bridge not found in list", GetType().Name);
  216. return;
  217. }
  218. bridges.Remove(bridge);
  219. }
  220. #endregion
  221. #endregion
  222. #region Private
  223. #region FindSession & P2PSessionClosed
  224. private P2PSession FindSessionByCallId(Guid callId, P2PVersion version)
  225. {
  226. List<P2PSession> sessions = (version == P2PVersion.P2PV2) ? p2pV2Sessions : p2pV1Sessions;
  227. foreach (P2PSession session in sessions)
  228. {
  229. if (session.Invitation.CallId == callId)
  230. return session;
  231. }
  232. return null;
  233. }
  234. private P2PSession FindSessionBySessionId(uint sessionId, P2PVersion version)
  235. {
  236. List<P2PSession> sessions = (version == P2PVersion.P2PV2) ? p2pV2Sessions : p2pV1Sessions;
  237. foreach (P2PSession session in sessions)
  238. {
  239. if (session.SessionId == sessionId)
  240. return session;
  241. }
  242. return null;
  243. }
  244. private P2PSession FindSessionByExpectedIdentifier(P2PMessage p2pMessage)
  245. {
  246. if (p2pMessage.Version == P2PVersion.P2PV2)
  247. {
  248. foreach (P2PSession session in p2pV2Sessions)
  249. {
  250. uint expected = session.RemoteIdentifier;
  251. if (p2pMessage.Header.Identifier == expected)
  252. return session;
  253. }
  254. }
  255. else
  256. {
  257. foreach (P2PSession session in p2pV1Sessions)
  258. {
  259. uint expected = session.RemoteIdentifier + 1;
  260. if (expected == session.RemoteBaseIdentifier)
  261. expected++;
  262. if (p2pMessage.Header.Identifier == expected)
  263. return session;
  264. }
  265. }
  266. return null;
  267. }
  268. private P2PSession FindSession(P2PMessage msg, SLPMessage slp)
  269. {
  270. P2PSession p2pSession = null;
  271. uint sessionID = (msg != null) ? msg.Header.SessionId : 0;
  272. if ((sessionID == 0) && (slp != null))
  273. {
  274. if (slp.BodyValues.ContainsKey("SessionID"))
  275. {
  276. if (!uint.TryParse(slp.BodyValues["SessionID"].Value, out sessionID))
  277. {
  278. Trace.WriteLineIf(Settings.TraceSwitch.TraceWarning,
  279. "Unable to parse SLP message SessionID", GetType().Name);
  280. sessionID = 0;
  281. }
  282. }
  283. if (sessionID == 0)
  284. {
  285. // We don't get a session ID in BYE requests
  286. // So we need to find the session by its call ID
  287. P2PVersion p2pVersion = slp.P2PVersion;
  288. p2pSession = FindSessionByCallId(slp.CallId, p2pVersion);
  289. if (p2pSession != null)
  290. return p2pSession;
  291. }
  292. }
  293. // Sometimes we only have a messageID to find the session with...
  294. if ((sessionID == 0) && (msg.Header.Identifier != 0))
  295. {
  296. p2pSession = FindSessionByExpectedIdentifier(msg);
  297. if (p2pSession != null)
  298. return p2pSession;
  299. }
  300. if (sessionID != 0)
  301. {
  302. p2pSession = FindSessionBySessionId(sessionID, msg.Version);
  303. if (p2pSession != null)
  304. return p2pSession;
  305. }
  306. return null;
  307. }
  308. private void P2PSessionClosed(object sender, ContactEventArgs args)
  309. {
  310. P2PSession session = sender as P2PSession;
  311. session.Closed -= P2PSessionClosed;
  312. Trace.WriteLineIf(Settings.TraceSwitch.TraceVerbose,
  313. String.Format("P2PSession {0} closed, removing", session.SessionId), GetType().Name);
  314. if (session.Version == P2PVersion.P2PV2)
  315. p2pV2Sessions.Remove(session);
  316. else
  317. p2pV1Sessions.Remove(session);
  318. session.Dispose();
  319. }
  320. #endregion
  321. #endregion
  322. }
  323. };