/// <summary> /// Creates a new multiparty (Group chat) /// </summary> /// <param name="inviteQueue">Contacts to be invited (don't add yourself)</param> /// <param name="onCreated">The handler to be executed when multiparty created (must be provided)</param> /// <exception cref="ArgumentNullException">inviteQueue or event handler is null</exception> /// <exception cref="InvalidOperationException">At least 2 contacts is required except you and contacts must support multiparty</exception> /// <returns>Transaction ID</returns> public int CreateMultiparty(List<Contact> inviteQueue, EventHandler<MultipartyCreatedEventArgs> onCreated) { if (inviteQueue == null || inviteQueue.Count == 0) throw new ArgumentNullException("inviteQueue"); if (onCreated == null) throw new ArgumentNullException("onCreated"); List<string> newQueue = new List<string>(); foreach (Contact c in inviteQueue) { if (c != null && !c.IsSibling(Owner) && !newQueue.Contains(c.SiblingString) && c.SupportsMultiparty) { newQueue.Add(c.SiblingString); } } if (newQueue.Count < 2) throw new InvalidOperationException("At least 2 contacts is required except you and contacts must support multiparty."); NSMessageProcessor nsmp = (NSMessageProcessor)MessageProcessor; int transId = nsmp.IncreaseTransactionID(); lock (multiParties) multiParties[transId] = new MultipartyObject(transId, newQueue, null, onCreated); string to = ((int)IMAddressInfoType.TemporaryGroup).ToString() + ":" + Guid.Empty.ToString("D").ToLowerInvariant() + "@" + Contact.DefaultHostDomain; string from = ((int)Owner.ClientType).ToString() + ":" + Owner.Account; MultiMimeMessage mmMessage = new MultiMimeMessage(to, from); mmMessage.RoutingHeaders[MIMERoutingHeaders.From][MIMERoutingHeaders.EPID] = MachineGuid.ToString("B").ToLowerInvariant(); mmMessage.ContentKey = MIMEContentHeaders.Publication; mmMessage.ContentHeaders[MIMEContentHeaders.URI] = "/circle"; mmMessage.ContentHeaders[MIMEContentHeaders.ContentType] = "application/multiparty+xml"; mmMessage.InnerBody = new byte[0]; NSMessage putPayload = new NSMessage("PUT"); putPayload.InnerMessage = mmMessage; nsmp.SendMessage(putPayload, transId); return transId; }
private void OnNFYPUTReceived(MultiMimeMessage multiMimeMessage, RoutingInfo routingInfo) { switch (multiMimeMessage.ContentHeaders[MIMEContentHeaders.ContentType].Value) { #region user xml case "application/user+xml": { if (multiMimeMessage.ContentHeaders[MIMEHeaderStrings.NotifType].Value == "Sync") { if (routingInfo.SenderGateway != null && routingInfo.SenderGateway.ClientType == IMAddressInfoType.Circle) { JoinMultiparty(routingInfo.SenderGateway); } //Sync the contact in contact list with the contact in gateway. // TODO: Set the NSMessagehandler.ContactList contact to the gateway // TODO: triger the ContactOnline event for the gateway contact. //Just wait for my fix. } if (multiMimeMessage.InnerBody == null || multiMimeMessage.InnerBody.Length == 0) return; //No xml content. if (multiMimeMessage.ContentHeaders[MIMEHeaderStrings.NotifType].Value == "Full") { //This is an initial NFY } XmlDocument xmlDoc = new XmlDocument(); xmlDoc.LoadXml(Encoding.UTF8.GetString(multiMimeMessage.InnerBody)); XmlNodeList services = xmlDoc.SelectNodes("//user/s"); XmlNodeList serviceEndPoints = xmlDoc.SelectNodes("//user/sep"); if (services.Count > 0) { foreach (XmlNode service in services) { ServiceShortNames serviceEnum = (ServiceShortNames)Enum.Parse(typeof(ServiceShortNames), service.Attributes["n"].Value); switch (serviceEnum) { case ServiceShortNames.IM: { foreach (XmlNode node in service.ChildNodes) { switch (node.Name) { case "Status": if (routingInfo.FromOwner && IsSignedIn == false) { // We have already signed in another place, but not here... // Don't set status... This place will set the status later. return; } PresenceStatus oldStatus = routingInfo.Sender.Status; PresenceStatus newStatus = ParseStatus(node.InnerText); routingInfo.Sender.SetStatus(newStatus); OnContactStatusChanged(new ContactStatusChangedEventArgs(routingInfo.Sender, routingInfo.SenderGateway, oldStatus, newStatus)); OnContactOnline(new ContactStatusChangedEventArgs(routingInfo.Sender, routingInfo.SenderGateway, oldStatus, newStatus)); break; case "CurrentMedia": //MSNP21TODO: UBX implementation break; } } break; } case ServiceShortNames.PE: { // Create a new reference to fire PersonalMessageChanged event. PersonalMessage personalMessage = new PersonalMessage(service.ChildNodes); if (!String.IsNullOrEmpty(personalMessage.Payload) && routingInfo.Sender.PersonalMessage != personalMessage) { // FriendlyName if (!String.IsNullOrEmpty(personalMessage.FriendlyName)) { //Only Windows Live Messenger Contact has friendly name. routingInfo.Sender.SetName(personalMessage.FriendlyName); } // UserTileLocation if (!String.IsNullOrEmpty(personalMessage.UserTileLocation) && routingInfo.Sender.UserTileLocation != personalMessage.UserTileLocation) { routingInfo.Sender.UserTileLocation = personalMessage.UserTileLocation; routingInfo.Sender.FireDisplayImageContextChangedEvent(personalMessage.UserTileLocation); } // Scene if (!String.IsNullOrEmpty(personalMessage.Scene)) { if (routingInfo.Sender.SceneContext != personalMessage.Scene) { routingInfo.Sender.SceneContext = personalMessage.Scene; routingInfo.Sender.FireSceneImageContextChangedEvent(personalMessage.Scene); } } // ColorScheme if (personalMessage.ColorScheme != Color.Empty) { if (routingInfo.Sender.ColorScheme != personalMessage.ColorScheme) { routingInfo.Sender.ColorScheme = personalMessage.ColorScheme; routingInfo.Sender.OnColorSchemeChanged(); } } // This must be final... routingInfo.Sender.PersonalMessage = personalMessage; } break; } case ServiceShortNames.PF: { // Profile Annotation, it is AB.Me.annotations/Live.Profile.Expression.LastChanged // <user><s n="PF" ts="2011-04-16T06:00:58Z"></s></user> if (routingInfo.FromOwner) { DateTime ts = WebServiceDateTimeConverter.ConvertToDateTime(service.Attributes["ts"].Value); } break; } } } } if (serviceEndPoints.Count > 0) { foreach (XmlNode serviceEndPoint in serviceEndPoints) { ServiceShortNames serviceEnum = (ServiceShortNames)Enum.Parse(typeof(ServiceShortNames), serviceEndPoint.Attributes["n"].Value); Guid epid = serviceEndPoint.Attributes["epid"] == null ? Guid.Empty : new Guid(serviceEndPoint.Attributes["epid"].Value); if (!routingInfo.Sender.EndPointData.ContainsKey(epid)) { lock (routingInfo.Sender.SyncObject) routingInfo.Sender.EndPointData.Add(epid, routingInfo.FromOwner ? new PrivateEndPointData(routingInfo.Sender.Account, epid) : new EndPointData(routingInfo.Sender.Account, epid)); } switch (serviceEnum) { case ServiceShortNames.IM: { foreach (XmlNode node in serviceEndPoint.ChildNodes) { switch (node.Name) { case "Capabilities": ClientCapabilities cap = ClientCapabilities.None; ClientCapabilitiesEx capEx = ClientCapabilitiesEx.None; string[] caps = node.InnerText.Split(':'); if (caps.Length > 1) { capEx = (ClientCapabilitiesEx)long.Parse(caps[1]); } cap = (ClientCapabilities)long.Parse(caps[0]); routingInfo.Sender.EndPointData[epid].IMCapabilities = cap; routingInfo.Sender.EndPointData[epid].IMCapabilitiesEx = capEx; break; } } break; } case ServiceShortNames.PE: { foreach (XmlNode node in serviceEndPoint.ChildNodes) { switch (node.Name) { case "Capabilities": ClientCapabilities cap = ClientCapabilities.None; ClientCapabilitiesEx capEx = ClientCapabilitiesEx.None; string[] caps = node.InnerText.Split(':'); if (caps.Length > 1) { capEx = (ClientCapabilitiesEx)long.Parse(caps[1]); } cap = (ClientCapabilities)long.Parse(caps[0]); routingInfo.Sender.EndPointData[epid].PECapabilities = cap; routingInfo.Sender.EndPointData[epid].PECapabilitiesEx = capEx; break; } } routingInfo.Sender.SetChangedPlace(new PlaceChangedEventArgs(routingInfo.Sender.EndPointData[epid], PlaceChangedReason.SignedIn)); break; } case ServiceShortNames.PD: { PrivateEndPointData privateEndPoint = routingInfo.Sender.EndPointData[epid] as PrivateEndPointData; foreach (XmlNode node in serviceEndPoint.ChildNodes) { switch (node.Name) { case "ClientType": privateEndPoint.ClientType = node.InnerText; break; case "EpName": privateEndPoint.Name = node.InnerText; break; case "Idle": privateEndPoint.Idle = bool.Parse(node.InnerText); break; case "State": privateEndPoint.State = ParseStatus(node.InnerText); break; } } Owner.SetChangedPlace(new PlaceChangedEventArgs(privateEndPoint, PlaceChangedReason.SignedIn)); break; } } } } } break; #endregion #region circles xml case "application/circles+xml": { if (routingInfo.SenderType == IMAddressInfoType.Circle) { Contact circle = ContactList.GetCircle(routingInfo.SenderAccount); if (circle == null) { Trace.WriteLineIf(Settings.TraceSwitch.TraceError, "[OnNFYReceived] Cannot complete the operation since circle not found: " + multiMimeMessage.From.ToString()); return; } if (multiMimeMessage.InnerBody == null || multiMimeMessage.InnerBody.Length == 0 || "<circle></circle>" == Encoding.UTF8.GetString(multiMimeMessage.InnerBody)) { // No xml content and full notify... Circle goes online... if (multiMimeMessage.ContentHeaders[MIMEHeaderStrings.NotifType].Value == "Full") { PresenceStatus oldStatus = circle.Status; PresenceStatus newStatus = PresenceStatus.Online; circle.SetStatus(newStatus); // The contact changed status OnContactStatusChanged(new ContactStatusChangedEventArgs(circle, oldStatus, newStatus)); // The contact goes online OnContactOnline(new ContactStatusChangedEventArgs(circle, oldStatus, newStatus)); } return; } XmlDocument xmlDoc = new XmlDocument(); xmlDoc.LoadXml(Encoding.UTF8.GetString(multiMimeMessage.InnerBody)); XmlNodeList ids = xmlDoc.SelectNodes("//circle/roster/user/id"); if (ids.Count == 0) { return; //I hate indent. } foreach (XmlNode node in ids) { IMAddressInfoType accountAddressType; string account; IMAddressInfoType viaAccountAddressType; string viaAccount; string fullAccount = node.InnerText; if (false == Contact.ParseFullAccount(fullAccount, out accountAddressType, out account, out viaAccountAddressType, out viaAccount)) { continue; } if (account == Owner.Account) continue; if (circle.ContactList.HasContact(account, accountAddressType)) { Contact contact = circle.ContactList.GetContact(account, accountAddressType); OnJoinedGroupChat(new GroupChatParticipationEventArgs(contact, circle)); } } } else if (routingInfo.SenderType == IMAddressInfoType.TemporaryGroup) { MultipartyObject mpo = GetMultipartyObject(routingInfo.SenderAccount); Contact group = null; if (mpo == null) { // Created remotely. NSMessageProcessor nsmp = (NSMessageProcessor)MessageProcessor; int transId = nsmp.IncreaseTransactionID(); group = new Contact(routingInfo.SenderAccount, IMAddressInfoType.TemporaryGroup, this); group.ContactList = new ContactList(new Guid(routingInfo.SenderAccount.Split('@')[0]), null, group, this); mpo = new MultipartyObject(transId, new List<string>(), group, null); lock (multiParties) multiParties[transId] = mpo; OnMultipartyCreatedRemotely(new MultipartyCreatedEventArgs(group)); group.SetStatus(PresenceStatus.Online); } else { group = mpo.MultiParty; } if (multiMimeMessage.InnerBody == null || multiMimeMessage.InnerBody.Length == 0) { // No xml content and full notify... Circle goes online... if (multiMimeMessage.ContentHeaders[MIMEHeaderStrings.NotifType].Value == "Full") { PresenceStatus oldStatus = group.Status; PresenceStatus newStatus = PresenceStatus.Online; group.SetStatus(newStatus); // The contact changed status OnContactStatusChanged(new ContactStatusChangedEventArgs(group, oldStatus, newStatus)); // The contact goes online OnContactOnline(new ContactStatusChangedEventArgs(group, oldStatus, newStatus)); } return; } // Join multiparty if state is Pending XmlDocument xmlDoc = new XmlDocument(); xmlDoc.LoadXml(Encoding.UTF8.GetString(multiMimeMessage.InnerBody)); XmlNodeList rosters = xmlDoc.SelectNodes("//circle/roster/user"); foreach (XmlNode roster in rosters) { string state = (roster["state"] == null) ? string.Empty : roster["state"].InnerText; string[] fullAccount = roster["id"].InnerText.Split(':'); IMAddressInfoType addressType = (IMAddressInfoType)int.Parse(fullAccount[0]); string memberAccount = fullAccount[1].ToLowerInvariant(); // Me contact if ("pending" == state.ToLowerInvariant() && addressType == Owner.ClientType && memberAccount == Owner.Account) { JoinMultiparty(group); } else { Contact part = group.ContactList.GetContactWithCreate(memberAccount, addressType); Contact real = ContactList.GetContactWithCreate(memberAccount, addressType); part.SetStatus(real.Status); OnJoinedGroupChat(new GroupChatParticipationEventArgs(part, group)); if (mpo.InviteQueueHash.Contains(part.SiblingString)) mpo.InviteQueueHash.Remove(part.SiblingString); } } } } break; #endregion #region network xml case "application/network+xml": { if (routingInfo.Sender.ClientType == IMAddressInfoType.RemoteNetwork && routingInfo.Sender.Account == RemoteNetworkGateways.FaceBookGatewayAccount) { string status = Encoding.UTF8.GetString(multiMimeMessage.InnerBody); PresenceStatus oldStatus = routingInfo.Sender.Status; PresenceStatus newStatus = PresenceStatus.Unknown; if (status.Contains("SignedIn")) newStatus = PresenceStatus.Online; else if (status.Contains("SignedOut")) newStatus = PresenceStatus.Offline; if (newStatus != PresenceStatus.Unknown) { routingInfo.Sender.SetStatus(newStatus); // The contact changed status OnContactStatusChanged(new ContactStatusChangedEventArgs(routingInfo.Sender, routingInfo.SenderGateway, oldStatus, newStatus)); if (newStatus == PresenceStatus.Online) OnContactOnline(new ContactStatusChangedEventArgs(routingInfo.Sender, routingInfo.SenderGateway, oldStatus, newStatus)); else OnContactOffline(new ContactStatusChangedEventArgs(routingInfo.Sender, routingInfo.SenderGateway, oldStatus, newStatus)); } } } break; #endregion } }