protected override void OnConnected(bool bSuccess, string strErrors) { try { if ((bSuccess == true) && (Client.Connected == true)) { this.Client.NoDelay = true; #if !WINDOWS_PHONE this.Client.SetSocketOption(System.Net.Sockets.SocketOptionLevel.Socket, Windows.Networking.Sockets.SocketOptionName.KeepAlive, true); #endif #if WINDOWS_PHONE var cancellationTokenSource = new System.Threading.CancellationTokenSource(); var task = Repeat.Interval( TimeSpan.FromSeconds(60), () => OnKeepAlive(), cancellationTokenSource.Token ); // this.Client.SetSocketOption(System.Net.Sockets.SocketOptionLevel.Socket, Windows.Networking.Sockets.SocketOptionName.KeepAlive, true); #endif XMPPClient.XMPPState = XMPPState.Connected; XMPPClient.FireConnectAttemptFinished(true); System.Diagnostics.Debug.WriteLine(string.Format("Successful TCP connection")); } else { XMPPClient.XMPPState = XMPPState.Unknown; XMPPClient.FireConnectAttemptFinished(false); System.Diagnostics.Debug.WriteLine(string.Format("Failed to connect: {0}", strErrors)); return; } if (XMPPClient.UseOldStyleTLS == true) { StartTLS(); } /// Send stream header if we haven't yet XMPPClient.XMPPState = XMPPState.Authenticating; OpenStreamStanza open = new OpenStreamStanza(this.XMPPClient); string strSend = open.XML; byte[] bStanza = System.Text.UTF8Encoding.UTF8.GetBytes(strSend); this.Send(bStanza); } catch (TimeoutException tx) { Console.WriteLine(tx.InnerException.ToString()); } catch (Exception ex) { Console.WriteLine(ex.ToString()); } }
/// <summary> /// /// </summary> /// <param name="stanza"></param> /// <returns></returns> public override bool NewXMLFragment(XMPPStanza stanza) { /// Looks like the crippled windows phone 7 libraries can't use output from xsd.exe, have to do this the hard way /// //XDocument doc = XDocument.Load(new StringReader(stanza.XML)); //XmlReader reader = XmlReader.Create(new StringReader(stanza.XML)); stanza.XML = stanza.XML.Replace("stream:", ""); // no support for namespaces in windows phone 7, remove them XElement xmlElem = XElement.Parse(stanza.XML); if (XMPPClient.XMPPState >= XMPPState.Authenticated) { //if (xmlElem.Name == "features") // return true; /// If we hit this and parse the stream featurs a second time we re-authenticate. Just return for now ////if (xmlElem.Name == "stream") ////{ //// XMPPClient.XMPPState = XMPPState.CanBind; ////} ///// TODO.. see if this new stream supports bind } if (xmlElem.Name == "features") { //foreach (XElement node in xmlElem.Descendants()) //{ // System.Diagnostics.Debug.WriteLine(node.Name); //} var Mechanisms = from mech in xmlElem.Descendants("{urn:ietf:params:xml:ns:xmpp-sasl}mechanism") select new Mechanism { Name = mech.Value, }; AuthMethodsSupported = AuthMethod.NotSpecified; foreach (Mechanism mech in Mechanisms) { if (mech.Name == "DIGEST-MD5") { AuthMethodsSupported |= AuthMethod.MD5; } else if (mech.Name == "PLAIN") { AuthMethodsSupported |= AuthMethod.Plain; } // else if (mech.Name == "X-GOOGLE-TOKEN") // AuthMethodsSupported |= AuthMethod.googletoken; } if ((AuthMethodsSupported == AuthMethod.NotSpecified) && (XMPPClient.XMPPState < XMPPState.Authenticated)) { throw new Exception("No acceptable authentication method was supplied"); } var tls = xmlElem.Descendants("{urn:ietf:params:xml:ns:xmpp-tls}starttls"); if (tls.Count() > 0) { FeatureTLS = true; } var session = xmlElem.Descendants("{urn:ietf:params:xml:ns:xmpp-session}session"); if (session.Count() > 0) { XMPPClient.ShouldDoSession = true; } var bind = xmlElem.Descendants("{urn:ietf:params:xml:ns:xmpp-bind}bind"); if (bind.Count() > 0) { XMPPClient.XMPPState = XMPPState.CanBind; return(true); } if ((FeatureTLS == true) && (XMPPClient.UseTLS == true)) { /// Tell the man we want to negotiate TLS XMPPClient.SendRawXML(StartTLS); } else { StartAuthentication(); } return(true); } else if (xmlElem.Name == "{urn:ietf:params:xml:ns:xmpp-tls}proceed") { XMPPClient.XMPPConnection.StartTLS(); /// After starting TLS, start our normal digest authentication (or plain) /// /// Bug or something changed after RFC. Now 6120 says we must reopen the stream, our response should not have a TLS option in it /// FeatureTLS = false; OpenStreamStanza open = new OpenStreamStanza(this.XMPPClient); XMPPClient.SendRawXML(open.XML); //StartAuthentication(); } else if (xmlElem.Name == "{urn:ietf:params:xml:ns:xmpp-sasl}challenge") { /// Build and send response /// string strChallenge = xmlElem.Value; byte[] bData = Convert.FromBase64String(strChallenge); string strUnbasedChallenge = System.Text.UTF8Encoding.UTF8.GetString(bData, 0, bData.Length); if (strUnbasedChallenge.IndexOf("rspauth") == 0) { string ResponseMessage = MD5Response.Replace("##RESPONSE##", "="); XMPPClient.SendRawXML(ResponseMessage); return(false); } //realm="ninethumbs.com",nonce="oFun3YWfVm/6nHCkNI/9a4XpcWIdQ5RH9E0IDVKH",qop="auth",charset=utf-8,algorithm=md5-sess //string strExampleResponse = "dXNlcm5hbWU9InRlc3QiLHJlYWxtPSJuaW5ldGh1bWJzLmNvbSIsbm9uY2U9InJaNjgreS9BeGp2SjJ6cjBCVUNxVUhQcG9ocFE4ZFkzR29JclpJcFkiLGNub25jZT0iVkdFRDNqNHUrUHE1M3IxYzNab2NhcGFzaWp1eTh2NjhoYXFzRC9IWjVKTT0iLG5jPTAwMDAwMDAxLGRpZ2VzdC11cmk9InhtcHAvbmluZXRodW1icy5jb20iLHFvcD1hdXRoLHJlc3BvbnNlPTdiM2MzOTVjZjU2MDA2Njg5MDg5MzdlYTk2YjEzZjI2LGNoYXJzZXQ9dXRmLTg="; //bData = Convert.FromBase64String(strExampleResponse); //string strUnbasedResponse = System.Text.UTF8Encoding.UTF8.GetString(bData, 0, bData.Length); //"username=\"test\",realm=\"ninethumbs.com\",nonce=\"rZ68+y/AxjvJ2zr0BUCqUHPpohpQ8dY3GoIrZIpY\",cnonce=\"VGED3j4u+Pq53r1c3Zocapasijuy8v68haqsD/HZ5JM=\",nc=00000001,digest-uri=\"xmpp/ninethumbs.com\",qop=auth,response=7b3c395cf5600668908937ea96b13f26,charset=utf-8"; if (AuthMethodUsed == AuthMethod.MD5) { string strRealm = XMPPClient.Domain; Match matchrealm = Regex.Match(strUnbasedChallenge, @"realm=""([^""]+)""", RegexOptions.IgnorePatternWhitespace | RegexOptions.IgnoreCase); if (matchrealm.Success == true) { strRealm = matchrealm.Groups[1].Value; } string strNonce = ""; Match matchnonce = Regex.Match(strUnbasedChallenge, @"nonce=""([^""]+)""", RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline); if (matchnonce.Success == true) { strNonce = matchnonce.Groups[1].Value; } string strQop = "auth"; Match matchqop = Regex.Match(strUnbasedChallenge, @"qop=""([^""]+)""", RegexOptions.IgnorePatternWhitespace | RegexOptions.IgnoreCase); if (matchqop.Success == true) { strQop = matchqop.Groups[1].Value; } string strAlgo = "md5-sess"; Match matchalgo = Regex.Match(strUnbasedChallenge, @"algorithm=([^\s,]+)", RegexOptions.IgnorePatternWhitespace | RegexOptions.IgnoreCase); if (matchalgo.Success == true) { strAlgo = matchalgo.Groups[1].Value; } Random rand = new Random(); string strCnonce = rand.Next().ToString("X8").ToLower(); // Compute our MD5 response, then base64 it string strResponse = GenerateMD5Response(strAlgo, XMPPClient.UserName, XMPPClient.Domain, XMPPClient.Password, strNonce, strCnonce); string ResponseMessage = MD5Response.Replace("##RESPONSE##", strResponse); XMPPClient.SendRawXML(ResponseMessage); } else if (AuthMethodUsed == AuthMethod.Plain) { /// Send plain text stuff /// } } else if (xmlElem.Name == "{urn:ietf:params:xml:ns:xmpp-sasl}success") /// Success { XMPPClient.XMPPState = XMPPState.Authenticated; OpenStreamStanza open = new OpenStreamStanza(this.XMPPClient); XMPPClient.SendRawXML(open.XML); //if (XMPPClient.UseTLS == true) //{ // /// Start a new stream for some strange reason, but don't close the old one. // /// // OpenStreamStanza open = new OpenStreamStanza(this.XMPPClient); // XMPPClient.SendRawXML(open.XML); //} //else //{ //XMPPClient.XMPPState = XMPPState.CanBind; //} } else if (xmlElem.Name == "{urn:ietf:params:xml:ns:xmpp-sasl}failure") /// Failed to authorize { XMPPClient.XMPPState = XMPPState.AuthenticationFailed; } return(false); }