Add customizable settings option for xmpp_endpoint

BUG:26525138

Change-Id: I8b198c5d7b29fdc11940443710c64731b1025066
Reviewed-on: https://weave-review.googlesource.com/2487
Reviewed-by: Alex Vakulenko <avakulenko@google.com>
diff --git a/include/weave/settings.h b/include/weave/settings.h
index 741fff2..7cb798d 100644
--- a/include/weave/settings.h
+++ b/include/weave/settings.h
@@ -61,6 +61,7 @@
   // Optional cloud information. Can be used for testing or debugging.
   std::string oauth_url;
   std::string service_url;
+  std::string xmpp_endpoint;
 
   // Cloud ID of the registered device. Empty if device is not registered.
   std::string cloud_id;
diff --git a/src/config.cc b/src/config.cc
index 44d20dd..21a1c1f 100644
--- a/src/config.cc
+++ b/src/config.cc
@@ -33,6 +33,7 @@
 const char kApiKey[] = "api_key";
 const char kOAuthURL[] = "oauth_url";
 const char kServiceURL[] = "service_url";
+const char kXmppEndpoint[] = "xmpp_endpoint";
 const char kName[] = "name";
 const char kDescription[] = "description";
 const char kLocation[] = "location";
@@ -51,6 +52,7 @@
 
 const char kWeaveUrl[] = "https://www.googleapis.com/weave/v1/";
 const char kDeprecatedUrl[] = "https://www.googleapis.com/clouddevices/v1/";
+const char kXmppEndpoint[] = "talk.google.com:5223";
 
 namespace {
 
@@ -69,6 +71,7 @@
   Config::Settings result;
   result.oauth_url = "https://accounts.google.com/o/oauth2/";
   result.service_url = kWeaveUrl;
+  result.xmpp_endpoint = kXmppEndpoint;
   result.local_anonymous_access_role = AuthScope::kViewer;
   result.pairing_modes.insert(PairingType::kPinCode);
   result.device_id = base::GenerateGUID();
@@ -119,6 +122,7 @@
   CHECK(!settings_.api_key.empty());
   CHECK(!settings_.oauth_url.empty());
   CHECK(!settings_.service_url.empty());
+  CHECK(!settings_.xmpp_endpoint.empty());
   CHECK(!settings_.oem_name.empty());
   CHECK(!settings_.model_name.empty());
   CHECK(!settings_.model_id.empty());
@@ -190,6 +194,10 @@
     set_service_url(tmp);
   }
 
+  if (dict->GetString(config_keys::kXmppEndpoint, &tmp)) {
+    set_xmpp_endpoint(tmp);
+  }
+
   if (dict->GetString(config_keys::kName, &tmp))
     set_name(tmp);
 
@@ -249,6 +257,7 @@
   dict.SetString(config_keys::kApiKey, settings_.api_key);
   dict.SetString(config_keys::kOAuthURL, settings_.oauth_url);
   dict.SetString(config_keys::kServiceURL, settings_.service_url);
+  dict.SetString(config_keys::kXmppEndpoint, settings_.xmpp_endpoint);
   dict.SetString(config_keys::kRefreshToken, settings_.refresh_token);
   dict.SetString(config_keys::kCloudId, settings_.cloud_id);
   dict.SetString(config_keys::kDeviceId, settings_.device_id);
diff --git a/src/config.h b/src/config.h
index 6dc0a07..8e0a8f3 100644
--- a/src/config.h
+++ b/src/config.h
@@ -68,6 +68,9 @@
     void set_service_url(const std::string& url) {
       settings_->service_url = url;
     }
+    void set_xmpp_endpoint(const std::string& endpoint) {
+      settings_->xmpp_endpoint = endpoint;
+    }
     void set_name(const std::string& name) { settings_->name = name; }
     void set_description(const std::string& description) {
       settings_->description = description;
diff --git a/src/config_unittest.cc b/src/config_unittest.cc
index 4b0e5b4..bb2743a 100644
--- a/src/config_unittest.cc
+++ b/src/config_unittest.cc
@@ -62,6 +62,7 @@
   EXPECT_EQ("", GetSettings().api_key);
   EXPECT_EQ("https://accounts.google.com/o/oauth2/", GetSettings().oauth_url);
   EXPECT_EQ("https://www.googleapis.com/weave/v1/", GetSettings().service_url);
+  EXPECT_EQ("talk.google.com:5223", GetSettings().xmpp_endpoint);
   EXPECT_EQ("", GetSettings().oem_name);
   EXPECT_EQ("", GetSettings().model_name);
   EXPECT_EQ("", GetSettings().model_id);
@@ -146,7 +147,8 @@
     "refresh_token": "state_refresh_token",
     "robot_account": "state_robot_account",
     "secret": "c3RhdGVfc2VjcmV0",
-    "service_url": "state_service_url"
+    "service_url": "state_service_url",
+    "xmpp_endpoint": "state_xmpp_endpoint"
   })";
   EXPECT_CALL(config_store_, LoadSettings(kConfigName)).WillOnce(Return(state));
 
@@ -157,6 +159,7 @@
   EXPECT_EQ("state_api_key", GetSettings().api_key);
   EXPECT_EQ("state_oauth_url", GetSettings().oauth_url);
   EXPECT_EQ("state_service_url", GetSettings().service_url);
+  EXPECT_EQ("state_xmpp_endpoint", GetSettings().xmpp_endpoint);
   EXPECT_EQ(GetDefaultSettings().oem_name, GetSettings().oem_name);
   EXPECT_EQ(GetDefaultSettings().model_name, GetSettings().model_name);
   EXPECT_EQ(GetDefaultSettings().model_id, GetSettings().model_id);
@@ -200,6 +203,9 @@
   change.set_service_url("set_service_url");
   EXPECT_EQ("set_service_url", GetSettings().service_url);
 
+  change.set_xmpp_endpoint("set_xmpp_endpoint");
+  EXPECT_EQ("set_xmpp_endpoint", GetSettings().xmpp_endpoint);
+
   change.set_name("set_name");
   EXPECT_EQ("set_name", GetSettings().name);
 
@@ -277,7 +283,8 @@
           'refresh_token': 'set_token',
           'robot_account': 'set_account',
           'secret': 'AQIDBAU=',
-          'service_url': 'set_service_url'
+          'service_url': 'set_service_url',
+          'xmpp_endpoint': 'set_xmpp_endpoint'
         })";
             EXPECT_JSON_EQ(expected, *test::CreateValue(json));
             callback.Run(nullptr);
diff --git a/src/device_registration_info.cc b/src/device_registration_info.cc
index 7c20084..0dc1f54 100644
--- a/src/device_registration_info.cc
+++ b/src/device_registration_info.cc
@@ -463,8 +463,9 @@
   current_notification_channel_ = pull_channel_.get();
 
   notification_channel_starting_ = true;
-  primary_notification_channel_.reset(new XmppChannel{
-      GetSettings().robot_account, access_token_, task_runner_, network_});
+  primary_notification_channel_.reset(
+      new XmppChannel{GetSettings().robot_account, access_token_,
+                      GetSettings().xmpp_endpoint, task_runner_, network_});
   primary_notification_channel_->Start(this);
 }
 
@@ -833,17 +834,25 @@
     const std::string& api_key,
     const std::string& oauth_url,
     const std::string& service_url,
+    const std::string& xmpp_endpoint,
     ErrorPtr* error) {
   if (HaveRegistrationCredentials()) {
     return Error::AddTo(error, FROM_HERE, kErrorAlreayRegistered,
                         "Unable to change config for registered device");
   }
   Config::Transaction change{config_};
-  change.set_client_id(client_id);
-  change.set_client_secret(client_secret);
-  change.set_api_key(api_key);
-  change.set_oauth_url(oauth_url);
-  change.set_service_url(service_url);
+  if (!client_id.empty())
+    change.set_client_id(client_id);
+  if (!client_secret.empty())
+    change.set_client_secret(client_secret);
+  if (!api_key.empty())
+    change.set_api_key(api_key);
+  if (!oauth_url.empty())
+    change.set_oauth_url(oauth_url);
+  if (!service_url.empty())
+    change.set_service_url(service_url);
+  if (!xmpp_endpoint.empty())
+    change.set_xmpp_endpoint(xmpp_endpoint);
   return true;
 }
 
diff --git a/src/device_registration_info.h b/src/device_registration_info.h
index f670b68..a296258 100644
--- a/src/device_registration_info.h
+++ b/src/device_registration_info.h
@@ -78,6 +78,7 @@
                            const std::string& api_key,
                            const std::string& oauth_url,
                            const std::string& service_url,
+                           const std::string& xmpp_endpoint,
                            ErrorPtr* error);
 
   void GetDeviceInfo(const CloudRequestDoneCallback& callback);
diff --git a/src/device_registration_info_unittest.cc b/src/device_registration_info_unittest.cc
index 7908c8b..bbc167e 100644
--- a/src/device_registration_info_unittest.cc
+++ b/src/device_registration_info_unittest.cc
@@ -44,6 +44,7 @@
 
 namespace test_data {
 
+const char kXmppEndpoint[] = "xmpp.server.com:1234";
 const char kServiceURL[] = "http://gcd.server.com/";
 const char kOAuthURL[] = "http://oauth.server.com/";
 const char kApiKey[] = "GOadRdTf9FERf0k4w6EFOof56fUJ3kFDdFL3d7f";
@@ -144,6 +145,7 @@
           settings->model_id = "AAAAA";
           settings->oauth_url = test_data::kOAuthURL;
           settings->service_url = test_data::kServiceURL;
+          settings->xmpp_endpoint = test_data::kXmppEndpoint;
           return true;
         }));
     config_.reset(new Config{&config_store_});
diff --git a/src/notification/xmpp_channel.cc b/src/notification/xmpp_channel.cc
index ceb45ed..f9d7924 100644
--- a/src/notification/xmpp_channel.cc
+++ b/src/notification/xmpp_channel.cc
@@ -7,6 +7,7 @@
 #include <string>
 
 #include <base/bind.h>
+#include <base/strings/string_number_conversions.h>
 #include <weave/provider/network.h>
 #include <weave/provider/task_runner.h>
 
@@ -16,6 +17,7 @@
 #include "src/notification/notification_parser.h"
 #include "src/notification/xml_node.h"
 #include "src/privet/openssl_utils.h"
+#include "src/string_utils.h"
 #include "src/utils.h"
 
 namespace weave {
@@ -74,9 +76,6 @@
     false,
 };
 
-const char kDefaultXmppHost[] = "talk.google.com";
-const uint16_t kDefaultXmppPort = 5223;
-
 // Used for keeping connection alive.
 const int kRegularPingIntervalSeconds = 60;
 const int kRegularPingTimeoutSeconds = 30;
@@ -91,10 +90,12 @@
 
 XmppChannel::XmppChannel(const std::string& account,
                          const std::string& access_token,
+                         const std::string& xmpp_endpoint,
                          provider::TaskRunner* task_runner,
                          provider::Network* network)
     : account_{account},
       access_token_{access_token},
+      xmpp_endpoint_{xmpp_endpoint},
       network_{network},
       backoff_entry_{&kDefaultBackoffPolicy},
       task_runner_{task_runner},
@@ -285,10 +286,16 @@
 void XmppChannel::CreateSslSocket() {
   CHECK(!stream_);
   state_ = XmppState::kConnecting;
-  LOG(INFO) << "Starting XMPP connection to " << kDefaultXmppHost << ":"
-            << kDefaultXmppPort;
+  LOG(INFO) << "Starting XMPP connection to: " << xmpp_endpoint_;
 
-  network_->OpenSslSocket(kDefaultXmppHost, kDefaultXmppPort,
+  std::pair<std::string, std::string> host_port =
+      SplitAtFirst(xmpp_endpoint_, ":", true);
+  CHECK(!host_port.first.empty());
+  CHECK(!host_port.second.empty());
+  uint32_t port = 0;
+  CHECK(base::StringToUint(host_port.second, &port)) << xmpp_endpoint_;
+
+  network_->OpenSslSocket(host_port.first, port,
                           base::Bind(&XmppChannel::OnSslSocketReady,
                                      task_ptr_factory_.GetWeakPtr()));
 }
diff --git a/src/notification/xmpp_channel.h b/src/notification/xmpp_channel.h
index 50e84d2..b0a4468 100644
--- a/src/notification/xmpp_channel.h
+++ b/src/notification/xmpp_channel.h
@@ -45,6 +45,7 @@
   // so you will need to reset the XmppClient every time this happens.
   XmppChannel(const std::string& account,
               const std::string& access_token,
+              const std::string& xmpp_endpoint,
               provider::TaskRunner* task_runner,
               provider::Network* network);
   ~XmppChannel() override = default;
@@ -124,12 +125,15 @@
   // Robot account name for the device.
   std::string account_;
 
-  // Full JID of this device.
-  std::string jid_;
-
   // OAuth access token for the account. Expires fairly frequently.
   std::string access_token_;
 
+  // Xmpp endpoint.
+  std::string xmpp_endpoint_;
+
+  // Full JID of this device.
+  std::string jid_;
+
   provider::Network* network_{nullptr};
   std::unique_ptr<Stream> stream_;
 
diff --git a/src/notification/xmpp_channel_unittest.cc b/src/notification/xmpp_channel_unittest.cc
index 674fe22..dfa2a79 100644
--- a/src/notification/xmpp_channel_unittest.cc
+++ b/src/notification/xmpp_channel_unittest.cc
@@ -26,6 +26,7 @@
 
 constexpr char kAccountName[] = "Account@Name";
 constexpr char kAccessToken[] = "AccessToken";
+constexpr char kEndpoint[] = "endpoint:456";
 
 constexpr char kStartStreamMessage[] =
     "<stream:stream to='clouddevices.gserviceaccount.com' "
@@ -84,7 +85,8 @@
  public:
   explicit FakeXmppChannel(provider::TaskRunner* task_runner,
                            provider::Network* network)
-      : XmppChannel{kAccountName, kAccessToken, task_runner, network},
+      : XmppChannel{kAccountName, kAccessToken, kEndpoint, task_runner,
+                    network},
         stream_{new test::FakeStream{task_runner_}},
         fake_stream_{stream_.get()} {}
 
@@ -122,7 +124,7 @@
 class XmppChannelTest : public ::testing::Test {
  protected:
   XmppChannelTest() {
-    EXPECT_CALL(network_, OpenSslSocket("talk.google.com", 5223, _))
+    EXPECT_CALL(network_, OpenSslSocket("endpoint", 456, _))
         .WillOnce(
             WithArgs<2>(Invoke(&xmpp_client_, &FakeXmppChannel::Connect)));
   }