1 // Copyright (c) 2011 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 "chrome/browser/chromeos/login/user_image_downloader.h"
6
7 #include "base/json/json_reader.h"
8 #include "base/logging.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/string_util.h"
11 #include "base/stringprintf.h"
12 #include "base/values.h"
13 #include "chrome/browser/chromeos/login/authenticator.h"
14 #include "chrome/browser/chromeos/login/image_downloader.h"
15 #include "chrome/browser/chromeos/login/user_manager.h"
16 #include "chrome/browser/profiles/profile_manager.h"
17 #include "chrome/common/net/url_fetcher.h"
18 #include "content/browser/browser_thread.h"
19 #include "googleurl/src/gurl.h"
20
21 namespace chromeos {
22
23 namespace {
24
25 // Contacts API URL that returns all user info.
26 // TODO(avayvod): Find the way to receive less data for the user.
27 const char kUserInfoURL[] =
28 "http://www.google.com/m8/feeds/contacts/default/thin?alt=json";
29
30 // Template for authorization header needed for all request to Contacts API.
31 const char kAuthorizationHeader[] = "Authorization: GoogleLogin auth=%s";
32
33 // Schema that identifies JSON node with image url.
34 const char kPhotoSchemaURL[] =
35 "http://schemas.google.com/contacts/2008/rel#photo";
36
37 } // namespace
38
UserImageDownloader(const std::string & username,const std::string & auth_token)39 UserImageDownloader::UserImageDownloader(const std::string& username,
40 const std::string& auth_token)
41 : username_(username),
42 auth_token_(auth_token) {
43 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
44 if (auth_token_.empty())
45 return;
46
47 profile_fetcher_.reset(new URLFetcher(GURL(kUserInfoURL),
48 URLFetcher::GET,
49 this));
50 profile_fetcher_->set_request_context(
51 ProfileManager::GetDefaultProfile()->GetRequestContext());
52 profile_fetcher_->set_extra_request_headers(
53 base::StringPrintf(kAuthorizationHeader, auth_token_.c_str()));
54 profile_fetcher_->Start();
55 }
56
~UserImageDownloader()57 UserImageDownloader::~UserImageDownloader() {
58 }
59
OnURLFetchComplete(const URLFetcher * source,const GURL & url,const net::URLRequestStatus & status,int response_code,const ResponseCookies & cookies,const std::string & data)60 void UserImageDownloader::OnURLFetchComplete(
61 const URLFetcher* source,
62 const GURL& url,
63 const net::URLRequestStatus& status,
64 int response_code,
65 const ResponseCookies& cookies,
66 const std::string& data) {
67 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
68 if (response_code != 200) {
69 LOG(ERROR) << "Response code is " << response_code;
70 LOG(ERROR) << "Url is " << url.spec();
71 LOG(ERROR) << "Data is " << data;
72 return;
73 }
74
75 if (source == profile_fetcher_.get()) {
76 GURL image_url;
77 if (!GetImageURL(data, &image_url)) {
78 LOG(ERROR) << "Didn't find image url in " << data;
79 return;
80 }
81 VLOG(1) << "Sending request to " << image_url;
82 new ImageDownloader(this, GURL(image_url), auth_token_);
83 }
84 }
85
OnImageDecoded(const SkBitmap & decoded_image)86 void UserImageDownloader::OnImageDecoded(const SkBitmap& decoded_image) {
87 // Save the image to file and its path to preferences.
88 chromeos::UserManager* user_manager = chromeos::UserManager::Get();
89 if (user_manager) {
90 if (user_manager->logged_in_user().email() == username_) {
91 user_manager->SetLoggedInUserImage(decoded_image);
92 }
93 user_manager->SaveUserImage(username_, decoded_image);
94 }
95 }
96
GetImageURL(const std::string & json_data,GURL * image_url) const97 bool UserImageDownloader::GetImageURL(const std::string& json_data,
98 GURL* image_url) const {
99 if (!image_url) {
100 NOTREACHED();
101 return false;
102 }
103
104 // Data is in JSON format with image url located at the following path:
105 // root > feed > entry > dictionary > link > dictionary > href.
106 scoped_ptr<Value> root(base::JSONReader::Read(json_data, true));
107 if (!root.get() || root->GetType() != Value::TYPE_DICTIONARY)
108 return false;
109
110 DictionaryValue* root_dictionary =
111 static_cast<DictionaryValue*>(root.get());
112 DictionaryValue* feed_dictionary = NULL;
113 if (!root_dictionary->GetDictionary("feed", &feed_dictionary))
114 return false;
115
116 ListValue* entry_list = NULL;
117 if (!feed_dictionary->GetList("entry", &entry_list))
118 return false;
119
120 return GetImageURLFromEntries(entry_list, image_url);
121 }
122
GetImageURLFromEntries(ListValue * entry_list,GURL * image_url) const123 bool UserImageDownloader::GetImageURLFromEntries(ListValue* entry_list,
124 GURL* image_url) const {
125 // The list contains info about all user's contacts including user
126 // himself. We need to find entry for the user and then get his image.
127 for (size_t i = 0; i < entry_list->GetSize(); ++i) {
128 DictionaryValue* entry_dictionary = NULL;
129 if (!entry_list->GetDictionary(i, &entry_dictionary))
130 continue;
131
132 ListValue* email_list = NULL;
133 if (!entry_dictionary->GetList("gd$email", &email_list))
134 continue;
135
136 // Match entry email address to understand that this is user's entry.
137 if (!IsUserEntry(email_list))
138 continue;
139
140 ListValue* link_list = NULL;
141 if (!entry_dictionary->GetList("link", &link_list))
142 continue;
143
144 if (GetImageURLFromLinks(link_list, image_url))
145 return true;
146 }
147
148 return false;
149 }
150
IsUserEntry(ListValue * email_list) const151 bool UserImageDownloader::IsUserEntry(ListValue* email_list) const {
152 for (size_t i = 0; i < email_list->GetSize(); ++i) {
153 DictionaryValue* email_dictionary = NULL;
154 if (!email_list->GetDictionary(i, &email_dictionary))
155 continue;
156
157 std::string email;
158 if (!email_dictionary->GetStringASCII("address", &email))
159 continue;
160
161 if (Authenticator::Canonicalize(email) == username_)
162 return true;
163 }
164 return false;
165 }
166
GetImageURLFromLinks(ListValue * link_list,GURL * image_url) const167 bool UserImageDownloader::GetImageURLFromLinks(ListValue* link_list,
168 GURL* image_url) const {
169 // In entry's list of links there should be one with rel pointing to photo
170 // schema.
171 for (size_t i = 0; i < link_list->GetSize(); ++i) {
172 DictionaryValue* link_dictionary = NULL;
173 if (!link_list->GetDictionary(i, &link_dictionary))
174 continue;
175
176 std::string rel;
177 if (!link_dictionary->GetStringASCII("rel", &rel))
178 continue;
179
180 if (rel != kPhotoSchemaURL)
181 continue;
182
183 std::string url;
184 if (!link_dictionary->GetStringASCII("href", &url))
185 continue;
186
187 *image_url = GURL(url);
188 return true;
189 }
190 return false;
191 }
192
193 } // namespace chromeos
194