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
21 namespace sync_file_system {
22 namespace drive_backend {
23
PutServiceMetadataToBatch(const ServiceMetadata & service_metadata,leveldb::WriteBatch * batch)24 void 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
PutFileMetadataToBatch(const FileMetadata & file,leveldb::WriteBatch * batch)32 void 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
PutFileTrackerToBatch(const FileTracker & tracker,leveldb::WriteBatch * batch)43 void 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
PutFileMetadataDeletionToBatch(const std::string & file_id,leveldb::WriteBatch * batch)55 void PutFileMetadataDeletionToBatch(const std::string& file_id,
56 leveldb::WriteBatch* batch) {
57 if (batch)
58 batch->Delete(kFileMetadataKeyPrefix + file_id);
59 }
60
PutFileTrackerDeletionToBatch(int64 tracker_id,leveldb::WriteBatch * batch)61 void PutFileTrackerDeletionToBatch(int64 tracker_id,
62 leveldb::WriteBatch* batch) {
63 if (batch)
64 batch->Delete(kFileTrackerKeyPrefix + base::Int64ToString(tracker_id));
65 }
66
PopulateFileDetailsByFileResource(const google_apis::FileResource & file_resource,FileDetails * details)67 void 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(file_resource.labels().is_trashed());
93 }
94
CreateFileMetadataFromFileResource(int64 change_id,const google_apis::FileResource & resource)95 scoped_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
CreateFileMetadataFromChangeResource(const google_apis::ChangeResource & change)113 scoped_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
CreateDeletedFileMetadata(int64 change_id,const std::string & file_id)130 scoped_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
CreateTemporaryFile(base::TaskRunner * file_task_runner)142 webkit_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
FileKindToString(FileKind file_kind)154 std::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
HasFileAsParent(const FileDetails & details,const std::string & file_id)168 bool 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
GetMimeTypeFromTitle(const base::FilePath & title)176 std::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
GDataErrorCodeToSyncStatusCode(google_apis::GDataErrorCode error)185 SyncStatusCode GDataErrorCodeToSyncStatusCode(
186 google_apis::GDataErrorCode error) {
187 // NOTE: Please update DriveFileSyncService::UpdateServiceState when you add
188 // more error code mapping.
189 switch (error) {
190 case google_apis::HTTP_SUCCESS:
191 case google_apis::HTTP_CREATED:
192 case google_apis::HTTP_NO_CONTENT:
193 case google_apis::HTTP_FOUND:
194 return SYNC_STATUS_OK;
195
196 case google_apis::HTTP_NOT_MODIFIED:
197 return SYNC_STATUS_NOT_MODIFIED;
198
199 case google_apis::HTTP_CONFLICT:
200 case google_apis::HTTP_PRECONDITION:
201 return SYNC_STATUS_HAS_CONFLICT;
202
203 case google_apis::HTTP_UNAUTHORIZED:
204 return SYNC_STATUS_AUTHENTICATION_FAILED;
205
206 case google_apis::GDATA_NO_CONNECTION:
207 return SYNC_STATUS_NETWORK_ERROR;
208
209 case google_apis::HTTP_INTERNAL_SERVER_ERROR:
210 case google_apis::HTTP_BAD_GATEWAY:
211 case google_apis::HTTP_SERVICE_UNAVAILABLE:
212 case google_apis::GDATA_CANCELLED:
213 case google_apis::GDATA_NOT_READY:
214 return SYNC_STATUS_SERVICE_TEMPORARILY_UNAVAILABLE;
215
216 case google_apis::HTTP_NOT_FOUND:
217 case google_apis::HTTP_GONE:
218 return SYNC_FILE_ERROR_NOT_FOUND;
219
220 case google_apis::GDATA_FILE_ERROR:
221 return SYNC_FILE_ERROR_FAILED;
222
223 case google_apis::HTTP_FORBIDDEN:
224 return SYNC_STATUS_ACCESS_FORBIDDEN;
225
226 case google_apis::HTTP_RESUME_INCOMPLETE:
227 case google_apis::HTTP_BAD_REQUEST:
228 case google_apis::HTTP_LENGTH_REQUIRED:
229 case google_apis::HTTP_NOT_IMPLEMENTED:
230 case google_apis::GDATA_PARSE_ERROR:
231 case google_apis::GDATA_OTHER_ERROR:
232 return SYNC_STATUS_FAILED;
233
234 case google_apis::GDATA_NO_SPACE:
235 return SYNC_FILE_ERROR_NO_SPACE;
236 }
237
238 // There's a case where DriveService layer returns GDataErrorCode==-1
239 // when network is unavailable. (http://crbug.com/223042)
240 // TODO(kinuko,nhiroki): We should identify from where this undefined error
241 // code is coming.
242 if (error == -1)
243 return SYNC_STATUS_NETWORK_ERROR;
244
245 util::Log(logging::LOG_WARNING,
246 FROM_HERE,
247 "Got unexpected error: %d",
248 static_cast<int>(error));
249 return SYNC_STATUS_FAILED;
250 }
251
CloneFileTracker(const FileTracker * obj)252 scoped_ptr<FileTracker> CloneFileTracker(const FileTracker* obj) {
253 if (!obj)
254 return scoped_ptr<FileTracker>();
255 return scoped_ptr<FileTracker>(new FileTracker(*obj));
256 }
257
258 } // namespace drive_backend
259 } // namespace sync_file_system
260