• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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