Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(209)

Side by Side Diff: chrome/browser/profiles/profile_downloader.cc

Issue 8772055: Use Google Plus API to get profile information (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fix linux build Created 9 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/profiles/profile_downloader.h" 5 #include "chrome/browser/profiles/profile_downloader.h"
6 6
7 #include <string> 7 #include <string>
8 #include <vector> 8 #include <vector>
9 9
10 #include "base/json/json_reader.h" 10 #include "base/json/json_reader.h"
(...skipping 20 matching lines...) Expand all
31 #include "skia/ext/image_operations.h" 31 #include "skia/ext/image_operations.h"
32 32
33 using content::BrowserThread; 33 using content::BrowserThread;
34 34
35 namespace { 35 namespace {
36 36
37 // Template for optional authorization header when using an OAuth access token. 37 // Template for optional authorization header when using an OAuth access token.
38 const char kAuthorizationHeader[] = 38 const char kAuthorizationHeader[] =
39 "Authorization: Bearer %s"; 39 "Authorization: Bearer %s";
40 40
41 // URL requesting Picasa API for user info. 41 // URL requesting user info.
42 const char kUserEntryURL[] = 42 const char kUserEntryURL[] =
43 "http://picasaweb.google.com/data/entry/api/user/default?alt=json"; 43 "https://www.googleapis.com/oauth2/v1/userinfo?alt=json";
44 44
45 // OAuth scope for the Picasa API. 45 // OAuth scope for the user info API.
46 const char kPicasaScope[] = "http://picasaweb.google.com/data/"; 46 const char kAPIScope[] = "https://www.googleapis.com/auth/userinfo.profile";
47 47
48 // Path in JSON dictionary to user's photo thumbnail URL. 48 // Path in JSON dictionary to user's photo thumbnail URL.
49 const char kPhotoThumbnailURLPath[] = "entry.gphoto$thumbnail.$t"; 49 const char kPhotoThumbnailURLPath[] = "picture";
50 50
51 const char kNickNamePath[] = "entry.gphoto$nickname.$t"; 51 const char kNickNamePath[] = "name";
52 52
53 // Path format for specifying thumbnail's size. 53 // Path format for specifying thumbnail's size.
54 const char kThumbnailSizeFormat[] = "s%d-c"; 54 const char kThumbnailSizeFormat[] = "s%d-c";
55 // Default Picasa thumbnail size. 55 // Default thumbnail size.
56 const int kDefaultThumbnailSize = 64; 56 const int kDefaultThumbnailSize = 64;
57 57
58 // Separator of URL path components. 58 // Separator of URL path components.
59 const char kURLPathSeparator = '/'; 59 const char kURLPathSeparator = '/';
60 60
61 // Photo ID of the Picasa Web Albums profile picture (base64 of 0). 61 // Photo ID of the Picasa Web Albums profile picture (base64 of 0).
62 const char kPicasaPhotoId[] = "AAAAAAAAAAA"; 62 const char kPicasaPhotoId[] = "AAAAAAAAAAA";
63 63
64 // Photo version of the default PWA profile picture (base64 of 1). 64 // Photo version of the default PWA profile picture (base64 of 1).
65 const char kDefaultPicasaPhotoVersion[] = "AAAAAAAAAAE"; 65 const char kDefaultPicasaPhotoVersion[] = "AAAAAAAAAAE";
66 66
67 // Photo ID of the Google+ profile picture (base64 of 2). 67 // Photo ID of the Google+ profile picture (base64 of 2).
68 const char kGooglePlusPhotoId[] = "AAAAAAAAAAI"; 68 const char kGooglePlusPhotoId[] = "AAAAAAAAAAI";
69 69
70 // Photo version of the default Google+ profile picture (base64 of 0). 70 // Photo version of the default Google+ profile picture (base64 of 0).
71 const char kDefaultGooglePlusPhotoVersion[] = "AAAAAAAAAAA"; 71 const char kDefaultGooglePlusPhotoVersion[] = "AAAAAAAAAAA";
72 72
73 // Number of path components in profile picture URL. 73 // The minimum number of path components in profile picture URL.
74 const size_t kProfileImageURLPathComponentsCount = 7; 74 const size_t kProfileImageURLPathComponentsCount = 6;
75 75
76 // Index of path component with photo ID. 76 // Index of path component with photo ID.
77 const int kPhotoIdPathComponentIndex = 2; 77 const int kPhotoIdPathComponentIndex = 2;
78 78
79 // Index of path component with photo version. 79 // Index of path component with photo version.
80 const int kPhotoVersionPathComponentIndex = 3; 80 const int kPhotoVersionPathComponentIndex = 3;
81 81
82 // Given an image URL this function builds a new URL set to |size|.
83 // For example, if |size| was set to 256 and |old_url| was either:
84 // https://example.com/--Abc/AAAAAAAAAAI/AAAAAAAAACQ/Efg/photo.jpg
85 // or
86 // https://example.com/--Abc/AAAAAAAAAAI/AAAAAAAAACQ/Efg/s64-c/photo.jpg
87 // then return value in |new_url| would be:
88 // https://example.com/--Abc/AAAAAAAAAAI/AAAAAAAAACQ/Efg/s256-c/photo.jpg
89 bool GetImageURLWithSize(const GURL& old_url, int size, GURL* new_url) {
90 DCHECK(new_url);
91 std::vector<std::string> components;
92 base::SplitString(old_url.path(), kURLPathSeparator, &components);
93 if (components.size() == 0)
94 return false;
95
96 const std::string& old_spec = old_url.spec();
97 std::string default_size_component(
98 base::StringPrintf(kThumbnailSizeFormat, kDefaultThumbnailSize));
99 std::string new_size_component(
100 base::StringPrintf(kThumbnailSizeFormat, size));
101
102 size_t pos = old_spec.find(default_size_component);
103 size_t end = std::string::npos;
104 if (pos != std::string::npos) {
105 // The default size is already specified in the URL so it needs to be
106 // replaced with the new size.
107 end = pos + default_size_component.size();
108 } else {
109 // The default size is not in the URL so try to insert it before the last
110 // component.
111 const std::string& file_name = old_url.ExtractFileName();
112 if (!file_name.empty()) {
113 pos = old_spec.find(file_name);
114 end = pos - 1;
115 }
116 }
117
118 if (pos != std::string::npos) {
119 std::string new_spec = old_spec.substr(0, pos) + new_size_component +
120 old_spec.substr(end);
121 *new_url = GURL(new_spec);
122 return new_url->is_valid();
123 }
124
125 // We can't set the image size, just use the default size.
126 *new_url = old_url;
127 return true;
128 }
129
82 } // namespace 130 } // namespace
83 131
84 bool ProfileDownloader::GetProfileNickNameAndImageURL(const std::string& data, 132 // static
85 string16* nick_name, 133 bool ProfileDownloader::GetProfileNameAndImageURL(const std::string& data,
86 std::string* url) const { 134 string16* nick_name,
135 std::string* url,
136 int image_size) {
87 DCHECK(nick_name); 137 DCHECK(nick_name);
88 DCHECK(url); 138 DCHECK(url);
89 *nick_name = string16(); 139 *nick_name = string16();
90 *url = std::string(); 140 *url = std::string();
91 141
92 int error_code = -1; 142 int error_code = -1;
93 std::string error_message; 143 std::string error_message;
94 scoped_ptr<base::Value> root_value(base::JSONReader::ReadAndReturnError( 144 scoped_ptr<base::Value> root_value(base::JSONReader::ReadAndReturnError(
95 data, false, &error_code, &error_message)); 145 data, false, &error_code, &error_message));
96 if (!root_value.get()) { 146 if (!root_value.get()) {
97 LOG(ERROR) << "Error while parsing Picasa user entry response: " 147 LOG(ERROR) << "Error while parsing user entry response: "
98 << error_message; 148 << error_message;
99 return false; 149 return false;
100 } 150 }
101 if (!root_value->IsType(base::Value::TYPE_DICTIONARY)) { 151 if (!root_value->IsType(base::Value::TYPE_DICTIONARY)) {
102 LOG(ERROR) << "JSON root is not a dictionary: " 152 LOG(ERROR) << "JSON root is not a dictionary: "
103 << root_value->GetType(); 153 << root_value->GetType();
104 return false; 154 return false;
105 } 155 }
106 base::DictionaryValue* root_dictionary = 156 base::DictionaryValue* root_dictionary =
107 static_cast<base::DictionaryValue*>(root_value.get()); 157 static_cast<base::DictionaryValue*>(root_value.get());
108 158
109 if (!root_dictionary->GetString(kNickNamePath, nick_name)) { 159 root_dictionary->GetString(kNickNamePath, nick_name);
110 LOG(ERROR) << "Can't find nick name path in JSON data: " 160
111 << data; 161 std::string url_string;
112 return false; 162 if (root_dictionary->GetString(kPhotoThumbnailURLPath, &url_string)) {
163 GURL new_url;
164 if (!GetImageURLWithSize(GURL(url_string), image_size, &new_url)) {
165 LOG(ERROR) << "GetImageURLWithSize failed for url: " << url_string;
166 return false;
167 }
168 *url = new_url.spec();
113 } 169 }
114 170
115 std::string thumbnail_url_string; 171 // The profile data is considered valid as long as it has a name or a picture.
116 if (!root_dictionary->GetString( 172 return !nick_name->empty() || !url->empty();
117 kPhotoThumbnailURLPath, &thumbnail_url_string)) {
118 LOG(ERROR) << "Can't find thumbnail path in JSON data: "
119 << data;
120 return false;
121 }
122
123 // Try to change the size of thumbnail we are going to get.
124 // Typical URL looks like this:
125 // http://lh0.ggpht.com/-abcd1aBCDEf/AAAA/AAA_A/abc12/s64-c/1234567890.jpg
126 std::string default_thumbnail_size_path_component(
127 base::StringPrintf(kThumbnailSizeFormat, kDefaultThumbnailSize));
128 int image_size = delegate_->GetDesiredImageSideLength();
129 std::string new_thumbnail_size_path_component(
130 base::StringPrintf(kThumbnailSizeFormat, image_size));
131 size_t thumbnail_size_pos =
132 thumbnail_url_string.find(default_thumbnail_size_path_component);
133 if (thumbnail_size_pos != std::string::npos) {
134 size_t thumbnail_size_end =
135 thumbnail_size_pos + default_thumbnail_size_path_component.size();
136 thumbnail_url_string =
137 thumbnail_url_string.substr(0, thumbnail_size_pos) +
138 new_thumbnail_size_path_component +
139 thumbnail_url_string.substr(
140 thumbnail_size_end,
141 thumbnail_url_string.size() - thumbnail_size_end);
142 } else {
143 LOG(WARNING) << "Hasn't found thumbnail size part in image URL: "
144 << thumbnail_url_string;
145 // Use the thumbnail URL we have.
146 }
147
148 GURL thumbnail_url(thumbnail_url_string);
149 if (!thumbnail_url.is_valid()) {
150 LOG(ERROR) << "Thumbnail URL is not valid: " << thumbnail_url_string;
151 return false;
152 }
153 *url = thumbnail_url.spec();
154 return true;
155 } 173 }
156 174
157 bool ProfileDownloader::IsDefaultProfileImageURL(const std::string& url) const { 175 // static
176 bool ProfileDownloader::IsDefaultProfileImageURL(const std::string& url) {
177 if (url.empty())
178 return true;
179
158 GURL image_url_object(url); 180 GURL image_url_object(url);
159 DCHECK(image_url_object.is_valid()); 181 DCHECK(image_url_object.is_valid());
160 VLOG(1) << "URL to check for default image: " << image_url_object.spec(); 182 VLOG(1) << "URL to check for default image: " << image_url_object.spec();
161 std::vector<std::string> path_components; 183 std::vector<std::string> path_components;
162 base::SplitString(image_url_object.path(), 184 base::SplitString(image_url_object.path(),
163 kURLPathSeparator, 185 kURLPathSeparator,
164 &path_components); 186 &path_components);
165 187
166 if (path_components.size() != kProfileImageURLPathComponentsCount) 188 if (path_components.size() < kProfileImageURLPathComponentsCount)
167 return false; 189 return false;
168 190
169 const std::string& photo_id = path_components[kPhotoIdPathComponentIndex]; 191 const std::string& photo_id = path_components[kPhotoIdPathComponentIndex];
170 const std::string& photo_version = 192 const std::string& photo_version =
171 path_components[kPhotoVersionPathComponentIndex]; 193 path_components[kPhotoVersionPathComponentIndex];
172 194
173 // There are at least two pairs of (ID, version) for the default photo: 195 // There are at least two pairs of (ID, version) for the default photo:
174 // the default Google+ profile photo and the default Picasa profile photo. 196 // the default Google+ profile photo and the default Picasa profile photo.
175 return ((photo_id == kPicasaPhotoId && 197 return ((photo_id == kPicasaPhotoId &&
176 photo_version == kDefaultPicasaPhotoVersion) || 198 photo_version == kDefaultPicasaPhotoVersion) ||
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
236 base::StringPrintf(kAuthorizationHeader, auth_token_.c_str())); 258 base::StringPrintf(kAuthorizationHeader, auth_token_.c_str()));
237 } 259 }
238 user_entry_fetcher_->Start(); 260 user_entry_fetcher_->Start();
239 } 261 }
240 262
241 void ProfileDownloader::StartFetchingOAuth2AccessToken() { 263 void ProfileDownloader::StartFetchingOAuth2AccessToken() {
242 TokenService* service = delegate_->GetBrowserProfile()->GetTokenService(); 264 TokenService* service = delegate_->GetBrowserProfile()->GetTokenService();
243 DCHECK(!service->GetOAuth2LoginRefreshToken().empty()); 265 DCHECK(!service->GetOAuth2LoginRefreshToken().empty());
244 266
245 std::vector<std::string> scopes; 267 std::vector<std::string> scopes;
246 scopes.push_back(kPicasaScope); 268 scopes.push_back(kAPIScope);
247 oauth2_access_token_fetcher_.reset(new OAuth2AccessTokenFetcher( 269 oauth2_access_token_fetcher_.reset(new OAuth2AccessTokenFetcher(
248 this, delegate_->GetBrowserProfile()->GetRequestContext())); 270 this, delegate_->GetBrowserProfile()->GetRequestContext()));
249 oauth2_access_token_fetcher_->Start( 271 oauth2_access_token_fetcher_->Start(
250 GaiaUrls::GetInstance()->oauth2_chrome_client_id(), 272 GaiaUrls::GetInstance()->oauth2_chrome_client_id(),
251 GaiaUrls::GetInstance()->oauth2_chrome_client_secret(), 273 GaiaUrls::GetInstance()->oauth2_chrome_client_secret(),
252 service->GetOAuth2LoginRefreshToken(), 274 service->GetOAuth2LoginRefreshToken(),
253 scopes); 275 scopes);
254 } 276 }
255 277
256 ProfileDownloader::~ProfileDownloader() {} 278 ProfileDownloader::~ProfileDownloader() {}
257 279
258 void ProfileDownloader::OnURLFetchComplete(const content::URLFetcher* source) { 280 void ProfileDownloader::OnURLFetchComplete(const content::URLFetcher* source) {
259 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 281 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
260 std::string data; 282 std::string data;
261 source->GetResponseAsString(&data); 283 source->GetResponseAsString(&data);
262 if (source->GetResponseCode() != 200) { 284 if (source->GetResponseCode() != 200) {
263 LOG(ERROR) << "Response code is " << source->GetResponseCode(); 285 LOG(ERROR) << "Response code is " << source->GetResponseCode();
264 LOG(ERROR) << "Url is " << source->GetURL().spec(); 286 LOG(ERROR) << "Url is " << source->GetURL().spec();
265 LOG(ERROR) << "Data is " << data; 287 LOG(ERROR) << "Data is " << data;
266 delegate_->OnDownloadComplete(this, false); 288 delegate_->OnDownloadComplete(this, false);
267 return; 289 return;
268 } 290 }
269 291
270 if (source == user_entry_fetcher_.get()) { 292 if (source == user_entry_fetcher_.get()) {
271 std::string image_url; 293 std::string image_url;
272 if (!GetProfileNickNameAndImageURL(data, &profile_full_name_, &image_url)) { 294 if (!GetProfileNameAndImageURL(data, &profile_full_name_, &image_url,
295 delegate_->GetDesiredImageSideLength())) {
273 delegate_->OnDownloadComplete(this, false); 296 delegate_->OnDownloadComplete(this, false);
274 return; 297 return;
275 } 298 }
276 if (IsDefaultProfileImageURL(image_url)) { 299 if (IsDefaultProfileImageURL(image_url)) {
277 VLOG(1) << "User has default profile picture"; 300 VLOG(1) << "User has default profile picture";
278 picture_status_ = PICTURE_DEFAULT; 301 picture_status_ = PICTURE_DEFAULT;
279 delegate_->OnDownloadComplete(this, true); 302 delegate_->OnDownloadComplete(this, true);
280 return; 303 return;
281 } 304 }
282 if (!image_url.empty() && image_url == delegate_->GetCachedPictureURL()) { 305 if (!image_url.empty() && image_url == delegate_->GetCachedPictureURL()) {
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
352 void ProfileDownloader::OnGetTokenSuccess(const std::string& access_token) { 375 void ProfileDownloader::OnGetTokenSuccess(const std::string& access_token) {
353 auth_token_ = access_token; 376 auth_token_ = access_token;
354 StartFetchingImage(); 377 StartFetchingImage();
355 } 378 }
356 379
357 // Callback for OAuth2AccessTokenFetcher on failure. 380 // Callback for OAuth2AccessTokenFetcher on failure.
358 void ProfileDownloader::OnGetTokenFailure(const GoogleServiceAuthError& error) { 381 void ProfileDownloader::OnGetTokenFailure(const GoogleServiceAuthError& error) {
359 LOG(WARNING) << "ProfileDownloader: token request using refresh token failed"; 382 LOG(WARNING) << "ProfileDownloader: token request using refresh token failed";
360 delegate_->OnDownloadComplete(this, false); 383 delegate_->OnDownloadComplete(this, false);
361 } 384 }
OLDNEW
« no previous file with comments | « chrome/browser/profiles/profile_downloader.h ('k') | chrome/browser/profiles/profile_downloader_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698