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/picasa_file_util.h"
      6 
      7 #include <string>
      8 #include <vector>
      9 
     10 #include "base/basictypes.h"
     11 #include "base/bind_helpers.h"
     12 #include "base/strings/string_util.h"
     13 #include "base/strings/stringprintf.h"
     14 #include "base/strings/sys_string_conversions.h"
     15 #include "base/strings/utf_string_conversions.h"
     16 #include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h"
     17 #include "chrome/browser/media_galleries/fileapi/picasa_data_provider.h"
     18 #include "chrome/browser/media_galleries/imported_media_gallery_registry.h"
     19 #include "chrome/common/media_galleries/picasa_types.h"
     20 #include "content/public/browser/browser_thread.h"
     21 #include "webkit/browser/fileapi/file_system_operation_context.h"
     22 #include "webkit/browser/fileapi/file_system_url.h"
     23 #include "webkit/browser/fileapi/native_file_util.h"
     24 #include "webkit/common/fileapi/file_system_util.h"
     25 
     26 using base::FilePath;
     27 using fileapi::DirectoryEntry;
     28 using fileapi::FileSystemOperationContext;
     29 using fileapi::FileSystemURL;
     30 
     31 namespace picasa {
     32 
     33 namespace {
     34 
     35 base::File::Error FindAlbumInfo(const std::string& key,
     36                                 const AlbumMap* map,
     37                                 AlbumInfo* album_info) {
     38   if (!map)
     39     return base::File::FILE_ERROR_FAILED;
     40 
     41   AlbumMap::const_iterator it = map->find(key);
     42 
     43   if (it == map->end())
     44     return base::File::FILE_ERROR_NOT_FOUND;
     45 
     46   if (album_info != NULL)
     47     *album_info = it->second;
     48 
     49   return base::File::FILE_OK;
     50 }
     51 
     52 std::vector<std::string> GetVirtualPathComponents(
     53     const fileapi::FileSystemURL& url) {
     54   ImportedMediaGalleryRegistry* imported_registry =
     55       ImportedMediaGalleryRegistry::GetInstance();
     56   base::FilePath root = imported_registry->ImportedRoot().AppendASCII("picasa");
     57 
     58   DCHECK(root.IsParent(url.path()) || root == url.path());
     59   base::FilePath virtual_path;
     60   root.AppendRelativePath(url.path(), &virtual_path);
     61 
     62   std::vector<std::string> result;
     63   fileapi::VirtualPath::GetComponentsUTF8Unsafe(virtual_path, &result);
     64   return result;
     65 }
     66 
     67 PicasaDataProvider::DataType GetDataTypeForURL(
     68     const fileapi::FileSystemURL& url) {
     69   std::vector<std::string> components = GetVirtualPathComponents(url);
     70   if (components.size() >= 2 && components[0] == kPicasaDirAlbums)
     71     return PicasaDataProvider::ALBUMS_IMAGES_DATA;
     72 
     73   return PicasaDataProvider::LIST_OF_ALBUMS_AND_FOLDERS_DATA;
     74 }
     75 
     76 }  // namespace
     77 
     78 const char kPicasaDirAlbums[]  = "albums";
     79 const char kPicasaDirFolders[] = "folders";
     80 
     81 PicasaFileUtil::PicasaFileUtil(MediaPathFilter* media_path_filter)
     82     : NativeMediaFileUtil(media_path_filter),
     83       weak_factory_(this) {
     84 }
     85 
     86 PicasaFileUtil::~PicasaFileUtil() {}
     87 
     88 void PicasaFileUtil::GetFileInfoOnTaskRunnerThread(
     89     scoped_ptr<fileapi::FileSystemOperationContext> context,
     90     const fileapi::FileSystemURL& url,
     91     const GetFileInfoCallback& callback) {
     92   PicasaDataProvider* data_provider = GetDataProvider();
     93   // |data_provider| may be NULL if the file system was revoked before this
     94   // operation had a chance to run.
     95   if (!data_provider) {
     96     GetFileInfoWithFreshDataProvider(context.Pass(), url, callback, false);
     97   } else {
     98     data_provider->RefreshData(
     99         GetDataTypeForURL(url),
    100         base::Bind(&PicasaFileUtil::GetFileInfoWithFreshDataProvider,
    101                    weak_factory_.GetWeakPtr(),
    102                    base::Passed(&context),
    103                    url,
    104                    callback));
    105   }
    106 }
    107 
    108 void PicasaFileUtil::ReadDirectoryOnTaskRunnerThread(
    109     scoped_ptr<fileapi::FileSystemOperationContext> context,
    110     const fileapi::FileSystemURL& url,
    111     const ReadDirectoryCallback& callback) {
    112   PicasaDataProvider* data_provider = GetDataProvider();
    113   // |data_provider| may be NULL if the file system was revoked before this
    114   // operation had a chance to run.
    115   if (!data_provider) {
    116     ReadDirectoryWithFreshDataProvider(context.Pass(), url, callback, false);
    117   } else {
    118     data_provider->RefreshData(
    119         GetDataTypeForURL(url),
    120         base::Bind(&PicasaFileUtil::ReadDirectoryWithFreshDataProvider,
    121                    weak_factory_.GetWeakPtr(),
    122                    base::Passed(&context),
    123                    url,
    124                    callback));
    125   }
    126 }
    127 
    128 base::File::Error PicasaFileUtil::GetFileInfoSync(
    129     FileSystemOperationContext* context, const FileSystemURL& url,
    130     base::File::Info* file_info, base::FilePath* platform_path) {
    131   DCHECK(context);
    132   DCHECK(file_info);
    133 
    134   if (platform_path)
    135     *platform_path = base::FilePath();
    136 
    137   std::vector<std::string> components = GetVirtualPathComponents(url);
    138 
    139   switch (components.size()) {
    140     case 0:
    141       // Root directory.
    142       file_info->is_directory = true;
    143       return base::File::FILE_OK;
    144     case 1:
    145       if (components[0] == kPicasaDirAlbums ||
    146           components[0] == kPicasaDirFolders) {
    147         file_info->is_directory = true;
    148         return base::File::FILE_OK;
    149       }
    150 
    151       break;
    152     case 2:
    153       if (components[0] == kPicasaDirAlbums) {
    154         scoped_ptr<AlbumMap> album_map = GetDataProvider()->GetAlbums();
    155         base::File::Error error =
    156             FindAlbumInfo(components[1], album_map.get(), NULL);
    157         if (error != base::File::FILE_OK)
    158           return error;
    159 
    160         file_info->is_directory = true;
    161         return base::File::FILE_OK;
    162       }
    163 
    164       if (components[0] == kPicasaDirFolders) {
    165         return NativeMediaFileUtil::GetFileInfoSync(context, url, file_info,
    166                                                     platform_path);
    167       }
    168       break;
    169     case 3:
    170       // NativeMediaFileUtil::GetInfo calls into virtual function
    171       // PicasaFileUtil::GetLocalFilePath, and that will handle both
    172       // album contents and folder contents.
    173       base::File::Error result = NativeMediaFileUtil::GetFileInfoSync(
    174           context, url, file_info, platform_path);
    175 
    176       DCHECK(components[0] == kPicasaDirAlbums ||
    177              components[0] == kPicasaDirFolders ||
    178              result == base::File::FILE_ERROR_NOT_FOUND);
    179 
    180       return result;
    181   }
    182 
    183   return base::File::FILE_ERROR_NOT_FOUND;
    184 }
    185 
    186 base::File::Error PicasaFileUtil::ReadDirectorySync(
    187     fileapi::FileSystemOperationContext* context,
    188     const fileapi::FileSystemURL& url,
    189     EntryList* file_list) {
    190   DCHECK(context);
    191   DCHECK(file_list);
    192   DCHECK(file_list->empty());
    193 
    194   base::File::Info file_info;
    195   base::FilePath platform_directory_path;
    196   base::File::Error error = GetFileInfoSync(
    197       context, url, &file_info, &platform_directory_path);
    198 
    199   if (error != base::File::FILE_OK)
    200     return error;
    201 
    202   if (!file_info.is_directory)
    203     return base::File::FILE_ERROR_NOT_A_DIRECTORY;
    204 
    205   std::vector<std::string> components = GetVirtualPathComponents(url);
    206   switch (components.size()) {
    207     case 0: {
    208       // Root directory.
    209       file_list->push_back(
    210           DirectoryEntry(kPicasaDirAlbums, DirectoryEntry::DIRECTORY, 0,
    211                          base::Time()));
    212       file_list->push_back(
    213           DirectoryEntry(kPicasaDirFolders, DirectoryEntry::DIRECTORY, 0,
    214                          base::Time()));
    215       break;
    216     }
    217     case 1:
    218       if (components[0] == kPicasaDirAlbums) {
    219         scoped_ptr<AlbumMap> albums = GetDataProvider()->GetAlbums();
    220         if (!albums)
    221           return base::File::FILE_ERROR_NOT_FOUND;
    222 
    223         for (AlbumMap::const_iterator it = albums->begin();
    224              it != albums->end(); ++it) {
    225           file_list->push_back(
    226               DirectoryEntry(it->first, DirectoryEntry::DIRECTORY, 0,
    227                              it->second.timestamp));
    228         }
    229       } else if (components[0] == kPicasaDirFolders) {
    230         scoped_ptr<AlbumMap> folders = GetDataProvider()->GetFolders();
    231         if (!folders)
    232           return base::File::FILE_ERROR_NOT_FOUND;
    233 
    234         for (AlbumMap::const_iterator it = folders->begin();
    235              it != folders->end(); ++it) {
    236           file_list->push_back(
    237               DirectoryEntry(it->first, DirectoryEntry::DIRECTORY, 0,
    238                              it->second.timestamp));
    239         }
    240       }
    241       break;
    242     case 2:
    243       if (components[0] == kPicasaDirAlbums) {
    244         scoped_ptr<AlbumMap> album_map = GetDataProvider()->GetAlbums();
    245         AlbumInfo album_info;
    246         base::File::Error error =
    247             FindAlbumInfo(components[1], album_map.get(), &album_info);
    248         if (error != base::File::FILE_OK)
    249           return error;
    250 
    251         scoped_ptr<AlbumImages> album_images =
    252             GetDataProvider()->FindAlbumImages(album_info.uid, &error);
    253         if (error != base::File::FILE_OK)
    254           return error;
    255 
    256         for (AlbumImages::const_iterator it = album_images->begin();
    257              it != album_images->end();
    258              ++it) {
    259           fileapi::DirectoryEntry entry;
    260           base::File::Info info;
    261 
    262           // Simply skip files that we can't get info on.
    263           if (fileapi::NativeFileUtil::GetFileInfo(it->second, &info) !=
    264               base::File::FILE_OK) {
    265             continue;
    266           }
    267 
    268           file_list->push_back(DirectoryEntry(
    269               it->first, DirectoryEntry::FILE, info.size, info.last_modified));
    270         }
    271       }
    272 
    273       if (components[0] == kPicasaDirFolders) {
    274         EntryList super_list;
    275         base::File::Error error =
    276             NativeMediaFileUtil::ReadDirectorySync(context, url, &super_list);
    277         if (error != base::File::FILE_OK)
    278           return error;
    279 
    280         for (EntryList::const_iterator it = super_list.begin();
    281              it != super_list.end(); ++it) {
    282           if (!it->is_directory)
    283             file_list->push_back(*it);
    284         }
    285       }
    286 
    287       break;
    288   }
    289 
    290   return base::File::FILE_OK;
    291 }
    292 
    293 base::File::Error PicasaFileUtil::DeleteDirectorySync(
    294     fileapi::FileSystemOperationContext* context,
    295     const fileapi::FileSystemURL& url) {
    296   return base::File::FILE_ERROR_SECURITY;
    297 }
    298 
    299 base::File::Error PicasaFileUtil::DeleteFileSync(
    300     fileapi::FileSystemOperationContext* context,
    301     const fileapi::FileSystemURL& url) {
    302   return base::File::FILE_ERROR_SECURITY;
    303 }
    304 
    305 base::File::Error PicasaFileUtil::GetLocalFilePath(
    306     FileSystemOperationContext* context, const FileSystemURL& url,
    307     base::FilePath* local_file_path) {
    308   DCHECK(local_file_path);
    309   DCHECK(url.is_valid());
    310   std::vector<std::string> components = GetVirtualPathComponents(url);
    311 
    312   switch (components.size()) {
    313     case 2:
    314       if (components[0] == kPicasaDirFolders) {
    315         scoped_ptr<AlbumMap> album_map = GetDataProvider()->GetFolders();
    316         AlbumInfo album_info;
    317         base::File::Error error =
    318             FindAlbumInfo(components[1], album_map.get(), &album_info);
    319         if (error != base::File::FILE_OK)
    320           return error;
    321 
    322         *local_file_path = album_info.path;
    323         return base::File::FILE_OK;
    324       }
    325       break;
    326     case 3:
    327       if (components[0] == kPicasaDirAlbums) {
    328         scoped_ptr<AlbumMap> album_map = GetDataProvider()->GetAlbums();
    329         AlbumInfo album_info;
    330         base::File::Error error =
    331             FindAlbumInfo(components[1], album_map.get(), &album_info);
    332         if (error != base::File::FILE_OK)
    333           return error;
    334 
    335         scoped_ptr<AlbumImages> album_images =
    336             GetDataProvider()->FindAlbumImages(album_info.uid, &error);
    337         if (error != base::File::FILE_OK)
    338           return error;
    339 
    340         AlbumImages::const_iterator it = album_images->find(components[2]);
    341         if (it == album_images->end())
    342           return base::File::FILE_ERROR_NOT_FOUND;
    343 
    344         *local_file_path = it->second;
    345         return base::File::FILE_OK;
    346       }
    347 
    348       if (components[0] == kPicasaDirFolders) {
    349         scoped_ptr<AlbumMap> album_map = GetDataProvider()->GetFolders();
    350         AlbumInfo album_info;
    351         base::File::Error error =
    352             FindAlbumInfo(components[1], album_map.get(), &album_info);
    353         if (error != base::File::FILE_OK)
    354           return error;
    355 
    356         // Not part of this class's mandate to check that it actually exists.
    357         *local_file_path = album_info.path.Append(url.path().BaseName());
    358         return base::File::FILE_OK;
    359       }
    360 
    361       return base::File::FILE_ERROR_NOT_FOUND;
    362       break;
    363   }
    364 
    365   // All other cases don't have a local path. The valid cases should be
    366   // intercepted by GetFileInfo()/CreateFileEnumerator(). Invalid cases
    367   // return a NOT_FOUND error.
    368   return base::File::FILE_ERROR_NOT_FOUND;
    369 }
    370 
    371 void PicasaFileUtil::GetFileInfoWithFreshDataProvider(
    372     scoped_ptr<fileapi::FileSystemOperationContext> context,
    373     const fileapi::FileSystemURL& url,
    374     const GetFileInfoCallback& callback,
    375     bool success) {
    376   if (!success) {
    377     content::BrowserThread::PostTask(
    378         content::BrowserThread::IO,
    379         FROM_HERE,
    380         base::Bind(callback, base::File::FILE_ERROR_IO, base::File::Info()));
    381     return;
    382   }
    383   NativeMediaFileUtil::GetFileInfoOnTaskRunnerThread(
    384       context.Pass(), url, callback);
    385 }
    386 
    387 void PicasaFileUtil::ReadDirectoryWithFreshDataProvider(
    388     scoped_ptr<fileapi::FileSystemOperationContext> context,
    389     const fileapi::FileSystemURL& url,
    390     const ReadDirectoryCallback& callback,
    391     bool success) {
    392   if (!success) {
    393     content::BrowserThread::PostTask(
    394         content::BrowserThread::IO,
    395         FROM_HERE,
    396         base::Bind(callback, base::File::FILE_ERROR_IO, EntryList(), false));
    397     return;
    398   }
    399   NativeMediaFileUtil::ReadDirectoryOnTaskRunnerThread(
    400       context.Pass(), url, callback);
    401 }
    402 
    403 PicasaDataProvider* PicasaFileUtil::GetDataProvider() {
    404   return ImportedMediaGalleryRegistry::PicasaDataProvider();
    405 }
    406 
    407 }  // namespace picasa
    408