• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/media_galleries/fileapi/iphoto_file_util.h"
6 
7 #include <set>
8 #include <string>
9 #include <vector>
10 
11 #include "base/bind_helpers.h"
12 #include "base/files/file_util.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "chrome/browser/media_galleries/fileapi/iphoto_data_provider.h"
16 #include "chrome/browser/media_galleries/fileapi/media_path_filter.h"
17 #include "chrome/browser/media_galleries/imported_media_gallery_registry.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "storage/browser/fileapi/file_system_operation_context.h"
20 #include "storage/browser/fileapi/file_system_url.h"
21 #include "storage/browser/fileapi/native_file_util.h"
22 #include "storage/common/blob/shareable_file_reference.h"
23 #include "storage/common/fileapi/directory_entry.h"
24 #include "storage/common/fileapi/file_system_util.h"
25 
26 using storage::DirectoryEntry;
27 
28 namespace iphoto {
29 
30 namespace {
31 
MakeDirectoryFileInfo(base::File::Info * file_info)32 base::File::Error MakeDirectoryFileInfo(base::File::Info* file_info) {
33   base::File::Info result;
34   result.is_directory = true;
35   *file_info = result;
36   return base::File::FILE_OK;
37 }
38 
39 template <typename T>
ContainsElement(const std::vector<T> & collection,const T & key)40 bool ContainsElement(const std::vector<T>& collection, const T& key) {
41   typename std::vector<T>::const_iterator it = collection.begin();
42   while (it != collection.end()) {
43     if (*it == key)
44       return true;
45     it++;
46   }
47   return false;
48 }
49 
GetVirtualPathComponents(const storage::FileSystemURL & url)50 std::vector<std::string> GetVirtualPathComponents(
51     const storage::FileSystemURL& url) {
52   ImportedMediaGalleryRegistry* imported_registry =
53       ImportedMediaGalleryRegistry::GetInstance();
54   base::FilePath root = imported_registry->ImportedRoot().AppendASCII("iphoto");
55 
56   DCHECK(root.IsParent(url.path()) || root == url.path());
57   base::FilePath virtual_path;
58   root.AppendRelativePath(url.path(), &virtual_path);
59 
60   std::vector<std::string> result;
61   storage::VirtualPath::GetComponentsUTF8Unsafe(virtual_path, &result);
62   return result;
63 }
64 
65 }  // namespace
66 
67 const char kIPhotoAlbumsDir[] = "Albums";
68 const char kIPhotoOriginalsDir[] = "Originals";
69 
IPhotoFileUtil(MediaPathFilter * media_path_filter)70 IPhotoFileUtil::IPhotoFileUtil(MediaPathFilter* media_path_filter)
71     : NativeMediaFileUtil(media_path_filter),
72       weak_factory_(this),
73       imported_registry_(NULL) {
74 }
75 
~IPhotoFileUtil()76 IPhotoFileUtil::~IPhotoFileUtil() {
77 }
78 
GetFileInfoOnTaskRunnerThread(scoped_ptr<storage::FileSystemOperationContext> context,const storage::FileSystemURL & url,const GetFileInfoCallback & callback)79 void IPhotoFileUtil::GetFileInfoOnTaskRunnerThread(
80     scoped_ptr<storage::FileSystemOperationContext> context,
81     const storage::FileSystemURL& url,
82     const GetFileInfoCallback& callback) {
83   IPhotoDataProvider* data_provider = GetDataProvider();
84   // |data_provider| may be NULL if the file system was revoked before this
85   // operation had a chance to run.
86   if (!data_provider) {
87     GetFileInfoWithFreshDataProvider(context.Pass(), url, callback, false);
88   } else {
89     data_provider->RefreshData(
90         base::Bind(&IPhotoFileUtil::GetFileInfoWithFreshDataProvider,
91                    weak_factory_.GetWeakPtr(), base::Passed(&context), url,
92                    callback));
93   }
94 }
95 
ReadDirectoryOnTaskRunnerThread(scoped_ptr<storage::FileSystemOperationContext> context,const storage::FileSystemURL & url,const ReadDirectoryCallback & callback)96 void IPhotoFileUtil::ReadDirectoryOnTaskRunnerThread(
97     scoped_ptr<storage::FileSystemOperationContext> context,
98     const storage::FileSystemURL& url,
99     const ReadDirectoryCallback& callback) {
100   IPhotoDataProvider* data_provider = GetDataProvider();
101   // |data_provider| may be NULL if the file system was revoked before this
102   // operation had a chance to run.
103   if (!data_provider) {
104     ReadDirectoryWithFreshDataProvider(context.Pass(), url, callback, false);
105   } else {
106     data_provider->RefreshData(
107         base::Bind(&IPhotoFileUtil::ReadDirectoryWithFreshDataProvider,
108                    weak_factory_.GetWeakPtr(), base::Passed(&context), url,
109                    callback));
110   }
111 }
112 
CreateSnapshotFileOnTaskRunnerThread(scoped_ptr<storage::FileSystemOperationContext> context,const storage::FileSystemURL & url,const CreateSnapshotFileCallback & callback)113 void IPhotoFileUtil::CreateSnapshotFileOnTaskRunnerThread(
114     scoped_ptr<storage::FileSystemOperationContext> context,
115     const storage::FileSystemURL& url,
116     const CreateSnapshotFileCallback& callback) {
117   IPhotoDataProvider* data_provider = GetDataProvider();
118   // |data_provider| may be NULL if the file system was revoked before this
119   // operation had a chance to run.
120   if (!data_provider) {
121     CreateSnapshotFileWithFreshDataProvider(context.Pass(), url, callback,
122                                             false);
123   } else {
124     data_provider->RefreshData(
125         base::Bind(&IPhotoFileUtil::CreateSnapshotFileWithFreshDataProvider,
126                    weak_factory_.GetWeakPtr(), base::Passed(&context), url,
127                    callback));
128   }
129 }
130 
GetFileInfoWithFreshDataProvider(scoped_ptr<storage::FileSystemOperationContext> context,const storage::FileSystemURL & url,const GetFileInfoCallback & callback,bool valid_parse)131 void IPhotoFileUtil::GetFileInfoWithFreshDataProvider(
132     scoped_ptr<storage::FileSystemOperationContext> context,
133     const storage::FileSystemURL& url,
134     const GetFileInfoCallback& callback,
135     bool valid_parse) {
136   if (!valid_parse) {
137     if (!callback.is_null()) {
138       content::BrowserThread::PostTask(
139           content::BrowserThread::IO,
140           FROM_HERE,
141           base::Bind(callback, base::File::FILE_ERROR_IO, base::File::Info()));
142     }
143     return;
144   }
145   NativeMediaFileUtil::GetFileInfoOnTaskRunnerThread(context.Pass(), url,
146                                                      callback);
147 }
148 
ReadDirectoryWithFreshDataProvider(scoped_ptr<storage::FileSystemOperationContext> context,const storage::FileSystemURL & url,const ReadDirectoryCallback & callback,bool valid_parse)149 void IPhotoFileUtil::ReadDirectoryWithFreshDataProvider(
150     scoped_ptr<storage::FileSystemOperationContext> context,
151     const storage::FileSystemURL& url,
152     const ReadDirectoryCallback& callback,
153     bool valid_parse) {
154   if (!valid_parse) {
155     if (!callback.is_null()) {
156       content::BrowserThread::PostTask(
157           content::BrowserThread::IO,
158           FROM_HERE,
159           base::Bind(callback, base::File::FILE_ERROR_IO, EntryList(), false));
160     }
161     return;
162   }
163   NativeMediaFileUtil::ReadDirectoryOnTaskRunnerThread(context.Pass(), url,
164                                                        callback);
165 }
166 
CreateSnapshotFileWithFreshDataProvider(scoped_ptr<storage::FileSystemOperationContext> context,const storage::FileSystemURL & url,const CreateSnapshotFileCallback & callback,bool valid_parse)167 void IPhotoFileUtil::CreateSnapshotFileWithFreshDataProvider(
168     scoped_ptr<storage::FileSystemOperationContext> context,
169     const storage::FileSystemURL& url,
170     const CreateSnapshotFileCallback& callback,
171     bool valid_parse) {
172   if (!valid_parse) {
173     if (!callback.is_null()) {
174       base::File::Info file_info;
175       base::FilePath platform_path;
176       scoped_refptr<storage::ShareableFileReference> file_ref;
177       content::BrowserThread::PostTask(
178           content::BrowserThread::IO,
179           FROM_HERE,
180           base::Bind(callback, base::File::FILE_ERROR_IO, file_info,
181                      platform_path, file_ref));
182     }
183     return;
184   }
185   NativeMediaFileUtil::CreateSnapshotFileOnTaskRunnerThread(context.Pass(), url,
186                                                             callback);
187 }
188 
189 // Begin actual implementation.
190 
GetFileInfoSync(storage::FileSystemOperationContext * context,const storage::FileSystemURL & url,base::File::Info * file_info,base::FilePath * platform_path)191 base::File::Error IPhotoFileUtil::GetFileInfoSync(
192     storage::FileSystemOperationContext* context,
193     const storage::FileSystemURL& url,
194     base::File::Info* file_info,
195     base::FilePath* platform_path) {
196   std::vector<std::string> components = GetVirtualPathComponents(url);
197 
198   if (components.size() == 0) {
199     return MakeDirectoryFileInfo(file_info);
200   }
201 
202   // The 'Albums' directory.
203   if (components[0] == kIPhotoAlbumsDir) {
204     if (components.size() == 1) {
205       return MakeDirectoryFileInfo(file_info);
206     } else if (components.size() == 2) {
207       std::vector<std::string> albums =
208           GetDataProvider()->GetAlbumNames();
209       if (ContainsElement(albums, components[1]))
210         return MakeDirectoryFileInfo(file_info);
211     } else if (components.size() == 3) {
212       if (components[2] == kIPhotoOriginalsDir) {
213         if (GetDataProvider()->HasOriginals(components[1]))
214           return MakeDirectoryFileInfo(file_info);
215         else
216           return base::File::FILE_ERROR_NOT_FOUND;
217       }
218 
219       base::FilePath location = GetDataProvider()->GetPhotoLocationInAlbum(
220           components[1], components[2]);
221       if (!location.empty()) {
222         return NativeMediaFileUtil::GetFileInfoSync(
223             context, url, file_info, platform_path);
224       }
225     } else if (components.size() == 4 &&
226                GetDataProvider()->HasOriginals(components[1]) &&
227                components[2] == kIPhotoOriginalsDir) {
228       base::FilePath location = GetDataProvider()->GetOriginalPhotoLocation(
229           components[1], components[3]);
230       if (!location.empty()) {
231         return NativeMediaFileUtil::GetFileInfoSync(
232             context, url, file_info, platform_path);
233       }
234     }
235   }
236 
237   return base::File::FILE_ERROR_NOT_FOUND;
238 }
239 
ReadDirectorySync(storage::FileSystemOperationContext * context,const storage::FileSystemURL & url,EntryList * file_list)240 base::File::Error IPhotoFileUtil::ReadDirectorySync(
241     storage::FileSystemOperationContext* context,
242     const storage::FileSystemURL& url,
243     EntryList* file_list) {
244   DCHECK(file_list->empty());
245   std::vector<std::string> components = GetVirtualPathComponents(url);
246 
247   // Root directory. Child is the /Albums dir.
248   if (components.size() == 0) {
249     file_list->push_back(DirectoryEntry(kIPhotoAlbumsDir,
250                                         DirectoryEntry::DIRECTORY,
251                                         0, base::Time()));
252     return base::File::FILE_OK;
253   }
254 
255   if (components[0] == kIPhotoAlbumsDir) {
256     if (components.size() == 1) {
257       // Albums dir contains all album names.
258       std::vector<std::string> albums =
259           GetDataProvider()->GetAlbumNames();
260       for (std::vector<std::string>::const_iterator it = albums.begin();
261            it != albums.end(); it++) {
262         file_list->push_back(DirectoryEntry(*it, DirectoryEntry::DIRECTORY,
263                                             0, base::Time()));
264       }
265       return base::File::FILE_OK;
266     } else if (components.size() == 2) {
267       std::vector<std::string> albums =
268           GetDataProvider()->GetAlbumNames();
269       if (!ContainsElement(albums, components[1]))
270         return base::File::FILE_ERROR_NOT_FOUND;
271 
272       // Album dirs contain all photos in them.
273       if (GetDataProvider()->HasOriginals(components[1])) {
274         file_list->push_back(DirectoryEntry(kIPhotoOriginalsDir,
275                                             DirectoryEntry::DIRECTORY,
276                                             0, base::Time()));
277       }
278       std::map<std::string, base::FilePath> locations =
279           GetDataProvider()->GetAlbumContents(components[1]);
280       for (std::map<std::string, base::FilePath>::const_iterator it =
281                locations.begin();
282            it != locations.end(); it++) {
283         base::File::Info info;
284         if (!base::GetFileInfo(it->second, &info))
285           return base::File::FILE_ERROR_IO;
286         file_list->push_back(DirectoryEntry(it->first, DirectoryEntry::FILE,
287                                             info.size, info.last_modified));
288       }
289       return base::File::FILE_OK;
290     } else if (components.size() == 3 &&
291                components[2] == kIPhotoOriginalsDir &&
292                GetDataProvider()->HasOriginals(components[1])) {
293       std::map<std::string, base::FilePath> originals =
294           GetDataProvider()->GetOriginals(components[1]);
295       for (std::map<std::string, base::FilePath>::const_iterator it =
296                originals.begin();
297            it != originals.end(); it++) {
298         base::File::Info info;
299         if (!base::GetFileInfo(it->second, &info))
300           return base::File::FILE_ERROR_IO;
301         file_list->push_back(DirectoryEntry(it->first, DirectoryEntry::FILE,
302                                             info.size, info.last_modified));
303       }
304       return base::File::FILE_OK;
305     }
306   }
307 
308   return base::File::FILE_ERROR_NOT_FOUND;
309 }
310 
DeleteDirectorySync(storage::FileSystemOperationContext * context,const storage::FileSystemURL & url)311 base::File::Error IPhotoFileUtil::DeleteDirectorySync(
312     storage::FileSystemOperationContext* context,
313     const storage::FileSystemURL& url) {
314   return base::File::FILE_ERROR_SECURITY;
315 }
316 
DeleteFileSync(storage::FileSystemOperationContext * context,const storage::FileSystemURL & url)317 base::File::Error IPhotoFileUtil::DeleteFileSync(
318     storage::FileSystemOperationContext* context,
319     const storage::FileSystemURL& url) {
320   return base::File::FILE_ERROR_SECURITY;
321 }
322 
GetLocalFilePath(storage::FileSystemOperationContext * context,const storage::FileSystemURL & url,base::FilePath * local_file_path)323 base::File::Error IPhotoFileUtil::GetLocalFilePath(
324     storage::FileSystemOperationContext* context,
325     const storage::FileSystemURL& url,
326     base::FilePath* local_file_path) {
327   std::vector<std::string> components = GetVirtualPathComponents(url);
328 
329   if (components.size() == 3 && components[0] == kIPhotoAlbumsDir) {
330     base::FilePath location = GetDataProvider()->GetPhotoLocationInAlbum(
331         components[1], components[2]);
332     if (!location.empty()) {
333       *local_file_path = location;
334       return base::File::FILE_OK;
335     }
336   }
337 
338   if (components.size() == 4 && components[0] == kIPhotoAlbumsDir &&
339       GetDataProvider()->HasOriginals(components[1]) &&
340       components[2] == kIPhotoOriginalsDir) {
341     base::FilePath location = GetDataProvider()->GetOriginalPhotoLocation(
342         components[1], components[3]);
343     if (!location.empty()) {
344       *local_file_path = location;
345       return base::File::FILE_OK;
346     }
347   }
348 
349   return base::File::FILE_ERROR_NOT_FOUND;
350 }
351 
GetDataProvider()352 IPhotoDataProvider* IPhotoFileUtil::GetDataProvider() {
353   if (!imported_registry_)
354     imported_registry_ = ImportedMediaGalleryRegistry::GetInstance();
355   return imported_registry_->IPhotoDataProvider();
356 }
357 
358 }  // namespace iphoto
359