9b49c94e4f04cd534acdca9b39720d98b9fbfcc0
[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/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"
23
24 using fileapi::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 fileapi::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   fileapi::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       weak_factory_(this),
62       imported_registry_(NULL) {
63 }
64
65 ITunesFileUtil::~ITunesFileUtil() {
66 }
67
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,
75                  callback));
76 }
77
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,
85                  callback));
86 }
87
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,
95                  callback));
96 }
97
98 // Contents of the iTunes media gallery:
99 //   /                                                - root directory
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
103 //
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);
110
111   if (components.size() == 0)
112     return MakeDirectoryFileInfo(file_info);
113
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();
119     if (platform_path)
120       *platform_path = file_path;
121     return fileapi::NativeFileUtil::GetFileInfo(file_path, file_info);
122   }
123
124   if (components[0] != kITunesMediaDir)
125     return base::File::FILE_ERROR_NOT_FOUND;
126
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,
131                                                 platform_path);
132   }
133
134   if (components[1] == kITunesMusicDir) {
135     switch (components.size()) {
136       case 2:
137         return MakeDirectoryFileInfo(file_info);
138
139       case 3:
140         if (GetDataProvider()->KnownArtist(components[2]))
141           return MakeDirectoryFileInfo(file_info);
142         break;
143
144       case 4:
145         if (GetDataProvider()->KnownAlbum(components[2], components[3]))
146           return MakeDirectoryFileInfo(file_info);
147         break;
148
149       case 5: {
150         base::FilePath location =
151             GetDataProvider()->GetTrackLocation(components[2], components[3],
152                                                 components[4]);
153          if (!location.empty()) {
154           return NativeMediaFileUtil::GetFileInfoSync(context, url, file_info,
155                                                       platform_path);
156         }
157         break;
158       }
159     }
160   }
161
162   return base::File::FILE_ERROR_NOT_FOUND;
163 }
164
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);
171
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,
181                                         0, base::Time()));
182     return base::File::FILE_OK;
183   }
184
185   if (components.size() == 1 && components[0] == kITunesLibraryXML)
186     return base::File::FILE_ERROR_NOT_A_DIRECTORY;
187
188   if (components[0] != kITunesMediaDir || components.size() > 5)
189     return base::File::FILE_ERROR_NOT_FOUND;
190
191   if (components.size() == 1) {
192     if (!GetDataProvider()->auto_add_path().empty()) {
193       file_list->push_back(DirectoryEntry(kITunesAutoAddDir,
194                                           DirectoryEntry::DIRECTORY,
195                                           0, base::Time()));
196     }
197     file_list->push_back(DirectoryEntry(kITunesMusicDir,
198                                         DirectoryEntry::DIRECTORY,
199                                         0, base::Time()));
200     return base::File::FILE_OK;
201   }
202
203   if (components[1] == kITunesAutoAddDir &&
204       !GetDataProvider()->auto_add_path().empty()) {
205     return NativeMediaFileUtil::ReadDirectorySync(context, url, file_list);
206   }
207
208   if (components[1] != kITunesMusicDir)
209     return base::File::FILE_ERROR_NOT_FOUND;
210
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,
217                                           0, base::Time()));
218     return base::File::FILE_OK;
219   }
220
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,
229                                           0, base::Time()));
230     return base::File::FILE_OK;
231   }
232
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,
244                                             file_info.size,
245                                             file_info.last_modified));
246       }
247     }
248     return base::File::FILE_OK;
249   }
250
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],
256                                                  components[3]);
257   if (!location.empty())
258     return base::File::FILE_ERROR_NOT_A_DIRECTORY;
259   return base::File::FILE_ERROR_NOT_FOUND;
260 }
261
262 base::File::Error ITunesFileUtil::DeleteDirectorySync(
263     fileapi::FileSystemOperationContext* context,
264     const fileapi::FileSystemURL& url) {
265   return base::File::FILE_ERROR_SECURITY;
266 }
267
268 base::File::Error ITunesFileUtil::DeleteFileSync(
269     fileapi::FileSystemOperationContext* context,
270     const fileapi::FileSystemURL& url) {
271   return base::File::FILE_ERROR_SECURITY;
272 }
273
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);
284   }
285
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);
294 }
295
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);
301
302   if (components.size() == 1 && components[0] == kITunesLibraryXML) {
303     *local_file_path = GetDataProvider()->library_path();
304     return base::File::FILE_OK;
305   }
306
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;
312
313     for (size_t i = 2; i < components.size(); ++i) {
314       *local_file_path = local_file_path->Append(
315           base::FilePath::FromUTF8Unsafe(components[i]));
316     }
317     return base::File::FILE_OK;
318   }
319
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;
324   }
325
326   *local_file_path = GetDataProvider()->GetTrackLocation(components[2],
327                                                          components[3],
328                                                          components[4]);
329   if (!local_file_path->empty())
330     return base::File::FILE_OK;
331
332   return base::File::FILE_ERROR_NOT_FOUND;
333 }
334
335 void ITunesFileUtil::GetFileInfoWithFreshDataProvider(
336     scoped_ptr<fileapi::FileSystemOperationContext> context,
337     const fileapi::FileSystemURL& url,
338     const GetFileInfoCallback& callback,
339     bool valid_parse) {
340   if (!valid_parse) {
341     if (!callback.is_null()) {
342       content::BrowserThread::PostTask(
343           content::BrowserThread::IO,
344           FROM_HERE,
345           base::Bind(callback, base::File::FILE_ERROR_IO,
346                      base::File::Info()));
347     }
348     return;
349   }
350   NativeMediaFileUtil::GetFileInfoOnTaskRunnerThread(context.Pass(), url,
351                                                      callback);
352 }
353
354 void ITunesFileUtil::ReadDirectoryWithFreshDataProvider(
355     scoped_ptr<fileapi::FileSystemOperationContext> context,
356     const fileapi::FileSystemURL& url,
357     const ReadDirectoryCallback& callback,
358     bool valid_parse) {
359   if (!valid_parse) {
360     if (!callback.is_null()) {
361       content::BrowserThread::PostTask(
362           content::BrowserThread::IO,
363           FROM_HERE,
364           base::Bind(callback, base::File::FILE_ERROR_IO, EntryList(), false));
365     }
366     return;
367   }
368   NativeMediaFileUtil::ReadDirectoryOnTaskRunnerThread(context.Pass(), url,
369                                                        callback);
370 }
371
372 void ITunesFileUtil::CreateSnapshotFileWithFreshDataProvider(
373     scoped_ptr<fileapi::FileSystemOperationContext> context,
374     const fileapi::FileSystemURL& url,
375     const CreateSnapshotFileCallback& callback,
376     bool valid_parse) {
377   if (!valid_parse) {
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,
384           FROM_HERE,
385           base::Bind(callback, base::File::FILE_ERROR_IO, file_info,
386                      platform_path, file_ref));
387     }
388     return;
389   }
390   NativeMediaFileUtil::CreateSnapshotFileOnTaskRunnerThread(context.Pass(), url,
391                                                             callback);
392 }
393
394 ITunesDataProvider* ITunesFileUtil::GetDataProvider() {
395   if (!imported_registry_)
396     imported_registry_ = ImportedMediaGalleryRegistry::GetInstance();
397   return imported_registry_->ITunesDataProvider();
398 }
399
400 }  // namespace itunes