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/itunes_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/utf_string_conversions.h"
     14 #include "chrome/browser/media_galleries/fileapi/itunes_data_provider.h"
     15 #include "chrome/browser/media_galleries/fileapi/media_path_filter.h"
     16 #include "chrome/browser/media_galleries/imported_media_gallery_registry.h"
     17 #include "content/public/browser/browser_thread.h"
     18 #include "storage/browser/fileapi/file_system_operation_context.h"
     19 #include "storage/browser/fileapi/file_system_url.h"
     20 #include "storage/browser/fileapi/native_file_util.h"
     21 #include "storage/common/blob/shareable_file_reference.h"
     22 #include "storage/common/fileapi/file_system_util.h"
     23 
     24 using storage::DirectoryEntry;
     25 
     26 namespace itunes {
     27 
     28 namespace {
     29 
     30 base::File::Error MakeDirectoryFileInfo(base::File::Info* file_info) {
     31   base::File::Info result;
     32   result.is_directory = true;
     33   *file_info = result;
     34   return base::File::FILE_OK;
     35 }
     36 
     37 std::vector<std::string> GetVirtualPathComponents(
     38     const storage::FileSystemURL& url) {
     39   ImportedMediaGalleryRegistry* imported_registry =
     40       ImportedMediaGalleryRegistry::GetInstance();
     41   base::FilePath root = imported_registry->ImportedRoot().AppendASCII("itunes");
     42 
     43   DCHECK(root.IsParent(url.path()) || root == url.path());
     44   base::FilePath virtual_path;
     45   root.AppendRelativePath(url.path(), &virtual_path);
     46 
     47   std::vector<std::string> result;
     48   storage::VirtualPath::GetComponentsUTF8Unsafe(virtual_path, &result);
     49   return result;
     50 }
     51 
     52 }  // namespace
     53 
     54 const char kITunesLibraryXML[] = "iTunes Music Library.xml";
     55 const char kITunesMediaDir[] = "iTunes Media";
     56 const char kITunesMusicDir[] = "Music";
     57 const char kITunesAutoAddDir[] = "Automatically Add to iTunes";
     58 
     59 ITunesFileUtil::ITunesFileUtil(MediaPathFilter* media_path_filter)
     60     : NativeMediaFileUtil(media_path_filter),
     61       imported_registry_(NULL),
     62       weak_factory_(this) {
     63 }
     64 
     65 ITunesFileUtil::~ITunesFileUtil() {
     66 }
     67 
     68 void ITunesFileUtil::GetFileInfoOnTaskRunnerThread(
     69     scoped_ptr<storage::FileSystemOperationContext> context,
     70     const storage::FileSystemURL& url,
     71     const GetFileInfoCallback& callback) {
     72   ITunesDataProvider* data_provider = GetDataProvider();
     73   // |data_provider| may be NULL if the file system was revoked before this
     74   // operation had a chance to run.
     75   if (!data_provider) {
     76     GetFileInfoWithFreshDataProvider(context.Pass(), url, callback, false);
     77   } else {
     78     data_provider->RefreshData(
     79         base::Bind(&ITunesFileUtil::GetFileInfoWithFreshDataProvider,
     80                    weak_factory_.GetWeakPtr(), base::Passed(&context), url,
     81                    callback));
     82   }
     83 }
     84 
     85 void ITunesFileUtil::ReadDirectoryOnTaskRunnerThread(
     86     scoped_ptr<storage::FileSystemOperationContext> context,
     87     const storage::FileSystemURL& url,
     88     const ReadDirectoryCallback& callback) {
     89   ITunesDataProvider* data_provider = GetDataProvider();
     90   // |data_provider| may be NULL if the file system was revoked before this
     91   // operation had a chance to run.
     92   if (!data_provider) {
     93     ReadDirectoryWithFreshDataProvider(context.Pass(), url, callback, false);
     94   } else {
     95     data_provider->RefreshData(
     96         base::Bind(&ITunesFileUtil::ReadDirectoryWithFreshDataProvider,
     97                    weak_factory_.GetWeakPtr(), base::Passed(&context), url,
     98                    callback));
     99   }
    100 }
    101 
    102 void ITunesFileUtil::CreateSnapshotFileOnTaskRunnerThread(
    103     scoped_ptr<storage::FileSystemOperationContext> context,
    104     const storage::FileSystemURL& url,
    105     const CreateSnapshotFileCallback& callback) {
    106   ITunesDataProvider* data_provider = GetDataProvider();
    107   // |data_provider| may be NULL if the file system was revoked before this
    108   // operation had a chance to run.
    109   if (!data_provider) {
    110     CreateSnapshotFileWithFreshDataProvider(context.Pass(), url, callback,
    111                                             false);
    112   } else {
    113     data_provider->RefreshData(
    114         base::Bind(&ITunesFileUtil::CreateSnapshotFileWithFreshDataProvider,
    115                    weak_factory_.GetWeakPtr(), base::Passed(&context), url,
    116                    callback));
    117   }
    118 }
    119 
    120 // Contents of the iTunes media gallery:
    121 //   /                                                - root directory
    122 //   /iTunes Music Library.xml                        - library xml file
    123 //   /iTunes Media/Automatically Add to iTunes        - auto-import directory
    124 //   /iTunes Media/Music/<Artist>/<Album>/<Track>     - tracks
    125 //
    126 base::File::Error ITunesFileUtil::GetFileInfoSync(
    127     storage::FileSystemOperationContext* context,
    128     const storage::FileSystemURL& url,
    129     base::File::Info* file_info,
    130     base::FilePath* platform_path) {
    131   std::vector<std::string> components = GetVirtualPathComponents(url);
    132 
    133   if (components.size() == 0)
    134     return MakeDirectoryFileInfo(file_info);
    135 
    136   if (components.size() == 1 && components[0] == kITunesLibraryXML) {
    137     // We can't just call NativeMediaFileUtil::GetFileInfoSync() here because it
    138     // uses the MediaPathFilter. At this point, |library_path_| is known good
    139     // because GetFileInfoWithFreshDataProvider() gates access to this method.
    140     base::FilePath file_path = GetDataProvider()->library_path();
    141     if (platform_path)
    142       *platform_path = file_path;
    143     return storage::NativeFileUtil::GetFileInfo(file_path, file_info);
    144   }
    145 
    146   if (components[0] != kITunesMediaDir)
    147     return base::File::FILE_ERROR_NOT_FOUND;
    148 
    149   if (components[1] == kITunesAutoAddDir) {
    150     if (GetDataProvider()->auto_add_path().empty())
    151       return base::File::FILE_ERROR_NOT_FOUND;
    152     return NativeMediaFileUtil::GetFileInfoSync(context, url, file_info,
    153                                                 platform_path);
    154   }
    155 
    156   if (components[1] == kITunesMusicDir) {
    157     switch (components.size()) {
    158       case 2:
    159         return MakeDirectoryFileInfo(file_info);
    160 
    161       case 3:
    162         if (GetDataProvider()->KnownArtist(components[2]))
    163           return MakeDirectoryFileInfo(file_info);
    164         break;
    165 
    166       case 4:
    167         if (GetDataProvider()->KnownAlbum(components[2], components[3]))
    168           return MakeDirectoryFileInfo(file_info);
    169         break;
    170 
    171       case 5: {
    172         base::FilePath location =
    173             GetDataProvider()->GetTrackLocation(components[2], components[3],
    174                                                 components[4]);
    175          if (!location.empty()) {
    176           return NativeMediaFileUtil::GetFileInfoSync(context, url, file_info,
    177                                                       platform_path);
    178         }
    179         break;
    180       }
    181     }
    182   }
    183 
    184   return base::File::FILE_ERROR_NOT_FOUND;
    185 }
    186 
    187 base::File::Error ITunesFileUtil::ReadDirectorySync(
    188     storage::FileSystemOperationContext* context,
    189     const storage::FileSystemURL& url,
    190     EntryList* file_list) {
    191   DCHECK(file_list->empty());
    192   std::vector<std::string> components = GetVirtualPathComponents(url);
    193 
    194   if (components.size() == 0) {
    195     base::File::Info xml_info;
    196     if (!base::GetFileInfo(GetDataProvider()->library_path(), &xml_info))
    197       return base::File::FILE_ERROR_IO;
    198     file_list->push_back(DirectoryEntry(kITunesLibraryXML,
    199                                         DirectoryEntry::FILE,
    200                                         xml_info.size, xml_info.last_modified));
    201     file_list->push_back(DirectoryEntry(kITunesMediaDir,
    202                                         DirectoryEntry::DIRECTORY,
    203                                         0, base::Time()));
    204     return base::File::FILE_OK;
    205   }
    206 
    207   if (components.size() == 1 && components[0] == kITunesLibraryXML)
    208     return base::File::FILE_ERROR_NOT_A_DIRECTORY;
    209 
    210   if (components[0] != kITunesMediaDir || components.size() > 5)
    211     return base::File::FILE_ERROR_NOT_FOUND;
    212 
    213   if (components.size() == 1) {
    214     if (!GetDataProvider()->auto_add_path().empty()) {
    215       file_list->push_back(DirectoryEntry(kITunesAutoAddDir,
    216                                           DirectoryEntry::DIRECTORY,
    217                                           0, base::Time()));
    218     }
    219     file_list->push_back(DirectoryEntry(kITunesMusicDir,
    220                                         DirectoryEntry::DIRECTORY,
    221                                         0, base::Time()));
    222     return base::File::FILE_OK;
    223   }
    224 
    225   if (components[1] == kITunesAutoAddDir &&
    226       !GetDataProvider()->auto_add_path().empty()) {
    227     return NativeMediaFileUtil::ReadDirectorySync(context, url, file_list);
    228   }
    229 
    230   if (components[1] != kITunesMusicDir)
    231     return base::File::FILE_ERROR_NOT_FOUND;
    232 
    233   if (components.size() == 2) {
    234     std::set<ITunesDataProvider::ArtistName> artists =
    235         GetDataProvider()->GetArtistNames();
    236     std::set<ITunesDataProvider::ArtistName>::const_iterator it;
    237     for (it = artists.begin(); it != artists.end(); ++it)
    238       file_list->push_back(DirectoryEntry(*it, DirectoryEntry::DIRECTORY,
    239                                           0, base::Time()));
    240     return base::File::FILE_OK;
    241   }
    242 
    243   if (components.size() == 3) {
    244     std::set<ITunesDataProvider::AlbumName> albums =
    245         GetDataProvider()->GetAlbumNames(components[2]);
    246     if (albums.size() == 0)
    247       return base::File::FILE_ERROR_NOT_FOUND;
    248     std::set<ITunesDataProvider::AlbumName>::const_iterator it;
    249     for (it = albums.begin(); it != albums.end(); ++it)
    250       file_list->push_back(DirectoryEntry(*it, DirectoryEntry::DIRECTORY,
    251                                           0, base::Time()));
    252     return base::File::FILE_OK;
    253   }
    254 
    255   if (components.size() == 4) {
    256     ITunesDataProvider::Album album =
    257         GetDataProvider()->GetAlbum(components[2], components[3]);
    258     if (album.size() == 0)
    259       return base::File::FILE_ERROR_NOT_FOUND;
    260     ITunesDataProvider::Album::const_iterator it;
    261     for (it = album.begin(); it != album.end(); ++it) {
    262       base::File::Info file_info;
    263       if (media_path_filter()->Match(it->second) &&
    264           base::GetFileInfo(it->second, &file_info)) {
    265         file_list->push_back(DirectoryEntry(it->first, DirectoryEntry::FILE,
    266                                             file_info.size,
    267                                             file_info.last_modified));
    268       }
    269     }
    270     return base::File::FILE_OK;
    271   }
    272 
    273   // At this point, the only choice is one of two errors, but figuring out
    274   // which one is required.
    275   DCHECK_EQ(4UL, components.size());
    276   base::FilePath location;
    277   location = GetDataProvider()->GetTrackLocation(components[1], components[2],
    278                                                  components[3]);
    279   if (!location.empty())
    280     return base::File::FILE_ERROR_NOT_A_DIRECTORY;
    281   return base::File::FILE_ERROR_NOT_FOUND;
    282 }
    283 
    284 base::File::Error ITunesFileUtil::DeleteDirectorySync(
    285     storage::FileSystemOperationContext* context,
    286     const storage::FileSystemURL& url) {
    287   return base::File::FILE_ERROR_SECURITY;
    288 }
    289 
    290 base::File::Error ITunesFileUtil::DeleteFileSync(
    291     storage::FileSystemOperationContext* context,
    292     const storage::FileSystemURL& url) {
    293   return base::File::FILE_ERROR_SECURITY;
    294 }
    295 
    296 base::File::Error ITunesFileUtil::CreateSnapshotFileSync(
    297     storage::FileSystemOperationContext* context,
    298     const storage::FileSystemURL& url,
    299     base::File::Info* file_info,
    300     base::FilePath* platform_path,
    301     scoped_refptr<storage::ShareableFileReference>* file_ref) {
    302   std::vector<std::string> components = GetVirtualPathComponents(url);
    303   if (components.size() != 1 || components[0] != kITunesLibraryXML) {
    304     return NativeMediaFileUtil::CreateSnapshotFileSync(context, url, file_info,
    305                                                        platform_path, file_ref);
    306   }
    307 
    308   // The following code is different than
    309   // NativeMediaFileUtil::CreateSnapshotFileSync in that it knows that the
    310   // library xml file is not a directory and it doesn't run mime sniffing on the
    311   // file. The only way to get here is by way of
    312   // CreateSnapshotFileWithFreshDataProvider() so the file has already been
    313   // parsed and deemed valid.
    314   *file_ref = scoped_refptr<storage::ShareableFileReference>();
    315   return GetFileInfoSync(context, url, file_info, platform_path);
    316 }
    317 
    318 base::File::Error ITunesFileUtil::GetLocalFilePath(
    319     storage::FileSystemOperationContext* context,
    320     const storage::FileSystemURL& url,
    321     base::FilePath* local_file_path) {
    322   std::vector<std::string> components = GetVirtualPathComponents(url);
    323 
    324   if (components.size() == 1 && components[0] == kITunesLibraryXML) {
    325     *local_file_path = GetDataProvider()->library_path();
    326     return base::File::FILE_OK;
    327   }
    328 
    329   if (components.size() >= 2 && components[0] == kITunesMediaDir &&
    330       components[1] == kITunesAutoAddDir) {
    331     *local_file_path = GetDataProvider()->auto_add_path();
    332     if (local_file_path->empty())
    333       return base::File::FILE_ERROR_NOT_FOUND;
    334 
    335     for (size_t i = 2; i < components.size(); ++i) {
    336       *local_file_path = local_file_path->Append(
    337           base::FilePath::FromUTF8Unsafe(components[i]));
    338     }
    339     return base::File::FILE_OK;
    340   }
    341 
    342   // Should only get here for files, i.e. the xml file and tracks.
    343   if (components[0] != kITunesMediaDir || components[1] != kITunesMusicDir||
    344       components.size() != 5) {
    345     return base::File::FILE_ERROR_NOT_FOUND;
    346   }
    347 
    348   *local_file_path = GetDataProvider()->GetTrackLocation(components[2],
    349                                                          components[3],
    350                                                          components[4]);
    351   if (!local_file_path->empty())
    352     return base::File::FILE_OK;
    353 
    354   return base::File::FILE_ERROR_NOT_FOUND;
    355 }
    356 
    357 void ITunesFileUtil::GetFileInfoWithFreshDataProvider(
    358     scoped_ptr<storage::FileSystemOperationContext> context,
    359     const storage::FileSystemURL& url,
    360     const GetFileInfoCallback& callback,
    361     bool valid_parse) {
    362   if (!valid_parse) {
    363     if (!callback.is_null()) {
    364       content::BrowserThread::PostTask(
    365           content::BrowserThread::IO,
    366           FROM_HERE,
    367           base::Bind(callback, base::File::FILE_ERROR_IO,
    368                      base::File::Info()));
    369     }
    370     return;
    371   }
    372   NativeMediaFileUtil::GetFileInfoOnTaskRunnerThread(context.Pass(), url,
    373                                                      callback);
    374 }
    375 
    376 void ITunesFileUtil::ReadDirectoryWithFreshDataProvider(
    377     scoped_ptr<storage::FileSystemOperationContext> context,
    378     const storage::FileSystemURL& url,
    379     const ReadDirectoryCallback& callback,
    380     bool valid_parse) {
    381   if (!valid_parse) {
    382     if (!callback.is_null()) {
    383       content::BrowserThread::PostTask(
    384           content::BrowserThread::IO,
    385           FROM_HERE,
    386           base::Bind(callback, base::File::FILE_ERROR_IO, EntryList(), false));
    387     }
    388     return;
    389   }
    390   NativeMediaFileUtil::ReadDirectoryOnTaskRunnerThread(context.Pass(), url,
    391                                                        callback);
    392 }
    393 
    394 void ITunesFileUtil::CreateSnapshotFileWithFreshDataProvider(
    395     scoped_ptr<storage::FileSystemOperationContext> context,
    396     const storage::FileSystemURL& url,
    397     const CreateSnapshotFileCallback& callback,
    398     bool valid_parse) {
    399   if (!valid_parse) {
    400     if (!callback.is_null()) {
    401       base::File::Info file_info;
    402       base::FilePath platform_path;
    403       scoped_refptr<storage::ShareableFileReference> file_ref;
    404       content::BrowserThread::PostTask(
    405           content::BrowserThread::IO,
    406           FROM_HERE,
    407           base::Bind(callback, base::File::FILE_ERROR_IO, file_info,
    408                      platform_path, file_ref));
    409     }
    410     return;
    411   }
    412   NativeMediaFileUtil::CreateSnapshotFileOnTaskRunnerThread(context.Pass(), url,
    413                                                             callback);
    414 }
    415 
    416 ITunesDataProvider* ITunesFileUtil::GetDataProvider() {
    417   if (!imported_registry_)
    418     imported_registry_ = ImportedMediaGalleryRegistry::GetInstance();
    419   return imported_registry_->ITunesDataProvider();
    420 }
    421 
    422 }  // namespace itunes
    423