PageRenderTime 44ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/MSNPSHARP_DEV/MSNPSharp/NSMessageHandler.MSNP21.cs

http://msnp-sharp.googlecode.com/
C# | 1927 lines | 1373 code | 384 blank | 170 comment | 317 complexity | 0724eb27bab0f61a6387a37595e18c21 MD5 | raw file

Large files files are truncated, but you can click here to view the full 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.Net;
  32. using System.Xml;
  33. using System.Web;
  34. using System.Text;
  35. using System.Drawing;
  36. using System.Diagnostics;
  37. using System.Collections.Generic;
  38. namespace MSNPSharp
  39. {
  40. using MSNPSharp.Core;
  41. using MSNPSharp.P2P;
  42. using MSNPSharp.Apps;
  43. partial class NSMessageHandler
  44. {
  45. #region Public Events
  46. /// <summary>
  47. /// Occurs when any contact changes status.
  48. /// </summary>
  49. public event EventHandler<ContactStatusChangedEventArgs> ContactStatusChanged;
  50. protected internal virtual void OnContactStatusChanged(ContactStatusChangedEventArgs e)
  51. {
  52. if (ContactStatusChanged != null)
  53. ContactStatusChanged(this, e);
  54. }
  55. /// <summary>
  56. /// Occurs when any contact goes from offline status to another status.
  57. /// </summary>
  58. public event EventHandler<ContactStatusChangedEventArgs> ContactOnline;
  59. protected internal virtual void OnContactOffline(ContactStatusChangedEventArgs e)
  60. {
  61. if (ContactOffline != null)
  62. ContactOffline(this, e);
  63. Trace.WriteLineIf(Settings.TraceSwitch.TraceVerbose,
  64. e.Contact.ToString() + " goes to " + e.NewStatus + " from " + e.OldStatus + (e.Via == null ? String.Empty : " via=" + e.Via.ToString()) + "\r\n", GetType().Name);
  65. }
  66. /// <summary>
  67. /// Occurs when any contact goes from any status to offline status.
  68. /// </summary>
  69. public event EventHandler<ContactStatusChangedEventArgs> ContactOffline;
  70. protected internal virtual void OnContactOnline(ContactStatusChangedEventArgs e)
  71. {
  72. if (ContactOnline != null)
  73. ContactOnline(this, e);
  74. Trace.WriteLineIf(Settings.TraceSwitch.TraceVerbose,
  75. e.Contact.ToString() + " goes to " + e.NewStatus + " from " + e.OldStatus + (e.Via == null ? String.Empty : " via=" + e.Via.ToString()) + "\r\n", GetType().Name);
  76. }
  77. /// <summary>
  78. /// Occurs when a user is typing.
  79. /// </summary>
  80. public event EventHandler<TypingArrivedEventArgs> TypingMessageReceived;
  81. protected virtual void OnTypingMessageReceived(TypingArrivedEventArgs e)
  82. {
  83. if (TypingMessageReceived != null)
  84. TypingMessageReceived(this, e);
  85. Trace.WriteLineIf(Settings.TraceSwitch.TraceVerbose,
  86. "TYPING: " + e.OriginalSender.ToString() + (e.Sender == e.OriginalSender ? String.Empty : ";by=" + e.Sender.ToString()));
  87. }
  88. /// <summary>
  89. /// Occurs when we receive a nudge message by a user.
  90. /// </summary>
  91. public event EventHandler<NudgeArrivedEventArgs> NudgeReceived;
  92. protected virtual void OnNudgeReceived(NudgeArrivedEventArgs e)
  93. {
  94. if (NudgeReceived != null)
  95. NudgeReceived(this, e);
  96. Trace.WriteLineIf(Settings.TraceSwitch.TraceVerbose,
  97. "NUDGE: " + e.OriginalSender + (e.Sender == e.OriginalSender ? String.Empty : ";by=" + e.Sender.ToString()));
  98. }
  99. /// <summary>
  100. /// Occurs when we receive a text message from a user.
  101. /// </summary>
  102. public event EventHandler<TextMessageArrivedEventArgs> TextMessageReceived;
  103. protected virtual void OnTextMessageReceived(TextMessageArrivedEventArgs e)
  104. {
  105. if (TextMessageReceived != null)
  106. TextMessageReceived(this, e);
  107. Trace.WriteLineIf(Settings.TraceSwitch.TraceVerbose,
  108. "TEXT MESSAGE: " + e.OriginalSender.ToString() + (e.Sender == e.OriginalSender ? String.Empty : ";by=" + e.Sender.ToString()) + "\r\n" + e.TextMessage.ToDebugString());
  109. }
  110. /// <summary>
  111. /// Fired when a contact sends a emoticon definition.
  112. /// </summary>
  113. public event EventHandler<EmoticonDefinitionEventArgs> EmoticonDefinitionReceived;
  114. protected virtual void OnEmoticonDefinitionReceived(EmoticonDefinitionEventArgs e)
  115. {
  116. if (EmoticonDefinitionReceived != null)
  117. EmoticonDefinitionReceived(this, e);
  118. }
  119. /// <summary>
  120. /// Fired when a contact sends a wink definition.
  121. /// </summary>
  122. public event EventHandler<WinkEventArgs> WinkDefinitionReceived;
  123. protected virtual void OnWinkDefinitionReceived(WinkEventArgs e)
  124. {
  125. if (WinkDefinitionReceived != null)
  126. WinkDefinitionReceived(this, e);
  127. }
  128. /// <summary>
  129. /// Occurs when a multiparty chat created remotely. Owner is joined automatically by the library.
  130. /// </summary>
  131. public event EventHandler<MultipartyCreatedEventArgs> MultipartyCreatedRemotely;
  132. protected virtual void OnMultipartyCreatedRemotely(MultipartyCreatedEventArgs e)
  133. {
  134. if (MultipartyCreatedRemotely != null)
  135. MultipartyCreatedRemotely(this, e);
  136. }
  137. /// <summary>
  138. /// Occurs when a contact joined the group chat.
  139. /// </summary>
  140. public event EventHandler<GroupChatParticipationEventArgs> JoinedGroupChat;
  141. protected virtual void OnJoinedGroupChat(GroupChatParticipationEventArgs e)
  142. {
  143. Trace.WriteLineIf(Settings.TraceSwitch.TraceVerbose,
  144. e.Contact + " joined group chat " + e.Via.ToString(), GetType().Name);
  145. if (JoinedGroupChat != null)
  146. JoinedGroupChat(this, e);
  147. }
  148. /// <summary>
  149. /// Occurs when a contact left the group chat.
  150. /// </summary>
  151. public event EventHandler<GroupChatParticipationEventArgs> LeftGroupChat;
  152. protected virtual void OnLeftGroupChat(GroupChatParticipationEventArgs e)
  153. {
  154. if (LeftGroupChat != null)
  155. LeftGroupChat(this, e);
  156. Trace.WriteLineIf(Settings.TraceSwitch.TraceVerbose,
  157. e.Contact + " left group chat " + e.Via.ToString(), GetType().Name);
  158. }
  159. /// <summary>
  160. /// Occurs after the user on another end point closed the IM window.
  161. /// </summary>
  162. public event EventHandler<CloseIMWindowEventArgs> RemoteEndPointCloseIMWindow;
  163. protected virtual void OnRemoteEndPointCloseIMWindow(CloseIMWindowEventArgs e)
  164. {
  165. if (RemoteEndPointCloseIMWindow != null)
  166. RemoteEndPointCloseIMWindow(this, e);
  167. if (e.Sender != null && e.SenderEndPoint != null)
  168. {
  169. string partiesString = string.Empty;
  170. foreach (Contact party in e.Parties)
  171. {
  172. partiesString += party.ToString() + "\r\n";
  173. }
  174. Trace.WriteLineIf(Settings.TraceSwitch.TraceVerbose,
  175. "User at End Point: " + e.SenderEndPoint.ToString() + " has closed the IM window.\r\n" +
  176. "Parties in the conversation: \r\n" +
  177. partiesString);
  178. }
  179. }
  180. #endregion
  181. #region MULTIPARTY
  182. #region CreateMultiparty
  183. internal class MultipartyObject
  184. {
  185. public event EventHandler<MultipartyCreatedEventArgs> MultipartyCreatedLocally;
  186. public int TransactionID;
  187. public List<string> InviteQueueHash;
  188. public Contact MultiParty;
  189. public MultipartyObject(int transId, List<string> inviteQueueHash, Contact multiParty,
  190. EventHandler<MultipartyCreatedEventArgs> onCreated)
  191. {
  192. TransactionID = transId;
  193. InviteQueueHash = new List<string>(inviteQueueHash);
  194. MultiParty = multiParty;
  195. if (onCreated != null)
  196. MultipartyCreatedLocally += onCreated;
  197. }
  198. internal void OnMultipartyCreatedLocally(object sender, MultipartyCreatedEventArgs e)
  199. {
  200. if (MultipartyCreatedLocally != null)
  201. {
  202. MultipartyCreatedLocally(sender, e);
  203. MultipartyCreatedLocally -= OnMultipartyCreatedLocally;
  204. }
  205. }
  206. };
  207. /// <summary>
  208. /// Creates a new multiparty (Group chat)
  209. /// </summary>
  210. /// <param name="inviteQueue">Contacts to be invited (don't add yourself)</param>
  211. /// <param name="onCreated">The handler to be executed when multiparty created (must be provided)</param>
  212. /// <exception cref="ArgumentNullException">inviteQueue or event handler is null</exception>
  213. /// <exception cref="InvalidOperationException">At least 2 contacts is required except you and contacts must support multiparty</exception>
  214. /// <returns>Transaction ID</returns>
  215. public int CreateMultiparty(List<Contact> inviteQueue, EventHandler<MultipartyCreatedEventArgs> onCreated)
  216. {
  217. if (inviteQueue == null || inviteQueue.Count == 0)
  218. throw new ArgumentNullException("inviteQueue");
  219. if (onCreated == null)
  220. throw new ArgumentNullException("onCreated");
  221. List<string> newQueue = new List<string>();
  222. foreach (Contact c in inviteQueue)
  223. {
  224. if (c != null &&
  225. !c.IsSibling(Owner) &&
  226. !newQueue.Contains(c.SiblingString) &&
  227. c.SupportsMultiparty)
  228. {
  229. newQueue.Add(c.SiblingString);
  230. }
  231. }
  232. if (newQueue.Count < 2)
  233. throw new InvalidOperationException("At least 2 contacts is required except you and contacts must support multiparty.");
  234. NSMessageProcessor nsmp = (NSMessageProcessor)MessageProcessor;
  235. int transId = nsmp.IncreaseTransactionID();
  236. lock (multiParties)
  237. multiParties[transId] = new MultipartyObject(transId, newQueue, null, onCreated);
  238. string to = ((int)IMAddressInfoType.TemporaryGroup).ToString() + ":" + Guid.Empty.ToString("D").ToLowerInvariant() + "@" + Contact.DefaultHostDomain;
  239. string from = ((int)Owner.ClientType).ToString() + ":" + Owner.Account;
  240. MultiMimeMessage mmMessage = new MultiMimeMessage(to, from);
  241. mmMessage.RoutingHeaders[MIMERoutingHeaders.From][MIMERoutingHeaders.EPID] = MachineGuid.ToString("B").ToLowerInvariant();
  242. mmMessage.ContentKey = MIMEContentHeaders.Publication;
  243. mmMessage.ContentHeaders[MIMEContentHeaders.URI] = "/circle";
  244. mmMessage.ContentHeaders[MIMEContentHeaders.ContentType] = "application/multiparty+xml";
  245. mmMessage.InnerBody = new byte[0];
  246. NSMessage putPayload = new NSMessage("PUT");
  247. putPayload.InnerMessage = mmMessage;
  248. nsmp.SendMessage(putPayload, transId);
  249. return transId;
  250. }
  251. #endregion
  252. #region GetMultiparty
  253. public Contact GetMultiparty(string tempGroupAddress)
  254. {
  255. if (!String.IsNullOrEmpty(tempGroupAddress))
  256. {
  257. lock (multiParties)
  258. {
  259. foreach (MultipartyObject group in multiParties.Values)
  260. {
  261. if (group.MultiParty != null && group.MultiParty.Account == tempGroupAddress)
  262. return group.MultiParty;
  263. }
  264. }
  265. }
  266. return null;
  267. }
  268. internal MultipartyObject GetMultipartyObject(string tempGroupAddress)
  269. {
  270. if (!String.IsNullOrEmpty(tempGroupAddress))
  271. {
  272. lock (multiParties)
  273. {
  274. foreach (MultipartyObject group in multiParties.Values)
  275. {
  276. if (group.MultiParty != null && group.MultiParty.Account == tempGroupAddress)
  277. return group;
  278. }
  279. }
  280. }
  281. return null;
  282. }
  283. #endregion
  284. #region InviteContactToMultiparty
  285. public void InviteContactToMultiparty(Contact contact, Contact group)
  286. {
  287. string to = ((int)group.ClientType).ToString() + ":" + group.Account;
  288. string from = ((int)Owner.ClientType).ToString() + ":" + Owner.Account;
  289. MultiMimeMessage mmMessage = new MultiMimeMessage(to, from);
  290. mmMessage.RoutingHeaders[MIMERoutingHeaders.From][MIMERoutingHeaders.EPID] = MachineGuid.ToString("B").ToLowerInvariant();
  291. mmMessage.RoutingHeaders[MIMERoutingHeaders.To][MIMERoutingHeaders.Path] = "IM";
  292. mmMessage.ContentKey = MIMEContentHeaders.Publication;
  293. mmMessage.ContentHeaders[MIMEContentHeaders.URI] = "/circle";
  294. mmMessage.ContentHeaders[MIMEContentHeaders.ContentType] = "application/multiparty+xml";
  295. string xml = "<circle><roster><id>IM</id><user><id>" + ((int)contact.ClientType).ToString() + ":" + contact.Account + "</id></user></roster></circle>";
  296. mmMessage.InnerBody = Encoding.UTF8.GetBytes(xml);
  297. NSMessage putPayload = new NSMessage("PUT");
  298. putPayload.InnerMessage = mmMessage;
  299. MessageProcessor.SendMessage(putPayload);
  300. }
  301. #region LeaveMultiparty
  302. public void LeaveMultiparty(Contact group)
  303. {
  304. string to = ((int)group.ClientType).ToString() + ":" + group.Account;
  305. string from = ((int)Owner.ClientType).ToString() + ":" + Owner.Account;
  306. MultiMimeMessage mmMessage = new MultiMimeMessage(to, from);
  307. mmMessage.RoutingHeaders[MIMERoutingHeaders.From][MIMERoutingHeaders.EPID] = MachineGuid.ToString("B").ToLowerInvariant();
  308. mmMessage.ContentKey = MIMEContentHeaders.Publication;
  309. mmMessage.ContentHeaders[MIMEContentHeaders.URI] = "/circle/roster(IM)/user(" + from + ")";
  310. mmMessage.ContentHeaders[MIMEContentHeaders.ContentType] = "application/circles+xml";
  311. mmMessage.InnerBody = new byte[0];
  312. NSMessage delPayload = new NSMessage("DEL");
  313. delPayload.InnerMessage = mmMessage;
  314. MessageProcessor.SendMessage(delPayload);
  315. lock (multiParties)
  316. {
  317. int delTransId = 0;
  318. foreach (MultipartyObject g in multiParties.Values)
  319. {
  320. if (g.MultiParty != null && g.MultiParty.Account == group.Account)
  321. {
  322. delTransId = g.TransactionID;
  323. break;
  324. }
  325. }
  326. if (delTransId != 0)
  327. multiParties.Remove(delTransId);
  328. }
  329. }
  330. #endregion
  331. #endregion
  332. #region JoinMultiparty
  333. internal void JoinMultiparty(Contact group)
  334. {
  335. if (group.ClientType == IMAddressInfoType.Circle || group.ClientType == IMAddressInfoType.TemporaryGroup)
  336. {
  337. string to = ((int)group.ClientType).ToString() + ":" + group.Account;
  338. string from = ((int)Owner.ClientType).ToString() + ":" + Owner.Account;
  339. MultiMimeMessage mmMessage = new MultiMimeMessage(to, from);
  340. mmMessage.RoutingHeaders[MIMERoutingHeaders.From][MIMERoutingHeaders.EPID] = MachineGuid.ToString("B").ToLowerInvariant();
  341. mmMessage.ContentKey = MIMEContentHeaders.Publication;
  342. mmMessage.ContentHeaders[MIMEContentHeaders.URI] = "/circle";
  343. mmMessage.ContentHeaders[MIMEContentHeaders.ContentType] = "application/circles+xml";
  344. string xml = "<circle><roster><id>IM</id><user><id>1:" + Owner.Account + "</id></user></roster></circle>";
  345. mmMessage.InnerBody = Encoding.UTF8.GetBytes(xml);
  346. NSMessage putPayload = new NSMessage("PUT");
  347. putPayload.InnerMessage = mmMessage;
  348. MessageProcessor.SendMessage(putPayload);
  349. OnJoinedGroupChat(new GroupChatParticipationEventArgs(Owner, group));
  350. }
  351. }
  352. #endregion
  353. #endregion
  354. #region MESSAGING
  355. #region SendTypingMessage
  356. protected internal virtual void SendTypingMessage(Contact remoteContact)
  357. {
  358. string to = ((int)remoteContact.ClientType).ToString() + ":" + remoteContact.Account;
  359. string from = ((int)Owner.ClientType).ToString() + ":" + Owner.Account;
  360. MultiMimeMessage mmMessage = new MultiMimeMessage(to, from);
  361. mmMessage.RoutingHeaders[MIMERoutingHeaders.From][MIMERoutingHeaders.EPID] = NSMessageHandler.MachineGuid.ToString("B").ToLowerInvariant();
  362. if (remoteContact.ClientType == IMAddressInfoType.Circle)
  363. {
  364. mmMessage.RoutingHeaders[MIMERoutingHeaders.To][MIMERoutingHeaders.Path] = "IM";
  365. }
  366. if (remoteContact.Via != null)
  367. {
  368. mmMessage.RoutingHeaders[MIMERoutingHeaders.To]["via"] =
  369. ((int)remoteContact.Via.ClientType).ToString() + ":" + remoteContact.Via.Account;
  370. }
  371. mmMessage.ContentKeyVersion = "2.0";
  372. mmMessage.ContentHeaders[MIMEContentHeaders.MessageType] = MessageTypes.ControlTyping;
  373. mmMessage.InnerBody = new byte[0];
  374. NSMessage sdgPayload = new NSMessage("SDG");
  375. sdgPayload.InnerMessage = mmMessage;
  376. MessageProcessor.SendMessage(sdgPayload);
  377. }
  378. #endregion
  379. #region SendNudge
  380. protected internal virtual void SendNudge(Contact remoteContact)
  381. {
  382. string to = ((int)remoteContact.ClientType).ToString() + ":" + remoteContact.Account;
  383. string from = ((int)Owner.ClientType).ToString() + ":" + Owner.Account;
  384. MultiMimeMessage mmMessage = new MultiMimeMessage(to, from);
  385. mmMessage.RoutingHeaders[MIMERoutingHeaders.From][MIMERoutingHeaders.EPID] = NSMessageHandler.MachineGuid.ToString("B").ToLowerInvariant();
  386. if (remoteContact.ClientType == IMAddressInfoType.Circle)
  387. {
  388. mmMessage.RoutingHeaders[MIMERoutingHeaders.To][MIMERoutingHeaders.Path] = "IM";
  389. }
  390. if (remoteContact.Via != null)
  391. {
  392. mmMessage.RoutingHeaders[MIMERoutingHeaders.To]["via"] =
  393. ((int)remoteContact.Via.ClientType).ToString() + ":" + remoteContact.Via.Account;
  394. }
  395. mmMessage.ContentKeyVersion = "2.0";
  396. mmMessage.ContentHeaders[MIMEContentHeaders.MessageType] = MessageTypes.Nudge;
  397. mmMessage.InnerBody = Encoding.ASCII.GetBytes("\r\n");
  398. NSMessage sdgPayload = new NSMessage("SDG");
  399. sdgPayload.InnerMessage = mmMessage;
  400. MessageProcessor.SendMessage(sdgPayload);
  401. }
  402. #endregion
  403. #region SendTextMessage
  404. protected internal virtual void SendTextMessage(Contact remoteContact, TextMessage textMessage)
  405. {
  406. textMessage.PrepareMessage();
  407. string to = ((int)remoteContact.ClientType).ToString() + ":" + remoteContact.Account;
  408. string from = ((int)Owner.ClientType).ToString() + ":" + Owner.Account;
  409. MultiMimeMessage mmMessage = new MultiMimeMessage(to, from);
  410. mmMessage.RoutingHeaders[MIMERoutingHeaders.From][MIMERoutingHeaders.EPID] = NSMessageHandler.MachineGuid.ToString("B").ToLowerInvariant();
  411. if (remoteContact.ClientType == IMAddressInfoType.Circle)
  412. {
  413. mmMessage.RoutingHeaders[MIMERoutingHeaders.To][MIMERoutingHeaders.Path] = "IM";
  414. }
  415. else if (remoteContact.Online)
  416. {
  417. mmMessage.RoutingHeaders[MIMERoutingHeaders.ServiceChannel] = "IM/Online";
  418. }
  419. else
  420. {
  421. mmMessage.RoutingHeaders[MIMERoutingHeaders.ServiceChannel] = "IM/Offline";
  422. }
  423. if (remoteContact.Via != null)
  424. {
  425. mmMessage.RoutingHeaders[MIMERoutingHeaders.To]["via"] =
  426. ((int)remoteContact.Via.ClientType).ToString() + ":" + remoteContact.Via.Account;
  427. }
  428. mmMessage.ContentKeyVersion = "2.0";
  429. mmMessage.ContentHeaders[MIMEContentHeaders.MessageType] = MessageTypes.Text;
  430. mmMessage.ContentHeaders[MIMEHeaderStrings.X_MMS_IM_Format] = textMessage.GetStyleString();
  431. mmMessage.InnerBody = Encoding.UTF8.GetBytes(textMessage.Text);
  432. NSMessage sdgPayload = new NSMessage("SDG");
  433. sdgPayload.InnerMessage = mmMessage;
  434. MessageProcessor.SendMessage(sdgPayload);
  435. }
  436. protected internal virtual void SendOIMMessage(Contact remoteContact, TextMessage textMessage)
  437. {
  438. SendTextMessage(remoteContact, textMessage);
  439. }
  440. #endregion
  441. #region SendMobileMessage
  442. /// <summary>
  443. /// Sends a mobile message to the specified remote contact. This only works when
  444. /// the remote contact has it's mobile device enabled and has MSN-direct enabled.
  445. /// </summary>
  446. /// <param name="receiver"></param>
  447. /// <param name="text"></param>
  448. protected internal virtual void SendMobileMessage(Contact receiver, string text)
  449. {
  450. TextMessage txtMsg = new TextMessage(text);
  451. string to = ((int)receiver.ClientType).ToString() + ":" + ((receiver.ClientType == IMAddressInfoType.Telephone) ? "tel:" + receiver.Account : receiver.Account);
  452. string from = ((int)Owner.ClientType).ToString() + ":" + Owner.Account;
  453. MultiMimeMessage mmMessage = new MultiMimeMessage(to, from);
  454. mmMessage.RoutingHeaders[MIMERoutingHeaders.From][MIMERoutingHeaders.EPID] = MachineGuid.ToString("B").ToLowerInvariant();
  455. mmMessage.RoutingHeaders[MIMERoutingHeaders.ServiceChannel] = "IM/Mobile";
  456. mmMessage.ContentKeyVersion = "2.0";
  457. mmMessage.ContentHeaders[MIMEContentHeaders.MessageType] = MessageTypes.Text;
  458. mmMessage.ContentHeaders[MIMEContentHeaders.MSIMFormat] = txtMsg.GetStyleString();
  459. mmMessage.InnerBody = Encoding.UTF8.GetBytes(txtMsg.Text);
  460. NSMessage sdgPayload = new NSMessage("SDG");
  461. sdgPayload.InnerMessage = mmMessage;
  462. MessageProcessor.SendMessage(sdgPayload);
  463. }
  464. #endregion
  465. #region SendEmoticonDefinitions
  466. protected internal virtual void SendEmoticonDefinitions(Contact remoteContact, List<Emoticon> emoticons, EmoticonType icontype)
  467. {
  468. EmoticonMessage emoticonMessage = new EmoticonMessage(emoticons, icontype);
  469. string to = ((int)remoteContact.ClientType).ToString() + ":" + remoteContact.Account;
  470. string from = ((int)Owner.ClientType).ToString() + ":" + Owner.Account;
  471. MultiMimeMessage mmMessage = new MultiMimeMessage(to, from);
  472. mmMessage.RoutingHeaders[MIMERoutingHeaders.From][MIMERoutingHeaders.EPID] = MachineGuid.ToString("B").ToLowerInvariant();
  473. mmMessage.ContentKeyVersion = "2.0";
  474. mmMessage.ContentHeaders[MIMEContentHeaders.MessageType] = MessageTypes.CustomEmoticon;
  475. mmMessage.ContentHeaders[MIMEContentHeaders.ContentType] = icontype == EmoticonType.AnimEmoticon ? "text/x-mms-animemoticon" : "text/x-mms-emoticon";
  476. mmMessage.InnerBody = emoticonMessage.GetBytes();
  477. NSMessage sdgPayload = new NSMessage("SDG");
  478. sdgPayload.InnerMessage = mmMessage;
  479. MessageProcessor.SendMessage(sdgPayload);
  480. }
  481. #endregion
  482. #endregion
  483. #region PRESENCE
  484. #region SignoutFrom
  485. internal void SignoutFrom(Guid endPointID)
  486. {
  487. string me = ((int)Owner.ClientType).ToString() + ":" + Owner.Account;
  488. MultiMimeMessage mmMessage = new MultiMimeMessage(me, me);
  489. mmMessage.RoutingHeaders[MIMERoutingHeaders.From][MIMERoutingHeaders.EPID] = MachineGuid.ToString("B").ToLowerInvariant();
  490. mmMessage.ContentKey = MIMEContentHeaders.Publication;
  491. mmMessage.ContentHeaders[MIMEContentHeaders.URI] = "/user";
  492. mmMessage.ContentHeaders[MIMEContentHeaders.ContentType] = "application/user+xml";
  493. string xml = "<user><sep n=\"IM\" epid=\"" + endPointID.ToString("B").ToLowerInvariant() + "\"/></user>";
  494. mmMessage.InnerBody = Encoding.UTF8.GetBytes(xml);
  495. NSMessage delPayload = new NSMessage("DEL");
  496. delPayload.InnerMessage = mmMessage;
  497. MessageProcessor.SendMessage(delPayload);
  498. // We will receive NFY DEL for normal users
  499. if (endPointID == MachineGuid && messageProcessor.Connected)
  500. messageProcessor.Disconnect();
  501. }
  502. #endregion
  503. #region SetScreenName & SetPersonalMessage
  504. /// <summary>
  505. /// Sets the contactlist owner's screenname. After receiving confirmation from the server
  506. /// this will set the Owner object's name which will in turn raise the NameChange event.
  507. /// </summary>
  508. internal void SetScreenName(string newName)
  509. {
  510. if (Owner == null)
  511. throw new MSNPSharpException("Not a valid owner");
  512. if (string.IsNullOrEmpty(newName))
  513. {
  514. newName = Owner.Account;
  515. }
  516. PersonalMessage pm = Owner.PersonalMessage;
  517. pm.FriendlyName = newName;
  518. SetPersonalMessage(pm);
  519. }
  520. /// <summary>
  521. /// Sets personal message.
  522. /// </summary>
  523. internal void SetPersonalMessage(PersonalMessage newPSM)
  524. {
  525. if (Owner == null)
  526. throw new MSNPSharpException("Not a valid owner");
  527. if (Owner.Status != PresenceStatus.Offline)
  528. {
  529. SetPresenceStatus(
  530. Owner.Status,
  531. Owner.LocalEndPointIMCapabilities, Owner.LocalEndPointIMCapabilitiesEx,
  532. Owner.LocalEndPointPECapabilities, Owner.LocalEndPointPECapabilitiesEx,
  533. Owner.EpName, newPSM, true);
  534. }
  535. }
  536. /// <summary>
  537. /// Sets the scene image and scheme context.
  538. /// </summary>
  539. internal void SetSceneData(SceneImage scimg, Color sccolor)
  540. {
  541. if (Owner == null)
  542. throw new MSNPSharpException("Not a valid owner");
  543. PersonalMessage pm = Owner.PersonalMessage;
  544. pm.ColorScheme = sccolor;
  545. pm.Scene = scimg.IsDefaultImage ? String.Empty : scimg.ContextPlain;
  546. SetPresenceStatus(Owner.Status,
  547. Owner.LocalEndPointIMCapabilities, Owner.LocalEndPointIMCapabilitiesEx,
  548. Owner.LocalEndPointPECapabilities, Owner.LocalEndPointPECapabilitiesEx,
  549. Owner.EpName, pm, true);
  550. }
  551. #endregion
  552. #region SetPresenceStatus
  553. /// <summary>
  554. /// Set the status of the contact list owner (the client).
  555. /// </summary>
  556. /// <remarks>You can only set the status _after_ SignedIn event. Otherwise you won't receive online notifications from other clients or the connection is closed by the server.</remarks>
  557. internal void SetPresenceStatus(
  558. PresenceStatus newStatus,
  559. ClientCapabilities newLocalIMCaps, ClientCapabilitiesEx newLocalIMCapsex,
  560. ClientCapabilities newLocalPECaps, ClientCapabilitiesEx newLocalPECapsex,
  561. string newEPName,
  562. PersonalMessage newPSM,
  563. bool forcePEservice)
  564. {
  565. if (IsSignedIn == false)
  566. throw new MSNPSharpException("Can't set status. You must wait for the SignedIn event before you can set an initial status.");
  567. if (newStatus == PresenceStatus.Offline)
  568. {
  569. SignoutFrom(MachineGuid);
  570. return;
  571. }
  572. bool setAll = (Owner.Status == PresenceStatus.Offline);
  573. if (setAll || forcePEservice ||
  574. newStatus != Owner.Status ||
  575. newLocalIMCaps != Owner.LocalEndPointIMCapabilities ||
  576. newLocalIMCapsex != Owner.LocalEndPointIMCapabilitiesEx ||
  577. newLocalPECaps != Owner.LocalEndPointPECapabilities ||
  578. newLocalPECapsex != Owner.LocalEndPointPECapabilitiesEx ||
  579. newEPName != Owner.EpName)
  580. {
  581. XmlDocument xmlDoc = new XmlDocument();
  582. XmlElement userElement = xmlDoc.CreateElement("user");
  583. // s.IM (Status, CurrentMedia)
  584. if (setAll || forcePEservice ||
  585. newStatus != Owner.Status)
  586. {
  587. XmlElement service = xmlDoc.CreateElement("s");
  588. service.SetAttribute("n", ServiceShortNames.IM.ToString());
  589. service.InnerXml =
  590. "<Status>" + ParseStatus(newStatus) + "</Status>" +
  591. "<CurrentMedia>" + MSNHttpUtility.XmlEncode(newPSM.CurrentMedia) + "</CurrentMedia>";
  592. userElement.AppendChild(service);
  593. // Don't call Owner.Status = newStatus.
  594. }
  595. // s.PE (UserTileLocation, FriendlyName, PSM, Scene, ColorScheme)
  596. if (setAll ||
  597. forcePEservice)
  598. {
  599. XmlElement service = xmlDoc.CreateElement("s");
  600. service.SetAttribute("n", ServiceShortNames.PE.ToString());
  601. service.InnerXml = newPSM.Payload;
  602. userElement.AppendChild(service);
  603. // Don't set owner.PersonalMessage here. It is replaced (with a new reference) when NFY PUT received.
  604. }
  605. // sep.IM (Capabilities)
  606. if (setAll ||
  607. newLocalIMCaps != Owner.LocalEndPointIMCapabilities ||
  608. newLocalIMCapsex != Owner.LocalEndPointIMCapabilitiesEx)
  609. {
  610. ClientCapabilities localIMCaps = setAll ? ClientCapabilities.DefaultIM : newLocalIMCaps;
  611. ClientCapabilitiesEx localIMCapsEx = setAll ? ClientCapabilitiesEx.DefaultIM : newLocalIMCapsex;
  612. XmlElement sep = xmlDoc.CreateElement("sep");
  613. sep.SetAttribute("n", ServiceShortNames.IM.ToString());
  614. XmlElement capabilities = xmlDoc.CreateElement("Capabilities");
  615. capabilities.InnerText = ((long)localIMCaps).ToString() + ":" + ((long)localIMCapsEx).ToString();
  616. sep.AppendChild(capabilities);
  617. userElement.AppendChild(sep);
  618. // Don't call Owner.LocalEndPointIMCapabilities. It is recursive call to this method.
  619. }
  620. // sep.PE (Capabilities)
  621. if (setAll ||
  622. newLocalPECaps != Owner.LocalEndPointPECapabilities ||
  623. newLocalPECapsex != Owner.LocalEndPointPECapabilitiesEx)
  624. {
  625. ClientCapabilities localPECaps = setAll ? ClientCapabilities.DefaultPE : newLocalPECaps;
  626. ClientCapabilitiesEx localPECapsEx = setAll ? ClientCapabilitiesEx.DefaultPE : newLocalPECapsex;
  627. XmlElement sep = xmlDoc.CreateElement("sep");
  628. sep.SetAttribute("n", ServiceShortNames.PE.ToString());
  629. XmlElement VER = xmlDoc.CreateElement("VER");
  630. VER.InnerText = Credentials.ClientInfo.MessengerClientName + ":" + Credentials.ClientInfo.MessengerClientBuildVer;
  631. sep.AppendChild(VER);
  632. XmlElement TYP = xmlDoc.CreateElement("TYP");
  633. TYP.InnerText = "1";
  634. sep.AppendChild(TYP);
  635. XmlElement capabilities = xmlDoc.CreateElement("Capabilities");
  636. capabilities.InnerText = ((long)localPECaps).ToString() + ":" + ((long)localPECapsEx).ToString();
  637. sep.AppendChild(capabilities);
  638. userElement.AppendChild(sep);
  639. // Don't call Owner.LocalEndPointPECapabilities. It is recursive call to this method.
  640. }
  641. // sep.PD (EpName, State)
  642. if (setAll ||
  643. newEPName != Owner.EpName ||
  644. newStatus != Owner.Status)
  645. {
  646. XmlElement sep = xmlDoc.CreateElement("sep");
  647. sep.SetAttribute("n", ServiceShortNames.PD.ToString());
  648. XmlElement clientType = xmlDoc.CreateElement("ClientType");
  649. clientType.InnerText = "1";
  650. sep.AppendChild(clientType);
  651. XmlElement epName = xmlDoc.CreateElement("EpName");
  652. epName.InnerText = MSNHttpUtility.XmlEncode(newEPName);
  653. sep.AppendChild(epName);
  654. XmlElement idle = xmlDoc.CreateElement("Idle");
  655. idle.InnerText = ((newStatus == PresenceStatus.Idle) ? "true" : "false");
  656. sep.AppendChild(idle);
  657. XmlElement state = xmlDoc.CreateElement("State");
  658. state.InnerText = ParseStatus(newStatus);
  659. sep.AppendChild(state);
  660. userElement.AppendChild(sep);
  661. // Don't set Owner.EpName. It is recursive call to this method.
  662. }
  663. if (userElement.HasChildNodes)
  664. {
  665. string xml = userElement.OuterXml;
  666. string me = ((int)Owner.ClientType).ToString() + ":" + Owner.Account;
  667. MultiMimeMessage mmMessage = new MultiMimeMessage(me, me);
  668. mmMessage.RoutingHeaders[MIMERoutingHeaders.From][MIMERoutingHeaders.EPID] = NSMessageHandler.MachineGuid.ToString("B").ToLowerInvariant();
  669. mmMessage.Stream = 1;
  670. mmMessage.ReliabilityHeaders[MIMEReliabilityHeaders.Flags] = "ACK";
  671. mmMessage.ContentKey = MIMEContentHeaders.Publication;
  672. mmMessage.ContentHeaders[MIMEContentHeaders.URI] = "/user";
  673. mmMessage.ContentHeaders[MIMEContentHeaders.ContentType] = "application/user+xml";
  674. mmMessage.InnerBody = System.Text.Encoding.UTF8.GetBytes(xml);
  675. NSMessage nsMessage = new NSMessage("PUT");
  676. nsMessage.InnerMessage = mmMessage;
  677. MessageProcessor.SendMessage(nsMessage);
  678. }
  679. }
  680. }
  681. #endregion
  682. #endregion
  683. #region COMMAND HANDLERS (PUT, DEL, NFY, SDG)
  684. #region OnPUTReceived
  685. /// <summary>
  686. /// Called when a PUT command message has been received.
  687. /// </summary>
  688. /// <param name="message"></param>
  689. protected virtual void OnPUTReceived(NSMessage message)
  690. {
  691. bool ok = message.CommandValues.Count > 0 && message.CommandValues[0].ToString() == "OK";
  692. if (multiParties.ContainsKey(message.TransactionID))
  693. {
  694. if (ok == false || message.InnerBody == null || message.InnerBody.Length == 0)
  695. {
  696. lock (multiParties)
  697. multiParties.Remove(message.TransactionID);
  698. return;
  699. }
  700. MultiMimeMessage mmMessage = new MultiMimeMessage(message.InnerBody);
  701. string[] tempGroup = mmMessage.From.Value.Split(':');
  702. IMAddressInfoType addressType = (IMAddressInfoType)int.Parse(tempGroup[0]);
  703. if (addressType == IMAddressInfoType.TemporaryGroup)
  704. {
  705. Contact group = new Contact(tempGroup[1].ToLowerInvariant(), IMAddressInfoType.TemporaryGroup, this);
  706. group.ContactList = new ContactList(new Guid(tempGroup[1].ToLowerInvariant().Split('@')[0]), null, group, this);
  707. MultipartyObject mpo = multiParties[message.TransactionID];
  708. mpo.TransactionID = message.TransactionID;
  709. mpo.MultiParty = group;
  710. JoinMultiparty(group);
  711. List<string> copy = new List<string>(mpo.InviteQueueHash);
  712. foreach (string siblingHash in copy)
  713. {
  714. string[] addressTypeAndAccount = siblingHash.Split(new char[] { ':' }, 2, StringSplitOptions.RemoveEmptyEntries);
  715. Contact contact = ContactList.GetContactWithCreate(addressTypeAndAccount[1], (IMAddressInfoType)Enum.Parse(typeof(IMAddressInfoType), addressTypeAndAccount[0].ToString()));
  716. InviteContactToMultiparty(contact, group);
  717. }
  718. mpo.OnMultipartyCreatedLocally(this, new MultipartyCreatedEventArgs(group));
  719. group.SetStatus(PresenceStatus.Online);
  720. Trace.WriteLineIf(Settings.TraceSwitch.TraceInfo, "MultipartyCreated: " + group.Account);
  721. }
  722. else
  723. {
  724. lock (multiParties)
  725. multiParties.Remove(message.TransactionID);
  726. }
  727. }
  728. }
  729. #endregion
  730. #region OnDELReceived
  731. /// <summary>
  732. /// Called when a DEL command message has been received.
  733. /// </summary>
  734. /// <param name="message"></param>
  735. protected virtual void OnDELReceived(NSMessage message)
  736. {
  737. bool ok = message.CommandValues.Count > 0 && message.CommandValues[0].ToString() == "OK";
  738. if (ok)
  739. {
  740. Trace.WriteLineIf(Settings.TraceSwitch.TraceVerbose, "DEL command accepted", GetType().Name);
  741. }
  742. }
  743. #endregion
  744. #region OnNFYReceived
  745. private void OnNFYPUTReceived(MultiMimeMessage multiMimeMessage, RoutingInfo routingInfo)
  746. {
  747. switch (multiMimeMessage.ContentHeaders[MIMEContentHeaders.ContentType].Value)
  748. {
  749. #region user xml
  750. case "application/user+xml":
  751. {
  752. if (multiMimeMessage.ContentHeaders[MIMEHeaderStrings.NotifType].Value == "Sync")
  753. {
  754. if (routingInfo.SenderGateway != null && routingInfo.SenderGateway.ClientType == IMAddressInfoType.Circle)
  755. {
  756. JoinMultiparty(routingInfo.SenderGateway);
  757. }
  758. //Sync the contact in contact list with the contact in gateway.
  759. // TODO: Set the NSMessagehandler.ContactList contact to the gateway
  760. // TODO: triger the ContactOnline event for the gateway contact.
  761. //Just wait for my fix.
  762. }
  763. if (multiMimeMessage.InnerBody == null || multiMimeMessage.InnerBody.Length == 0)
  764. return; //No xml content.
  765. if (multiMimeMessage.ContentHeaders[MIMEHeaderStrings.NotifType].Value == "Full")
  766. {
  767. //This is an initial NFY
  768. }
  769. XmlDocument xmlDoc = new XmlDocument();
  770. xmlDoc.LoadXml(Encoding.UTF8.GetString(multiMimeMessage.InnerBody));
  771. XmlNodeList services = xmlDoc.SelectNodes("//user/s");
  772. XmlNodeList serviceEndPoints = xmlDoc.SelectNodes("//user/sep");
  773. if (services.Count > 0)
  774. {
  775. foreach (XmlNode service in services)
  776. {
  777. ServiceShortNames serviceEnum = (ServiceShortNames)Enum.Parse(typeof(ServiceShortNames), service.Attributes["n"].Value);
  778. switch (serviceEnum)
  779. {
  780. case ServiceShortNames.IM:
  781. {
  782. foreach (XmlNode node in service.ChildNodes)
  783. {
  784. switch (node.Name)
  785. {
  786. case "Status":
  787. if (routingInfo.FromOwner && IsSignedIn == false)
  788. {
  789. // We have already signed in another place, but not here...
  790. // Don't set status... This place will set the status later.
  791. return;
  792. }
  793. PresenceStatus oldStatus = routingInfo.Sender.Status;
  794. PresenceStatus newStatus = ParseStatus(node.InnerText);
  795. routingInfo.Sender.SetStatus(newStatus);
  796. OnContactStatusChanged(new ContactStatusChangedEventArgs(routingInfo.Sender, routingInfo.SenderGateway, oldStatus, newStatus));
  797. OnContactOnline(new ContactStatusChangedEventArgs(routingInfo.Sender, routingInfo.SenderGateway, oldStatus, newStatus));
  798. break;
  799. case "CurrentMedia":
  800. //MSNP21TODO: UBX implementation
  801. break;
  802. }
  803. }
  804. break;
  805. }
  806. case ServiceShortNames.PE:
  807. {
  808. // Create a new reference to fire PersonalMessageChanged event.
  809. PersonalMessage personalMessage = new PersonalMessage(service.ChildNodes);
  810. if (!String.IsNullOrEmpty(personalMessage.Payload) &&
  811. routingInfo.Sender.PersonalMessage != personalMessage)
  812. {
  813. // FriendlyName
  814. if (!String.IsNullOrEmpty(personalMessage.FriendlyName))
  815. {
  816. //Only Windows Live Messenger Contact has friendly name.
  817. routingInfo.Sender.SetName(personalMessage.FriendlyName);
  818. }
  819. // UserTileLocation
  820. if (!String.IsNullOrEmpty(personalMessage.UserTileLocation) && routingInfo.Sender.UserTileLocation != personalMessage.UserTileLocation)
  821. {
  822. routingInfo.Sender.UserTileLocation = personalMessage.UserTileLocation;
  823. routingInfo.Sender.FireDisplayImageContextChangedEvent(personalMessage.UserTileLocation);
  824. }
  825. // Scene
  826. if (!String.IsNullOrEmpty(personalMessage.Scene))
  827. {
  828. if (routingInfo.Sender.SceneContext != personalMessage.Scene)
  829. {
  830. routingInfo.Sender.SceneContext = personalMessage.Scene;
  831. routingInfo.Sender.FireSceneImageContextChangedEvent(personalMessage.Scene);
  832. }
  833. }
  834. // ColorScheme
  835. if (personalMessage.ColorScheme != Color.Empty)
  836. {
  837. if (routingInfo.Sender.ColorScheme != personalMessage.ColorScheme)
  838. {
  839. routingInfo.Sender.ColorScheme = personalMessage.ColorScheme;
  840. routingInfo.Sender.OnColorSchemeChanged();
  841. }
  842. }
  843. // This must be final...
  844. routingInfo.Sender.PersonalMessage = personalMessage;
  845. }
  846. break;
  847. }
  848. case ServiceShortNames.PF:
  849. {
  850. // Profile Annotation, it is AB.Me.annotations/Live.Profile.Expression.LastChanged
  851. // <user><s n="PF" ts="2011-04-16T06:00:58Z"></s></user>
  852. if (routingInfo.FromOwner)
  853. {
  854. DateTime ts = WebServiceDateTimeConverter.ConvertToDateTime(service.Attributes["ts"].Value);
  855. }
  856. break;
  857. }
  858. }
  859. }
  860. }
  861. if (serviceEndPoints.Count > 0)
  862. {
  863. foreach (XmlNode serviceEndPoint in serviceEndPoints)
  864. {
  865. ServiceShortNames serviceEnum = (ServiceShortNames)Enum.Parse(typeof(ServiceShortNames), serviceEndPoint.Attributes["n"].Value);
  866. Guid epid = serviceEndPoint.Attributes["epid"] == null ? Guid.Empty : new Guid

Large files files are truncated, but you can click here to view the full file