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;
37 std::vector<std::string> GetVirtualPathComponents(
38 const fileapi::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 fileapi::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),
62 imported_registry_(NULL) {
65 ITunesFileUtil::~ITunesFileUtil() {
68 void ITunesFileUtil::GetFileInfoOnTaskRunnerThread(
69 scoped_ptr<fileapi::FileSystemOperationContext> context,
70 const fileapi::FileSystemURL& url,
71 const GetFileInfoCallback& callback) {
72 GetDataProvider()->RefreshData(
73 base::Bind(&ITunesFileUtil::GetFileInfoWithFreshDataProvider,
74 weak_factory_.GetWeakPtr(), base::Passed(&context), url,
78 void ITunesFileUtil::ReadDirectoryOnTaskRunnerThread(
79 scoped_ptr<fileapi::FileSystemOperationContext> context,
80 const fileapi::FileSystemURL& url,
81 const ReadDirectoryCallback& callback) {
82 GetDataProvider()->RefreshData(
83 base::Bind(&ITunesFileUtil::ReadDirectoryWithFreshDataProvider,
84 weak_factory_.GetWeakPtr(), base::Passed(&context), url,
88 void ITunesFileUtil::CreateSnapshotFileOnTaskRunnerThread(
89 scoped_ptr<fileapi::FileSystemOperationContext> context,
90 const fileapi::FileSystemURL& url,
91 const CreateSnapshotFileCallback& callback) {
92 GetDataProvider()->RefreshData(
93 base::Bind(&ITunesFileUtil::CreateSnapshotFileWithFreshDataProvider,
94 weak_factory_.GetWeakPtr(), base::Passed(&context), url,
98 // Contents of the iTunes media gallery:
100 // /iTunes Music Library.xml - library xml file
101 // /iTunes Media/Automatically Add to iTunes - auto-import directory
102 // /iTunes Media/Music/<Artist>/<Album>/<Track> - tracks
104 base::File::Error ITunesFileUtil::GetFileInfoSync(
105 fileapi::FileSystemOperationContext* context,
106 const fileapi::FileSystemURL& url,
107 base::File::Info* file_info,
108 base::FilePath* platform_path) {
109 std::vector<std::string> components = GetVirtualPathComponents(url);
111 if (components.size() == 0)
112 return MakeDirectoryFileInfo(file_info);
114 if (components.size() == 1 && components[0] == kITunesLibraryXML) {
115 // We can't just call NativeMediaFileUtil::GetFileInfoSync() here because it
116 // uses the MediaPathFilter. At this point, |library_path_| is known good
117 // because GetFileInfoWithFreshDataProvider() gates access to this method.
118 base::FilePath file_path = GetDataProvider()->library_path();
120 *platform_path = file_path;
121 return fileapi::NativeFileUtil::GetFileInfo(file_path, file_info);
124 if (components[0] != kITunesMediaDir)
125 return base::File::FILE_ERROR_NOT_FOUND;
127 if (components[1] == kITunesAutoAddDir) {
128 if (GetDataProvider()->auto_add_path().empty())
129 return base::File::FILE_ERROR_NOT_FOUND;
130 return NativeMediaFileUtil::GetFileInfoSync(context, url, file_info,
134 if (components[1] == kITunesMusicDir) {
135 switch (components.size()) {
137 return MakeDirectoryFileInfo(file_info);
140 if (GetDataProvider()->KnownArtist(components[2]))
141 return MakeDirectoryFileInfo(file_info);
145 if (GetDataProvider()->KnownAlbum(components[2], components[3]))
146 return MakeDirectoryFileInfo(file_info);
150 base::FilePath location =
151 GetDataProvider()->GetTrackLocation(components[2], components[3],
153 if (!location.empty()) {
154 return NativeMediaFileUtil::GetFileInfoSync(context, url, file_info,
162 return base::File::FILE_ERROR_NOT_FOUND;
165 base::File::Error ITunesFileUtil::ReadDirectorySync(
166 fileapi::FileSystemOperationContext* context,
167 const fileapi::FileSystemURL& url,
168 EntryList* file_list) {
169 DCHECK(file_list->empty());
170 std::vector<std::string> components = GetVirtualPathComponents(url);
172 if (components.size() == 0) {
173 base::File::Info xml_info;
174 if (!base::GetFileInfo(GetDataProvider()->library_path(), &xml_info))
175 return base::File::FILE_ERROR_IO;
176 file_list->push_back(DirectoryEntry(kITunesLibraryXML,
177 DirectoryEntry::FILE,
178 xml_info.size, xml_info.last_modified));
179 file_list->push_back(DirectoryEntry(kITunesMediaDir,
180 DirectoryEntry::DIRECTORY,
182 return base::File::FILE_OK;
185 if (components.size() == 1 && components[0] == kITunesLibraryXML)
186 return base::File::FILE_ERROR_NOT_A_DIRECTORY;
188 if (components[0] != kITunesMediaDir || components.size() > 5)
189 return base::File::FILE_ERROR_NOT_FOUND;
191 if (components.size() == 1) {
192 if (!GetDataProvider()->auto_add_path().empty()) {
193 file_list->push_back(DirectoryEntry(kITunesAutoAddDir,
194 DirectoryEntry::DIRECTORY,
197 file_list->push_back(DirectoryEntry(kITunesMusicDir,
198 DirectoryEntry::DIRECTORY,
200 return base::File::FILE_OK;
203 if (components[1] == kITunesAutoAddDir &&
204 !GetDataProvider()->auto_add_path().empty()) {
205 return NativeMediaFileUtil::ReadDirectorySync(context, url, file_list);
208 if (components[1] != kITunesMusicDir)
209 return base::File::FILE_ERROR_NOT_FOUND;
211 if (components.size() == 2) {
212 std::set<ITunesDataProvider::ArtistName> artists =
213 GetDataProvider()->GetArtistNames();
214 std::set<ITunesDataProvider::ArtistName>::const_iterator it;
215 for (it = artists.begin(); it != artists.end(); ++it)
216 file_list->push_back(DirectoryEntry(*it, DirectoryEntry::DIRECTORY,
218 return base::File::FILE_OK;
221 if (components.size() == 3) {
222 std::set<ITunesDataProvider::AlbumName> albums =
223 GetDataProvider()->GetAlbumNames(components[2]);
224 if (albums.size() == 0)
225 return base::File::FILE_ERROR_NOT_FOUND;
226 std::set<ITunesDataProvider::AlbumName>::const_iterator it;
227 for (it = albums.begin(); it != albums.end(); ++it)
228 file_list->push_back(DirectoryEntry(*it, DirectoryEntry::DIRECTORY,
230 return base::File::FILE_OK;
233 if (components.size() == 4) {
234 ITunesDataProvider::Album album =
235 GetDataProvider()->GetAlbum(components[2], components[3]);
236 if (album.size() == 0)
237 return base::File::FILE_ERROR_NOT_FOUND;
238 ITunesDataProvider::Album::const_iterator it;
239 for (it = album.begin(); it != album.end(); ++it) {
240 base::File::Info file_info;
241 if (media_path_filter()->Match(it->second) &&
242 base::GetFileInfo(it->second, &file_info)) {
243 file_list->push_back(DirectoryEntry(it->first, DirectoryEntry::FILE,
245 file_info.last_modified));
248 return base::File::FILE_OK;
251 // At this point, the only choice is one of two errors, but figuring out
252 // which one is required.
253 DCHECK_EQ(4UL, components.size());
254 base::FilePath location;
255 location = GetDataProvider()->GetTrackLocation(components[1], components[2],
257 if (!location.empty())
258 return base::File::FILE_ERROR_NOT_A_DIRECTORY;
259 return base::File::FILE_ERROR_NOT_FOUND;
262 base::File::Error ITunesFileUtil::DeleteDirectorySync(
263 fileapi::FileSystemOperationContext* context,
264 const fileapi::FileSystemURL& url) {
265 return base::File::FILE_ERROR_SECURITY;
268 base::File::Error ITunesFileUtil::DeleteFileSync(
269 fileapi::FileSystemOperationContext* context,
270 const fileapi::FileSystemURL& url) {
271 return base::File::FILE_ERROR_SECURITY;
274 base::File::Error ITunesFileUtil::CreateSnapshotFileSync(
275 fileapi::FileSystemOperationContext* context,
276 const fileapi::FileSystemURL& url,
277 base::File::Info* file_info,
278 base::FilePath* platform_path,
279 scoped_refptr<webkit_blob::ShareableFileReference>* file_ref) {
280 std::vector<std::string> components = GetVirtualPathComponents(url);
281 if (components.size() != 1 || components[0] != kITunesLibraryXML) {
282 return NativeMediaFileUtil::CreateSnapshotFileSync(context, url, file_info,
283 platform_path, file_ref);
286 // The following code is different than
287 // NativeMediaFileUtil::CreateSnapshotFileSync in that it knows that the
288 // library xml file is not a directory and it doesn't run mime sniffing on the
289 // file. The only way to get here is by way of
290 // CreateSnapshotFileWithFreshDataProvider() so the file has already been
291 // parsed and deemed valid.
292 *file_ref = scoped_refptr<webkit_blob::ShareableFileReference>();
293 return GetFileInfoSync(context, url, file_info, platform_path);
296 base::File::Error ITunesFileUtil::GetLocalFilePath(
297 fileapi::FileSystemOperationContext* context,
298 const fileapi::FileSystemURL& url,
299 base::FilePath* local_file_path) {
300 std::vector<std::string> components = GetVirtualPathComponents(url);
302 if (components.size() == 1 && components[0] == kITunesLibraryXML) {
303 *local_file_path = GetDataProvider()->library_path();
304 return base::File::FILE_OK;
307 if (components.size() >= 2 && components[0] == kITunesMediaDir &&
308 components[1] == kITunesAutoAddDir) {
309 *local_file_path = GetDataProvider()->auto_add_path();
310 if (local_file_path->empty())
311 return base::File::FILE_ERROR_NOT_FOUND;
313 for (size_t i = 2; i < components.size(); ++i) {
314 *local_file_path = local_file_path->Append(
315 base::FilePath::FromUTF8Unsafe(components[i]));
317 return base::File::FILE_OK;
320 // Should only get here for files, i.e. the xml file and tracks.
321 if (components[0] != kITunesMediaDir || components[1] != kITunesMusicDir||
322 components.size() != 5) {
323 return base::File::FILE_ERROR_NOT_FOUND;
326 *local_file_path = GetDataProvider()->GetTrackLocation(components[2],
329 if (!local_file_path->empty())
330 return base::File::FILE_OK;
332 return base::File::FILE_ERROR_NOT_FOUND;
335 void ITunesFileUtil::GetFileInfoWithFreshDataProvider(
336 scoped_ptr<fileapi::FileSystemOperationContext> context,
337 const fileapi::FileSystemURL& url,
338 const GetFileInfoCallback& callback,
341 if (!callback.is_null()) {
342 content::BrowserThread::PostTask(
343 content::BrowserThread::IO,
345 base::Bind(callback, base::File::FILE_ERROR_IO,
346 base::File::Info()));
350 NativeMediaFileUtil::GetFileInfoOnTaskRunnerThread(context.Pass(), url,
354 void ITunesFileUtil::ReadDirectoryWithFreshDataProvider(
355 scoped_ptr<fileapi::FileSystemOperationContext> context,
356 const fileapi::FileSystemURL& url,
357 const ReadDirectoryCallback& callback,
360 if (!callback.is_null()) {
361 content::BrowserThread::PostTask(
362 content::BrowserThread::IO,
364 base::Bind(callback, base::File::FILE_ERROR_IO, EntryList(), false));
368 NativeMediaFileUtil::ReadDirectoryOnTaskRunnerThread(context.Pass(), url,
372 void ITunesFileUtil::CreateSnapshotFileWithFreshDataProvider(
373 scoped_ptr<fileapi::FileSystemOperationContext> context,
374 const fileapi::FileSystemURL& url,
375 const CreateSnapshotFileCallback& callback,
378 if (!callback.is_null()) {
379 base::File::Info file_info;
380 base::FilePath platform_path;
381 scoped_refptr<webkit_blob::ShareableFileReference> file_ref;
382 content::BrowserThread::PostTask(
383 content::BrowserThread::IO,
385 base::Bind(callback, base::File::FILE_ERROR_IO, file_info,
386 platform_path, file_ref));
390 NativeMediaFileUtil::CreateSnapshotFileOnTaskRunnerThread(context.Pass(), url,
394 ITunesDataProvider* ITunesFileUtil::GetDataProvider() {
395 if (!imported_registry_)
396 imported_registry_ = ImportedMediaGalleryRegistry::GetInstance();
397 return imported_registry_->ITunesDataProvider();
400 } // namespace itunes