普通文本  |  194行  |  6.07 KB

// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/chromeos/login/user_image_downloader.h"

#include "base/json/json_reader.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/string_util.h"
#include "base/stringprintf.h"
#include "base/values.h"
#include "chrome/browser/chromeos/login/authenticator.h"
#include "chrome/browser/chromeos/login/image_downloader.h"
#include "chrome/browser/chromeos/login/user_manager.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/net/url_fetcher.h"
#include "content/browser/browser_thread.h"
#include "googleurl/src/gurl.h"

namespace chromeos {

namespace {

// Contacts API URL that returns all user info.
// TODO(avayvod): Find the way to receive less data for the user.
const char kUserInfoURL[] =
    "http://www.google.com/m8/feeds/contacts/default/thin?alt=json";

// Template for authorization header needed for all request to Contacts API.
const char kAuthorizationHeader[] = "Authorization: GoogleLogin auth=%s";

// Schema that identifies JSON node with image url.
const char kPhotoSchemaURL[] =
    "http://schemas.google.com/contacts/2008/rel#photo";

}  // namespace

UserImageDownloader::UserImageDownloader(const std::string& username,
                                         const std::string& auth_token)
    : username_(username),
      auth_token_(auth_token) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  if (auth_token_.empty())
    return;

  profile_fetcher_.reset(new URLFetcher(GURL(kUserInfoURL),
                                        URLFetcher::GET,
                                        this));
  profile_fetcher_->set_request_context(
      ProfileManager::GetDefaultProfile()->GetRequestContext());
  profile_fetcher_->set_extra_request_headers(
      base::StringPrintf(kAuthorizationHeader, auth_token_.c_str()));
  profile_fetcher_->Start();
}

UserImageDownloader::~UserImageDownloader() {
}

void UserImageDownloader::OnURLFetchComplete(
    const URLFetcher* source,
    const GURL& url,
    const net::URLRequestStatus& status,
    int response_code,
    const ResponseCookies& cookies,
    const std::string& data) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  if (response_code != 200) {
    LOG(ERROR) << "Response code is " << response_code;
    LOG(ERROR) << "Url is " << url.spec();
    LOG(ERROR) << "Data is " << data;
    return;
  }

  if (source == profile_fetcher_.get()) {
    GURL image_url;
    if (!GetImageURL(data, &image_url)) {
      LOG(ERROR) << "Didn't find image url in " << data;
      return;
    }
    VLOG(1) << "Sending request to " << image_url;
    new ImageDownloader(this, GURL(image_url), auth_token_);
  }
}

void UserImageDownloader::OnImageDecoded(const SkBitmap& decoded_image) {
  // Save the image to file and its path to preferences.
  chromeos::UserManager* user_manager = chromeos::UserManager::Get();
  if (user_manager) {
    if (user_manager->logged_in_user().email() == username_) {
      user_manager->SetLoggedInUserImage(decoded_image);
    }
    user_manager->SaveUserImage(username_, decoded_image);
  }
}

bool UserImageDownloader::GetImageURL(const std::string& json_data,
                                      GURL* image_url) const {
  if (!image_url) {
    NOTREACHED();
    return false;
  }

  // Data is in JSON format with image url located at the following path:
  // root > feed > entry > dictionary > link > dictionary > href.
  scoped_ptr<Value> root(base::JSONReader::Read(json_data, true));
  if (!root.get() || root->GetType() != Value::TYPE_DICTIONARY)
    return false;

  DictionaryValue* root_dictionary =
      static_cast<DictionaryValue*>(root.get());
  DictionaryValue* feed_dictionary = NULL;
  if (!root_dictionary->GetDictionary("feed", &feed_dictionary))
    return false;

  ListValue* entry_list = NULL;
  if (!feed_dictionary->GetList("entry", &entry_list))
    return false;

  return GetImageURLFromEntries(entry_list, image_url);
}

bool UserImageDownloader::GetImageURLFromEntries(ListValue* entry_list,
                                                 GURL* image_url) const {
  // The list contains info about all user's contacts including user
  // himself. We need to find entry for the user and then get his image.
  for (size_t i = 0; i < entry_list->GetSize(); ++i) {
    DictionaryValue* entry_dictionary = NULL;
    if (!entry_list->GetDictionary(i, &entry_dictionary))
      continue;

    ListValue* email_list = NULL;
    if (!entry_dictionary->GetList("gd$email", &email_list))
      continue;

    // Match entry email address to understand that this is user's entry.
    if (!IsUserEntry(email_list))
      continue;

    ListValue* link_list = NULL;
    if (!entry_dictionary->GetList("link", &link_list))
      continue;

    if (GetImageURLFromLinks(link_list, image_url))
      return true;
  }

  return false;
}

bool UserImageDownloader::IsUserEntry(ListValue* email_list) const {
  for (size_t i = 0; i < email_list->GetSize(); ++i) {
    DictionaryValue* email_dictionary = NULL;
    if (!email_list->GetDictionary(i, &email_dictionary))
      continue;

    std::string email;
    if (!email_dictionary->GetStringASCII("address", &email))
      continue;

    if (Authenticator::Canonicalize(email) == username_)
      return true;
  }
  return false;
}

bool UserImageDownloader::GetImageURLFromLinks(ListValue* link_list,
                                               GURL* image_url) const {
  // In entry's list of links there should be one with rel pointing to photo
  // schema.
  for (size_t i = 0; i < link_list->GetSize(); ++i) {
    DictionaryValue* link_dictionary = NULL;
    if (!link_list->GetDictionary(i, &link_dictionary))
      continue;

    std::string rel;
    if (!link_dictionary->GetStringASCII("rel", &rel))
      continue;

    if (rel != kPhotoSchemaURL)
      continue;

    std::string url;
    if (!link_dictionary->GetStringASCII("href", &url))
      continue;

    *image_url = GURL(url);
    return true;
  }
  return false;
}

}  // namespace chromeos