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