Nathan Bullock | bdabded | 2015-02-10 20:15:53 -0500 | [diff] [blame] | 1 | // Copyright 2015 The Chromium OS Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "xmpp/xmpp_client.h" |
| 6 | |
| 7 | #include <arpa/inet.h> |
| 8 | #include <netdb.h> |
| 9 | #include <netinet/in.h> |
| 10 | #include <unistd.h> |
| 11 | |
| 12 | #include <string> |
| 13 | |
| 14 | #include <base/files/file_util.h> |
| 15 | #include <chromeos/data_encoding.h> |
| 16 | #include <chromeos/syslog_logging.h> |
| 17 | |
| 18 | namespace buffet { |
| 19 | |
| 20 | namespace { |
| 21 | |
| 22 | std::string BuildXmppStartStreamCommand() { |
| 23 | return "<stream:stream to='clouddevices.gserviceaccount.com' " |
| 24 | "xmlns:stream='http://etherx.jabber.org/streams' " |
| 25 | "xml:lang='*' version='1.0' xmlns='jabber:client'>"; |
| 26 | } |
| 27 | |
| 28 | std::string BuildXmppAuthenticateCommand( |
| 29 | const std::string& account, const std::string& token) { |
| 30 | chromeos::Blob credentials; |
| 31 | credentials.push_back(0); |
| 32 | credentials.insert(credentials.end(), account.begin(), account.end()); |
| 33 | credentials.push_back(0); |
| 34 | credentials.insert(credentials.end(), token.begin(), token.end()); |
| 35 | std::string msg = "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' " |
| 36 | "mechanism='X-OAUTH2' auth:service='oauth2' " |
| 37 | "auth:allow-non-google-login='true' " |
| 38 | "auth:client-uses-full-bind-result='true' " |
| 39 | "xmlns:auth='http://www.google.com/talk/protocol/auth'>" + |
| 40 | chromeos::data_encoding::Base64Encode(credentials) + |
| 41 | "</auth>"; |
| 42 | return msg; |
| 43 | } |
| 44 | |
| 45 | std::string BuildXmppBindCommand() { |
| 46 | return "<iq type='set' id='0'>" |
| 47 | "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/></iq>"; |
| 48 | } |
| 49 | |
| 50 | std::string BuildXmppStartSessionCommand() { |
| 51 | return "<iq type='set' id='1'>" |
| 52 | "<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/></iq>"; |
| 53 | } |
| 54 | |
| 55 | std::string BuildXmppSubscribeCommand(const std::string& account) { |
| 56 | return "<iq type='set' to='" + account + "' " |
| 57 | "id='pushsubscribe1'><subscribe xmlns='google:push'>" |
| 58 | "<item channel='cloud_devices' from=''/>" |
| 59 | "</subscribe></iq>"; |
| 60 | } |
| 61 | |
| 62 | } // namespace |
| 63 | |
Nathan Bullock | f12f7f0 | 2015-02-20 14:46:53 -0500 | [diff] [blame] | 64 | bool XmppClient::Read() { |
Nathan Bullock | bdabded | 2015-02-10 20:15:53 -0500 | [diff] [blame] | 65 | std::string msg; |
| 66 | if (!connection_->Read(&msg) || msg.size() <= 0) { |
Nathan Bullock | f12f7f0 | 2015-02-20 14:46:53 -0500 | [diff] [blame] | 67 | LOG(ERROR) << "Failed to read from stream. The socket was probably closed"; |
| 68 | return false; |
Nathan Bullock | bdabded | 2015-02-10 20:15:53 -0500 | [diff] [blame] | 69 | } |
| 70 | |
| 71 | // TODO(nathanbullock): Need to add support for TLS (brillo:191). |
| 72 | switch (state_) { |
| 73 | case XmppState::kStarted: |
| 74 | if (std::string::npos != msg.find(":features") && |
| 75 | std::string::npos != msg.find("X-GOOGLE-TOKEN")) { |
| 76 | state_ = XmppState::kAuthenticationStarted; |
| 77 | connection_->Write(BuildXmppAuthenticateCommand( |
| 78 | account_, access_token_)); |
| 79 | } |
| 80 | break; |
| 81 | case XmppState::kAuthenticationStarted: |
| 82 | if (std::string::npos != msg.find("success")) { |
| 83 | state_ = XmppState::kStreamRestartedPostAuthentication; |
| 84 | connection_->Write(BuildXmppStartStreamCommand()); |
Nathan Bullock | f12f7f0 | 2015-02-20 14:46:53 -0500 | [diff] [blame] | 85 | } else if (std::string::npos != msg.find("not-authorized")) { |
| 86 | state_ = XmppState::kAuthenticationFailed; |
| 87 | return false; |
Nathan Bullock | bdabded | 2015-02-10 20:15:53 -0500 | [diff] [blame] | 88 | } |
| 89 | break; |
| 90 | case XmppState::kStreamRestartedPostAuthentication: |
| 91 | if (std::string::npos != msg.find(":features") && |
| 92 | std::string::npos != msg.find(":xmpp-session")) { |
| 93 | state_ = XmppState::kBindSent; |
| 94 | connection_->Write(BuildXmppBindCommand()); |
| 95 | } |
| 96 | break; |
| 97 | case XmppState::kBindSent: |
| 98 | if (std::string::npos != msg.find("iq") && |
| 99 | std::string::npos != msg.find("result")) { |
| 100 | state_ = XmppState::kSessionStarted; |
| 101 | connection_->Write(BuildXmppStartSessionCommand()); |
| 102 | } |
| 103 | break; |
| 104 | case XmppState::kSessionStarted: |
| 105 | if (std::string::npos != msg.find("iq") && |
| 106 | std::string::npos != msg.find("result")) { |
| 107 | state_ = XmppState::kSubscribeStarted; |
| 108 | connection_->Write(BuildXmppSubscribeCommand(account_)); |
| 109 | } |
| 110 | break; |
| 111 | case XmppState::kSubscribeStarted: |
| 112 | if (std::string::npos != msg.find("iq") && |
| 113 | std::string::npos != msg.find("result")) { |
| 114 | state_ = XmppState::kSubscribed; |
| 115 | } |
| 116 | break; |
| 117 | default: |
| 118 | break; |
| 119 | } |
Nathan Bullock | f12f7f0 | 2015-02-20 14:46:53 -0500 | [diff] [blame] | 120 | return true; |
Nathan Bullock | bdabded | 2015-02-10 20:15:53 -0500 | [diff] [blame] | 121 | } |
| 122 | |
| 123 | void XmppClient::StartStream() { |
| 124 | state_ = XmppState::kStarted; |
| 125 | connection_->Write(BuildXmppStartStreamCommand()); |
| 126 | } |
| 127 | |
| 128 | } // namespace buffet |