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::PlatformFileError
MakeDirectoryFileInfo(
31 base::PlatformFileInfo
* file_info
) {
32 base::PlatformFileInfo result
;
33 result
.is_directory
= true;
35 return base::PLATFORM_FILE_OK
;
40 const char kITunesLibraryXML
[] = "iTunes Music Library.xml";
41 const char kITunesMediaDir
[] = "iTunes Media";
42 const char kITunesMusicDir
[] = "Music";
43 const char kITunesAutoAddDir
[] = "Automatically Add to iTunes";
45 ITunesFileUtil::ITunesFileUtil(MediaPathFilter
* media_path_filter
)
46 : NativeMediaFileUtil(media_path_filter
),
48 imported_registry_(NULL
) {
51 ITunesFileUtil::~ITunesFileUtil() {
54 void ITunesFileUtil::GetFileInfoOnTaskRunnerThread(
55 scoped_ptr
<fileapi::FileSystemOperationContext
> context
,
56 const fileapi::FileSystemURL
& url
,
57 const GetFileInfoCallback
& callback
) {
58 GetDataProvider()->RefreshData(
59 base::Bind(&ITunesFileUtil::GetFileInfoWithFreshDataProvider
,
60 weak_factory_
.GetWeakPtr(), base::Passed(&context
), url
,
64 void ITunesFileUtil::ReadDirectoryOnTaskRunnerThread(
65 scoped_ptr
<fileapi::FileSystemOperationContext
> context
,
66 const fileapi::FileSystemURL
& url
,
67 const ReadDirectoryCallback
& callback
) {
68 GetDataProvider()->RefreshData(
69 base::Bind(&ITunesFileUtil::ReadDirectoryWithFreshDataProvider
,
70 weak_factory_
.GetWeakPtr(), base::Passed(&context
), url
,
74 void ITunesFileUtil::CreateSnapshotFileOnTaskRunnerThread(
75 scoped_ptr
<fileapi::FileSystemOperationContext
> context
,
76 const fileapi::FileSystemURL
& url
,
77 const CreateSnapshotFileCallback
& callback
) {
78 GetDataProvider()->RefreshData(
79 base::Bind(&ITunesFileUtil::CreateSnapshotFileWithFreshDataProvider
,
80 weak_factory_
.GetWeakPtr(), base::Passed(&context
), url
,
84 // Contents of the iTunes media gallery:
86 // /iTunes Music Library.xml - library xml file
87 // /iTunes Media/Automatically Add to iTunes - auto-import directory
88 // /iTunes Media/Music/<Artist>/<Album>/<Track> - tracks
90 base::PlatformFileError
ITunesFileUtil::GetFileInfoSync(
91 fileapi::FileSystemOperationContext
* context
,
92 const fileapi::FileSystemURL
& url
,
93 base::PlatformFileInfo
* file_info
,
94 base::FilePath
* platform_path
) {
95 std::vector
<std::string
> components
;
96 fileapi::VirtualPath::GetComponentsUTF8Unsafe(url
.path(), &components
);
98 if (components
.size() == 0)
99 return MakeDirectoryFileInfo(file_info
);
101 if (components
.size() == 1 && components
[0] == kITunesLibraryXML
) {
102 // We can't just call NativeMediaFileUtil::GetFileInfoSync() here because it
103 // uses the MediaPathFilter. At this point, |library_path_| is known good
104 // because GetFileInfoWithFreshDataProvider() gates access to this method.
105 base::FilePath file_path
= GetDataProvider()->library_path();
107 *platform_path
= file_path
;
108 return fileapi::NativeFileUtil::GetFileInfo(file_path
, file_info
);
111 if (components
[0] != kITunesMediaDir
)
112 return base::PLATFORM_FILE_ERROR_NOT_FOUND
;
114 if (components
[1] == kITunesAutoAddDir
) {
115 if (GetDataProvider()->auto_add_path().empty())
116 return base::PLATFORM_FILE_ERROR_NOT_FOUND
;
117 return NativeMediaFileUtil::GetFileInfoSync(context
, url
, file_info
,
121 if (components
[1] == kITunesMusicDir
) {
122 switch (components
.size()) {
124 return MakeDirectoryFileInfo(file_info
);
127 if (GetDataProvider()->KnownArtist(components
[2]))
128 return MakeDirectoryFileInfo(file_info
);
132 if (GetDataProvider()->KnownAlbum(components
[2], components
[3]))
133 return MakeDirectoryFileInfo(file_info
);
137 base::FilePath location
=
138 GetDataProvider()->GetTrackLocation(components
[2], components
[3],
140 if (!location
.empty()) {
141 return NativeMediaFileUtil::GetFileInfoSync(context
, url
, file_info
,
149 return base::PLATFORM_FILE_ERROR_NOT_FOUND
;
152 base::PlatformFileError
ITunesFileUtil::ReadDirectorySync(
153 fileapi::FileSystemOperationContext
* context
,
154 const fileapi::FileSystemURL
& url
,
155 EntryList
* file_list
) {
156 DCHECK(file_list
->empty());
157 std::vector
<std::string
> components
;
158 fileapi::VirtualPath::GetComponentsUTF8Unsafe(url
.path(), &components
);
160 if (components
.size() == 0) {
161 base::File::Info xml_info
;
162 if (!base::GetFileInfo(GetDataProvider()->library_path(), &xml_info
))
163 return base::PLATFORM_FILE_ERROR_IO
;
164 file_list
->push_back(DirectoryEntry(kITunesLibraryXML
,
165 DirectoryEntry::FILE,
166 xml_info
.size
, xml_info
.last_modified
));
167 file_list
->push_back(DirectoryEntry(kITunesMediaDir
,
168 DirectoryEntry::DIRECTORY
,
170 return base::PLATFORM_FILE_OK
;
173 if (components
.size() == 1 && components
[0] == kITunesLibraryXML
)
174 return base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY
;
176 if (components
[0] != kITunesMediaDir
|| components
.size() > 5)
177 return base::PLATFORM_FILE_ERROR_NOT_FOUND
;
179 if (components
.size() == 1) {
180 if (!GetDataProvider()->auto_add_path().empty()) {
181 file_list
->push_back(DirectoryEntry(kITunesAutoAddDir
,
182 DirectoryEntry::DIRECTORY
,
185 file_list
->push_back(DirectoryEntry(kITunesMusicDir
,
186 DirectoryEntry::DIRECTORY
,
188 return base::PLATFORM_FILE_OK
;
191 if (components
[1] == kITunesAutoAddDir
&&
192 !GetDataProvider()->auto_add_path().empty()) {
193 return NativeMediaFileUtil::ReadDirectorySync(context
, url
, file_list
);
196 if (components
[1] != kITunesMusicDir
)
197 return base::PLATFORM_FILE_ERROR_NOT_FOUND
;
199 if (components
.size() == 2) {
200 std::set
<ITunesDataProvider::ArtistName
> artists
=
201 GetDataProvider()->GetArtistNames();
202 std::set
<ITunesDataProvider::ArtistName
>::const_iterator it
;
203 for (it
= artists
.begin(); it
!= artists
.end(); ++it
)
204 file_list
->push_back(DirectoryEntry(*it
, DirectoryEntry::DIRECTORY
,
206 return base::PLATFORM_FILE_OK
;
209 if (components
.size() == 3) {
210 std::set
<ITunesDataProvider::AlbumName
> albums
=
211 GetDataProvider()->GetAlbumNames(components
[2]);
212 if (albums
.size() == 0)
213 return base::PLATFORM_FILE_ERROR_NOT_FOUND
;
214 std::set
<ITunesDataProvider::AlbumName
>::const_iterator it
;
215 for (it
= albums
.begin(); it
!= albums
.end(); ++it
)
216 file_list
->push_back(DirectoryEntry(*it
, DirectoryEntry::DIRECTORY
,
218 return base::PLATFORM_FILE_OK
;
221 if (components
.size() == 4) {
222 ITunesDataProvider::Album album
=
223 GetDataProvider()->GetAlbum(components
[2], components
[3]);
224 if (album
.size() == 0)
225 return base::PLATFORM_FILE_ERROR_NOT_FOUND
;
226 ITunesDataProvider::Album::const_iterator it
;
227 for (it
= album
.begin(); it
!= album
.end(); ++it
) {
228 base::File::Info file_info
;
229 if (media_path_filter()->Match(it
->second
) &&
230 base::GetFileInfo(it
->second
, &file_info
)) {
231 file_list
->push_back(DirectoryEntry(it
->first
, DirectoryEntry::FILE,
233 file_info
.last_modified
));
236 return base::PLATFORM_FILE_OK
;
239 // At this point, the only choice is one of two errors, but figuring out
240 // which one is required.
241 DCHECK_EQ(4UL, components
.size());
242 base::FilePath location
;
243 location
= GetDataProvider()->GetTrackLocation(components
[1], components
[2],
245 if (!location
.empty())
246 return base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY
;
247 return base::PLATFORM_FILE_ERROR_NOT_FOUND
;
250 base::PlatformFileError
ITunesFileUtil::DeleteDirectorySync(
251 fileapi::FileSystemOperationContext
* context
,
252 const fileapi::FileSystemURL
& url
) {
253 return base::PLATFORM_FILE_ERROR_SECURITY
;
256 base::PlatformFileError
ITunesFileUtil::DeleteFileSync(
257 fileapi::FileSystemOperationContext
* context
,
258 const fileapi::FileSystemURL
& url
) {
259 return base::PLATFORM_FILE_ERROR_SECURITY
;
262 base::PlatformFileError
ITunesFileUtil::CreateSnapshotFileSync(
263 fileapi::FileSystemOperationContext
* context
,
264 const fileapi::FileSystemURL
& url
,
265 base::PlatformFileInfo
* file_info
,
266 base::FilePath
* platform_path
,
267 scoped_refptr
<webkit_blob::ShareableFileReference
>* file_ref
) {
268 DCHECK(!url
.path().IsAbsolute());
269 if (url
.path() != base::FilePath().AppendASCII(kITunesLibraryXML
)) {
270 return NativeMediaFileUtil::CreateSnapshotFileSync(context
, url
, file_info
,
271 platform_path
, file_ref
);
274 // The following code is different than
275 // NativeMediaFileUtil::CreateSnapshotFileSync in that it knows that the
276 // library xml file is not a directory and it doesn't run mime sniffing on the
277 // file. The only way to get here is by way of
278 // CreateSnapshotFileWithFreshDataProvider() so the file has already been
279 // parsed and deemed valid.
280 *file_ref
= scoped_refptr
<webkit_blob::ShareableFileReference
>();
281 return GetFileInfoSync(context
, url
, file_info
, platform_path
);
284 base::PlatformFileError
ITunesFileUtil::GetLocalFilePath(
285 fileapi::FileSystemOperationContext
* context
,
286 const fileapi::FileSystemURL
& url
,
287 base::FilePath
* local_file_path
) {
288 std::vector
<std::string
> components
;
289 fileapi::VirtualPath::GetComponentsUTF8Unsafe(url
.path(), &components
);
291 if (components
.size() == 1 && components
[0] == kITunesLibraryXML
) {
292 *local_file_path
= GetDataProvider()->library_path();
293 return base::PLATFORM_FILE_OK
;
296 if (components
.size() >= 2 && components
[0] == kITunesMediaDir
&&
297 components
[1] == kITunesAutoAddDir
) {
298 *local_file_path
= GetDataProvider()->auto_add_path();
299 if (local_file_path
->empty())
300 return base::PLATFORM_FILE_ERROR_NOT_FOUND
;
302 for (size_t i
= 2; i
< components
.size(); ++i
) {
303 *local_file_path
= local_file_path
->Append(
304 base::FilePath::FromUTF8Unsafe(components
[i
]));
306 return base::PLATFORM_FILE_OK
;
309 // Should only get here for files, i.e. the xml file and tracks.
310 if (components
[0] != kITunesMediaDir
|| components
[1] != kITunesMusicDir
||
311 components
.size() != 5) {
312 return base::PLATFORM_FILE_ERROR_NOT_FOUND
;
315 *local_file_path
= GetDataProvider()->GetTrackLocation(components
[2],
318 if (!local_file_path
->empty())
319 return base::PLATFORM_FILE_OK
;
321 return base::PLATFORM_FILE_ERROR_NOT_FOUND
;
324 void ITunesFileUtil::GetFileInfoWithFreshDataProvider(
325 scoped_ptr
<fileapi::FileSystemOperationContext
> context
,
326 const fileapi::FileSystemURL
& url
,
327 const GetFileInfoCallback
& callback
,
330 if (!callback
.is_null()) {
331 content::BrowserThread::PostTask(
332 content::BrowserThread::IO
,
334 base::Bind(callback
, base::PLATFORM_FILE_ERROR_IO
,
335 base::PlatformFileInfo()));
339 NativeMediaFileUtil::GetFileInfoOnTaskRunnerThread(context
.Pass(), url
,
343 void ITunesFileUtil::ReadDirectoryWithFreshDataProvider(
344 scoped_ptr
<fileapi::FileSystemOperationContext
> context
,
345 const fileapi::FileSystemURL
& url
,
346 const ReadDirectoryCallback
& callback
,
349 if (!callback
.is_null()) {
350 content::BrowserThread::PostTask(
351 content::BrowserThread::IO
,
353 base::Bind(callback
, base::PLATFORM_FILE_ERROR_IO
, EntryList(),
358 NativeMediaFileUtil::ReadDirectoryOnTaskRunnerThread(context
.Pass(), url
,
362 void ITunesFileUtil::CreateSnapshotFileWithFreshDataProvider(
363 scoped_ptr
<fileapi::FileSystemOperationContext
> context
,
364 const fileapi::FileSystemURL
& url
,
365 const CreateSnapshotFileCallback
& callback
,
368 if (!callback
.is_null()) {
369 base::PlatformFileInfo file_info
;
370 base::FilePath platform_path
;
371 scoped_refptr
<webkit_blob::ShareableFileReference
> file_ref
;
372 content::BrowserThread::PostTask(
373 content::BrowserThread::IO
,
375 base::Bind(callback
, base::PLATFORM_FILE_ERROR_IO
, file_info
,
376 platform_path
, file_ref
));
380 NativeMediaFileUtil::CreateSnapshotFileOnTaskRunnerThread(context
.Pass(), url
,
384 ITunesDataProvider
* ITunesFileUtil::GetDataProvider() {
385 if (!imported_registry_
)
386 imported_registry_
= ImportedMediaGalleryRegistry::GetInstance();
387 return imported_registry_
->ITunesDataProvider();
390 } // namespace itunes