Home | History | Annotate | Download | only in fileapi
      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 
     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>
     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 
     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 
     70 IPhotoFileUtil::IPhotoFileUtil(MediaPathFilter* media_path_filter)
     71     : NativeMediaFileUtil(media_path_filter),
     72       weak_factory_(this),
     73       imported_registry_(NULL) {
     74 }
     75 
     76 IPhotoFileUtil::~IPhotoFileUtil() {
     77 }
     78 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    311 base::File::Error IPhotoFileUtil::DeleteDirectorySync(
    312     storage::FileSystemOperationContext* context,
    313     const storage::FileSystemURL& url) {
    314   return base::File::FILE_ERROR_SECURITY;
    315 }
    316 
    317 base::File::Error IPhotoFileUtil::DeleteFileSync(
    318     storage::FileSystemOperationContext* context,
    319     const storage::FileSystemURL& url) {
    320   return base::File::FILE_ERROR_SECURITY;
    321 }
    322 
    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 
    352 IPhotoDataProvider* IPhotoFileUtil::GetDataProvider() {
    353   if (!imported_registry_)
    354     imported_registry_ = ImportedMediaGalleryRegistry::GetInstance();
    355   return imported_registry_->IPhotoDataProvider();
    356 }
    357 
    358 }  // namespace iphoto
    359