Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / media_galleries / fileapi / itunes_file_util.cc
blobadd998203214118059402c5522912cc22c04664b
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"
7 #include <set>
8 #include <string>
9 #include <vector>
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;
26 namespace itunes {
28 namespace {
30 base::File::Error MakeDirectoryFileInfo(base::File::Info* file_info) {
31 base::File::Info result;
32 result.is_directory = true;
33 *file_info = result;
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);
49 return result;
52 } // namespace
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),
62 weak_factory_(this) {
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.
75 if (!data_provider) {
76 GetFileInfoWithFreshDataProvider(context.Pass(), url, callback, false);
77 } else {
78 data_provider->RefreshData(
79 base::Bind(&ITunesFileUtil::GetFileInfoWithFreshDataProvider,
80 weak_factory_.GetWeakPtr(), base::Passed(&context), url,
81 callback));
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.
92 if (!data_provider) {
93 ReadDirectoryWithFreshDataProvider(context.Pass(), url, callback, false);
94 } else {
95 data_provider->RefreshData(
96 base::Bind(&ITunesFileUtil::ReadDirectoryWithFreshDataProvider,
97 weak_factory_.GetWeakPtr(), base::Passed(&context), url,
98 callback));
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,
111 false);
112 } else {
113 data_provider->RefreshData(
114 base::Bind(&ITunesFileUtil::CreateSnapshotFileWithFreshDataProvider,
115 weak_factory_.GetWeakPtr(), base::Passed(&context), url,
116 callback));
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();
141 if (platform_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,
153 platform_path);
156 if (components[1] == kITunesMusicDir) {
157 switch (components.size()) {
158 case 2:
159 return MakeDirectoryFileInfo(file_info);
161 case 3:
162 if (GetDataProvider()->KnownArtist(components[2]))
163 return MakeDirectoryFileInfo(file_info);
164 break;
166 case 4:
167 if (GetDataProvider()->KnownAlbum(components[2], components[3]))
168 return MakeDirectoryFileInfo(file_info);
169 break;
171 case 5: {
172 base::FilePath location =
173 GetDataProvider()->GetTrackLocation(components[2], components[3],
174 components[4]);
175 if (!location.empty()) {
176 return NativeMediaFileUtil::GetFileInfoSync(context, url, file_info,
177 platform_path);
179 break;
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,
203 0, base::Time()));
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,
217 0, base::Time()));
219 file_list->push_back(DirectoryEntry(kITunesMusicDir,
220 DirectoryEntry::DIRECTORY,
221 0, base::Time()));
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,
239 0, base::Time()));
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,
251 0, base::Time()));
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,
266 file_info.size,
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],
278 components[3]);
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],
349 components[3],
350 components[4]);
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,
361 bool valid_parse) {
362 if (!valid_parse) {
363 if (!callback.is_null()) {
364 content::BrowserThread::PostTask(
365 content::BrowserThread::IO,
366 FROM_HERE,
367 base::Bind(callback, base::File::FILE_ERROR_IO,
368 base::File::Info()));
370 return;
372 NativeMediaFileUtil::GetFileInfoOnTaskRunnerThread(context.Pass(), url,
373 callback);
376 void ITunesFileUtil::ReadDirectoryWithFreshDataProvider(
377 scoped_ptr<storage::FileSystemOperationContext> context,
378 const storage::FileSystemURL& url,
379 const ReadDirectoryCallback& callback,
380 bool valid_parse) {
381 if (!valid_parse) {
382 if (!callback.is_null()) {
383 content::BrowserThread::PostTask(
384 content::BrowserThread::IO,
385 FROM_HERE,
386 base::Bind(callback, base::File::FILE_ERROR_IO, EntryList(), false));
388 return;
390 NativeMediaFileUtil::ReadDirectoryOnTaskRunnerThread(context.Pass(), url,
391 callback);
394 void ITunesFileUtil::CreateSnapshotFileWithFreshDataProvider(
395 scoped_ptr<storage::FileSystemOperationContext> context,
396 const storage::FileSystemURL& url,
397 const CreateSnapshotFileCallback& callback,
398 bool valid_parse) {
399 if (!valid_parse) {
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,
406 FROM_HERE,
407 base::Bind(callback, base::File::FILE_ERROR_IO, file_info,
408 platform_path, file_ref));
410 return;
412 NativeMediaFileUtil::CreateSnapshotFileOnTaskRunnerThread(context.Pass(), url,
413 callback);
416 ITunesDataProvider* ITunesFileUtil::GetDataProvider() {
417 if (!imported_registry_)
418 imported_registry_ = ImportedMediaGalleryRegistry::GetInstance();
419 return imported_registry_->ITunesDataProvider();
422 } // namespace itunes