/// <summary> /// Initiates the stream. /// </summary> private void OpenStream() { // Generate new client and message ids _clientId = Guid.NewGuid().ToString(); _messageId = 1; // Stream initialization request var xml = Xml.Element("stream:stream", "jabber:client") .Attr("to", "connect.logitech.com") .Attr("version", "1.0") .Attr("xmlns:stream", "http://etherx.jabber.org/streams") .Attr("xml:lang", CultureInfo.CurrentCulture.Name); Send(xml.ToXmlString(xmlDeclaration: true, leaveOpen: true)); // Create a new parser instance. if (_parser != null) { _parser.Dispose(); } _parser = new StreamParser(_stream, true); // The first element of the stream must be <stream:features>. var features = _parser.NextElement("stream:features"); MessageReceived?.Invoke(this, new MessageReceivedEventArgs(features.OuterXml)); // Enable keepalive if (_heartbeat != null) { _heartbeat.Dispose(); } _heartbeat = new System.Timers.Timer(); _heartbeat.Elapsed += HeatbeatAsync; _heartbeat.Interval = 30000; _heartbeat.Start(); Connected = true; }
/// <summary> /// Gets the session token for the supplied Logitech username and password. /// </summary> /// <param name="ip">IP address of the Harmony Hub.</param> /// <param name="username">Username.</param> /// <param name="password">Passwprd.</param> /// <returns>Harmony session token.</returns> private string GetSessionToken(string ip, string username, string password, bool bypassLogitech) { var sessionToken = ""; var authToken = "";; if (!bypassLogitech) { // Authenticate to Logitech authToken = LoginToLogitech(username, password); if (string.IsNullOrEmpty(authToken)) { throw new AuthTokenException("Could not get token from Logitech server."); } } // Swap auth token for a session token using (var authClient = new TcpClient(ip, 5222)) { using (var authStream = authClient.GetStream()) { // Initialize stream var streamXml = Xml.Element("stream:stream", "jabber:client") .Attr("to", "connect.logitech.com") .Attr("version", "1.0") .Attr("xmlns:stream", "http://etherx.jabber.org/streams") .Attr("xml:lang", CultureInfo.CurrentCulture.Name); byte[] streamBuffer = Encoding.UTF8.GetBytes(streamXml.ToXmlString(xmlDeclaration: true, leaveOpen: true)); authStream.Write(streamBuffer, 0, streamBuffer.Length); // Begin reading auth stream using (var authParser = new StreamParser(authStream, true)) { // The first element of the stream must be <stream:features>. var features = authParser.NextElement("stream:features"); MessageReceived?.Invoke(this, new MessageReceivedEventArgs(features.OuterXml)); // Auth as guest // <auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>AGd1ZXN0QHguY29tAGd1ZXN0</auth> var saslXml = Xml.Element("auth", "urn:ietf:params:xml:ns:xmpp-sasl") .Attr("mechanism", "PLAIN") .Text(Convert.ToBase64String(Encoding.UTF8.GetBytes("\0" + "*****@*****.**" + "\0" + "guest"))); byte[] saslBuffer = Encoding.UTF8.GetBytes(saslXml.ToXmlString()); authStream.Write(saslBuffer, 0, saslBuffer.Length); // Handle response while (true) { var response = authParser.NextElement("challenge", "success", "failure"); MessageReceived?.Invoke(this, new MessageReceivedEventArgs(response.OuterXml)); if (response.Name != "success") { throw new SaslAuthenticationException("SASL authentication failed."); } break; } // Swap the authToken for a sessionToken on the Harmony Hub /* * <iq type="get" id="3174962747" from="guest"> * <oa xmlns="connect.logitech.com" mime="vnd.logitech.connect/vnd.logitech.pair"> * token=y6jZtSuYYOoQ2XXiU9cYovqtT+cCbcyjhWqGbhQsLV/mWi4dJVglFEBGpm08OjCW:name=SOMEID#iOS6.0.1#iPhone * </oa> * </iq> */ var methodOrToken = bypassLogitech ? "method=pair" : "token=" + authToken; var swapXml = Xml.Element("iq") .Attr("type", "get") .Attr("id", _clientId + "_" + _messageId.ToString()) .Child(Xml.Element("oa", "connect.logitech.com") .Attr("mime", HarmonyMimeTypes.Pair) .Text(methodOrToken + ":name=foo#iOS8.3.0#iPhone")); byte[] swapBuffer = Encoding.UTF8.GetBytes(swapXml.ToXmlString()); authStream.Write(swapBuffer, 0, swapBuffer.Length); // Handle response while (true) { XmlElement ret = authParser.NextElement("iq"); MessageReceived?.Invoke(this, new MessageReceivedEventArgs(ret.OuterXml)); if (ret.Name == "iq") { if (ret.FirstChild != null && ret.FirstChild.Name == "oa") { if (ret.FirstChild.InnerXml.Contains("status=succeeded")) { const string identityRegEx = "identity=([A-Z0-9]{8}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{12}):status"; var regex = new Regex(identityRegEx, RegexOptions.IgnoreCase | RegexOptions.Singleline); var match = regex.Match(ret.FirstChild.InnerXml); if (match.Success) { sessionToken = match.Groups[1].ToString(); break; } else { throw new AuthTokenException("AuthToken swap failed."); } } } } } } // Kill stream byte[] killBuffer = Encoding.UTF8.GetBytes("</stream:stream>"); authStream.Write(killBuffer, 0, killBuffer.Length); } } return(sessionToken); }