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

Side by Side Diff: chrome/browser/sync_file_system/drive_backend/api_util.cc

Issue 23787003: [SyncFS] Move SyncFS V1 files from drive_backend to drive_backend_v1 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: move back metadata_db_migration_util* Created 7 years, 3 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 2013 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/sync_file_system/drive_backend/api_util.h"
6
7 #include <algorithm>
8 #include <functional>
9 #include <sstream>
10 #include <string>
11
12 #include "base/file_util.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/threading/sequenced_worker_pool.h"
16 #include "base/values.h"
17 #include "chrome/browser/drive/drive_api_service.h"
18 #include "chrome/browser/drive/drive_api_util.h"
19 #include "chrome/browser/drive/drive_uploader.h"
20 #include "chrome/browser/drive/gdata_wapi_service.h"
21 #include "chrome/browser/google_apis/drive_api_parser.h"
22 #include "chrome/browser/profiles/profile.h"
23 #include "chrome/browser/signin/profile_oauth2_token_service.h"
24 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
25 #include "chrome/browser/sync_file_system/drive_backend/drive_file_sync_util.h"
26 #include "chrome/browser/sync_file_system/logger.h"
27 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
28 #include "chrome/common/extensions/extension.h"
29 #include "content/public/browser/browser_thread.h"
30 #include "extensions/common/constants.h"
31 #include "net/base/mime_util.h"
32
33 namespace sync_file_system {
34 namespace drive_backend {
35
36 namespace {
37
38 enum ParentType {
39 PARENT_TYPE_ROOT_OR_EMPTY,
40 PARENT_TYPE_DIRECTORY,
41 };
42
43 const char kSyncRootDirectoryName[] = "Chrome Syncable FileSystem";
44 const char kSyncRootDirectoryNameDev[] = "Chrome Syncable FileSystem Dev";
45 const char kMimeTypeOctetStream[] = "application/octet-stream";
46
47 const char kFakeServerBaseUrl[] = "https://fake_server/";
48 const char kFakeDownloadServerBaseUrl[] = "https://fake_download_server/";
49
50 void EmptyGDataErrorCodeCallback(google_apis::GDataErrorCode error) {}
51
52 bool HasParentLinkTo(const ScopedVector<google_apis::Link>& links,
53 const std::string& parent_resource_id,
54 ParentType parent_type) {
55 bool has_parent = false;
56
57 for (ScopedVector<google_apis::Link>::const_iterator itr = links.begin();
58 itr != links.end(); ++itr) {
59 if ((*itr)->type() == google_apis::Link::LINK_PARENT) {
60 has_parent = true;
61 if (drive::util::ExtractResourceIdFromUrl((*itr)->href()) ==
62 parent_resource_id)
63 return true;
64 }
65 }
66
67 return parent_type == PARENT_TYPE_ROOT_OR_EMPTY && !has_parent;
68 }
69
70 struct TitleAndParentQuery
71 : std::unary_function<const google_apis::ResourceEntry*, bool> {
72 TitleAndParentQuery(const std::string& title,
73 const std::string& parent_resource_id,
74 ParentType parent_type)
75 : title(title),
76 parent_resource_id(parent_resource_id),
77 parent_type(parent_type) {}
78
79 bool operator()(const google_apis::ResourceEntry* entry) const {
80 return entry->title() == title &&
81 HasParentLinkTo(entry->links(), parent_resource_id, parent_type);
82 }
83
84 const std::string& title;
85 const std::string& parent_resource_id;
86 ParentType parent_type;
87 };
88
89 void FilterEntriesByTitleAndParent(
90 ScopedVector<google_apis::ResourceEntry>* entries,
91 const std::string& title,
92 const std::string& parent_resource_id,
93 ParentType parent_type) {
94 typedef ScopedVector<google_apis::ResourceEntry>::iterator iterator;
95 iterator itr = std::partition(entries->begin(),
96 entries->end(),
97 TitleAndParentQuery(title,
98 parent_resource_id,
99 parent_type));
100 entries->erase(itr, entries->end());
101 }
102
103 google_apis::ResourceEntry* GetDocumentByTitleAndParent(
104 const ScopedVector<google_apis::ResourceEntry>& entries,
105 const std::string& title,
106 const std::string& parent_resource_id,
107 ParentType parent_type) {
108 typedef ScopedVector<google_apis::ResourceEntry>::const_iterator iterator;
109 iterator found =
110 std::find_if(entries.begin(),
111 entries.end(),
112 TitleAndParentQuery(title, parent_resource_id, parent_type));
113 if (found != entries.end())
114 return *found;
115 return NULL;
116 }
117
118 void EntryAdapterForEnsureTitleUniqueness(
119 scoped_ptr<google_apis::ResourceEntry> entry,
120 const APIUtil::EnsureUniquenessCallback& callback,
121 APIUtil::EnsureUniquenessStatus status,
122 google_apis::GDataErrorCode error) {
123 callback.Run(error, status, entry.Pass());
124 }
125
126 void UploadResultAdapter(const APIUtil::ResourceEntryCallback& callback,
127 google_apis::GDataErrorCode error,
128 const GURL& upload_location,
129 scoped_ptr<google_apis::ResourceEntry> entry) {
130 callback.Run(error, entry.Pass());
131 }
132
133 std::string GetMimeTypeFromTitle(const std::string& title) {
134 base::FilePath::StringType extension =
135 base::FilePath::FromUTF8Unsafe(title).Extension();
136 std::string mime_type;
137 if (extension.empty() ||
138 !net::GetWellKnownMimeTypeFromExtension(extension.substr(1), &mime_type))
139 return kMimeTypeOctetStream;
140 return mime_type;
141 }
142
143 bool CreateTemporaryFile(const base::FilePath& dir_path,
144 webkit_blob::ScopedFile* temp_file) {
145 base::FilePath temp_file_path;
146 const bool success = file_util::CreateDirectory(dir_path) &&
147 file_util::CreateTemporaryFileInDir(dir_path, &temp_file_path);
148 if (!success)
149 return success;
150 *temp_file =
151 webkit_blob::ScopedFile(temp_file_path,
152 webkit_blob::ScopedFile::DELETE_ON_SCOPE_OUT,
153 base::MessageLoopProxy::current().get());
154 return success;
155 }
156
157 } // namespace
158
159 APIUtil::APIUtil(Profile* profile,
160 const base::FilePath& temp_dir_path)
161 : wapi_url_generator_(
162 GURL(google_apis::GDataWapiUrlGenerator::kBaseUrlForProduction),
163 GURL(google_apis::GDataWapiUrlGenerator::
164 kBaseDownloadUrlForProduction)),
165 drive_api_url_generator_(
166 GURL(google_apis::DriveApiUrlGenerator::kBaseUrlForProduction),
167 GURL(google_apis::DriveApiUrlGenerator::
168 kBaseDownloadUrlForProduction)),
169 upload_next_key_(0),
170 temp_dir_path_(temp_dir_path) {
171 OAuth2TokenService* oauth_service =
172 ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
173 if (IsDriveAPIDisabled()) {
174 drive_service_.reset(new drive::GDataWapiService(
175 oauth_service,
176 profile->GetRequestContext(),
177 content::BrowserThread::GetBlockingPool(),
178 GURL(google_apis::GDataWapiUrlGenerator::kBaseUrlForProduction),
179 GURL(google_apis::GDataWapiUrlGenerator::kBaseDownloadUrlForProduction),
180 std::string() /* custom_user_agent */));
181 } else {
182 drive_service_.reset(new drive::DriveAPIService(
183 oauth_service,
184 profile->GetRequestContext(),
185 content::BrowserThread::GetBlockingPool(),
186 GURL(google_apis::DriveApiUrlGenerator::kBaseUrlForProduction),
187 GURL(google_apis::DriveApiUrlGenerator::kBaseDownloadUrlForProduction),
188 GURL(google_apis::GDataWapiUrlGenerator::kBaseUrlForProduction),
189 std::string() /* custom_user_agent */));
190 }
191
192 drive_service_->Initialize();
193 drive_service_->AddObserver(this);
194 net::NetworkChangeNotifier::AddConnectionTypeObserver(this);
195
196 drive_uploader_.reset(new drive::DriveUploader(
197 drive_service_.get(), content::BrowserThread::GetBlockingPool()));
198 }
199
200 scoped_ptr<APIUtil> APIUtil::CreateForTesting(
201 const base::FilePath& temp_dir_path,
202 scoped_ptr<drive::DriveServiceInterface> drive_service,
203 scoped_ptr<drive::DriveUploaderInterface> drive_uploader) {
204 return make_scoped_ptr(new APIUtil(
205 temp_dir_path,
206 GURL(kFakeServerBaseUrl),
207 GURL(kFakeDownloadServerBaseUrl),
208 drive_service.Pass(),
209 drive_uploader.Pass()));
210 }
211
212 APIUtil::APIUtil(const base::FilePath& temp_dir_path,
213 const GURL& base_url,
214 const GURL& base_download_url,
215 scoped_ptr<drive::DriveServiceInterface> drive_service,
216 scoped_ptr<drive::DriveUploaderInterface> drive_uploader)
217 : wapi_url_generator_(base_url, base_download_url),
218 drive_api_url_generator_(base_url, base_download_url),
219 upload_next_key_(0),
220 temp_dir_path_(temp_dir_path) {
221 drive_service_ = drive_service.Pass();
222 drive_service_->Initialize();
223 drive_service_->AddObserver(this);
224 net::NetworkChangeNotifier::AddConnectionTypeObserver(this);
225
226 drive_uploader_ = drive_uploader.Pass();
227 }
228
229 APIUtil::~APIUtil() {
230 DCHECK(CalledOnValidThread());
231 net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
232 drive_service_->RemoveObserver(this);
233 }
234
235 void APIUtil::AddObserver(APIUtilObserver* observer) {
236 DCHECK(CalledOnValidThread());
237 observers_.AddObserver(observer);
238 }
239
240 void APIUtil::RemoveObserver(APIUtilObserver* observer) {
241 DCHECK(CalledOnValidThread());
242 observers_.RemoveObserver(observer);
243 }
244
245 void APIUtil::GetDriveRootResourceId(const GDataErrorCallback& callback) {
246 DCHECK(CalledOnValidThread());
247 DCHECK(!IsDriveAPIDisabled());
248 DVLOG(2) << "Getting resource id for Drive root";
249
250 drive_service_->GetAboutResource(
251 base::Bind(&APIUtil::DidGetDriveRootResourceId, AsWeakPtr(), callback));
252 }
253
254 void APIUtil::DidGetDriveRootResourceId(
255 const GDataErrorCallback& callback,
256 google_apis::GDataErrorCode error,
257 scoped_ptr<google_apis::AboutResource> about_resource) {
258 DCHECK(CalledOnValidThread());
259
260 if (error != google_apis::HTTP_SUCCESS) {
261 DVLOG(2) << "Error on getting resource id for Drive root: " << error;
262 callback.Run(error);
263 return;
264 }
265
266 DCHECK(about_resource);
267 root_resource_id_ = about_resource->root_folder_id();
268 DCHECK(!root_resource_id_.empty());
269 DVLOG(2) << "Got resource id for Drive root: " << root_resource_id_;
270 callback.Run(error);
271 }
272
273 void APIUtil::GetDriveDirectoryForSyncRoot(const ResourceIdCallback& callback) {
274 DCHECK(CalledOnValidThread());
275
276 if (GetRootResourceId().empty()) {
277 GetDriveRootResourceId(
278 base::Bind(&APIUtil::DidGetDriveRootResourceIdForGetSyncRoot,
279 AsWeakPtr(), callback));
280 return;
281 }
282
283 DVLOG(2) << "Getting Drive directory for SyncRoot";
284 std::string directory_name(GetSyncRootDirectoryName());
285 SearchByTitle(directory_name,
286 std::string(),
287 base::Bind(&APIUtil::DidGetDirectory,
288 AsWeakPtr(),
289 std::string(),
290 directory_name,
291 callback));
292 }
293
294 void APIUtil::DidGetDriveRootResourceIdForGetSyncRoot(
295 const ResourceIdCallback& callback,
296 google_apis::GDataErrorCode error) {
297 DCHECK(CalledOnValidThread());
298 if (error != google_apis::HTTP_SUCCESS) {
299 DVLOG(2) << "Error on getting Drive directory for SyncRoot: " << error;
300 callback.Run(error, std::string());
301 return;
302 }
303 GetDriveDirectoryForSyncRoot(callback);
304 }
305
306 void APIUtil::GetDriveDirectoryForOrigin(
307 const std::string& sync_root_resource_id,
308 const GURL& origin,
309 const ResourceIdCallback& callback) {
310 DCHECK(CalledOnValidThread());
311 DVLOG(2) << "Getting Drive directory for Origin: " << origin;
312
313 std::string directory_name(OriginToDirectoryTitle(origin));
314 SearchByTitle(directory_name,
315 sync_root_resource_id,
316 base::Bind(&APIUtil::DidGetDirectory,
317 AsWeakPtr(),
318 sync_root_resource_id,
319 directory_name,
320 callback));
321 }
322
323 void APIUtil::DidGetDirectory(const std::string& parent_resource_id,
324 const std::string& directory_name,
325 const ResourceIdCallback& callback,
326 google_apis::GDataErrorCode error,
327 scoped_ptr<google_apis::ResourceList> feed) {
328 DCHECK(CalledOnValidThread());
329 DCHECK(IsStringASCII(directory_name));
330
331 if (error != google_apis::HTTP_SUCCESS) {
332 DVLOG(2) << "Error on getting Drive directory: " << error;
333 callback.Run(error, std::string());
334 return;
335 }
336
337 std::string resource_id;
338 ParentType parent_type = PARENT_TYPE_DIRECTORY;
339 if (parent_resource_id.empty()) {
340 resource_id = GetRootResourceId();
341 DCHECK(!resource_id.empty());
342 parent_type = PARENT_TYPE_ROOT_OR_EMPTY;
343 } else {
344 resource_id = parent_resource_id;
345 }
346 std::string title(directory_name);
347 google_apis::ResourceEntry* entry = GetDocumentByTitleAndParent(
348 feed->entries(), title, resource_id, parent_type);
349 if (!entry) {
350 DVLOG(2) << "Directory not found. Creating: " << directory_name;
351 drive_service_->AddNewDirectory(resource_id,
352 directory_name,
353 base::Bind(&APIUtil::DidCreateDirectory,
354 AsWeakPtr(),
355 parent_resource_id,
356 title,
357 callback));
358 return;
359 }
360 DVLOG(2) << "Found Drive directory.";
361
362 // TODO(tzik): Handle error.
363 DCHECK_EQ(google_apis::ENTRY_KIND_FOLDER, entry->kind());
364 DCHECK_EQ(directory_name, entry->title());
365
366 if (entry->title() == GetSyncRootDirectoryName())
367 EnsureSyncRootIsNotInMyDrive(entry->resource_id());
368
369 callback.Run(error, entry->resource_id());
370 }
371
372 void APIUtil::DidCreateDirectory(const std::string& parent_resource_id,
373 const std::string& title,
374 const ResourceIdCallback& callback,
375 google_apis::GDataErrorCode error,
376 scoped_ptr<google_apis::ResourceEntry> entry) {
377 DCHECK(CalledOnValidThread());
378
379 if (error != google_apis::HTTP_SUCCESS &&
380 error != google_apis::HTTP_CREATED) {
381 DVLOG(2) << "Error on creating Drive directory: " << error;
382 callback.Run(error, std::string());
383 return;
384 }
385 DVLOG(2) << "Created Drive directory.";
386
387 DCHECK(entry);
388 // Check if any other client creates a directory with same title.
389 EnsureTitleUniqueness(
390 parent_resource_id,
391 title,
392 base::Bind(&APIUtil::DidEnsureUniquenessForCreateDirectory,
393 AsWeakPtr(),
394 callback));
395 }
396
397 void APIUtil::DidEnsureUniquenessForCreateDirectory(
398 const ResourceIdCallback& callback,
399 google_apis::GDataErrorCode error,
400 EnsureUniquenessStatus status,
401 scoped_ptr<google_apis::ResourceEntry> entry) {
402 DCHECK(CalledOnValidThread());
403
404 if (error != google_apis::HTTP_SUCCESS) {
405 callback.Run(error, std::string());
406 return;
407 }
408
409 if (status == NO_DUPLICATES_FOUND)
410 error = google_apis::HTTP_CREATED;
411
412 DCHECK(entry) << "No entry: " << error;
413
414 if (!entry->is_folder()) {
415 // TODO(kinuko): Fix this. http://crbug.com/237090
416 util::Log(
417 logging::LOG_ERROR,
418 FROM_HERE,
419 "A file is left for CreateDirectory due to file-folder conflict!");
420 callback.Run(google_apis::HTTP_CONFLICT, std::string());
421 return;
422 }
423
424 if (entry->title() == GetSyncRootDirectoryName())
425 EnsureSyncRootIsNotInMyDrive(entry->resource_id());
426
427 callback.Run(error, entry->resource_id());
428 }
429
430 void APIUtil::GetLargestChangeStamp(const ChangeStampCallback& callback) {
431 DCHECK(CalledOnValidThread());
432 DVLOG(2) << "Getting largest change id";
433
434 drive_service_->GetAboutResource(
435 base::Bind(&APIUtil::DidGetLargestChangeStamp, AsWeakPtr(), callback));
436 }
437
438 void APIUtil::GetResourceEntry(const std::string& resource_id,
439 const ResourceEntryCallback& callback) {
440 DCHECK(CalledOnValidThread());
441 DVLOG(2) << "Getting ResourceEntry for: " << resource_id;
442
443 drive_service_->GetResourceEntry(
444 resource_id,
445 base::Bind(&APIUtil::DidGetResourceEntry, AsWeakPtr(), callback));
446 }
447
448 void APIUtil::DidGetLargestChangeStamp(
449 const ChangeStampCallback& callback,
450 google_apis::GDataErrorCode error,
451 scoped_ptr<google_apis::AboutResource> about_resource) {
452 DCHECK(CalledOnValidThread());
453
454 int64 largest_change_id = 0;
455 if (error == google_apis::HTTP_SUCCESS) {
456 DCHECK(about_resource);
457 largest_change_id = about_resource->largest_change_id();
458 root_resource_id_ = about_resource->root_folder_id();
459 DVLOG(2) << "Got largest change id: " << largest_change_id;
460 } else {
461 DVLOG(2) << "Error on getting largest change id: " << error;
462 }
463
464 callback.Run(error, largest_change_id);
465 }
466
467 void APIUtil::SearchByTitle(const std::string& title,
468 const std::string& directory_resource_id,
469 const ResourceListCallback& callback) {
470 DCHECK(CalledOnValidThread());
471 DCHECK(!title.empty());
472 DVLOG(2) << "Searching resources in the directory [" << directory_resource_id
473 << "] with title [" << title << "]";
474
475 drive_service_->SearchByTitle(
476 title,
477 directory_resource_id,
478 base::Bind(&APIUtil::DidGetResourceList, AsWeakPtr(), callback));
479 }
480
481 void APIUtil::ListFiles(const std::string& directory_resource_id,
482 const ResourceListCallback& callback) {
483 DCHECK(CalledOnValidThread());
484 DVLOG(2) << "Listing resources in the directory [" << directory_resource_id
485 << "]";
486
487 drive_service_->GetResourceListInDirectory(directory_resource_id, callback);
488 }
489
490 void APIUtil::ListChanges(int64 start_changestamp,
491 const ResourceListCallback& callback) {
492 DCHECK(CalledOnValidThread());
493 DVLOG(2) << "Listing changes since: " << start_changestamp;
494
495 drive_service_->GetChangeList(
496 start_changestamp,
497 base::Bind(&APIUtil::DidGetResourceList, AsWeakPtr(), callback));
498 }
499
500 void APIUtil::ContinueListing(const GURL& next_link,
501 const ResourceListCallback& callback) {
502 DCHECK(CalledOnValidThread());
503 DVLOG(2) << "Continue listing on feed: " << next_link.spec();
504
505 drive_service_->GetRemainingFileList(
506 next_link,
507 base::Bind(&APIUtil::DidGetResourceList, AsWeakPtr(), callback));
508 }
509
510 void APIUtil::DownloadFile(const std::string& resource_id,
511 const std::string& local_file_md5,
512 const DownloadFileCallback& callback) {
513 DCHECK(CalledOnValidThread());
514 DCHECK(!temp_dir_path_.empty());
515 DVLOG(2) << "Downloading file [" << resource_id << "]";
516
517 scoped_ptr<webkit_blob::ScopedFile> temp_file(new webkit_blob::ScopedFile);
518 webkit_blob::ScopedFile* temp_file_ptr = temp_file.get();
519 content::BrowserThread::PostTaskAndReplyWithResult(
520 content::BrowserThread::FILE, FROM_HERE,
521 base::Bind(&CreateTemporaryFile, temp_dir_path_, temp_file_ptr),
522 base::Bind(&APIUtil::DidGetTemporaryFileForDownload,
523 AsWeakPtr(), resource_id, local_file_md5,
524 base::Passed(&temp_file), callback));
525 }
526
527 void APIUtil::UploadNewFile(const std::string& directory_resource_id,
528 const base::FilePath& local_file_path,
529 const std::string& title,
530 const UploadFileCallback& callback) {
531 DCHECK(CalledOnValidThread());
532 DVLOG(2) << "Uploading new file into the directory [" << directory_resource_id
533 << "] with title [" << title << "]";
534
535 std::string mime_type = GetMimeTypeFromTitle(title);
536 UploadKey upload_key = RegisterUploadCallback(callback);
537 ResourceEntryCallback did_upload_callback =
538 base::Bind(&APIUtil::DidUploadNewFile,
539 AsWeakPtr(),
540 directory_resource_id,
541 title,
542 upload_key);
543 drive_uploader_->UploadNewFile(
544 directory_resource_id,
545 local_file_path,
546 title,
547 mime_type,
548 base::Bind(&UploadResultAdapter, did_upload_callback),
549 google_apis::ProgressCallback());
550 }
551
552 void APIUtil::UploadExistingFile(const std::string& resource_id,
553 const std::string& remote_file_md5,
554 const base::FilePath& local_file_path,
555 const UploadFileCallback& callback) {
556 DCHECK(CalledOnValidThread());
557 DVLOG(2) << "Uploading existing file [" << resource_id << "]";
558 drive_service_->GetResourceEntry(
559 resource_id,
560 base::Bind(&APIUtil::DidGetResourceEntry,
561 AsWeakPtr(),
562 base::Bind(&APIUtil::UploadExistingFileInternal,
563 AsWeakPtr(),
564 remote_file_md5,
565 local_file_path,
566 callback)));
567 }
568
569 void APIUtil::CreateDirectory(const std::string& parent_resource_id,
570 const std::string& title,
571 const ResourceIdCallback& callback) {
572 DCHECK(CalledOnValidThread());
573 // TODO(kinuko): This will call EnsureTitleUniqueness and will delete
574 // directories if there're duplicated directories. This must be ok
575 // for current design but we'll need to merge directories when we support
576 // 'real' directories.
577 drive_service_->AddNewDirectory(parent_resource_id,
578 title,
579 base::Bind(&APIUtil::DidCreateDirectory,
580 AsWeakPtr(),
581 parent_resource_id,
582 title,
583 callback));
584 }
585
586 void APIUtil::DeleteFile(const std::string& resource_id,
587 const std::string& remote_file_md5,
588 const GDataErrorCallback& callback) {
589 DCHECK(CalledOnValidThread());
590 DVLOG(2) << "Deleting file: " << resource_id;
591
592 // Load actual remote_file_md5 to check for conflict before deletion.
593 if (!remote_file_md5.empty()) {
594 drive_service_->GetResourceEntry(
595 resource_id,
596 base::Bind(&APIUtil::DidGetResourceEntry,
597 AsWeakPtr(),
598 base::Bind(&APIUtil::DeleteFileInternal,
599 AsWeakPtr(),
600 remote_file_md5,
601 callback)));
602 return;
603 }
604
605 // Expected remote_file_md5 is empty so do a force delete.
606 drive_service_->DeleteResource(
607 resource_id,
608 std::string(),
609 base::Bind(&APIUtil::DidDeleteFile, AsWeakPtr(), callback));
610 return;
611 }
612
613 GURL APIUtil::ResourceIdToResourceLink(const std::string& resource_id) const {
614 return IsDriveAPIDisabled()
615 ? wapi_url_generator_.GenerateEditUrl(resource_id)
616 : drive_api_url_generator_.GetFilesGetUrl(resource_id);
617 }
618
619 void APIUtil::EnsureSyncRootIsNotInMyDrive(
620 const std::string& sync_root_resource_id) {
621 DCHECK(CalledOnValidThread());
622
623 if (GetRootResourceId().empty()) {
624 GetDriveRootResourceId(
625 base::Bind(&APIUtil::DidGetDriveRootResourceIdForEnsureSyncRoot,
626 AsWeakPtr(), sync_root_resource_id));
627 return;
628 }
629
630 DVLOG(2) << "Ensuring the sync root directory is not in 'My Drive'.";
631 drive_service_->RemoveResourceFromDirectory(
632 GetRootResourceId(),
633 sync_root_resource_id,
634 base::Bind(&EmptyGDataErrorCodeCallback));
635 }
636
637 void APIUtil::DidGetDriveRootResourceIdForEnsureSyncRoot(
638 const std::string& sync_root_resource_id,
639 google_apis::GDataErrorCode error) {
640 DCHECK(CalledOnValidThread());
641
642 if (error != google_apis::HTTP_SUCCESS) {
643 DVLOG(2) << "Error on ensuring the sync root directory is not in"
644 << " 'My Drive': " << error;
645 // Give up ensuring the sync root directory is not in 'My Drive'. This will
646 // be retried at some point.
647 return;
648 }
649
650 DCHECK(!GetRootResourceId().empty());
651 EnsureSyncRootIsNotInMyDrive(sync_root_resource_id);
652 }
653
654 // static
655 // TODO(calvinlo): Delete this when Sync Directory Operations are supported by
656 // default.
657 std::string APIUtil::GetSyncRootDirectoryName() {
658 return IsSyncFSDirectoryOperationEnabled() ? kSyncRootDirectoryNameDev
659 : kSyncRootDirectoryName;
660 }
661
662 // static
663 std::string APIUtil::OriginToDirectoryTitle(const GURL& origin) {
664 DCHECK(origin.SchemeIs(extensions::kExtensionScheme));
665 return origin.host();
666 }
667
668 // static
669 GURL APIUtil::DirectoryTitleToOrigin(const std::string& title) {
670 return extensions::Extension::GetBaseURLFromExtensionId(title);
671 }
672
673 void APIUtil::OnReadyToSendRequests() {
674 DCHECK(CalledOnValidThread());
675 FOR_EACH_OBSERVER(APIUtilObserver, observers_, OnAuthenticated());
676 }
677
678 void APIUtil::OnConnectionTypeChanged(
679 net::NetworkChangeNotifier::ConnectionType type) {
680 DCHECK(CalledOnValidThread());
681 if (type != net::NetworkChangeNotifier::CONNECTION_NONE) {
682 FOR_EACH_OBSERVER(APIUtilObserver, observers_, OnNetworkConnected());
683 return;
684 }
685 // We're now disconnected, reset the drive_uploader_ to force stop
686 // uploading, otherwise the uploader may get stuck.
687 // TODO(kinuko): Check the uploader behavior if it's the expected behavior
688 // (http://crbug.com/223818)
689 CancelAllUploads(google_apis::GDATA_NO_CONNECTION);
690 }
691
692 void APIUtil::DidGetResourceList(
693 const ResourceListCallback& callback,
694 google_apis::GDataErrorCode error,
695 scoped_ptr<google_apis::ResourceList> resource_list) {
696 DCHECK(CalledOnValidThread());
697
698 if (error != google_apis::HTTP_SUCCESS) {
699 DVLOG(2) << "Error on listing resource: " << error;
700 callback.Run(error, scoped_ptr<google_apis::ResourceList>());
701 return;
702 }
703
704 DVLOG(2) << "Got resource list";
705 DCHECK(resource_list);
706 callback.Run(error, resource_list.Pass());
707 }
708
709 void APIUtil::DidGetResourceEntry(
710 const ResourceEntryCallback& callback,
711 google_apis::GDataErrorCode error,
712 scoped_ptr<google_apis::ResourceEntry> entry) {
713 DCHECK(CalledOnValidThread());
714
715 if (error != google_apis::HTTP_SUCCESS) {
716 DVLOG(2) << "Error on getting resource entry:" << error;
717 callback.Run(error, scoped_ptr<google_apis::ResourceEntry>());
718 return;
719 }
720
721 if (entry->deleted()) {
722 DVLOG(2) << "Got resource entry, the entry was trashed.";
723 callback.Run(google_apis::HTTP_NOT_FOUND, entry.Pass());
724 return;
725 }
726
727 DVLOG(2) << "Got resource entry";
728 DCHECK(entry);
729 callback.Run(error, entry.Pass());
730 }
731
732 void APIUtil::DidGetTemporaryFileForDownload(
733 const std::string& resource_id,
734 const std::string& local_file_md5,
735 scoped_ptr<webkit_blob::ScopedFile> local_file,
736 const DownloadFileCallback& callback,
737 bool success) {
738 if (!success) {
739 DVLOG(2) << "Error in creating a temp file under "
740 << temp_dir_path_.value();
741 callback.Run(google_apis::GDATA_FILE_ERROR, std::string(), 0, base::Time(),
742 local_file.Pass());
743 return;
744 }
745 drive_service_->GetResourceEntry(
746 resource_id,
747 base::Bind(&APIUtil::DidGetResourceEntry,
748 AsWeakPtr(),
749 base::Bind(&APIUtil::DownloadFileInternal,
750 AsWeakPtr(),
751 local_file_md5,
752 base::Passed(&local_file),
753 callback)));
754 }
755
756 void APIUtil::DownloadFileInternal(
757 const std::string& local_file_md5,
758 scoped_ptr<webkit_blob::ScopedFile> local_file,
759 const DownloadFileCallback& callback,
760 google_apis::GDataErrorCode error,
761 scoped_ptr<google_apis::ResourceEntry> entry) {
762 DCHECK(CalledOnValidThread());
763
764 if (error != google_apis::HTTP_SUCCESS) {
765 DVLOG(2) << "Error on getting resource entry for download";
766 callback.Run(error, std::string(), 0, base::Time(),
767 local_file.Pass());
768 return;
769 }
770 DCHECK(entry);
771
772 DVLOG(2) << "Got resource entry for download";
773 // If local file and remote file are same, cancel the download.
774 if (local_file_md5 == entry->file_md5()) {
775 callback.Run(google_apis::HTTP_NOT_MODIFIED,
776 local_file_md5,
777 entry->file_size(),
778 entry->updated_time(),
779 local_file.Pass());
780 return;
781 }
782
783 DVLOG(2) << "Downloading file: " << entry->resource_id();
784 const std::string& resource_id = entry->resource_id();
785 const base::FilePath& local_file_path = local_file->path();
786 drive_service_->DownloadFile(local_file_path,
787 resource_id,
788 base::Bind(&APIUtil::DidDownloadFile,
789 AsWeakPtr(),
790 base::Passed(&entry),
791 base::Passed(&local_file),
792 callback),
793 google_apis::GetContentCallback(),
794 google_apis::ProgressCallback());
795 }
796
797 void APIUtil::DidDownloadFile(scoped_ptr<google_apis::ResourceEntry> entry,
798 scoped_ptr<webkit_blob::ScopedFile> local_file,
799 const DownloadFileCallback& callback,
800 google_apis::GDataErrorCode error,
801 const base::FilePath& downloaded_file_path) {
802 DCHECK(CalledOnValidThread());
803 if (error == google_apis::HTTP_SUCCESS)
804 DVLOG(2) << "Download completed";
805 else
806 DVLOG(2) << "Error on downloading file: " << error;
807
808 callback.Run(
809 error, entry->file_md5(), entry->file_size(), entry->updated_time(),
810 local_file.Pass());
811 }
812
813 void APIUtil::DidUploadNewFile(const std::string& parent_resource_id,
814 const std::string& title,
815 UploadKey upload_key,
816 google_apis::GDataErrorCode error,
817 scoped_ptr<google_apis::ResourceEntry> entry) {
818 UploadFileCallback callback = GetAndUnregisterUploadCallback(upload_key);
819 DCHECK(!callback.is_null());
820 if (error != google_apis::HTTP_SUCCESS &&
821 error != google_apis::HTTP_CREATED) {
822 DVLOG(2) << "Error on uploading new file: " << error;
823 callback.Run(error, std::string(), std::string());
824 return;
825 }
826
827 DVLOG(2) << "Upload completed";
828 EnsureTitleUniqueness(parent_resource_id,
829 title,
830 base::Bind(&APIUtil::DidEnsureUniquenessForCreateFile,
831 AsWeakPtr(),
832 entry->resource_id(),
833 callback));
834 }
835
836 void APIUtil::DidEnsureUniquenessForCreateFile(
837 const std::string& expected_resource_id,
838 const UploadFileCallback& callback,
839 google_apis::GDataErrorCode error,
840 EnsureUniquenessStatus status,
841 scoped_ptr<google_apis::ResourceEntry> entry) {
842 if (error != google_apis::HTTP_SUCCESS) {
843 DVLOG(2) << "Error on uploading new file: " << error;
844 callback.Run(error, std::string(), std::string());
845 return;
846 }
847
848 switch (status) {
849 case NO_DUPLICATES_FOUND:
850 // The file was uploaded successfully and no conflict was detected.
851 DCHECK(entry);
852 DVLOG(2) << "No conflict detected on uploading new file";
853 callback.Run(
854 google_apis::HTTP_CREATED, entry->resource_id(), entry->file_md5());
855 return;
856
857 case RESOLVED_DUPLICATES:
858 // The file was uploaded successfully but a conflict was detected.
859 // The duplicated file was deleted successfully.
860 DCHECK(entry);
861 if (entry->resource_id() != expected_resource_id) {
862 // TODO(kinuko): We should check local vs remote md5 here.
863 DVLOG(2) << "Conflict detected on uploading new file";
864 callback.Run(google_apis::HTTP_CONFLICT,
865 entry->resource_id(),
866 entry->file_md5());
867 return;
868 }
869
870 DVLOG(2) << "Conflict detected on uploading new file and resolved";
871 callback.Run(
872 google_apis::HTTP_CREATED, entry->resource_id(), entry->file_md5());
873 return;
874
875 default:
876 NOTREACHED() << "Unknown status from EnsureTitleUniqueness:" << status
877 << " for " << expected_resource_id;
878 }
879 }
880
881 void APIUtil::UploadExistingFileInternal(
882 const std::string& remote_file_md5,
883 const base::FilePath& local_file_path,
884 const UploadFileCallback& callback,
885 google_apis::GDataErrorCode error,
886 scoped_ptr<google_apis::ResourceEntry> entry) {
887 DCHECK(CalledOnValidThread());
888
889 if (error != google_apis::HTTP_SUCCESS) {
890 DVLOG(2) << "Error on uploading existing file: " << error;
891 callback.Run(error, std::string(), std::string());
892 return;
893 }
894 DCHECK(entry);
895
896 // If remote file's hash value is different from the expected one, conflict
897 // might have occurred.
898 if (!remote_file_md5.empty() && remote_file_md5 != entry->file_md5()) {
899 DVLOG(2) << "Conflict detected before uploading existing file";
900 callback.Run(google_apis::HTTP_CONFLICT, std::string(), std::string());
901 return;
902 }
903
904 std::string mime_type = GetMimeTypeFromTitle(entry->title());
905 UploadKey upload_key = RegisterUploadCallback(callback);
906 ResourceEntryCallback did_upload_callback =
907 base::Bind(&APIUtil::DidUploadExistingFile, AsWeakPtr(), upload_key);
908 drive_uploader_->UploadExistingFile(
909 entry->resource_id(),
910 local_file_path,
911 mime_type,
912 entry->etag(),
913 base::Bind(&UploadResultAdapter, did_upload_callback),
914 google_apis::ProgressCallback());
915 }
916
917 bool APIUtil::IsAuthenticated() const {
918 return drive_service_->HasRefreshToken();
919 }
920
921 void APIUtil::DidUploadExistingFile(
922 UploadKey upload_key,
923 google_apis::GDataErrorCode error,
924 scoped_ptr<google_apis::ResourceEntry> entry) {
925 DCHECK(CalledOnValidThread());
926 UploadFileCallback callback = GetAndUnregisterUploadCallback(upload_key);
927 DCHECK(!callback.is_null());
928 if (error != google_apis::HTTP_SUCCESS) {
929 DVLOG(2) << "Error on uploading existing file: " << error;
930 callback.Run(error, std::string(), std::string());
931 return;
932 }
933
934 DCHECK(entry);
935 DVLOG(2) << "Upload completed";
936 callback.Run(error, entry->resource_id(), entry->file_md5());
937 }
938
939 void APIUtil::DeleteFileInternal(const std::string& remote_file_md5,
940 const GDataErrorCallback& callback,
941 google_apis::GDataErrorCode error,
942 scoped_ptr<google_apis::ResourceEntry> entry) {
943 DCHECK(CalledOnValidThread());
944
945 if (error != google_apis::HTTP_SUCCESS) {
946 DVLOG(2) << "Error on getting resource entry for deleting file: " << error;
947 callback.Run(error);
948 return;
949 }
950 DCHECK(entry);
951
952 // If remote file's hash value is different from the expected one, conflict
953 // might have occurred.
954 if (!remote_file_md5.empty() && remote_file_md5 != entry->file_md5()) {
955 DVLOG(2) << "Conflict detected before deleting file";
956 callback.Run(google_apis::HTTP_CONFLICT);
957 return;
958 }
959 DVLOG(2) << "Got resource entry for deleting file";
960
961 // Move the file to trash (don't delete it completely).
962 drive_service_->DeleteResource(
963 entry->resource_id(),
964 entry->etag(),
965 base::Bind(&APIUtil::DidDeleteFile, AsWeakPtr(), callback));
966 }
967
968 void APIUtil::DidDeleteFile(const GDataErrorCallback& callback,
969 google_apis::GDataErrorCode error) {
970 DCHECK(CalledOnValidThread());
971 if (error == google_apis::HTTP_SUCCESS)
972 DVLOG(2) << "Deletion completed";
973 else
974 DVLOG(2) << "Error on deleting file: " << error;
975
976 callback.Run(error);
977 }
978
979 void APIUtil::EnsureTitleUniqueness(const std::string& parent_resource_id,
980 const std::string& expected_title,
981 const EnsureUniquenessCallback& callback) {
982 DCHECK(CalledOnValidThread());
983 DVLOG(2) << "Checking if there's no conflict on entry creation";
984
985 const google_apis::GetResourceListCallback& bound_callback =
986 base::Bind(&APIUtil::DidListEntriesToEnsureUniqueness,
987 AsWeakPtr(),
988 parent_resource_id,
989 expected_title,
990 callback);
991
992 SearchByTitle(expected_title, parent_resource_id, bound_callback);
993 }
994
995 void APIUtil::DidListEntriesToEnsureUniqueness(
996 const std::string& parent_resource_id,
997 const std::string& expected_title,
998 const EnsureUniquenessCallback& callback,
999 google_apis::GDataErrorCode error,
1000 scoped_ptr<google_apis::ResourceList> feed) {
1001 DCHECK(CalledOnValidThread());
1002
1003 if (error != google_apis::HTTP_SUCCESS) {
1004 DVLOG(2) << "Error on listing resource for ensuring title uniqueness";
1005 callback.Run(
1006 error, NO_DUPLICATES_FOUND, scoped_ptr<google_apis::ResourceEntry>());
1007 return;
1008 }
1009 DVLOG(2) << "Got resource list for ensuring title uniqueness";
1010
1011 // This filtering is needed only on WAPI. Once we move to Drive API we can
1012 // drop this.
1013 std::string resource_id;
1014 ParentType parent_type = PARENT_TYPE_DIRECTORY;
1015 if (parent_resource_id.empty()) {
1016 resource_id = GetRootResourceId();
1017 DCHECK(!resource_id.empty());
1018 parent_type = PARENT_TYPE_ROOT_OR_EMPTY;
1019 } else {
1020 resource_id = parent_resource_id;
1021 }
1022 ScopedVector<google_apis::ResourceEntry> entries;
1023 entries.swap(*feed->mutable_entries());
1024 FilterEntriesByTitleAndParent(
1025 &entries, expected_title, resource_id, parent_type);
1026
1027 if (entries.empty()) {
1028 DVLOG(2) << "Uploaded file is not found";
1029 callback.Run(google_apis::HTTP_NOT_FOUND,
1030 NO_DUPLICATES_FOUND,
1031 scoped_ptr<google_apis::ResourceEntry>());
1032 return;
1033 }
1034
1035 if (entries.size() >= 2) {
1036 DVLOG(2) << "Conflict detected on creating entry";
1037 for (size_t i = 0; i < entries.size() - 1; ++i) {
1038 // TODO(tzik): Replace published_time with creation time after we move to
1039 // Drive API.
1040 if (entries[i]->published_time() < entries.back()->published_time())
1041 std::swap(entries[i], entries.back());
1042 }
1043
1044 scoped_ptr<google_apis::ResourceEntry> earliest_entry(entries.back());
1045 entries.back() = NULL;
1046 entries.get().pop_back();
1047
1048 DeleteEntriesForEnsuringTitleUniqueness(
1049 entries.Pass(),
1050 base::Bind(&EntryAdapterForEnsureTitleUniqueness,
1051 base::Passed(&earliest_entry),
1052 callback,
1053 RESOLVED_DUPLICATES));
1054 return;
1055 }
1056
1057 DVLOG(2) << "no conflict detected";
1058 DCHECK_EQ(1u, entries.size());
1059 scoped_ptr<google_apis::ResourceEntry> entry(entries.front());
1060 entries.weak_clear();
1061
1062 callback.Run(google_apis::HTTP_SUCCESS, NO_DUPLICATES_FOUND, entry.Pass());
1063 }
1064
1065 void APIUtil::DeleteEntriesForEnsuringTitleUniqueness(
1066 ScopedVector<google_apis::ResourceEntry> entries,
1067 const GDataErrorCallback& callback) {
1068 DCHECK(CalledOnValidThread());
1069 DVLOG(2) << "Cleaning up conflict on entry creation";
1070
1071 if (entries.empty()) {
1072 callback.Run(google_apis::HTTP_SUCCESS);
1073 return;
1074 }
1075
1076 scoped_ptr<google_apis::ResourceEntry> entry(entries.back());
1077 entries.back() = NULL;
1078 entries.get().pop_back();
1079
1080 // We don't care conflicts here as other clients may be also deleting this
1081 // file, so passing an empty etag.
1082 drive_service_->DeleteResource(
1083 entry->resource_id(),
1084 std::string(), // empty etag
1085 base::Bind(&APIUtil::DidDeleteEntriesForEnsuringTitleUniqueness,
1086 AsWeakPtr(),
1087 base::Passed(&entries),
1088 callback));
1089 }
1090
1091 void APIUtil::DidDeleteEntriesForEnsuringTitleUniqueness(
1092 ScopedVector<google_apis::ResourceEntry> entries,
1093 const GDataErrorCallback& callback,
1094 google_apis::GDataErrorCode error) {
1095 DCHECK(CalledOnValidThread());
1096
1097 if (error != google_apis::HTTP_SUCCESS &&
1098 error != google_apis::HTTP_NOT_FOUND) {
1099 DVLOG(2) << "Error on deleting file: " << error;
1100 callback.Run(error);
1101 return;
1102 }
1103
1104 DVLOG(2) << "Deletion completed";
1105 DeleteEntriesForEnsuringTitleUniqueness(entries.Pass(), callback);
1106 }
1107
1108 APIUtil::UploadKey APIUtil::RegisterUploadCallback(
1109 const UploadFileCallback& callback) {
1110 const bool inserted = upload_callback_map_.insert(
1111 std::make_pair(upload_next_key_, callback)).second;
1112 CHECK(inserted);
1113 return upload_next_key_++;
1114 }
1115
1116 APIUtil::UploadFileCallback APIUtil::GetAndUnregisterUploadCallback(
1117 UploadKey key) {
1118 UploadFileCallback callback;
1119 UploadCallbackMap::iterator found = upload_callback_map_.find(key);
1120 if (found == upload_callback_map_.end())
1121 return callback;
1122 callback = found->second;
1123 upload_callback_map_.erase(found);
1124 return callback;
1125 }
1126
1127 void APIUtil::CancelAllUploads(google_apis::GDataErrorCode error) {
1128 if (upload_callback_map_.empty())
1129 return;
1130 for (UploadCallbackMap::iterator iter = upload_callback_map_.begin();
1131 iter != upload_callback_map_.end();
1132 ++iter) {
1133 iter->second.Run(error, std::string(), std::string());
1134 }
1135 upload_callback_map_.clear();
1136 drive_uploader_.reset(new drive::DriveUploader(
1137 drive_service_.get(), content::BrowserThread::GetBlockingPool()));
1138 }
1139
1140 std::string APIUtil::GetRootResourceId() const {
1141 if (IsDriveAPIDisabled())
1142 return drive_service_->GetRootResourceId();
1143 return root_resource_id_;
1144 }
1145
1146 } // namespace drive_backend
1147 } // namespace sync_file_system
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698