drive_backend_util.cc revision effb81e5f8246d0db0270817048dc992db66e9fb
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/drive_backend_util.h" 6 7#include "base/file_util.h" 8#include "base/logging.h" 9#include "base/memory/scoped_vector.h" 10#include "base/message_loop/message_loop_proxy.h" 11#include "base/strings/string_number_conversions.h" 12#include "chrome/browser/drive/drive_api_util.h" 13#include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h" 14#include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.h" 15#include "chrome/browser/sync_file_system/logger.h" 16#include "google_apis/drive/drive_api_parser.h" 17#include "google_apis/drive/gdata_wapi_parser.h" 18#include "net/base/mime_util.h" 19#include "third_party/leveldatabase/src/include/leveldb/write_batch.h" 20 21namespace sync_file_system { 22namespace drive_backend { 23 24void PutServiceMetadataToBatch(const ServiceMetadata& service_metadata, 25 leveldb::WriteBatch* batch) { 26 std::string value; 27 bool success = service_metadata.SerializeToString(&value); 28 DCHECK(success); 29 batch->Put(kServiceMetadataKey, value); 30} 31 32void PutFileMetadataToBatch(const FileMetadata& file, 33 leveldb::WriteBatch* batch) { 34 if (!batch) 35 return; 36 37 std::string value; 38 bool success = file.SerializeToString(&value); 39 DCHECK(success); 40 batch->Put(kFileMetadataKeyPrefix + file.file_id(), value); 41} 42 43void PutFileTrackerToBatch(const FileTracker& tracker, 44 leveldb::WriteBatch* batch) { 45 if (!batch) 46 return; 47 48 std::string value; 49 bool success = tracker.SerializeToString(&value); 50 DCHECK(success); 51 batch->Put(kFileTrackerKeyPrefix + base::Int64ToString(tracker.tracker_id()), 52 value); 53} 54 55void PutFileMetadataDeletionToBatch(const std::string& file_id, 56 leveldb::WriteBatch* batch) { 57 if (batch) 58 batch->Delete(kFileMetadataKeyPrefix + file_id); 59} 60 61void PutFileTrackerDeletionToBatch(int64 tracker_id, 62 leveldb::WriteBatch* batch) { 63 if (batch) 64 batch->Delete(kFileTrackerKeyPrefix + base::Int64ToString(tracker_id)); 65} 66 67void PopulateFileDetailsByFileResource( 68 const google_apis::FileResource& file_resource, 69 FileDetails* details) { 70 details->clear_parent_folder_ids(); 71 for (std::vector<google_apis::ParentReference>::const_iterator itr = 72 file_resource.parents().begin(); 73 itr != file_resource.parents().end(); 74 ++itr) { 75 details->add_parent_folder_ids(itr->file_id()); 76 } 77 details->set_title(file_resource.title()); 78 79 google_apis::DriveEntryKind kind = drive::util::GetKind(file_resource); 80 if (kind == google_apis::ENTRY_KIND_FILE) 81 details->set_file_kind(FILE_KIND_FILE); 82 else if (kind == google_apis::ENTRY_KIND_FOLDER) 83 details->set_file_kind(FILE_KIND_FOLDER); 84 else 85 details->set_file_kind(FILE_KIND_UNSUPPORTED); 86 87 details->set_md5(file_resource.md5_checksum()); 88 details->set_etag(file_resource.etag()); 89 details->set_creation_time(file_resource.created_date().ToInternalValue()); 90 details->set_modification_time( 91 file_resource.modified_date().ToInternalValue()); 92 details->set_missing(false); 93} 94 95scoped_ptr<FileMetadata> CreateFileMetadataFromFileResource( 96 int64 change_id, 97 const google_apis::FileResource& resource) { 98 scoped_ptr<FileMetadata> file(new FileMetadata); 99 file->set_file_id(resource.file_id()); 100 101 FileDetails* details = file->mutable_details(); 102 details->set_change_id(change_id); 103 104 if (resource.labels().is_trashed()) { 105 details->set_missing(true); 106 return file.Pass(); 107 } 108 109 PopulateFileDetailsByFileResource(resource, details); 110 return file.Pass(); 111} 112 113scoped_ptr<FileMetadata> CreateFileMetadataFromChangeResource( 114 const google_apis::ChangeResource& change) { 115 scoped_ptr<FileMetadata> file(new FileMetadata); 116 file->set_file_id(change.file_id()); 117 118 FileDetails* details = file->mutable_details(); 119 details->set_change_id(change.change_id()); 120 121 if (change.is_deleted()) { 122 details->set_missing(true); 123 return file.Pass(); 124 } 125 126 PopulateFileDetailsByFileResource(*change.file(), details); 127 return file.Pass(); 128} 129 130scoped_ptr<FileMetadata> CreateDeletedFileMetadata( 131 int64 change_id, 132 const std::string& file_id) { 133 scoped_ptr<FileMetadata> file(new FileMetadata); 134 file->set_file_id(file_id); 135 136 FileDetails* details = file->mutable_details(); 137 details->set_change_id(change_id); 138 details->set_missing(true); 139 return file.Pass(); 140} 141 142webkit_blob::ScopedFile CreateTemporaryFile( 143 base::TaskRunner* file_task_runner) { 144 base::FilePath temp_file_path; 145 if (!base::CreateTemporaryFile(&temp_file_path)) 146 return webkit_blob::ScopedFile(); 147 148 return webkit_blob::ScopedFile( 149 temp_file_path, 150 webkit_blob::ScopedFile::DELETE_ON_SCOPE_OUT, 151 file_task_runner); 152} 153 154std::string FileKindToString(FileKind file_kind) { 155 switch (file_kind) { 156 case FILE_KIND_UNSUPPORTED: 157 return "unsupported"; 158 case FILE_KIND_FILE: 159 return "file"; 160 case FILE_KIND_FOLDER: 161 return "folder"; 162 } 163 164 NOTREACHED(); 165 return "unknown"; 166} 167 168bool HasFileAsParent(const FileDetails& details, const std::string& file_id) { 169 for (int i = 0; i < details.parent_folder_ids_size(); ++i) { 170 if (details.parent_folder_ids(i) == file_id) 171 return true; 172 } 173 return false; 174} 175 176std::string GetMimeTypeFromTitle(const base::FilePath& title) { 177 base::FilePath::StringType extension = title.Extension(); 178 std::string mime_type; 179 if (extension.empty() || 180 !net::GetWellKnownMimeTypeFromExtension(extension.substr(1), &mime_type)) 181 return kMimeTypeOctetStream; 182 return mime_type; 183} 184 185scoped_ptr<google_apis::ResourceEntry> GetOldestCreatedFolderResource( 186 ScopedVector<google_apis::ResourceEntry> candidates) { 187 scoped_ptr<google_apis::ResourceEntry> oldest; 188 for (size_t i = 0; i < candidates.size(); ++i) { 189 google_apis::ResourceEntry* entry = candidates[i]; 190 if (!entry->is_folder() || entry->deleted()) 191 continue; 192 193 if (!oldest || oldest->published_time() > entry->published_time()) { 194 oldest.reset(entry); 195 candidates[i] = NULL; 196 } 197 } 198 199 return oldest.Pass(); 200} 201 202SyncStatusCode GDataErrorCodeToSyncStatusCode( 203 google_apis::GDataErrorCode error) { 204 // NOTE: Please update DriveFileSyncService::UpdateServiceState when you add 205 // more error code mapping. 206 switch (error) { 207 case google_apis::HTTP_SUCCESS: 208 case google_apis::HTTP_CREATED: 209 case google_apis::HTTP_NO_CONTENT: 210 case google_apis::HTTP_FOUND: 211 return SYNC_STATUS_OK; 212 213 case google_apis::HTTP_NOT_MODIFIED: 214 return SYNC_STATUS_NOT_MODIFIED; 215 216 case google_apis::HTTP_CONFLICT: 217 case google_apis::HTTP_PRECONDITION: 218 return SYNC_STATUS_HAS_CONFLICT; 219 220 case google_apis::HTTP_UNAUTHORIZED: 221 return SYNC_STATUS_AUTHENTICATION_FAILED; 222 223 case google_apis::GDATA_NO_CONNECTION: 224 return SYNC_STATUS_NETWORK_ERROR; 225 226 case google_apis::HTTP_INTERNAL_SERVER_ERROR: 227 case google_apis::HTTP_BAD_GATEWAY: 228 case google_apis::HTTP_SERVICE_UNAVAILABLE: 229 case google_apis::GDATA_CANCELLED: 230 case google_apis::GDATA_NOT_READY: 231 return SYNC_STATUS_SERVICE_TEMPORARILY_UNAVAILABLE; 232 233 case google_apis::HTTP_NOT_FOUND: 234 case google_apis::HTTP_GONE: 235 return SYNC_FILE_ERROR_NOT_FOUND; 236 237 case google_apis::GDATA_FILE_ERROR: 238 return SYNC_FILE_ERROR_FAILED; 239 240 case google_apis::HTTP_FORBIDDEN: 241 return SYNC_STATUS_ACCESS_FORBIDDEN; 242 243 case google_apis::HTTP_RESUME_INCOMPLETE: 244 case google_apis::HTTP_BAD_REQUEST: 245 case google_apis::HTTP_LENGTH_REQUIRED: 246 case google_apis::HTTP_NOT_IMPLEMENTED: 247 case google_apis::GDATA_PARSE_ERROR: 248 case google_apis::GDATA_OTHER_ERROR: 249 return SYNC_STATUS_FAILED; 250 251 case google_apis::GDATA_NO_SPACE: 252 return SYNC_FILE_ERROR_NO_SPACE; 253 } 254 255 // There's a case where DriveService layer returns GDataErrorCode==-1 256 // when network is unavailable. (http://crbug.com/223042) 257 // TODO(kinuko,nhiroki): We should identify from where this undefined error 258 // code is coming. 259 if (error == -1) 260 return SYNC_STATUS_NETWORK_ERROR; 261 262 util::Log(logging::LOG_WARNING, 263 FROM_HERE, 264 "Got unexpected error: %d", 265 static_cast<int>(error)); 266 return SYNC_STATUS_FAILED; 267} 268 269scoped_ptr<FileTracker> CloneFileTracker(const FileTracker* obj) { 270 if (!obj) 271 return scoped_ptr<FileTracker>(); 272 return scoped_ptr<FileTracker>(new FileTracker(*obj)); 273} 274 275} // namespace drive_backend 276} // namespace sync_file_system 277