Home | History | Annotate | Download | only in login
      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 
     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 
     57 UserImageDownloader::~UserImageDownloader() {
     58 }
     59 
     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 
     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 
     97 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 
    123 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 
    151 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 
    167 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