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

Side by Side Diff: chrome/browser/importer/toolbar_importer.cc

Issue 18120005: Remove Google Toolbar importer (aka google.com/bookmarks importer). (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: -explicit Created 7 years, 5 months 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
(Empty)
1 // Copyright (c) 2012 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/importer/toolbar_importer.h"
6
7 #include <limits>
8
9 #include "base/bind.h"
10 #include "base/rand_util.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_split.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "chrome/browser/bookmarks/imported_bookmark_entry.h"
15 #include "chrome/browser/first_run/first_run.h"
16 #include "chrome/browser/importer/importer_bridge.h"
17 #include "chrome/browser/importer/importer_data_types.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "grit/generated_resources.h"
21 #include "net/base/load_flags.h"
22 #include "net/url_request/url_fetcher.h"
23 #include "third_party/libxml/chromium/libxml_utils.h"
24
25 using content::BrowserThread;
26
27 // Toolbar5Importer
28 const char Toolbar5Importer::kXmlApiReplyXmlTag[] = "xml_api_reply";
29 const char Toolbar5Importer::kBookmarksXmlTag[] = "bookmarks";
30 const char Toolbar5Importer::kBookmarkXmlTag[] = "bookmark";
31 const char Toolbar5Importer::kTitleXmlTag[] = "title";
32 const char Toolbar5Importer::kUrlXmlTag[] = "url";
33 const char Toolbar5Importer::kTimestampXmlTag[] = "timestamp";
34 const char Toolbar5Importer::kLabelsXmlTag[] = "labels";
35 const char Toolbar5Importer::kLabelsXmlCloseTag[] = "/labels";
36 const char Toolbar5Importer::kLabelXmlTag[] = "label";
37 const char Toolbar5Importer::kAttributesXmlTag[] = "attributes";
38
39 const char Toolbar5Importer::kRandomNumberToken[] = "{random_number}";
40 const char Toolbar5Importer::kAuthorizationToken[] = "{auth_token}";
41 const char Toolbar5Importer::kAuthorizationTokenPrefix[] = "/*";
42 const char Toolbar5Importer::kAuthorizationTokenSuffix[] = "*/";
43 const char Toolbar5Importer::kMaxNumToken[] = "{max_num}";
44 const char Toolbar5Importer::kMaxTimestampToken[] = "{max_timestamp}";
45
46 const char Toolbar5Importer::kT5AuthorizationTokenUrl[] =
47 "http://www.google.com/notebook/token?zx={random_number}";
48 const char Toolbar5Importer::kT5FrontEndUrlTemplate[] =
49 "http://www.google.com/notebook/toolbar?cmd=list&tok={auth_token}&"
50 "num={max_num}&min={max_timestamp}&all=0&zx={random_number}";
51
52 // Importer methods.
53
54 // The constructor should set the initial state to NOT_USED.
55 Toolbar5Importer::Toolbar5Importer()
56 : state_(NOT_USED),
57 items_to_import_(importer::NONE),
58 token_fetcher_(NULL),
59 data_fetcher_(NULL) {
60 }
61
62 // The destructor insures that the fetchers are currently not being used, as
63 // their thread-safe implementation requires that they are cancelled from the
64 // thread in which they were constructed.
65 Toolbar5Importer::~Toolbar5Importer() {
66 DCHECK(!token_fetcher_);
67 DCHECK(!data_fetcher_);
68 }
69
70 void Toolbar5Importer::StartImport(
71 const importer::SourceProfile& source_profile,
72 uint16 items,
73 ImporterBridge* bridge) {
74 DCHECK(bridge);
75
76 bridge_ = bridge;
77 items_to_import_ = items;
78 DCHECK(source_profile.request_context_getter.get());
79 request_context_getter_ = source_profile.request_context_getter;
80 state_ = INITIALIZED;
81
82 bridge_->NotifyStarted();
83 ContinueImport();
84 }
85
86 // The public cancel method serves two functions, as a callback from the UI
87 // as well as an internal callback in case of cancel. An internal callback
88 // is required since the URLFetcher must be destroyed from the thread it was
89 // created.
90 void Toolbar5Importer::Cancel() {
91 // In the case when the thread is not importing messages we are to
92 // cancel as soon as possible.
93 Importer::Cancel();
94
95 // If we are conducting network operations, post a message to the importer
96 // thread for synchronization.
97 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
98 EndImport();
99 } else {
100 BrowserThread::PostTask(
101 BrowserThread::UI, FROM_HERE,
102 base::Bind(&Toolbar5Importer::Cancel, this));
103 }
104 }
105
106 void Toolbar5Importer::OnURLFetchComplete(const net::URLFetcher* source) {
107 if (cancelled()) {
108 EndImport();
109 return;
110 }
111
112 if (200 != source->GetResponseCode()) { // HTTP/Ok
113 // Cancelling here will update the UI and bypass the rest of bookmark
114 // import.
115 EndImportBookmarks();
116 return;
117 }
118
119 std::string data;
120 source->GetResponseAsString(&data);
121 switch (state_) {
122 case GET_AUTHORIZATION_TOKEN:
123 GetBookmarkDataFromServer(data);
124 break;
125 case GET_BOOKMARKS:
126 GetBookmarksFromServerDataResponse(data);
127 break;
128 default:
129 NOTREACHED() << "Invalid state.";
130 EndImportBookmarks();
131 break;
132 }
133 }
134
135 void Toolbar5Importer::ContinueImport() {
136 DCHECK((items_to_import_ == importer::FAVORITES) ||
137 (items_to_import_ == importer::NONE)) <<
138 "The items requested are not supported";
139
140 // The order here is important. Each Begin... will clear the flag
141 // of its item before its task finishes and re-enters this method.
142 if (importer::NONE == items_to_import_) {
143 EndImport();
144 return;
145 }
146 if ((items_to_import_ & importer::FAVORITES) && !cancelled()) {
147 items_to_import_ &= ~importer::FAVORITES;
148 BeginImportBookmarks();
149 return;
150 }
151 // TODO(brg): Import history, autocomplete, other toolbar information
152 // in a future release.
153
154 // This code should not be reached, but gracefully handles the possibility
155 // that StartImport was called with unsupported items_to_import.
156 if (!cancelled())
157 EndImport();
158 }
159
160 void Toolbar5Importer::EndImport() {
161 if (state_ != DONE) {
162 state_ = DONE;
163 // By spec the fetchers must be destroyed within the same
164 // thread they are created. The importer is destroyed in the ui_thread
165 // so when we complete in the file_thread we destroy them first.
166 if (NULL != token_fetcher_) {
167 delete token_fetcher_;
168 token_fetcher_ = NULL;
169 }
170
171 if (NULL != data_fetcher_) {
172 delete data_fetcher_;
173 data_fetcher_ = NULL;
174 }
175
176 if (bridge_.get())
177 bridge_->NotifyEnded();
178 }
179 }
180
181 void Toolbar5Importer::BeginImportBookmarks() {
182 bridge_->NotifyItemStarted(importer::FAVORITES);
183 GetAuthenticationFromServer();
184 }
185
186 void Toolbar5Importer::EndImportBookmarks() {
187 bridge_->NotifyItemEnded(importer::FAVORITES);
188 ContinueImport();
189 }
190
191
192 // Notebook front-end connection manager implementation follows.
193 void Toolbar5Importer::GetAuthenticationFromServer() {
194 if (cancelled()) {
195 EndImport();
196 return;
197 }
198
199 // Authentication is a token string retrieved from the authentication server
200 // To access it we call the url below with a random number replacing the
201 // value in the string.
202 state_ = GET_AUTHORIZATION_TOKEN;
203
204 // Random number construction.
205 int random = base::RandInt(0, std::numeric_limits<int>::max());
206 std::string random_string = base::UintToString(random);
207
208 // Retrieve authorization token from the network.
209 std::string url_string(kT5AuthorizationTokenUrl);
210 url_string.replace(url_string.find(kRandomNumberToken),
211 arraysize(kRandomNumberToken) - 1,
212 random_string);
213 GURL url(url_string);
214
215 // Because the importer is started as the result of a user action which
216 // explicitly requires authentication, sending cookies here is reasonable.
217 token_fetcher_ = net::URLFetcher::Create(
218 url, net::URLFetcher::GET, this);
219 token_fetcher_->SetRequestContext(request_context_getter_.get());
220 token_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES);
221 token_fetcher_->Start();
222 }
223
224 void Toolbar5Importer::GetBookmarkDataFromServer(const std::string& response) {
225 if (cancelled()) {
226 EndImport();
227 return;
228 }
229
230 state_ = GET_BOOKMARKS;
231
232 // Parse and verify the authorization token from the response.
233 std::string token;
234 if (!ParseAuthenticationTokenResponse(response, &token)) {
235 EndImportBookmarks();
236 return;
237 }
238
239 // Build the Toolbar FE connection string, and call the server for
240 // the xml blob. We must tag the connection string with a random number.
241 std::string conn_string = kT5FrontEndUrlTemplate;
242 int random = base::RandInt(0, std::numeric_limits<int>::max());
243 std::string random_string = base::UintToString(random);
244 conn_string.replace(conn_string.find(kRandomNumberToken),
245 arraysize(kRandomNumberToken) - 1,
246 random_string);
247 conn_string.replace(conn_string.find(kAuthorizationToken),
248 arraysize(kAuthorizationToken) - 1,
249 token);
250 GURL url(conn_string);
251
252 // Because the importer is started as the result of a user action which
253 // explicitly requires authentication, sending cookies here is reasonable.
254 data_fetcher_ = net::URLFetcher::Create(
255 url, net::URLFetcher::GET, this);
256 data_fetcher_->SetRequestContext(request_context_getter_.get());
257 data_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES);
258 data_fetcher_->Start();
259 }
260
261 void Toolbar5Importer::GetBookmarksFromServerDataResponse(
262 const std::string& response) {
263 if (cancelled()) {
264 EndImport();
265 return;
266 }
267
268 state_ = PARSE_BOOKMARKS;
269
270 XmlReader reader;
271 if (reader.Load(response) && !cancelled()) {
272 // Construct Bookmarks
273 std::vector<ImportedBookmarkEntry> bookmarks;
274 if (ParseBookmarksFromReader(&reader, &bookmarks,
275 bridge_->GetLocalizedString(IDS_BOOKMARK_GROUP_FROM_GOOGLE_TOOLBAR)))
276 AddBookmarksToChrome(bookmarks);
277 }
278 EndImportBookmarks();
279 }
280
281 bool Toolbar5Importer::ParseAuthenticationTokenResponse(
282 const std::string& response,
283 std::string* token) {
284 DCHECK(token);
285
286 *token = response;
287 size_t position = token->find(kAuthorizationTokenPrefix);
288 if (0 != position)
289 return false;
290 token->replace(position, arraysize(kAuthorizationTokenPrefix) - 1, "");
291
292 position = token->find(kAuthorizationTokenSuffix);
293 if (token->size() != (position + (arraysize(kAuthorizationTokenSuffix) - 1)))
294 return false;
295 token->replace(position, arraysize(kAuthorizationTokenSuffix) - 1, "");
296
297 return true;
298 }
299
300 // Parsing
301 bool Toolbar5Importer::ParseBookmarksFromReader(
302 XmlReader* reader,
303 std::vector<ImportedBookmarkEntry>* bookmarks,
304 const string16& bookmark_group_string) {
305 DCHECK(reader);
306 DCHECK(bookmarks);
307
308 // The XML blob returned from the server is described in the
309 // Toolbar-Notebook/Bookmarks Protocol document located at
310 // https://docs.google.com/a/google.com/Doc?docid=cgt3m7dr_24djt62m&hl=en
311 // We are searching for the section with structure
312 // <bookmarks><bookmark>...</bookmark><bookmark>...</bookmark></bookmarks>
313
314 // Locate the |bookmarks| blob.
315 if (!reader->SkipToElement())
316 return false;
317
318 if (!LocateNextTagByName(reader, kBookmarksXmlTag))
319 return false;
320
321 // Parse each |bookmark| blob
322 while (LocateNextTagWithStopByName(reader, kBookmarkXmlTag,
323 kBookmarksXmlTag)) {
324 ImportedBookmarkEntry bookmark_entry;
325 std::vector<BookmarkFolderType> folders;
326 if (ExtractBookmarkInformation(reader, &bookmark_entry, &folders,
327 bookmark_group_string)) {
328 // For each folder we create a new bookmark entry. Duplicates will
329 // be detected when we attempt to create the bookmark in the profile.
330 for (std::vector<BookmarkFolderType>::iterator folder = folders.begin();
331 folder != folders.end();
332 ++folder) {
333 bookmark_entry.path = *folder;
334 bookmarks->push_back(bookmark_entry);
335 }
336 }
337 }
338
339 if (0 == bookmarks->size())
340 return false;
341
342 return true;
343 }
344
345 bool Toolbar5Importer::LocateNextOpenTag(XmlReader* reader) {
346 DCHECK(reader);
347
348 while (!reader->SkipToElement()) {
349 if (!reader->Read())
350 return false;
351 }
352 return true;
353 }
354
355 bool Toolbar5Importer::LocateNextTagByName(XmlReader* reader,
356 const std::string& tag) {
357 DCHECK(reader);
358
359 // Locate the |tag| blob.
360 while (tag != reader->NodeName()) {
361 if (!reader->Read() || !LocateNextOpenTag(reader))
362 return false;
363 }
364 return true;
365 }
366
367 bool Toolbar5Importer::LocateNextTagWithStopByName(XmlReader* reader,
368 const std::string& tag,
369 const std::string& stop) {
370 DCHECK(reader);
371
372 DCHECK_NE(tag, stop);
373 // Locate the |tag| blob.
374 while (tag != reader->NodeName()) {
375 // Move to the next open tag.
376 if (!reader->Read() || !LocateNextOpenTag(reader))
377 return false;
378 // If we encounter the stop word return false.
379 if (stop == reader->NodeName())
380 return false;
381 }
382 return true;
383 }
384
385 bool Toolbar5Importer::ExtractBookmarkInformation(
386 XmlReader* reader,
387 ImportedBookmarkEntry* bookmark_entry,
388 std::vector<BookmarkFolderType>* bookmark_folders,
389 const string16& bookmark_group_string) {
390 DCHECK(reader);
391 DCHECK(bookmark_entry);
392 DCHECK(bookmark_folders);
393
394 // The following is a typical bookmark entry.
395 // The reader should be pointing to the <title> tag at the moment.
396 //
397 // <bookmark>
398 // <title>MyTitle</title>
399 // <url>http://www.sohu.com/</url>
400 // <timestamp>1153328691085181</timestamp>
401 // <id>N123nasdf239</id>
402 // <notebook_id>Bxxxxxxx</notebook_id> (for bookmarks, a special id is used)
403 // <section_id>Sxxxxxx</section_id>
404 // <has_highlight>0</has_highlight>
405 // <labels>
406 // <label>China</label>
407 // <label>^k</label> (if this special label is present, the note is deleted)
408 // </labels>
409 // <attributes>
410 // <attribute>
411 // <name>favicon_url</name>
412 // <value>http://www.sohu.com/favicon.ico</value>
413 // </attribute>
414 // <attribute>
415 // <name>favicon_timestamp</name>
416 // <value>1153328653</value>
417 // </attribute>
418 // <attribute>
419 // <name>notebook_name</name>
420 // <value>My notebook 0</value>
421 // </attribute>
422 // <attribute>
423 // <name>section_name</name>
424 // <value>My section 0</value>
425 // </attribute>
426 // </attributes>
427 // </bookmark>
428 //
429 // We parse the blob in order, title->url->timestamp etc. Any failure
430 // causes us to skip this bookmark.
431
432 if (!ExtractTitleFromXmlReader(reader, bookmark_entry))
433 return false;
434 if (!ExtractUrlFromXmlReader(reader, bookmark_entry))
435 return false;
436 if (!ExtractTimeFromXmlReader(reader, bookmark_entry))
437 return false;
438 if (!ExtractFoldersFromXmlReader(reader, bookmark_folders,
439 bookmark_group_string))
440 return false;
441
442 return true;
443 }
444
445 bool Toolbar5Importer::ExtractNamedValueFromXmlReader(XmlReader* reader,
446 const std::string& name,
447 std::string* buffer) {
448 DCHECK(reader);
449 DCHECK(buffer);
450
451 if (name != reader->NodeName())
452 return false;
453 if (!reader->ReadElementContent(buffer))
454 return false;
455 return true;
456 }
457
458 bool Toolbar5Importer::ExtractTitleFromXmlReader(
459 XmlReader* reader,
460 ImportedBookmarkEntry* entry) {
461 DCHECK(reader);
462 DCHECK(entry);
463
464 if (!LocateNextTagWithStopByName(reader, kTitleXmlTag, kUrlXmlTag))
465 return false;
466 std::string buffer;
467 if (!ExtractNamedValueFromXmlReader(reader, kTitleXmlTag, &buffer)) {
468 return false;
469 }
470 entry->title = UTF8ToUTF16(buffer);
471 return true;
472 }
473
474 bool Toolbar5Importer::ExtractUrlFromXmlReader(
475 XmlReader* reader,
476 ImportedBookmarkEntry* entry) {
477 DCHECK(reader);
478 DCHECK(entry);
479
480 if (!LocateNextTagWithStopByName(reader, kUrlXmlTag, kTimestampXmlTag))
481 return false;
482 std::string buffer;
483 if (!ExtractNamedValueFromXmlReader(reader, kUrlXmlTag, &buffer)) {
484 return false;
485 }
486 entry->url = GURL(buffer);
487 return true;
488 }
489
490 bool Toolbar5Importer::ExtractTimeFromXmlReader(
491 XmlReader* reader,
492 ImportedBookmarkEntry* entry) {
493 DCHECK(reader);
494 DCHECK(entry);
495 if (!LocateNextTagWithStopByName(reader, kTimestampXmlTag, kLabelsXmlTag))
496 return false;
497 std::string buffer;
498 if (!ExtractNamedValueFromXmlReader(reader, kTimestampXmlTag, &buffer)) {
499 return false;
500 }
501 int64 timestamp;
502 if (!base::StringToInt64(buffer, &timestamp)) {
503 return false;
504 }
505 entry->creation_time = base::Time::FromTimeT(timestamp);
506 return true;
507 }
508
509 bool Toolbar5Importer::ExtractFoldersFromXmlReader(
510 XmlReader* reader,
511 std::vector<BookmarkFolderType>* bookmark_folders,
512 const string16& bookmark_group_string) {
513 DCHECK(reader);
514 DCHECK(bookmark_folders);
515
516 // Read in the labels for this bookmark from the xml. There may be many
517 // labels for any one bookmark.
518 if (!LocateNextTagWithStopByName(reader, kLabelsXmlTag, kAttributesXmlTag))
519 return false;
520
521 // It is within scope to have an empty labels section, so we do not
522 // return false if the labels are empty.
523 if (!reader->Read() || !LocateNextOpenTag(reader))
524 return false;
525
526 std::vector<string16> label_vector;
527 while (kLabelXmlTag == reader->NodeName()) {
528 std::string label_buffer;
529 if (!reader->ReadElementContent(&label_buffer)) {
530 label_buffer = "";
531 }
532 label_vector.push_back(UTF8ToUTF16(label_buffer));
533 LocateNextOpenTag(reader);
534 }
535
536 if (0 == label_vector.size()) {
537 if (!first_run::IsChromeFirstRun()) {
538 bookmark_folders->resize(1);
539 (*bookmark_folders)[0].push_back(bookmark_group_string);
540 }
541 return true;
542 }
543
544 // We will be making one bookmark folder for each label.
545 bookmark_folders->resize(label_vector.size());
546
547 for (size_t index = 0; index < label_vector.size(); ++index) {
548 // If this is the first run then we place favorites with no labels
549 // in the title bar. Else they are placed in the "Google Toolbar" folder.
550 if (!first_run::IsChromeFirstRun() || !label_vector[index].empty()) {
551 (*bookmark_folders)[index].push_back(bookmark_group_string);
552 }
553
554 // If the label and is in the form "xxx:yyy:zzz" this was created from an
555 // IE or Firefox folder. We undo the label creation and recreate the
556 // correct folder.
557 std::vector<string16> folder_names;
558 base::SplitString(label_vector[index], ':', &folder_names);
559 (*bookmark_folders)[index].insert((*bookmark_folders)[index].end(),
560 folder_names.begin(), folder_names.end());
561 }
562
563 return true;
564 }
565
566 void Toolbar5Importer::AddBookmarksToChrome(
567 const std::vector<ImportedBookmarkEntry>& bookmarks) {
568 if (!bookmarks.empty() && !cancelled()) {
569 const string16& first_folder_name =
570 bridge_->GetLocalizedString(IDS_BOOKMARK_GROUP_FROM_GOOGLE_TOOLBAR);
571 bridge_->AddBookmarks(bookmarks, first_folder_name);
572 }
573 }
OLDNEW
« no previous file with comments | « chrome/browser/importer/toolbar_importer.h ('k') | chrome/browser/importer/toolbar_importer_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698