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.
5 #include "chrome/browser/media_galleries/fileapi/itunes_file_util.h"
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/blob/shareable_file_reference.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/fileapi/file_system_util.h"
24 using storage::DirectoryEntry
;
30 base::File::Error
MakeDirectoryFileInfo(base::File::Info
* file_info
) {
31 base::File::Info result
;
32 result
.is_directory
= true;
34 return base::File::FILE_OK
;
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");
43 DCHECK(root
.IsParent(url
.path()) || root
== url
.path());
44 base::FilePath virtual_path
;
45 root
.AppendRelativePath(url
.path(), &virtual_path
);
47 std::vector
<std::string
> result
;
48 storage::VirtualPath::GetComponentsUTF8Unsafe(virtual_path
, &result
);
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";
59 ITunesFileUtil::ITunesFileUtil(MediaPathFilter
* media_path_filter
)
60 : NativeMediaFileUtil(media_path_filter
),
61 imported_registry_(NULL
),
65 ITunesFileUtil::~ITunesFileUtil() {
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.
76 GetFileInfoWithFreshDataProvider(context
.Pass(), url
, callback
, false);
78 data_provider
->RefreshData(
79 base::Bind(&ITunesFileUtil::GetFileInfoWithFreshDataProvider
,
80 weak_factory_
.GetWeakPtr(), base::Passed(&context
), url
,
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.
93 ReadDirectoryWithFreshDataProvider(context
.Pass(), url
, callback
, false);
95 data_provider
->RefreshData(
96 base::Bind(&ITunesFileUtil::ReadDirectoryWithFreshDataProvider
,
97 weak_factory_
.GetWeakPtr(), base::Passed(&context
), url
,
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
,
113 data_provider
->RefreshData(
114 base::Bind(&ITunesFileUtil::CreateSnapshotFileWithFreshDataProvider
,
115 weak_factory_
.GetWeakPtr(), base::Passed(&context
), url
,
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
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
);
133 if (components
.size() == 0)
134 return MakeDirectoryFileInfo(file_info
);
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();
142 *platform_path
= file_path
;
143 return storage::NativeFileUtil::GetFileInfo(file_path
, file_info
);
146 if (components
[0] != kITunesMediaDir
)
147 return base::File::FILE_ERROR_NOT_FOUND
;
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
,
156 if (components
[1] == kITunesMusicDir
) {
157 switch (components
.size()) {
159 return MakeDirectoryFileInfo(file_info
);
162 if (GetDataProvider()->KnownArtist(components
[2]))
163 return MakeDirectoryFileInfo(file_info
);
167 if (GetDataProvider()->KnownAlbum(components
[2], components
[3]))
168 return MakeDirectoryFileInfo(file_info
);
172 base::FilePath location
=
173 GetDataProvider()->GetTrackLocation(components
[2], components
[3],
175 if (!location
.empty()) {
176 return NativeMediaFileUtil::GetFileInfoSync(context
, url
, file_info
,
184 return base::File::FILE_ERROR_NOT_FOUND
;
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
);
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
,
204 return base::File::FILE_OK
;
207 if (components
.size() == 1 && components
[0] == kITunesLibraryXML
)
208 return base::File::FILE_ERROR_NOT_A_DIRECTORY
;
210 if (components
[0] != kITunesMediaDir
|| components
.size() > 5)
211 return base::File::FILE_ERROR_NOT_FOUND
;
213 if (components
.size() == 1) {
214 if (!GetDataProvider()->auto_add_path().empty()) {
215 file_list
->push_back(DirectoryEntry(kITunesAutoAddDir
,
216 DirectoryEntry::DIRECTORY
,
219 file_list
->push_back(DirectoryEntry(kITunesMusicDir
,
220 DirectoryEntry::DIRECTORY
,
222 return base::File::FILE_OK
;
225 if (components
[1] == kITunesAutoAddDir
&&
226 !GetDataProvider()->auto_add_path().empty()) {
227 return NativeMediaFileUtil::ReadDirectorySync(context
, url
, file_list
);
230 if (components
[1] != kITunesMusicDir
)
231 return base::File::FILE_ERROR_NOT_FOUND
;
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
,
240 return base::File::FILE_OK
;
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
,
252 return base::File::FILE_OK
;
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,
267 file_info
.last_modified
));
270 return base::File::FILE_OK
;
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],
279 if (!location
.empty())
280 return base::File::FILE_ERROR_NOT_A_DIRECTORY
;
281 return base::File::FILE_ERROR_NOT_FOUND
;
284 base::File::Error
ITunesFileUtil::DeleteDirectorySync(
285 storage::FileSystemOperationContext
* context
,
286 const storage::FileSystemURL
& url
) {
287 return base::File::FILE_ERROR_SECURITY
;
290 base::File::Error
ITunesFileUtil::DeleteFileSync(
291 storage::FileSystemOperationContext
* context
,
292 const storage::FileSystemURL
& url
) {
293 return base::File::FILE_ERROR_SECURITY
;
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
);
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
);
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
);
324 if (components
.size() == 1 && components
[0] == kITunesLibraryXML
) {
325 *local_file_path
= GetDataProvider()->library_path();
326 return base::File::FILE_OK
;
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
;
335 for (size_t i
= 2; i
< components
.size(); ++i
) {
336 *local_file_path
= local_file_path
->Append(
337 base::FilePath::FromUTF8Unsafe(components
[i
]));
339 return base::File::FILE_OK
;
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
;
348 *local_file_path
= GetDataProvider()->GetTrackLocation(components
[2],
351 if (!local_file_path
->empty())
352 return base::File::FILE_OK
;
354 return base::File::FILE_ERROR_NOT_FOUND
;
357 void ITunesFileUtil::GetFileInfoWithFreshDataProvider(
358 scoped_ptr
<storage::FileSystemOperationContext
> context
,
359 const storage::FileSystemURL
& url
,
360 const GetFileInfoCallback
& callback
,
363 if (!callback
.is_null()) {
364 content::BrowserThread::PostTask(
365 content::BrowserThread::IO
,
367 base::Bind(callback
, base::File::FILE_ERROR_IO
,
368 base::File::Info()));
372 NativeMediaFileUtil::GetFileInfoOnTaskRunnerThread(context
.Pass(), url
,
376 void ITunesFileUtil::ReadDirectoryWithFreshDataProvider(
377 scoped_ptr
<storage::FileSystemOperationContext
> context
,
378 const storage::FileSystemURL
& url
,
379 const ReadDirectoryCallback
& callback
,
382 if (!callback
.is_null()) {
383 content::BrowserThread::PostTask(
384 content::BrowserThread::IO
,
386 base::Bind(callback
, base::File::FILE_ERROR_IO
, EntryList(), false));
390 NativeMediaFileUtil::ReadDirectoryOnTaskRunnerThread(context
.Pass(), url
,
394 void ITunesFileUtil::CreateSnapshotFileWithFreshDataProvider(
395 scoped_ptr
<storage::FileSystemOperationContext
> context
,
396 const storage::FileSystemURL
& url
,
397 const CreateSnapshotFileCallback
& callback
,
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
,
407 base::Bind(callback
, base::File::FILE_ERROR_IO
, file_info
,
408 platform_path
, file_ref
));
412 NativeMediaFileUtil::CreateSnapshotFileOnTaskRunnerThread(context
.Pass(), url
,
416 ITunesDataProvider
* ITunesFileUtil::GetDataProvider() {
417 if (!imported_registry_
)
418 imported_registry_
= ImportedMediaGalleryRegistry::GetInstance();
419 return imported_registry_
->ITunesDataProvider();
422 } // namespace itunes