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