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