Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / media_galleries / fileapi / itunes_file_util.cc
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.
4
5 #include "chrome/browser/media_galleries/fileapi/itunes_file_util.h"
6
7 #include <set>
8 #include <string>
9 #include <vector>
10
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/fileapi/file_system_operation_context.h"
19 #include "storage/browser/fileapi/file_system_url.h"
20 #include "storage/browser/fileapi/native_file_util.h"
21 #include "storage/common/blob/shareable_file_reference.h"
22 #include "storage/common/fileapi/file_system_util.h"
23
24 using storage::DirectoryEntry;
25
26 namespace itunes {
27
28 namespace {
29
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;
35 }
36
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");
42
43   DCHECK(root.IsParent(url.path()) || root == url.path());
44   base::FilePath virtual_path;
45   root.AppendRelativePath(url.path(), &virtual_path);
46
47   std::vector<std::string> result;
48   storage::VirtualPath::GetComponentsUTF8Unsafe(virtual_path, &result);
49   return result;
50 }
51
52 }  // namespace
53
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";
58
59 ITunesFileUtil::ITunesFileUtil(MediaPathFilter* media_path_filter)
60     : NativeMediaFileUtil(media_path_filter),
61       imported_registry_(NULL),
62       weak_factory_(this) {
63 }
64
65 ITunesFileUtil::~ITunesFileUtil() {
66 }
67
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));
82   }
83 }
84
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));
99   }
100 }
101
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));
117   }
118 }
119
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
125 //
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);
132
133   if (components.size() == 0)
134     return MakeDirectoryFileInfo(file_info);
135
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);
144   }
145
146   if (components[0] != kITunesMediaDir)
147     return base::File::FILE_ERROR_NOT_FOUND;
148
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);
154   }
155
156   if (components[1] == kITunesMusicDir) {
157     switch (components.size()) {
158       case 2:
159         return MakeDirectoryFileInfo(file_info);
160
161       case 3:
162         if (GetDataProvider()->KnownArtist(components[2]))
163           return MakeDirectoryFileInfo(file_info);
164         break;
165
166       case 4:
167         if (GetDataProvider()->KnownAlbum(components[2], components[3]))
168           return MakeDirectoryFileInfo(file_info);
169         break;
170
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);
178         }
179         break;
180       }
181     }
182   }
183
184   return base::File::FILE_ERROR_NOT_FOUND;
185 }
186
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);
193
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;
205   }
206
207   if (components.size() == 1 && components[0] == kITunesLibraryXML)
208     return base::File::FILE_ERROR_NOT_A_DIRECTORY;
209
210   if (components[0] != kITunesMediaDir || components.size() > 5)
211     return base::File::FILE_ERROR_NOT_FOUND;
212
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()));
218     }
219     file_list->push_back(DirectoryEntry(kITunesMusicDir,
220                                         DirectoryEntry::DIRECTORY,
221                                         0, base::Time()));
222     return base::File::FILE_OK;
223   }
224
225   if (components[1] == kITunesAutoAddDir &&
226       !GetDataProvider()->auto_add_path().empty()) {
227     return NativeMediaFileUtil::ReadDirectorySync(context, url, file_list);
228   }
229
230   if (components[1] != kITunesMusicDir)
231     return base::File::FILE_ERROR_NOT_FOUND;
232
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;
241   }
242
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;
253   }
254
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));
268       }
269     }
270     return base::File::FILE_OK;
271   }
272
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;
282 }
283
284 base::File::Error ITunesFileUtil::DeleteDirectorySync(
285     storage::FileSystemOperationContext* context,
286     const storage::FileSystemURL& url) {
287   return base::File::FILE_ERROR_SECURITY;
288 }
289
290 base::File::Error ITunesFileUtil::DeleteFileSync(
291     storage::FileSystemOperationContext* context,
292     const storage::FileSystemURL& url) {
293   return base::File::FILE_ERROR_SECURITY;
294 }
295
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);
306   }
307
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);
316 }
317
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);
323
324   if (components.size() == 1 && components[0] == kITunesLibraryXML) {
325     *local_file_path = GetDataProvider()->library_path();
326     return base::File::FILE_OK;
327   }
328
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;
334
335     for (size_t i = 2; i < components.size(); ++i) {
336       *local_file_path = local_file_path->Append(
337           base::FilePath::FromUTF8Unsafe(components[i]));
338     }
339     return base::File::FILE_OK;
340   }
341
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;
346   }
347
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;
353
354   return base::File::FILE_ERROR_NOT_FOUND;
355 }
356
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()));
369     }
370     return;
371   }
372   NativeMediaFileUtil::GetFileInfoOnTaskRunnerThread(context.Pass(), url,
373                                                      callback);
374 }
375
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));
387     }
388     return;
389   }
390   NativeMediaFileUtil::ReadDirectoryOnTaskRunnerThread(context.Pass(), url,
391                                                        callback);
392 }
393
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));
409     }
410     return;
411   }
412   NativeMediaFileUtil::CreateSnapshotFileOnTaskRunnerThread(context.Pass(), url,
413                                                             callback);
414 }
415
416 ITunesDataProvider* ITunesFileUtil::GetDataProvider() {
417   if (!imported_registry_)
418     imported_registry_ = ImportedMediaGalleryRegistry::GetInstance();
419   return imported_registry_->ITunesDataProvider();
420 }
421
422 }  // namespace itunes