blob: f2baddef2528631f602f61f7cc83fbc8d2576577 [file] [log] [blame]
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001// 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"
Ben Murdoch9ab55632013-07-18 11:57:30 +010010#include "base/message_loop/message_loop.h"
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010011#include "base/strings/string_util.h"
12#include "base/strings/utf_string_conversions.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000013#include "net/base/escape.h"
14#include "net/base/io_buffer.h"
15#include "net/base/load_flags.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000016#include "net/base/upload_bytes_element_reader.h"
17#include "net/base/upload_data_stream.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000018#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
22namespace {
23
24const char kCloudPrintJsonName[] = "cloud_print";
25const char kEnabledOptionName[] = "enabled";
26
27const char kEmailOptionName[] = "email";
28const char kPasswordOptionName[] = "password";
29const char kProxyIdOptionName[] = "proxy_id";
30const char kRobotEmailOptionName[] = "robot_email";
31const char kRobotTokenOptionName[] = "robot_refresh_token";
32const char kAuthTokenOptionName[] = "auth_token";
33const char kXmppAuthTokenOptionName[] = "xmpp_auth_token";
34
35const char kClientLoginUrl[] = "https://www.google.com/accounts/ClientLogin";
36
37const int64 kRequestTimeoutMs = 10 * 1000;
38
39class ServiceStateURLRequestDelegate : public net::URLRequest::Delegate {
40 public:
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000041 virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE {
Torne (Richard Coles)58218062012-11-14 11:43:16 +000042 if (request->GetResponseCode() == 200) {
43 Read(request);
44 if (request->status().is_io_pending())
45 return;
46 }
47 request->Cancel();
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010048 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +000049
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000050 virtual void OnReadCompleted(net::URLRequest* request,
51 int bytes_read) OVERRIDE {
Torne (Richard Coles)58218062012-11-14 11:43:16 +000052 Read(request);
53 if (!request->status().is_io_pending())
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010054 base::MessageLoop::current()->Quit();
55 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +000056
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;
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010067 while (request->Read(buf.get(), kBufSize, &num_bytes)) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +000068 data_.append(buf->data(), buf->data() + num_bytes);
69 }
70 }
71 std::string data_;
72};
73
74
75void 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
84ServiceState::ServiceState() {
85 Reset();
86}
87
88ServiceState::~ServiceState() {
89}
90
91void 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
100bool 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
129bool 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
137std::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
160std::string ServiceState::LoginToGoogle(const std::string& service,
161 const std::string& email,
162 const std::string& password) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100163 base::MessageLoop loop(base::MessageLoop::TYPE_IO);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000164
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
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000184 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)));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000188 request.SetExtraRequestHeaderByName(
189 "Content-Type", "application/x-www-form-urlencoded", true);
190 request.set_method("POST");
191 request.Start();
192
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100193 base::MessageLoop::current()->PostDelayedTask(
194 FROM_HERE,
195 base::MessageLoop::QuitClosure(),
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000196 base::TimeDelta::FromMilliseconds(kRequestTimeoutMs));
197
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100198 base::MessageLoop::current()->Run();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000199
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
212bool 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}