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

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: Address review comments 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.
Ivan Korotkov 2011/12/04 15:08:52 URL requesting G+ API for user info
sail 2011/12/05 19:26:12 It turns out that using the G+ API won't work. It
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";
Ivan Korotkov 2011/12/04 15:08:52 We probably don't need this one since we're not us
sail 2011/12/05 19:26:12 Since we're using the G+ API it seems safer to lea
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:
84 // https://example.com/--Abc/AAAAAAAAAAI/AAAAAAAAACQ/Efg/photo.jpg
85 // then return value in |new_url| would be:
86 // https://example.com/--Abc/AAAAAAAAAAI/AAAAAAAAACQ/Efg/s256-c/photo.jpg
Munjal (Google) 2011/12/05 09:09:24 It would be nice to mention the other case also wh
sail 2011/12/05 19:26:12 Done.
87 bool GetImageURLWithSize(const GURL& old_url, int size, GURL* new_url) {
88 DCHECK(new_url);
89 std::vector<std::string> components;
90 base::SplitString(old_url.path(), kURLPathSeparator, &components);
91 if (components.size() == 0)
92 return false;
93
94 const std::string& old_spec = old_url.spec();
95 std::string default_size_component(
96 base::StringPrintf(kThumbnailSizeFormat, kDefaultThumbnailSize));
97 std::string new_size_component(
98 base::StringPrintf(kThumbnailSizeFormat, size));
99
100 size_t pos = old_spec.find(default_size_component);
101 size_t end = std::string::npos;
102 if (pos != std::string::npos) {
103 // The default size is already specified in the URL so it needs to be
104 // replaced with the new size.
105 end = pos + default_size_component.size();
106 } else {
107 // The default size is not in the URL so try to insert it before the last
108 // component.
109 const std::string& file_name = old_url.ExtractFileName();
110 if (!file_name.empty()) {
111 pos = old_spec.find(file_name);
112 end = pos - 1;
113 }
114 }
115
116 if (pos != std::string::npos) {
Munjal (Google) 2011/12/05 09:09:24 NIt: this can be a DCHECK or a CHECk instead of if
sail 2011/12/05 19:26:12 pos can be std::string::npos if the URL doesn't ha
117 std::string new_spec = old_spec.substr(0, pos) + new_size_component +
118 old_spec.substr(end);
119 *new_url = GURL(new_spec);
120 return new_url->is_valid();
121 }
122
123 // We can't set the image size, just use the default size.
124 *new_url = old_url;
125 return true;
126 }
127
82 } // namespace 128 } // namespace
83 129
84 bool ProfileDownloader::GetProfileNickNameAndImageURL(const std::string& data, 130 // static
85 string16* nick_name, 131 bool ProfileDownloader::GetProfileNameAndImageURL(const std::string& data,
86 std::string* url) const { 132 string16* nick_name,
133 std::string* url,
134 int image_size) {
87 DCHECK(nick_name); 135 DCHECK(nick_name);
88 DCHECK(url); 136 DCHECK(url);
89 *nick_name = string16(); 137 *nick_name = string16();
90 *url = std::string(); 138 *url = std::string();
91 139
92 int error_code = -1; 140 int error_code = -1;
93 std::string error_message; 141 std::string error_message;
94 scoped_ptr<base::Value> root_value(base::JSONReader::ReadAndReturnError( 142 scoped_ptr<base::Value> root_value(base::JSONReader::ReadAndReturnError(
95 data, false, &error_code, &error_message)); 143 data, false, &error_code, &error_message));
96 if (!root_value.get()) { 144 if (!root_value.get()) {
97 LOG(ERROR) << "Error while parsing Picasa user entry response: " 145 LOG(ERROR) << "Error while parsing user entry response: "
98 << error_message; 146 << error_message;
99 return false; 147 return false;
100 } 148 }
101 if (!root_value->IsType(base::Value::TYPE_DICTIONARY)) { 149 if (!root_value->IsType(base::Value::TYPE_DICTIONARY)) {
102 LOG(ERROR) << "JSON root is not a dictionary: " 150 LOG(ERROR) << "JSON root is not a dictionary: "
103 << root_value->GetType(); 151 << root_value->GetType();
104 return false; 152 return false;
105 } 153 }
106 base::DictionaryValue* root_dictionary = 154 base::DictionaryValue* root_dictionary =
107 static_cast<base::DictionaryValue*>(root_value.get()); 155 static_cast<base::DictionaryValue*>(root_value.get());
108 156
109 if (!root_dictionary->GetString(kNickNamePath, nick_name)) { 157 root_dictionary->GetString(kNickNamePath, nick_name);
110 LOG(ERROR) << "Can't find nick name path in JSON data: " 158
111 << data; 159 std::string url_string;
112 return false; 160 if (root_dictionary->GetString(kPhotoThumbnailURLPath, &url_string)) {
161 GURL new_url;
162 if (!GetImageURLWithSize(GURL(url_string), image_size, &new_url)) {
163 LOG(ERROR) << "GetImageURLWithSize failed for url: " << url_string;
164 return false;
165 }
166 *url = new_url.spec();
113 } 167 }
114 168
115 std::string thumbnail_url_string; 169 // The profile data is considered valid as long as it has a name or a picture.
116 if (!root_dictionary->GetString( 170 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 } 171 }
156 172
157 bool ProfileDownloader::IsDefaultProfileImageURL(const std::string& url) const { 173 bool ProfileDownloader::IsDefaultProfileImageURL(const std::string& url) const {
174 if (url.empty())
175 return true;
176
158 GURL image_url_object(url); 177 GURL image_url_object(url);
159 DCHECK(image_url_object.is_valid()); 178 DCHECK(image_url_object.is_valid());
160 VLOG(1) << "URL to check for default image: " << image_url_object.spec(); 179 VLOG(1) << "URL to check for default image: " << image_url_object.spec();
161 std::vector<std::string> path_components; 180 std::vector<std::string> path_components;
162 base::SplitString(image_url_object.path(), 181 base::SplitString(image_url_object.path(),
163 kURLPathSeparator, 182 kURLPathSeparator,
164 &path_components); 183 &path_components);
165 184
166 if (path_components.size() != kProfileImageURLPathComponentsCount) 185 if (path_components.size() < kProfileImageURLPathComponentsCount)
167 return false; 186 return false;
168 187
169 const std::string& photo_id = path_components[kPhotoIdPathComponentIndex]; 188 const std::string& photo_id = path_components[kPhotoIdPathComponentIndex];
170 const std::string& photo_version = 189 const std::string& photo_version =
171 path_components[kPhotoVersionPathComponentIndex]; 190 path_components[kPhotoVersionPathComponentIndex];
172 191
173 // There are at least two pairs of (ID, version) for the default photo: 192 // 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. 193 // the default Google+ profile photo and the default Picasa profile photo.
175 return ((photo_id == kPicasaPhotoId && 194 return ((photo_id == kPicasaPhotoId &&
176 photo_version == kDefaultPicasaPhotoVersion) || 195 photo_version == kDefaultPicasaPhotoVersion) ||
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
236 base::StringPrintf(kAuthorizationHeader, auth_token_.c_str())); 255 base::StringPrintf(kAuthorizationHeader, auth_token_.c_str()));
237 } 256 }
238 user_entry_fetcher_->Start(); 257 user_entry_fetcher_->Start();
239 } 258 }
240 259
241 void ProfileDownloader::StartFetchingOAuth2AccessToken() { 260 void ProfileDownloader::StartFetchingOAuth2AccessToken() {
242 TokenService* service = delegate_->GetBrowserProfile()->GetTokenService(); 261 TokenService* service = delegate_->GetBrowserProfile()->GetTokenService();
243 DCHECK(!service->GetOAuth2LoginRefreshToken().empty()); 262 DCHECK(!service->GetOAuth2LoginRefreshToken().empty());
244 263
245 std::vector<std::string> scopes; 264 std::vector<std::string> scopes;
246 scopes.push_back(kPicasaScope); 265 scopes.push_back(kAPIScope);
247 oauth2_access_token_fetcher_.reset(new OAuth2AccessTokenFetcher( 266 oauth2_access_token_fetcher_.reset(new OAuth2AccessTokenFetcher(
248 this, delegate_->GetBrowserProfile()->GetRequestContext())); 267 this, delegate_->GetBrowserProfile()->GetRequestContext()));
249 oauth2_access_token_fetcher_->Start( 268 oauth2_access_token_fetcher_->Start(
250 GaiaUrls::GetInstance()->oauth2_chrome_client_id(), 269 GaiaUrls::GetInstance()->oauth2_chrome_client_id(),
251 GaiaUrls::GetInstance()->oauth2_chrome_client_secret(), 270 GaiaUrls::GetInstance()->oauth2_chrome_client_secret(),
252 service->GetOAuth2LoginRefreshToken(), 271 service->GetOAuth2LoginRefreshToken(),
253 scopes); 272 scopes);
254 } 273 }
255 274
256 ProfileDownloader::~ProfileDownloader() {} 275 ProfileDownloader::~ProfileDownloader() {}
257 276
258 void ProfileDownloader::OnURLFetchComplete(const content::URLFetcher* source) { 277 void ProfileDownloader::OnURLFetchComplete(const content::URLFetcher* source) {
259 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 278 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
260 std::string data; 279 std::string data;
261 source->GetResponseAsString(&data); 280 source->GetResponseAsString(&data);
262 if (source->GetResponseCode() != 200) { 281 if (source->GetResponseCode() != 200) {
263 LOG(ERROR) << "Response code is " << source->GetResponseCode(); 282 LOG(ERROR) << "Response code is " << source->GetResponseCode();
264 LOG(ERROR) << "Url is " << source->GetURL().spec(); 283 LOG(ERROR) << "Url is " << source->GetURL().spec();
265 LOG(ERROR) << "Data is " << data; 284 LOG(ERROR) << "Data is " << data;
266 delegate_->OnDownloadComplete(this, false); 285 delegate_->OnDownloadComplete(this, false);
267 return; 286 return;
268 } 287 }
269 288
270 if (source == user_entry_fetcher_.get()) { 289 if (source == user_entry_fetcher_.get()) {
271 std::string image_url; 290 std::string image_url;
272 if (!GetProfileNickNameAndImageURL(data, &profile_full_name_, &image_url)) { 291 if (!GetProfileNameAndImageURL(data, &profile_full_name_, &image_url,
292 delegate_->GetDesiredImageSideLength())) {
273 delegate_->OnDownloadComplete(this, false); 293 delegate_->OnDownloadComplete(this, false);
274 return; 294 return;
275 } 295 }
276 if (IsDefaultProfileImageURL(image_url)) { 296 if (IsDefaultProfileImageURL(image_url)) {
277 VLOG(1) << "User has default profile picture"; 297 VLOG(1) << "User has default profile picture";
278 picture_status_ = PICTURE_DEFAULT; 298 picture_status_ = PICTURE_DEFAULT;
279 delegate_->OnDownloadComplete(this, true); 299 delegate_->OnDownloadComplete(this, true);
280 return; 300 return;
281 } 301 }
282 if (!image_url.empty() && image_url == delegate_->GetCachedPictureURL()) { 302 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) { 372 void ProfileDownloader::OnGetTokenSuccess(const std::string& access_token) {
353 auth_token_ = access_token; 373 auth_token_ = access_token;
354 StartFetchingImage(); 374 StartFetchingImage();
355 } 375 }
356 376
357 // Callback for OAuth2AccessTokenFetcher on failure. 377 // Callback for OAuth2AccessTokenFetcher on failure.
358 void ProfileDownloader::OnGetTokenFailure(const GoogleServiceAuthError& error) { 378 void ProfileDownloader::OnGetTokenFailure(const GoogleServiceAuthError& error) {
359 LOG(WARNING) << "ProfileDownloader: token request using refresh token failed"; 379 LOG(WARNING) << "ProfileDownloader: token request using refresh token failed";
360 delegate_->OnDownloadComplete(this, false); 380 delegate_->OnDownloadComplete(this, false);
361 } 381 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698