// 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