Home | History | Annotate | Download | only in service
      1 // Copyright (c) 2012 The Chromium 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 "cloud_print/service/service_state.h"
      6 
      7 #include "base/json/json_reader.h"
      8 #include "base/json/json_writer.h"
      9 #include "base/logging.h"
     10 #include "base/message_loop/message_loop.h"
     11 #include "base/strings/string_util.h"
     12 #include "base/strings/utf_string_conversions.h"
     13 #include "net/base/escape.h"
     14 #include "net/base/io_buffer.h"
     15 #include "net/base/load_flags.h"
     16 #include "net/base/upload_bytes_element_reader.h"
     17 #include "net/base/upload_data_stream.h"
     18 #include "net/url_request/url_request.h"
     19 #include "net/url_request/url_request_context.h"
     20 #include "net/url_request/url_request_context_builder.h"
     21 
     22 namespace {
     23 
     24 const char kCloudPrintJsonName[] = "cloud_print";
     25 const char kEnabledOptionName[] = "enabled";
     26 
     27 const char kEmailOptionName[] = "email";
     28 const char kPasswordOptionName[] = "password";
     29 const char kProxyIdOptionName[] = "proxy_id";
     30 const char kRobotEmailOptionName[] = "robot_email";
     31 const char kRobotTokenOptionName[] = "robot_refresh_token";
     32 const char kAuthTokenOptionName[] = "auth_token";
     33 const char kXmppAuthTokenOptionName[] = "xmpp_auth_token";
     34 
     35 const char kClientLoginUrl[] = "https://www.google.com/accounts/ClientLogin";
     36 
     37 const int64 kRequestTimeoutMs = 10 * 1000;
     38 
     39 class ServiceStateURLRequestDelegate : public net::URLRequest::Delegate {
     40  public:
     41   virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE {
     42     if (request->GetResponseCode() == 200) {
     43       Read(request);
     44       if (request->status().is_io_pending())
     45         return;
     46     }
     47     request->Cancel();
     48   }
     49 
     50   virtual void OnReadCompleted(net::URLRequest* request,
     51                                int bytes_read) OVERRIDE {
     52     Read(request);
     53     if (!request->status().is_io_pending())
     54       base::MessageLoop::current()->Quit();
     55   }
     56 
     57   const std::string& data() const {
     58     return data_;
     59   }
     60 
     61  private:
     62   void Read(net::URLRequest* request) {
     63     // Read as many bytes as are available synchronously.
     64     const int kBufSize = 100000;
     65     scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(kBufSize));
     66     int num_bytes = 0;
     67     while (request->Read(buf.get(), kBufSize, &num_bytes)) {
     68       data_.append(buf->data(), buf->data() + num_bytes);
     69     }
     70   }
     71   std::string data_;
     72 };
     73 
     74 
     75 void SetNotEmptyJsonString(base::DictionaryValue* dictionary,
     76                            const std::string& name,
     77                            const std::string& value) {
     78   if (!value.empty())
     79     dictionary->SetString(name, value);
     80 }
     81 
     82 }  // namespace
     83 
     84 ServiceState::ServiceState() {
     85   Reset();
     86 }
     87 
     88 ServiceState::~ServiceState() {
     89 }
     90 
     91 void ServiceState::Reset() {
     92   email_.clear();
     93   proxy_id_.clear();
     94   robot_email_.clear();
     95   robot_token_.clear();
     96   auth_token_.clear();
     97   xmpp_auth_token_.clear();
     98 }
     99 
    100 bool ServiceState::FromString(const std::string& json) {
    101   Reset();
    102   scoped_ptr<base::Value> data(base::JSONReader::Read(json));
    103   if (!data.get())
    104     return false;
    105 
    106   const base::DictionaryValue* services = NULL;
    107   if (!data->GetAsDictionary(&services))
    108     return false;
    109 
    110   const base::DictionaryValue* cloud_print = NULL;
    111   if (!services->GetDictionary(kCloudPrintJsonName, &cloud_print))
    112     return false;
    113 
    114   bool valid_file = true;
    115   // Don't exit on fail. Collect all data for re-use by user later.
    116   if (!cloud_print->GetBoolean(kEnabledOptionName, &valid_file))
    117     valid_file = false;
    118 
    119   cloud_print->GetString(kEmailOptionName, &email_);
    120   cloud_print->GetString(kProxyIdOptionName, &proxy_id_);
    121   cloud_print->GetString(kRobotEmailOptionName, &robot_email_);
    122   cloud_print->GetString(kRobotTokenOptionName, &robot_token_);
    123   cloud_print->GetString(kAuthTokenOptionName, &auth_token_);
    124   cloud_print->GetString(kXmppAuthTokenOptionName, &xmpp_auth_token_);
    125 
    126   return valid_file && IsValid();
    127 }
    128 
    129 bool ServiceState::IsValid() const {
    130   if (email_.empty() || proxy_id_.empty())
    131     return false;
    132   bool valid_robot = !robot_email_.empty() && !robot_token_.empty();
    133   bool valid_auth = !auth_token_.empty() && !xmpp_auth_token_.empty();
    134   return valid_robot || valid_auth;
    135 }
    136 
    137 std::string ServiceState::ToString() {
    138   scoped_ptr<base::DictionaryValue> services(new DictionaryValue());
    139 
    140   scoped_ptr<base::DictionaryValue> cloud_print(new DictionaryValue());
    141   cloud_print->SetBoolean(kEnabledOptionName, true);
    142 
    143   SetNotEmptyJsonString(cloud_print.get(), kEmailOptionName, email_);
    144   SetNotEmptyJsonString(cloud_print.get(), kProxyIdOptionName, proxy_id_);
    145   SetNotEmptyJsonString(cloud_print.get(), kRobotEmailOptionName, robot_email_);
    146   SetNotEmptyJsonString(cloud_print.get(), kRobotTokenOptionName, robot_token_);
    147   SetNotEmptyJsonString(cloud_print.get(), kAuthTokenOptionName, auth_token_);
    148   SetNotEmptyJsonString(cloud_print.get(), kXmppAuthTokenOptionName,
    149                         xmpp_auth_token_);
    150 
    151   services->Set(kCloudPrintJsonName, cloud_print.release());
    152 
    153   std::string json;
    154   base::JSONWriter::WriteWithOptions(services.get(),
    155                                      base::JSONWriter::OPTIONS_PRETTY_PRINT,
    156                                      &json);
    157   return json;
    158 }
    159 
    160 std::string ServiceState::LoginToGoogle(const std::string& service,
    161                                         const std::string& email,
    162                                         const std::string& password) {
    163   base::MessageLoop loop(base::MessageLoop::TYPE_IO);
    164 
    165   net::URLRequestContextBuilder builder;
    166   scoped_ptr<net::URLRequestContext> context(builder.Build());
    167 
    168   ServiceStateURLRequestDelegate fetcher_delegate;
    169   GURL url(kClientLoginUrl);
    170 
    171   std::string post_body;
    172   post_body += "accountType=GOOGLE";
    173   post_body += "&Email=" + net::EscapeUrlEncodedData(email, true);
    174   post_body += "&Passwd=" + net::EscapeUrlEncodedData(password, true);
    175   post_body += "&source=" + net::EscapeUrlEncodedData("CP-Service", true);
    176   post_body += "&service=" + net::EscapeUrlEncodedData(service, true);
    177 
    178   net::URLRequest request(url, &fetcher_delegate, context.get());
    179   int load_flags = request.load_flags();
    180   load_flags = load_flags | net::LOAD_DO_NOT_SEND_COOKIES;
    181   load_flags = load_flags | net::LOAD_DO_NOT_SAVE_COOKIES;
    182   request.set_load_flags(load_flags);
    183 
    184   scoped_ptr<net::UploadElementReader> reader(
    185       net::UploadOwnedBytesElementReader::CreateWithString(post_body));
    186   request.set_upload(make_scoped_ptr(
    187       net::UploadDataStream::CreateWithReader(reader.Pass(), 0)));
    188   request.SetExtraRequestHeaderByName(
    189       "Content-Type", "application/x-www-form-urlencoded", true);
    190   request.set_method("POST");
    191   request.Start();
    192 
    193   base::MessageLoop::current()->PostDelayedTask(
    194       FROM_HERE,
    195       base::MessageLoop::QuitClosure(),
    196       base::TimeDelta::FromMilliseconds(kRequestTimeoutMs));
    197 
    198   base::MessageLoop::current()->Run();
    199 
    200   const char kAuthStart[] = "Auth=";
    201   std::vector<std::string> lines;
    202   Tokenize(fetcher_delegate.data(), "\r\n", &lines);
    203   for (size_t i = 0; i < lines.size(); ++i) {
    204     std::vector<std::string> tokens;
    205     if (StartsWithASCII(lines[i], kAuthStart, false))
    206       return lines[i].substr(arraysize(kAuthStart) - 1);
    207   }
    208 
    209   return std::string();
    210 }
    211 
    212 bool ServiceState::Configure(const std::string& email,
    213                              const std::string& password,
    214                              const std::string& proxy_id) {
    215   robot_token_.clear();
    216   robot_email_.clear();
    217   email_ = email;
    218   proxy_id_ = proxy_id;
    219   auth_token_ = LoginToGoogle("cloudprint", email_, password);
    220   xmpp_auth_token_ = LoginToGoogle("chromiumsync", email_, password);
    221   return IsValid();
    222 }
    223