- add sources.
[platform/framework/web/crosswalk.git] / src / components / autofill / content / browser / wallet / wallet_signin_helper.cc
1 // Copyright 2013 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 "components/autofill/content/browser/wallet/wallet_signin_helper.h"
6
7 #include "base/callback_helpers.h"
8 #include "base/json/json_reader.h"
9 #include "base/logging.h"
10 #include "base/rand_util.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/time/time.h"
14 #include "base/values.h"
15 #include "components/autofill/content/browser/wallet/wallet_service_url.h"
16 #include "components/autofill/content/browser/wallet/wallet_signin_helper_delegate.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "google_apis/gaia/google_service_auth_error.h"
19 #include "net/base/escape.h"
20 #include "net/cookies/canonical_cookie.h"
21 #include "net/cookies/cookie_monster.h"
22 #include "net/cookies/cookie_options.h"
23 #include "net/cookies/cookie_store.h"
24 #include "net/url_request/url_fetcher.h"
25 #include "net/url_request/url_request_context.h"
26 #include "net/url_request/url_request_context_getter.h"
27
28 namespace autofill {
29 namespace wallet {
30
31 namespace {
32
33 // Toolbar::GetAccountInfo API URL (JSON).
34 const char kGetAccountInfoUrlFormat[] =
35     "https://clients1.google.com/tbproxy/getaccountinfo?key=%d&rv=2"
36     "&requestor=chrome";
37
38 const char kWalletCookieName[] = "gdtoken";
39
40 // Callback for retrieving Google Wallet cookies. |callback| is passed the
41 // retrieved cookies and posted back to the UI thread. |cookies| is any Google
42 // Wallet cookies.
43 void GetGoogleCookiesCallback(
44     const base::Callback<void(const std::string&)>& callback,
45     const net::CookieList& cookies) {
46   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
47
48   // Cookies for parent domains will also be returned; we only want cookies with
49   // exact host matches.
50   std::string host = wallet::GetPassiveAuthUrl().host();
51   std::string wallet_cookie;
52   for (size_t i = 0; i < cookies.size(); ++i) {
53     if (LowerCaseEqualsASCII(cookies[i].Name(), kWalletCookieName) &&
54         LowerCaseEqualsASCII(cookies[i].Domain(), host.c_str())) {
55       wallet_cookie = cookies[i].Value();
56       break;
57     }
58   }
59   content::BrowserThread::PostTask(content::BrowserThread::UI,
60                                    FROM_HERE,
61                                    base::Bind(callback, wallet_cookie));
62 }
63
64 // Gets Google Wallet cookies. Must be called on the IO thread.
65 // |request_context_getter| is a getter for the current request context.
66 // |callback| is called when retrieving cookies is completed.
67 void GetGoogleCookies(
68     scoped_refptr<net::URLRequestContextGetter> request_context_getter,
69     const base::Callback<void(const std::string&)>& callback) {
70   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
71
72   net::URLRequestContext* url_request_context =
73       request_context_getter->GetURLRequestContext();
74   net::CookieStore* cookie_store = url_request_context ?
75       url_request_context->cookie_store() : NULL;
76   net::CookieMonster* cookie_monster = cookie_store ?
77       cookie_store->GetCookieMonster() : NULL;
78   if (!cookie_monster) {
79     content::BrowserThread::PostTask(content::BrowserThread::UI,
80                                      FROM_HERE,
81                                      base::Bind(callback, std::string()));
82     return;
83   }
84
85   net::CookieOptions cookie_options;
86   cookie_options.set_include_httponly();
87   cookie_monster->GetAllCookiesForURLWithOptionsAsync(
88       wallet::GetPassiveAuthUrl().GetWithEmptyPath(),
89       cookie_options,
90       base::Bind(&GetGoogleCookiesCallback, callback));
91 }
92
93 }  // namespace
94
95 WalletSigninHelper::WalletSigninHelper(
96     WalletSigninHelperDelegate* delegate,
97     net::URLRequestContextGetter* getter)
98     : delegate_(delegate),
99       getter_(getter),
100       state_(IDLE),
101       weak_ptr_factory_(this) {
102   DCHECK(delegate_);
103 }
104
105 WalletSigninHelper::~WalletSigninHelper() {
106 }
107
108 void WalletSigninHelper::StartPassiveSignin() {
109   DCHECK_EQ(IDLE, state_);
110   DCHECK(!url_fetcher_);
111
112   state_ = PASSIVE_EXECUTING_SIGNIN;
113   emails_.clear();
114   const GURL& url = wallet::GetPassiveAuthUrl();
115   url_fetcher_.reset(net::URLFetcher::Create(
116       0, url, net::URLFetcher::GET, this));
117   url_fetcher_->SetRequestContext(getter_);
118   url_fetcher_->Start();
119 }
120
121 void WalletSigninHelper::StartUserNameFetch() {
122   DCHECK_EQ(state_, IDLE);
123   DCHECK(!url_fetcher_);
124
125   state_ = USERNAME_FETCHING_USERINFO;
126   emails_.clear();
127   StartFetchingUserNameFromSession();
128 }
129
130 void WalletSigninHelper::StartWalletCookieValueFetch() {
131   scoped_refptr<net::URLRequestContextGetter> request_context(getter_);
132   if (!request_context.get()) {
133     ReturnWalletCookieValue(std::string());
134     return;
135   }
136
137   base::Callback<void(const std::string&)> callback = base::Bind(
138       &WalletSigninHelper::ReturnWalletCookieValue,
139       weak_ptr_factory_.GetWeakPtr());
140
141   base::Closure task = base::Bind(&GetGoogleCookies, request_context, callback);
142   content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, task);
143 }
144
145 std::string WalletSigninHelper::GetGetAccountInfoUrlForTesting() const {
146   return base::StringPrintf(kGetAccountInfoUrlFormat, 0);
147 }
148
149 void WalletSigninHelper::OnServiceError(const GoogleServiceAuthError& error) {
150   const State state_with_error = state_;
151   state_ = IDLE;
152   url_fetcher_.reset();
153
154   switch(state_with_error) {
155     case IDLE:
156       NOTREACHED();
157       break;
158
159     case PASSIVE_EXECUTING_SIGNIN:  /*FALLTHROUGH*/
160     case PASSIVE_FETCHING_USERINFO:
161       delegate_->OnPassiveSigninFailure(error);
162       break;
163
164     case USERNAME_FETCHING_USERINFO:
165       delegate_->OnUserNameFetchFailure(error);
166       break;
167   }
168 }
169
170 void WalletSigninHelper::OnOtherError() {
171   OnServiceError(GoogleServiceAuthError::AuthErrorNone());
172 }
173
174 void WalletSigninHelper::OnURLFetchComplete(
175     const net::URLFetcher* fetcher) {
176   DCHECK_EQ(url_fetcher_.get(), fetcher);
177   if (!fetcher->GetStatus().is_success() ||
178       fetcher->GetResponseCode() < 200 ||
179       fetcher->GetResponseCode() >= 300) {
180     LOG(ERROR) << "URLFetchFailure: state=" << state_
181                << " r=" << fetcher->GetResponseCode()
182                << " s=" << fetcher->GetStatus().status()
183                << " e=" << fetcher->GetStatus().error();
184     OnOtherError();
185     return;
186   }
187
188   switch (state_) {
189     case USERNAME_FETCHING_USERINFO:  /*FALLTHROUGH*/
190     case PASSIVE_FETCHING_USERINFO:
191       ProcessGetAccountInfoResponseAndFinish();
192       break;
193
194     case PASSIVE_EXECUTING_SIGNIN:
195       if (ParseSignInResponse()) {
196         url_fetcher_.reset();
197         state_ = PASSIVE_FETCHING_USERINFO;
198         StartFetchingUserNameFromSession();
199       }
200       break;
201
202     default:
203       NOTREACHED() << "unexpected state_=" << state_;
204   }
205 }
206
207 void WalletSigninHelper::StartFetchingUserNameFromSession() {
208   const int random_number = static_cast<int>(base::RandUint64() % INT_MAX);
209   url_fetcher_.reset(
210       net::URLFetcher::Create(
211           0,
212           GURL(base::StringPrintf(kGetAccountInfoUrlFormat, random_number)),
213           net::URLFetcher::GET,
214           this));
215   url_fetcher_->SetRequestContext(getter_);
216   url_fetcher_->Start();  // This will result in OnURLFetchComplete callback.
217 }
218
219 void WalletSigninHelper::ProcessGetAccountInfoResponseAndFinish() {
220   if (!ParseGetAccountInfoResponse(url_fetcher_.get(), &emails_)) {
221     LOG(ERROR) << "failed to get the user emails";
222     OnOtherError();
223     return;
224   }
225
226   const State finishing_state = state_;
227   state_ = IDLE;
228   url_fetcher_.reset();
229   switch(finishing_state) {
230     case USERNAME_FETCHING_USERINFO:
231       delegate_->OnUserNameFetchSuccess(emails_);
232       break;
233
234     case PASSIVE_FETCHING_USERINFO:
235       delegate_->OnPassiveSigninSuccess(emails_);
236       break;
237
238     default:
239       NOTREACHED() << "unexpected state_=" << finishing_state;
240   }
241 }
242
243 bool WalletSigninHelper::ParseSignInResponse() {
244   if (!url_fetcher_) {
245     NOTREACHED();
246     return false;
247   }
248
249   std::string data;
250   if (!url_fetcher_->GetResponseAsString(&data)) {
251     DVLOG(1) << "failed to GetResponseAsString";
252     OnOtherError();
253     return false;
254   }
255
256   if (!LowerCaseEqualsASCII(data, "yes")) {
257     OnServiceError(
258         GoogleServiceAuthError(GoogleServiceAuthError::USER_NOT_SIGNED_UP));
259     return false;
260   }
261
262   return true;
263 }
264
265 bool WalletSigninHelper::ParseGetAccountInfoResponse(
266     const net::URLFetcher* fetcher, std::vector<std::string>* emails) {
267   DCHECK(emails);
268
269   std::string data;
270   if (!fetcher->GetResponseAsString(&data)) {
271     DVLOG(1) << "failed to GetResponseAsString";
272     return false;
273   }
274
275   scoped_ptr<base::Value> value(base::JSONReader::Read(data));
276   if (!value.get() || value->GetType() != base::Value::TYPE_DICTIONARY) {
277     DVLOG(1) << "failed to parse JSON response";
278     return false;
279   }
280
281   DictionaryValue* dict = static_cast<base::DictionaryValue*>(value.get());
282   base::ListValue* user_info;
283   if (!dict->GetListWithoutPathExpansion("user_info", &user_info)) {
284     DVLOG(1) << "no user_info in JSON response";
285     return false;
286   }
287
288   if (user_info->empty()) {
289     DVLOG(1) << "empty list in JSON response";
290     return false;
291   }
292
293   // |user_info| will contain each signed in user in the cookie jar.
294   for (size_t i = 0; i < user_info->GetSize(); ++i) {
295     base::DictionaryValue* user_info_detail;
296     if (!user_info->GetDictionary(i, &user_info_detail)) {
297       DVLOG(1) << "malformed dictionary in JSON response";
298       return false;
299     }
300
301     std::string email;
302     if (!user_info_detail->GetStringWithoutPathExpansion("email", &email) ||
303         email.empty()) {
304       DVLOG(1) << "no email in JSON response";
305       return false;
306     }
307
308     emails->push_back(email);
309   }
310
311   return true;
312 }
313
314 void WalletSigninHelper::ReturnWalletCookieValue(
315     const std::string& cookie_value) {
316   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
317
318   delegate_->OnDidFetchWalletCookieValue(cookie_value);
319 }
320
321 }  // namespace wallet
322 }  // namespace autofill