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/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_operation_context.h"
19 #include "webkit/browser/fileapi/file_system_url.h"
20 #include "webkit/browser/fileapi/native_file_util.h"
21 #include "webkit/common/blob/shareable_file_reference.h"
22 #include "webkit/common/fileapi/file_system_util.h"
24 using fileapi::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
;
39 const char kITunesLibraryXML
[] = "iTunes Music Library.xml";
40 const char kITunesMediaDir
[] = "iTunes Media";
41 const char kITunesMusicDir
[] = "Music";
42 const char kITunesAutoAddDir
[] = "Automatically Add to iTunes";
44 ITunesFileUtil::ITunesFileUtil(MediaPathFilter
* media_path_filter
)
45 : NativeMediaFileUtil(media_path_filter
),
47 imported_registry_(NULL
) {
50 ITunesFileUtil::~ITunesFileUtil() {
53 void ITunesFileUtil::GetFileInfoOnTaskRunnerThread(
54 scoped_ptr
<fileapi::FileSystemOperationContext
> context
,
55 const fileapi::FileSystemURL
& url
,
56 const GetFileInfoCallback
& callback
) {
57 GetDataProvider()->RefreshData(
58 base::Bind(&ITunesFileUtil::GetFileInfoWithFreshDataProvider
,
59 weak_factory_
.GetWeakPtr(), base::Passed(&context
), url
,
63 void ITunesFileUtil::ReadDirectoryOnTaskRunnerThread(
64 scoped_ptr
<fileapi::FileSystemOperationContext
> context
,
65 const fileapi::FileSystemURL
& url
,
66 const ReadDirectoryCallback
& callback
) {
67 GetDataProvider()->RefreshData(
68 base::Bind(&ITunesFileUtil::ReadDirectoryWithFreshDataProvider
,
69 weak_factory_
.GetWeakPtr(), base::Passed(&context
), url
,
73 void ITunesFileUtil::CreateSnapshotFileOnTaskRunnerThread(
74 scoped_ptr
<fileapi::FileSystemOperationContext
> context
,
75 const fileapi::FileSystemURL
& url
,
76 const CreateSnapshotFileCallback
& callback
) {
77 GetDataProvider()->RefreshData(
78 base::Bind(&ITunesFileUtil::CreateSnapshotFileWithFreshDataProvider
,
79 weak_factory_
.GetWeakPtr(), base::Passed(&context
), url
,
83 // Contents of the iTunes media gallery:
85 // /iTunes Music Library.xml - library xml file
86 // /iTunes Media/Automatically Add to iTunes - auto-import directory
87 // /iTunes Media/Music/<Artist>/<Album>/<Track> - tracks
89 base::File::Error
ITunesFileUtil::GetFileInfoSync(
90 fileapi::FileSystemOperationContext
* context
,
91 const fileapi::FileSystemURL
& url
,
92 base::File::Info
* file_info
,
93 base::FilePath
* platform_path
) {
94 std::vector
<std::string
> components
;
95 fileapi::VirtualPath::GetComponentsUTF8Unsafe(url
.path(), &components
);
97 if (components
.size() == 0)
98 return MakeDirectoryFileInfo(file_info
);
100 if (components
.size() == 1 && components
[0] == kITunesLibraryXML
) {
101 // We can't just call NativeMediaFileUtil::GetFileInfoSync() here because it
102 // uses the MediaPathFilter. At this point, |library_path_| is known good
103 // because GetFileInfoWithFreshDataProvider() gates access to this method.
104 base::FilePath file_path
= GetDataProvider()->library_path();
106 *platform_path
= file_path
;
107 return fileapi::NativeFileUtil::GetFileInfo(file_path
, file_info
);
110 if (components
[0] != kITunesMediaDir
)
111 return base::File::FILE_ERROR_NOT_FOUND
;
113 if (components
[1] == kITunesAutoAddDir
) {
114 if (GetDataProvider()->auto_add_path().empty())
115 return base::File::FILE_ERROR_NOT_FOUND
;
116 return NativeMediaFileUtil::GetFileInfoSync(context
, url
, file_info
,
120 if (components
[1] == kITunesMusicDir
) {
121 switch (components
.size()) {
123 return MakeDirectoryFileInfo(file_info
);
126 if (GetDataProvider()->KnownArtist(components
[2]))
127 return MakeDirectoryFileInfo(file_info
);
131 if (GetDataProvider()->KnownAlbum(components
[2], components
[3]))
132 return MakeDirectoryFileInfo(file_info
);
136 base::FilePath location
=
137 GetDataProvider()->GetTrackLocation(components
[2], components
[3],
139 if (!location
.empty()) {
140 return NativeMediaFileUtil::GetFileInfoSync(context
, url
, file_info
,
148 return base::File::FILE_ERROR_NOT_FOUND
;
151 base::File::Error
ITunesFileUtil::ReadDirectorySync(
152 fileapi::FileSystemOperationContext
* context
,
153 const fileapi::FileSystemURL
& url
,
154 EntryList
* file_list
) {
155 DCHECK(file_list
->empty());
156 std::vector
<std::string
> components
;
157 fileapi::VirtualPath::GetComponentsUTF8Unsafe(url
.path(), &components
);
159 if (components
.size() == 0) {
160 base::File::Info xml_info
;
161 if (!base::GetFileInfo(GetDataProvider()->library_path(), &xml_info
))
162 return base::File::FILE_ERROR_IO
;
163 file_list
->push_back(DirectoryEntry(kITunesLibraryXML
,
164 DirectoryEntry::FILE,
165 xml_info
.size
, xml_info
.last_modified
));
166 file_list
->push_back(DirectoryEntry(kITunesMediaDir
,
167 DirectoryEntry::DIRECTORY
,
169 return base::File::FILE_OK
;
172 if (components
.size() == 1 && components
[0] == kITunesLibraryXML
)
173 return base::File::FILE_ERROR_NOT_A_DIRECTORY
;
175 if (components
[0] != kITunesMediaDir
|| components
.size() > 5)
176 return base::File::FILE_ERROR_NOT_FOUND
;
178 if (components
.size() == 1) {
179 if (!GetDataProvider()->auto_add_path().empty()) {
180 file_list
->push_back(DirectoryEntry(kITunesAutoAddDir
,
181 DirectoryEntry::DIRECTORY
,
184 file_list
->push_back(DirectoryEntry(kITunesMusicDir
,
185 DirectoryEntry::DIRECTORY
,
187 return base::File::FILE_OK
;
190 if (components
[1] == kITunesAutoAddDir
&&
191 !GetDataProvider()->auto_add_path().empty()) {
192 return NativeMediaFileUtil::ReadDirectorySync(context
, url
, file_list
);
195 if (components
[1] != kITunesMusicDir
)
196 return base::File::FILE_ERROR_NOT_FOUND
;
198 if (components
.size() == 2) {
199 std::set
<ITunesDataProvider::ArtistName
> artists
=
200 GetDataProvider()->GetArtistNames();
201 std::set
<ITunesDataProvider::ArtistName
>::const_iterator it
;
202 for (it
= artists
.begin(); it
!= artists
.end(); ++it
)
203 file_list
->push_back(DirectoryEntry(*it
, DirectoryEntry::DIRECTORY
,
205 return base::File::FILE_OK
;
208 if (components
.size() == 3) {
209 std::set
<ITunesDataProvider::AlbumName
> albums
=
210 GetDataProvider()->GetAlbumNames(components
[2]);
211 if (albums
.size() == 0)
212 return base::File::FILE_ERROR_NOT_FOUND
;
213 std::set
<ITunesDataProvider::AlbumName
>::const_iterator it
;
214 for (it
= albums
.begin(); it
!= albums
.end(); ++it
)
215 file_list
->push_back(DirectoryEntry(*it
, DirectoryEntry::DIRECTORY
,
217 return base::File::FILE_OK
;
220 if (components
.size() == 4) {
221 ITunesDataProvider::Album album
=
222 GetDataProvider()->GetAlbum(components
[2], components
[3]);
223 if (album
.size() == 0)
224 return base::File::FILE_ERROR_NOT_FOUND
;
225 ITunesDataProvider::Album::const_iterator it
;
226 for (it
= album
.begin(); it
!= album
.end(); ++it
) {
227 base::File::Info file_info
;
228 if (media_path_filter()->Match(it
->second
) &&
229 base::GetFileInfo(it
->second
, &file_info
)) {
230 file_list
->push_back(DirectoryEntry(it
->first
, DirectoryEntry::FILE,
232 file_info
.last_modified
));
235 return base::File::FILE_OK
;
238 // At this point, the only choice is one of two errors, but figuring out
239 // which one is required.
240 DCHECK_EQ(4UL, components
.size());
241 base::FilePath location
;
242 location
= GetDataProvider()->GetTrackLocation(components
[1], components
[2],
244 if (!location
.empty())
245 return base::File::FILE_ERROR_NOT_A_DIRECTORY
;
246 return base::File::FILE_ERROR_NOT_FOUND
;
249 base::File::Error
ITunesFileUtil::DeleteDirectorySync(
250 fileapi::FileSystemOperationContext
* context
,
251 const fileapi::FileSystemURL
& url
) {
252 return base::File::FILE_ERROR_SECURITY
;
255 base::File::Error
ITunesFileUtil::DeleteFileSync(
256 fileapi::FileSystemOperationContext
* context
,
257 const fileapi::FileSystemURL
& url
) {
258 return base::File::FILE_ERROR_SECURITY
;
261 base::File::Error
ITunesFileUtil::CreateSnapshotFileSync(
262 fileapi::FileSystemOperationContext
* context
,
263 const fileapi::FileSystemURL
& url
,
264 base::File::Info
* file_info
,
265 base::FilePath
* platform_path
,
266 scoped_refptr
<webkit_blob::ShareableFileReference
>* file_ref
) {
267 DCHECK(!url
.path().IsAbsolute());
268 if (url
.path() != base::FilePath().AppendASCII(kITunesLibraryXML
)) {
269 return NativeMediaFileUtil::CreateSnapshotFileSync(context
, url
, file_info
,
270 platform_path
, file_ref
);
273 // The following code is different than
274 // NativeMediaFileUtil::CreateSnapshotFileSync in that it knows that the
275 // library xml file is not a directory and it doesn't run mime sniffing on the
276 // file. The only way to get here is by way of
277 // CreateSnapshotFileWithFreshDataProvider() so the file has already been
278 // parsed and deemed valid.
279 *file_ref
= scoped_refptr
<webkit_blob::ShareableFileReference
>();
280 return GetFileInfoSync(context
, url
, file_info
, platform_path
);
283 base::File::Error
ITunesFileUtil::GetLocalFilePath(
284 fileapi::FileSystemOperationContext
* context
,
285 const fileapi::FileSystemURL
& url
,
286 base::FilePath
* local_file_path
) {
287 std::vector
<std::string
> components
;
288 fileapi::VirtualPath::GetComponentsUTF8Unsafe(url
.path(), &components
);
290 if (components
.size() == 1 && components
[0] == kITunesLibraryXML
) {
291 *local_file_path
= GetDataProvider()->library_path();
292 return base::File::FILE_OK
;
295 if (components
.size() >= 2 && components
[0] == kITunesMediaDir
&&
296 components
[1] == kITunesAutoAddDir
) {
297 *local_file_path
= GetDataProvider()->auto_add_path();
298 if (local_file_path
->empty())
299 return base::File::FILE_ERROR_NOT_FOUND
;
301 for (size_t i
= 2; i
< components
.size(); ++i
) {
302 *local_file_path
= local_file_path
->Append(
303 base::FilePath::FromUTF8Unsafe(components
[i
]));
305 return base::File::FILE_OK
;
308 // Should only get here for files, i.e. the xml file and tracks.
309 if (components
[0] != kITunesMediaDir
|| components
[1] != kITunesMusicDir
||
310 components
.size() != 5) {
311 return base::File::FILE_ERROR_NOT_FOUND
;
314 *local_file_path
= GetDataProvider()->GetTrackLocation(components
[2],
317 if (!local_file_path
->empty())
318 return base::File::FILE_OK
;
320 return base::File::FILE_ERROR_NOT_FOUND
;
323 void ITunesFileUtil::GetFileInfoWithFreshDataProvider(
324 scoped_ptr
<fileapi::FileSystemOperationContext
> context
,
325 const fileapi::FileSystemURL
& url
,
326 const GetFileInfoCallback
& callback
,
329 if (!callback
.is_null()) {
330 content::BrowserThread::PostTask(
331 content::BrowserThread::IO
,
333 base::Bind(callback
, base::File::FILE_ERROR_IO
,
334 base::File::Info()));
338 NativeMediaFileUtil::GetFileInfoOnTaskRunnerThread(context
.Pass(), url
,
342 void ITunesFileUtil::ReadDirectoryWithFreshDataProvider(
343 scoped_ptr
<fileapi::FileSystemOperationContext
> context
,
344 const fileapi::FileSystemURL
& url
,
345 const ReadDirectoryCallback
& callback
,
348 if (!callback
.is_null()) {
349 content::BrowserThread::PostTask(
350 content::BrowserThread::IO
,
352 base::Bind(callback
, base::File::FILE_ERROR_IO
, EntryList(), false));
356 NativeMediaFileUtil::ReadDirectoryOnTaskRunnerThread(context
.Pass(), url
,
360 void ITunesFileUtil::CreateSnapshotFileWithFreshDataProvider(
361 scoped_ptr
<fileapi::FileSystemOperationContext
> context
,
362 const fileapi::FileSystemURL
& url
,
363 const CreateSnapshotFileCallback
& callback
,
366 if (!callback
.is_null()) {
367 base::File::Info file_info
;
368 base::FilePath platform_path
;
369 scoped_refptr
<webkit_blob::ShareableFileReference
> file_ref
;
370 content::BrowserThread::PostTask(
371 content::BrowserThread::IO
,
373 base::Bind(callback
, base::File::FILE_ERROR_IO
, file_info
,
374 platform_path
, file_ref
));
378 NativeMediaFileUtil::CreateSnapshotFileOnTaskRunnerThread(context
.Pass(), url
,
382 ITunesDataProvider
* ITunesFileUtil::GetDataProvider() {
383 if (!imported_registry_
)
384 imported_registry_
= ImportedMediaGalleryRegistry::GetInstance();
385 return imported_registry_
->ITunesDataProvider();
388 } // namespace itunes