Upstream version 7.35.139.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/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 }  // namespace
38
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";
43
44 ITunesFileUtil::ITunesFileUtil(MediaPathFilter* media_path_filter)
45     : NativeMediaFileUtil(media_path_filter),
46       weak_factory_(this),
47       imported_registry_(NULL) {
48 }
49
50 ITunesFileUtil::~ITunesFileUtil() {
51 }
52
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.
60   if (!data_provider) {
61     GetFileInfoWithFreshDataProvider(context.Pass(), url, callback, false);
62   } else {
63     data_provider->RefreshData(
64         base::Bind(&ITunesFileUtil::GetFileInfoWithFreshDataProvider,
65                    weak_factory_.GetWeakPtr(), base::Passed(&context), url,
66                    callback));
67   }
68 }
69
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.
77   if (!data_provider) {
78     ReadDirectoryWithFreshDataProvider(context.Pass(), url, callback, false);
79   } else {
80     data_provider->RefreshData(
81         base::Bind(&ITunesFileUtil::ReadDirectoryWithFreshDataProvider,
82                    weak_factory_.GetWeakPtr(), base::Passed(&context), url,
83                    callback));
84   }
85 }
86
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.
94   if (!data_provider) {
95     CreateSnapshotFileWithFreshDataProvider(context.Pass(), url, callback,
96                                             false);
97   } else {
98     data_provider->RefreshData(
99         base::Bind(&ITunesFileUtil::CreateSnapshotFileWithFreshDataProvider,
100                    weak_factory_.GetWeakPtr(), base::Passed(&context), url,
101                    callback));
102   }
103 }
104
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
110 //
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);
118
119   if (components.size() == 0)
120     return MakeDirectoryFileInfo(file_info);
121
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();
127     if (platform_path)
128       *platform_path = file_path;
129     return fileapi::NativeFileUtil::GetFileInfo(file_path, file_info);
130   }
131
132   if (components[0] != kITunesMediaDir)
133     return base::File::FILE_ERROR_NOT_FOUND;
134
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,
139                                                 platform_path);
140   }
141
142   if (components[1] == kITunesMusicDir) {
143     switch (components.size()) {
144       case 2:
145         return MakeDirectoryFileInfo(file_info);
146
147       case 3:
148         if (GetDataProvider()->KnownArtist(components[2]))
149           return MakeDirectoryFileInfo(file_info);
150         break;
151
152       case 4:
153         if (GetDataProvider()->KnownAlbum(components[2], components[3]))
154           return MakeDirectoryFileInfo(file_info);
155         break;
156
157       case 5: {
158         base::FilePath location =
159             GetDataProvider()->GetTrackLocation(components[2], components[3],
160                                                 components[4]);
161          if (!location.empty()) {
162           return NativeMediaFileUtil::GetFileInfoSync(context, url, file_info,
163                                                       platform_path);
164         }
165         break;
166       }
167     }
168   }
169
170   return base::File::FILE_ERROR_NOT_FOUND;
171 }
172
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);
180
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,
190                                         0, base::Time()));
191     return base::File::FILE_OK;
192   }
193
194   if (components.size() == 1 && components[0] == kITunesLibraryXML)
195     return base::File::FILE_ERROR_NOT_A_DIRECTORY;
196
197   if (components[0] != kITunesMediaDir || components.size() > 5)
198     return base::File::FILE_ERROR_NOT_FOUND;
199
200   if (components.size() == 1) {
201     if (!GetDataProvider()->auto_add_path().empty()) {
202       file_list->push_back(DirectoryEntry(kITunesAutoAddDir,
203                                           DirectoryEntry::DIRECTORY,
204                                           0, base::Time()));
205     }
206     file_list->push_back(DirectoryEntry(kITunesMusicDir,
207                                         DirectoryEntry::DIRECTORY,
208                                         0, base::Time()));
209     return base::File::FILE_OK;
210   }
211
212   if (components[1] == kITunesAutoAddDir &&
213       !GetDataProvider()->auto_add_path().empty()) {
214     return NativeMediaFileUtil::ReadDirectorySync(context, url, file_list);
215   }
216
217   if (components[1] != kITunesMusicDir)
218     return base::File::FILE_ERROR_NOT_FOUND;
219
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,
226                                           0, base::Time()));
227     return base::File::FILE_OK;
228   }
229
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,
238                                           0, base::Time()));
239     return base::File::FILE_OK;
240   }
241
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,
253                                             file_info.size,
254                                             file_info.last_modified));
255       }
256     }
257     return base::File::FILE_OK;
258   }
259
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],
265                                                  components[3]);
266   if (!location.empty())
267     return base::File::FILE_ERROR_NOT_A_DIRECTORY;
268   return base::File::FILE_ERROR_NOT_FOUND;
269 }
270
271 base::File::Error ITunesFileUtil::DeleteDirectorySync(
272     fileapi::FileSystemOperationContext* context,
273     const fileapi::FileSystemURL& url) {
274   return base::File::FILE_ERROR_SECURITY;
275 }
276
277 base::File::Error ITunesFileUtil::DeleteFileSync(
278     fileapi::FileSystemOperationContext* context,
279     const fileapi::FileSystemURL& url) {
280   return base::File::FILE_ERROR_SECURITY;
281 }
282
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);
293   }
294
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);
303 }
304
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);
311
312   if (components.size() == 1 && components[0] == kITunesLibraryXML) {
313     *local_file_path = GetDataProvider()->library_path();
314     return base::File::FILE_OK;
315   }
316
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;
322
323     for (size_t i = 2; i < components.size(); ++i) {
324       *local_file_path = local_file_path->Append(
325           base::FilePath::FromUTF8Unsafe(components[i]));
326     }
327     return base::File::FILE_OK;
328   }
329
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;
334   }
335
336   *local_file_path = GetDataProvider()->GetTrackLocation(components[2],
337                                                          components[3],
338                                                          components[4]);
339   if (!local_file_path->empty())
340     return base::File::FILE_OK;
341
342   return base::File::FILE_ERROR_NOT_FOUND;
343 }
344
345 void ITunesFileUtil::GetFileInfoWithFreshDataProvider(
346     scoped_ptr<fileapi::FileSystemOperationContext> context,
347     const fileapi::FileSystemURL& url,
348     const GetFileInfoCallback& callback,
349     bool valid_parse) {
350   if (!valid_parse) {
351     if (!callback.is_null()) {
352       content::BrowserThread::PostTask(
353           content::BrowserThread::IO,
354           FROM_HERE,
355           base::Bind(callback, base::File::FILE_ERROR_IO,
356                      base::File::Info()));
357     }
358     return;
359   }
360   NativeMediaFileUtil::GetFileInfoOnTaskRunnerThread(context.Pass(), url,
361                                                      callback);
362 }
363
364 void ITunesFileUtil::ReadDirectoryWithFreshDataProvider(
365     scoped_ptr<fileapi::FileSystemOperationContext> context,
366     const fileapi::FileSystemURL& url,
367     const ReadDirectoryCallback& callback,
368     bool valid_parse) {
369   if (!valid_parse) {
370     if (!callback.is_null()) {
371       content::BrowserThread::PostTask(
372           content::BrowserThread::IO,
373           FROM_HERE,
374           base::Bind(callback, base::File::FILE_ERROR_IO, EntryList(), false));
375     }
376     return;
377   }
378   NativeMediaFileUtil::ReadDirectoryOnTaskRunnerThread(context.Pass(), url,
379                                                        callback);
380 }
381
382 void ITunesFileUtil::CreateSnapshotFileWithFreshDataProvider(
383     scoped_ptr<fileapi::FileSystemOperationContext> context,
384     const fileapi::FileSystemURL& url,
385     const CreateSnapshotFileCallback& callback,
386     bool valid_parse) {
387   if (!valid_parse) {
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,
394           FROM_HERE,
395           base::Bind(callback, base::File::FILE_ERROR_IO, file_info,
396                      platform_path, file_ref));
397     }
398     return;
399   }
400   NativeMediaFileUtil::CreateSnapshotFileOnTaskRunnerThread(context.Pass(), url,
401                                                             callback);
402 }
403
404 ITunesDataProvider* ITunesFileUtil::GetDataProvider() {
405   if (!imported_registry_)
406     imported_registry_ = ImportedMediaGalleryRegistry::GetInstance();
407   return imported_registry_->ITunesDataProvider();
408 }
409
410 }  // namespace itunes