• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/request_priority.h"
17 #include "net/base/upload_bytes_element_reader.h"
18 #include "net/base/upload_data_stream.h"
19 #include "net/url_request/url_request.h"
20 #include "net/url_request/url_request_context.h"
21 #include "net/url_request/url_request_context_builder.h"
22 
23 namespace {
24 
25 const char kCloudPrintJsonName[] = "cloud_print";
26 const char kEnabledOptionName[] = "enabled";
27 
28 const char kEmailOptionName[] = "email";
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:
OnResponseStarted(net::URLRequest * request)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 
OnReadCompleted(net::URLRequest * request,int bytes_read)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 
data() const57   const std::string& data() const {
58     return data_;
59   }
60 
61  private:
Read(net::URLRequest * request)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 
SetNotEmptyJsonString(base::DictionaryValue * dictionary,const std::string & name,const std::string & value)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 
ServiceState()84 ServiceState::ServiceState() {
85   Reset();
86 }
87 
~ServiceState()88 ServiceState::~ServiceState() {
89 }
90 
Reset()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 
FromString(const std::string & json)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 
IsValid() const129 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 
ToString()137 std::string ServiceState::ToString() {
138   scoped_ptr<base::DictionaryValue> services(new base::DictionaryValue());
139 
140   scoped_ptr<base::DictionaryValue> cloud_print(new base::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 
LoginToGoogle(const std::string & service,const std::string & email,const std::string & password)160 std::string ServiceState::LoginToGoogle(const std::string& service,
161                                         const std::string& email,
162                                         const std::string& password) {
163   base::MessageLoopForIO loop;
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(
179       url, net::DEFAULT_PRIORITY, &fetcher_delegate, context.get());
180   int load_flags = request.load_flags();
181   load_flags = load_flags | net::LOAD_DO_NOT_SEND_COOKIES;
182   load_flags = load_flags | net::LOAD_DO_NOT_SAVE_COOKIES;
183   request.SetLoadFlags(load_flags);
184 
185   scoped_ptr<net::UploadElementReader> reader(
186       net::UploadOwnedBytesElementReader::CreateWithString(post_body));
187   request.set_upload(make_scoped_ptr(
188       net::UploadDataStream::CreateWithReader(reader.Pass(), 0)));
189   request.SetExtraRequestHeaderByName(
190       "Content-Type", "application/x-www-form-urlencoded", true);
191   request.set_method("POST");
192   request.Start();
193 
194   base::MessageLoop::current()->PostDelayedTask(
195       FROM_HERE,
196       base::MessageLoop::QuitClosure(),
197       base::TimeDelta::FromMilliseconds(kRequestTimeoutMs));
198 
199   base::MessageLoop::current()->Run();
200 
201   const char kAuthStart[] = "Auth=";
202   std::vector<std::string> lines;
203   Tokenize(fetcher_delegate.data(), "\r\n", &lines);
204   for (size_t i = 0; i < lines.size(); ++i) {
205     if (StartsWithASCII(lines[i], kAuthStart, false))
206       return lines[i].substr(arraysize(kAuthStart) - 1);
207   }
208 
209   return std::string();
210 }
211 
Configure(const std::string & email,const std::string & password,const std::string & proxy_id)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