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 ITunesDataProvider* data_provider = GetDataProvider();
58 // |data_provider| may be NULL if the file system was revoked before this
59 // operation had a chance to run.
61 GetFileInfoWithFreshDataProvider(context.Pass(), url, callback, false);
63 data_provider->RefreshData(
64 base::Bind(&ITunesFileUtil::GetFileInfoWithFreshDataProvider,
65 weak_factory_.GetWeakPtr(), base::Passed(&context), url,
70 void ITunesFileUtil::ReadDirectoryOnTaskRunnerThread(
71 scoped_ptr<fileapi::FileSystemOperationContext> context,
72 const fileapi::FileSystemURL& url,
73 const ReadDirectoryCallback& callback) {
74 ITunesDataProvider* data_provider = GetDataProvider();
75 // |data_provider| may be NULL if the file system was revoked before this
76 // operation had a chance to run.
78 ReadDirectoryWithFreshDataProvider(context.Pass(), url, callback, false);
80 data_provider->RefreshData(
81 base::Bind(&ITunesFileUtil::ReadDirectoryWithFreshDataProvider,
82 weak_factory_.GetWeakPtr(), base::Passed(&context), url,
87 void ITunesFileUtil::CreateSnapshotFileOnTaskRunnerThread(
88 scoped_ptr<fileapi::FileSystemOperationContext> context,
89 const fileapi::FileSystemURL& url,
90 const CreateSnapshotFileCallback& callback) {
91 ITunesDataProvider* data_provider = GetDataProvider();
92 // |data_provider| may be NULL if the file system was revoked before this
93 // operation had a chance to run.
95 CreateSnapshotFileWithFreshDataProvider(context.Pass(), url, callback,
98 data_provider->RefreshData(
99 base::Bind(&ITunesFileUtil::CreateSnapshotFileWithFreshDataProvider,
100 weak_factory_.GetWeakPtr(), base::Passed(&context), url,
105 // Contents of the iTunes media gallery:
106 // / - root directory
107 // /iTunes Music Library.xml - library xml file
108 // /iTunes Media/Automatically Add to iTunes - auto-import directory
109 // /iTunes Media/Music/<Artist>/<Album>/<Track> - tracks
111 base::File::Error ITunesFileUtil::GetFileInfoSync(
112 fileapi::FileSystemOperationContext* context,
113 const fileapi::FileSystemURL& url,
114 base::File::Info* file_info,
115 base::FilePath* platform_path) {
116 std::vector<std::string> components;
117 fileapi::VirtualPath::GetComponentsUTF8Unsafe(url.path(), &components);
119 if (components.size() == 0)
120 return MakeDirectoryFileInfo(file_info);
122 if (components.size() == 1 && components[0] == kITunesLibraryXML) {
123 // We can't just call NativeMediaFileUtil::GetFileInfoSync() here because it
124 // uses the MediaPathFilter. At this point, |library_path_| is known good
125 // because GetFileInfoWithFreshDataProvider() gates access to this method.
126 base::FilePath file_path = GetDataProvider()->library_path();
128 *platform_path = file_path;
129 return fileapi::NativeFileUtil::GetFileInfo(file_path, file_info);
132 if (components[0] != kITunesMediaDir)
133 return base::File::FILE_ERROR_NOT_FOUND;
135 if (components[1] == kITunesAutoAddDir) {
136 if (GetDataProvider()->auto_add_path().empty())
137 return base::File::FILE_ERROR_NOT_FOUND;
138 return NativeMediaFileUtil::GetFileInfoSync(context, url, file_info,
142 if (components[1] == kITunesMusicDir) {
143 switch (components.size()) {
145 return MakeDirectoryFileInfo(file_info);
148 if (GetDataProvider()->KnownArtist(components[2]))
149 return MakeDirectoryFileInfo(file_info);
153 if (GetDataProvider()->KnownAlbum(components[2], components[3]))
154 return MakeDirectoryFileInfo(file_info);
158 base::FilePath location =
159 GetDataProvider()->GetTrackLocation(components[2], components[3],
161 if (!location.empty()) {
162 return NativeMediaFileUtil::GetFileInfoSync(context, url, file_info,
170 return base::File::FILE_ERROR_NOT_FOUND;
173 base::File::Error ITunesFileUtil::ReadDirectorySync(
174 fileapi::FileSystemOperationContext* context,
175 const fileapi::FileSystemURL& url,
176 EntryList* file_list) {
177 DCHECK(file_list->empty());
178 std::vector<std::string> components;
179 fileapi::VirtualPath::GetComponentsUTF8Unsafe(url.path(), &components);
181 if (components.size() == 0) {
182 base::File::Info xml_info;
183 if (!base::GetFileInfo(GetDataProvider()->library_path(), &xml_info))
184 return base::File::FILE_ERROR_IO;
185 file_list->push_back(DirectoryEntry(kITunesLibraryXML,
186 DirectoryEntry::FILE,
187 xml_info.size, xml_info.last_modified));
188 file_list->push_back(DirectoryEntry(kITunesMediaDir,
189 DirectoryEntry::DIRECTORY,
191 return base::File::FILE_OK;
194 if (components.size() == 1 && components[0] == kITunesLibraryXML)
195 return base::File::FILE_ERROR_NOT_A_DIRECTORY;
197 if (components[0] != kITunesMediaDir || components.size() > 5)
198 return base::File::FILE_ERROR_NOT_FOUND;
200 if (components.size() == 1) {
201 if (!GetDataProvider()->auto_add_path().empty()) {
202 file_list->push_back(DirectoryEntry(kITunesAutoAddDir,
203 DirectoryEntry::DIRECTORY,
206 file_list->push_back(DirectoryEntry(kITunesMusicDir,
207 DirectoryEntry::DIRECTORY,
209 return base::File::FILE_OK;
212 if (components[1] == kITunesAutoAddDir &&
213 !GetDataProvider()->auto_add_path().empty()) {
214 return NativeMediaFileUtil::ReadDirectorySync(context, url, file_list);
217 if (components[1] != kITunesMusicDir)
218 return base::File::FILE_ERROR_NOT_FOUND;
220 if (components.size() == 2) {
221 std::set<ITunesDataProvider::ArtistName> artists =
222 GetDataProvider()->GetArtistNames();
223 std::set<ITunesDataProvider::ArtistName>::const_iterator it;
224 for (it = artists.begin(); it != artists.end(); ++it)
225 file_list->push_back(DirectoryEntry(*it, DirectoryEntry::DIRECTORY,
227 return base::File::FILE_OK;
230 if (components.size() == 3) {
231 std::set<ITunesDataProvider::AlbumName> albums =
232 GetDataProvider()->GetAlbumNames(components[2]);
233 if (albums.size() == 0)
234 return base::File::FILE_ERROR_NOT_FOUND;
235 std::set<ITunesDataProvider::AlbumName>::const_iterator it;
236 for (it = albums.begin(); it != albums.end(); ++it)
237 file_list->push_back(DirectoryEntry(*it, DirectoryEntry::DIRECTORY,
239 return base::File::FILE_OK;
242 if (components.size() == 4) {
243 ITunesDataProvider::Album album =
244 GetDataProvider()->GetAlbum(components[2], components[3]);
245 if (album.size() == 0)
246 return base::File::FILE_ERROR_NOT_FOUND;
247 ITunesDataProvider::Album::const_iterator it;
248 for (it = album.begin(); it != album.end(); ++it) {
249 base::File::Info file_info;
250 if (media_path_filter()->Match(it->second) &&
251 base::GetFileInfo(it->second, &file_info)) {
252 file_list->push_back(DirectoryEntry(it->first, DirectoryEntry::FILE,
254 file_info.last_modified));
257 return base::File::FILE_OK;
260 // At this point, the only choice is one of two errors, but figuring out
261 // which one is required.
262 DCHECK_EQ(4UL, components.size());
263 base::FilePath location;
264 location = GetDataProvider()->GetTrackLocation(components[1], components[2],
266 if (!location.empty())
267 return base::File::FILE_ERROR_NOT_A_DIRECTORY;
268 return base::File::FILE_ERROR_NOT_FOUND;
271 base::File::Error ITunesFileUtil::DeleteDirectorySync(
272 fileapi::FileSystemOperationContext* context,
273 const fileapi::FileSystemURL& url) {
274 return base::File::FILE_ERROR_SECURITY;
277 base::File::Error ITunesFileUtil::DeleteFileSync(
278 fileapi::FileSystemOperationContext* context,
279 const fileapi::FileSystemURL& url) {
280 return base::File::FILE_ERROR_SECURITY;
283 base::File::Error ITunesFileUtil::CreateSnapshotFileSync(
284 fileapi::FileSystemOperationContext* context,
285 const fileapi::FileSystemURL& url,
286 base::File::Info* file_info,
287 base::FilePath* platform_path,
288 scoped_refptr<webkit_blob::ShareableFileReference>* file_ref) {
289 DCHECK(!url.path().IsAbsolute());
290 if (url.path() != base::FilePath().AppendASCII(kITunesLibraryXML)) {
291 return NativeMediaFileUtil::CreateSnapshotFileSync(context, url, file_info,
292 platform_path, file_ref);
295 // The following code is different than
296 // NativeMediaFileUtil::CreateSnapshotFileSync in that it knows that the
297 // library xml file is not a directory and it doesn't run mime sniffing on the
298 // file. The only way to get here is by way of
299 // CreateSnapshotFileWithFreshDataProvider() so the file has already been
300 // parsed and deemed valid.
301 *file_ref = scoped_refptr<webkit_blob::ShareableFileReference>();
302 return GetFileInfoSync(context, url, file_info, platform_path);
305 base::File::Error ITunesFileUtil::GetLocalFilePath(
306 fileapi::FileSystemOperationContext* context,
307 const fileapi::FileSystemURL& url,
308 base::FilePath* local_file_path) {
309 std::vector<std::string> components;
310 fileapi::VirtualPath::GetComponentsUTF8Unsafe(url.path(), &components);
312 if (components.size() == 1 && components[0] == kITunesLibraryXML) {
313 *local_file_path = GetDataProvider()->library_path();
314 return base::File::FILE_OK;
317 if (components.size() >= 2 && components[0] == kITunesMediaDir &&
318 components[1] == kITunesAutoAddDir) {
319 *local_file_path = GetDataProvider()->auto_add_path();
320 if (local_file_path->empty())
321 return base::File::FILE_ERROR_NOT_FOUND;
323 for (size_t i = 2; i < components.size(); ++i) {
324 *local_file_path = local_file_path->Append(
325 base::FilePath::FromUTF8Unsafe(components[i]));
327 return base::File::FILE_OK;
330 // Should only get here for files, i.e. the xml file and tracks.
331 if (components[0] != kITunesMediaDir || components[1] != kITunesMusicDir||
332 components.size() != 5) {
333 return base::File::FILE_ERROR_NOT_FOUND;
336 *local_file_path = GetDataProvider()->GetTrackLocation(components[2],
339 if (!local_file_path->empty())
340 return base::File::FILE_OK;
342 return base::File::FILE_ERROR_NOT_FOUND;
345 void ITunesFileUtil::GetFileInfoWithFreshDataProvider(
346 scoped_ptr<fileapi::FileSystemOperationContext> context,
347 const fileapi::FileSystemURL& url,
348 const GetFileInfoCallback& callback,
351 if (!callback.is_null()) {
352 content::BrowserThread::PostTask(
353 content::BrowserThread::IO,
355 base::Bind(callback, base::File::FILE_ERROR_IO,
356 base::File::Info()));
360 NativeMediaFileUtil::GetFileInfoOnTaskRunnerThread(context.Pass(), url,
364 void ITunesFileUtil::ReadDirectoryWithFreshDataProvider(
365 scoped_ptr<fileapi::FileSystemOperationContext> context,
366 const fileapi::FileSystemURL& url,
367 const ReadDirectoryCallback& callback,
370 if (!callback.is_null()) {
371 content::BrowserThread::PostTask(
372 content::BrowserThread::IO,
374 base::Bind(callback, base::File::FILE_ERROR_IO, EntryList(), false));
378 NativeMediaFileUtil::ReadDirectoryOnTaskRunnerThread(context.Pass(), url,
382 void ITunesFileUtil::CreateSnapshotFileWithFreshDataProvider(
383 scoped_ptr<fileapi::FileSystemOperationContext> context,
384 const fileapi::FileSystemURL& url,
385 const CreateSnapshotFileCallback& callback,
388 if (!callback.is_null()) {
389 base::File::Info file_info;
390 base::FilePath platform_path;
391 scoped_refptr<webkit_blob::ShareableFileReference> file_ref;
392 content::BrowserThread::PostTask(
393 content::BrowserThread::IO,
395 base::Bind(callback, base::File::FILE_ERROR_IO, file_info,
396 platform_path, file_ref));
400 NativeMediaFileUtil::CreateSnapshotFileOnTaskRunnerThread(context.Pass(), url,
404 ITunesDataProvider* ITunesFileUtil::GetDataProvider() {
405 if (!imported_registry_)
406 imported_registry_ = ImportedMediaGalleryRegistry::GetInstance();
407 return imported_registry_->ITunesDataProvider();
410 } // namespace itunes