- add sources.
[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::PlatformFileError MakeDirectoryFileInfo(
31     base::PlatformFileInfo* file_info) {
32   base::PlatformFileInfo result;
33   result.is_directory = true;
34   *file_info = result;
35   return base::PLATFORM_FILE_OK;
36 }
37
38 }  // namespace
39
40 const char kITunesLibraryXML[] = "iTunes Music Library.xml";
41 const char kITunesMediaDir[] = "iTunes Media";
42 const char kITunesMusicDir[] = "Music";
43 const char kITunesAutoAddDir[] = "Automatically Add to iTunes";
44
45 ITunesFileUtil::ITunesFileUtil(MediaPathFilter* media_path_filter)
46     : NativeMediaFileUtil(media_path_filter),
47       weak_factory_(this),
48       imported_registry_(NULL) {
49 }
50
51 ITunesFileUtil::~ITunesFileUtil() {
52 }
53
54 void ITunesFileUtil::GetFileInfoOnTaskRunnerThread(
55     scoped_ptr<fileapi::FileSystemOperationContext> context,
56     const fileapi::FileSystemURL& url,
57     const GetFileInfoCallback& callback) {
58   GetDataProvider()->RefreshData(
59       base::Bind(&ITunesFileUtil::GetFileInfoWithFreshDataProvider,
60                  weak_factory_.GetWeakPtr(), base::Passed(&context), url,
61                  callback));
62 }
63
64 void ITunesFileUtil::ReadDirectoryOnTaskRunnerThread(
65     scoped_ptr<fileapi::FileSystemOperationContext> context,
66     const fileapi::FileSystemURL& url,
67     const ReadDirectoryCallback& callback) {
68   GetDataProvider()->RefreshData(
69       base::Bind(&ITunesFileUtil::ReadDirectoryWithFreshDataProvider,
70                  weak_factory_.GetWeakPtr(), base::Passed(&context), url,
71                  callback));
72 }
73
74 void ITunesFileUtil::CreateSnapshotFileOnTaskRunnerThread(
75     scoped_ptr<fileapi::FileSystemOperationContext> context,
76     const fileapi::FileSystemURL& url,
77     const CreateSnapshotFileCallback& callback) {
78   GetDataProvider()->RefreshData(
79       base::Bind(&ITunesFileUtil::CreateSnapshotFileWithFreshDataProvider,
80                  weak_factory_.GetWeakPtr(), base::Passed(&context), url,
81                  callback));
82 }
83
84 // Contents of the iTunes media gallery:
85 //   /                                                - root directory
86 //   /iTunes Music Library.xml                        - library xml file
87 //   /iTunes Media/Automatically Add to iTunes        - auto-import directory
88 //   /iTunes Media/Music/<Artist>/<Album>/<Track>     - tracks
89 //
90 base::PlatformFileError ITunesFileUtil::GetFileInfoSync(
91     fileapi::FileSystemOperationContext* context,
92     const fileapi::FileSystemURL& url,
93     base::PlatformFileInfo* file_info,
94     base::FilePath* platform_path) {
95   std::vector<std::string> components;
96   fileapi::VirtualPath::GetComponentsUTF8Unsafe(url.path(), &components);
97
98   if (components.size() == 0)
99     return MakeDirectoryFileInfo(file_info);
100
101   if (components.size() == 1 && components[0] == kITunesLibraryXML) {
102     // We can't just call NativeMediaFileUtil::GetFileInfoSync() here because it
103     // uses the MediaPathFilter. At this point, |library_path_| is known good
104     // because GetFileInfoWithFreshDataProvider() gates access to this method.
105     base::FilePath file_path = GetDataProvider()->library_path();
106     if (platform_path)
107       *platform_path = file_path;
108     return fileapi::NativeFileUtil::GetFileInfo(file_path, file_info);
109   }
110
111   if (components[0] != kITunesMediaDir)
112     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
113
114   if (components[1] == kITunesAutoAddDir) {
115     if (GetDataProvider()->auto_add_path().empty())
116       return base::PLATFORM_FILE_ERROR_NOT_FOUND;
117     return NativeMediaFileUtil::GetFileInfoSync(context, url, file_info,
118                                                 platform_path);
119   }
120
121   if (components[1] == kITunesMusicDir) {
122     switch (components.size()) {
123       case 2:
124         return MakeDirectoryFileInfo(file_info);
125
126       case 3:
127         if (GetDataProvider()->KnownArtist(components[2]))
128           return MakeDirectoryFileInfo(file_info);
129         break;
130
131       case 4:
132         if (GetDataProvider()->KnownAlbum(components[2], components[3]))
133           return MakeDirectoryFileInfo(file_info);
134         break;
135
136       case 5: {
137         base::FilePath location =
138             GetDataProvider()->GetTrackLocation(components[2], components[3],
139                                                 components[4]);
140          if (!location.empty()) {
141           return NativeMediaFileUtil::GetFileInfoSync(context, url, file_info,
142                                                       platform_path);
143         }
144         break;
145       }
146     }
147   }
148
149   return base::PLATFORM_FILE_ERROR_NOT_FOUND;
150 }
151
152 base::PlatformFileError ITunesFileUtil::ReadDirectorySync(
153     fileapi::FileSystemOperationContext* context,
154     const fileapi::FileSystemURL& url,
155     EntryList* file_list) {
156   DCHECK(file_list->empty());
157   std::vector<std::string> components;
158   fileapi::VirtualPath::GetComponentsUTF8Unsafe(url.path(), &components);
159
160   if (components.size() == 0) {
161     base::PlatformFileInfo xml_info;
162     if (!file_util::GetFileInfo(GetDataProvider()->library_path(), &xml_info))
163       return base::PLATFORM_FILE_ERROR_IO;
164     file_list->push_back(DirectoryEntry(kITunesLibraryXML,
165                                         DirectoryEntry::FILE,
166                                         xml_info.size, xml_info.last_modified));
167     file_list->push_back(DirectoryEntry(kITunesMediaDir,
168                                         DirectoryEntry::DIRECTORY,
169                                         0, base::Time()));
170     return base::PLATFORM_FILE_OK;
171   }
172
173   if (components.size() == 1 && components[0] == kITunesLibraryXML)
174     return base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY;
175
176   if (components[0] != kITunesMediaDir || components.size() > 5)
177     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
178
179   if (components.size() == 1) {
180     if (!GetDataProvider()->auto_add_path().empty()) {
181       file_list->push_back(DirectoryEntry(kITunesAutoAddDir,
182                                           DirectoryEntry::DIRECTORY,
183                                           0, base::Time()));
184     }
185     file_list->push_back(DirectoryEntry(kITunesMusicDir,
186                                         DirectoryEntry::DIRECTORY,
187                                         0, base::Time()));
188     return base::PLATFORM_FILE_OK;
189   }
190
191   if (components[1] == kITunesAutoAddDir &&
192       !GetDataProvider()->auto_add_path().empty()) {
193     return NativeMediaFileUtil::ReadDirectorySync(context, url, file_list);
194   }
195
196   if (components[1] != kITunesMusicDir)
197     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
198
199   if (components.size() == 2) {
200     std::set<ITunesDataProvider::ArtistName> artists =
201         GetDataProvider()->GetArtistNames();
202     std::set<ITunesDataProvider::ArtistName>::const_iterator it;
203     for (it = artists.begin(); it != artists.end(); ++it)
204       file_list->push_back(DirectoryEntry(*it, DirectoryEntry::DIRECTORY,
205                                           0, base::Time()));
206     return base::PLATFORM_FILE_OK;
207   }
208
209   if (components.size() == 3) {
210     std::set<ITunesDataProvider::AlbumName> albums =
211         GetDataProvider()->GetAlbumNames(components[2]);
212     if (albums.size() == 0)
213       return base::PLATFORM_FILE_ERROR_NOT_FOUND;
214     std::set<ITunesDataProvider::AlbumName>::const_iterator it;
215     for (it = albums.begin(); it != albums.end(); ++it)
216       file_list->push_back(DirectoryEntry(*it, DirectoryEntry::DIRECTORY,
217                                           0, base::Time()));
218     return base::PLATFORM_FILE_OK;
219   }
220
221   if (components.size() == 4) {
222     ITunesDataProvider::Album album =
223         GetDataProvider()->GetAlbum(components[2], components[3]);
224     if (album.size() == 0)
225       return base::PLATFORM_FILE_ERROR_NOT_FOUND;
226     ITunesDataProvider::Album::const_iterator it;
227     for (it = album.begin(); it != album.end(); ++it) {
228       base::PlatformFileInfo file_info;
229       if (media_path_filter()->Match(it->second) &&
230           file_util::GetFileInfo(it->second, &file_info)) {
231         file_list->push_back(DirectoryEntry(it->first, DirectoryEntry::FILE,
232                                             file_info.size,
233                                             file_info.last_modified));
234       }
235     }
236     return base::PLATFORM_FILE_OK;
237   }
238
239   // At this point, the only choice is one of two errors, but figuring out
240   // which one is required.
241   DCHECK_EQ(4UL, components.size());
242   base::FilePath location;
243   location = GetDataProvider()->GetTrackLocation(components[1], components[2],
244                                                  components[3]);
245   if (!location.empty())
246     return base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY;
247   return base::PLATFORM_FILE_ERROR_NOT_FOUND;
248 }
249
250 base::PlatformFileError ITunesFileUtil::CreateSnapshotFileSync(
251     fileapi::FileSystemOperationContext* context,
252     const fileapi::FileSystemURL& url,
253     base::PlatformFileInfo* file_info,
254     base::FilePath* platform_path,
255     scoped_refptr<webkit_blob::ShareableFileReference>* file_ref) {
256   DCHECK(!url.path().IsAbsolute());
257   if (url.path() != base::FilePath().AppendASCII(kITunesLibraryXML)) {
258     return NativeMediaFileUtil::CreateSnapshotFileSync(context, url, file_info,
259                                                        platform_path, file_ref);
260   }
261
262   // The following code is different than
263   // NativeMediaFileUtil::CreateSnapshotFileSync in that it knows that the
264   // library xml file is not a directory and it doesn't run mime sniffing on the
265   // file. The only way to get here is by way of
266   // CreateSnapshotFileWithFreshDataProvider() so the file has already been
267   // parsed and deemed valid.
268   *file_ref = scoped_refptr<webkit_blob::ShareableFileReference>();
269   return GetFileInfoSync(context, url, file_info, platform_path);
270 }
271
272 base::PlatformFileError ITunesFileUtil::GetLocalFilePath(
273     fileapi::FileSystemOperationContext* context,
274     const fileapi::FileSystemURL& url,
275     base::FilePath* local_file_path) {
276   std::vector<std::string> components;
277   fileapi::VirtualPath::GetComponentsUTF8Unsafe(url.path(), &components);
278
279   if (components.size() == 1 && components[0] == kITunesLibraryXML) {
280     *local_file_path = GetDataProvider()->library_path();
281     return base::PLATFORM_FILE_OK;
282   }
283
284   if (components.size() >= 2 && components[0] == kITunesMediaDir &&
285       components[1] == kITunesAutoAddDir) {
286     *local_file_path = GetDataProvider()->auto_add_path();
287     if (local_file_path->empty())
288       return base::PLATFORM_FILE_ERROR_NOT_FOUND;
289
290     for (size_t i = 2; i < components.size(); ++i) {
291       *local_file_path = local_file_path->Append(
292           base::FilePath::FromUTF8Unsafe(components[i]));
293     }
294     return base::PLATFORM_FILE_OK;
295   }
296
297   // Should only get here for files, i.e. the xml file and tracks.
298   if (components[0] != kITunesMediaDir || components[1] != kITunesMusicDir||
299       components.size() != 5) {
300     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
301   }
302
303   *local_file_path = GetDataProvider()->GetTrackLocation(components[2],
304                                                          components[3],
305                                                          components[4]);
306   if (!local_file_path->empty())
307     return base::PLATFORM_FILE_OK;
308
309   return base::PLATFORM_FILE_ERROR_NOT_FOUND;
310 }
311
312 void ITunesFileUtil::GetFileInfoWithFreshDataProvider(
313     scoped_ptr<fileapi::FileSystemOperationContext> context,
314     const fileapi::FileSystemURL& url,
315     const GetFileInfoCallback& callback,
316     bool valid_parse) {
317   if (!valid_parse) {
318     if (!callback.is_null()) {
319       content::BrowserThread::PostTask(
320           content::BrowserThread::IO,
321           FROM_HERE,
322           base::Bind(callback, base::PLATFORM_FILE_ERROR_IO,
323                      base::PlatformFileInfo()));
324     }
325     return;
326   }
327   NativeMediaFileUtil::GetFileInfoOnTaskRunnerThread(context.Pass(), url,
328                                                      callback);
329 }
330
331 void ITunesFileUtil::ReadDirectoryWithFreshDataProvider(
332     scoped_ptr<fileapi::FileSystemOperationContext> context,
333     const fileapi::FileSystemURL& url,
334     const ReadDirectoryCallback& callback,
335     bool valid_parse) {
336   if (!valid_parse) {
337     if (!callback.is_null()) {
338       content::BrowserThread::PostTask(
339           content::BrowserThread::IO,
340           FROM_HERE,
341           base::Bind(callback, base::PLATFORM_FILE_ERROR_IO, EntryList(),
342                      false));
343     }
344     return;
345   }
346   NativeMediaFileUtil::ReadDirectoryOnTaskRunnerThread(context.Pass(), url,
347                                                        callback);
348 }
349
350 void ITunesFileUtil::CreateSnapshotFileWithFreshDataProvider(
351     scoped_ptr<fileapi::FileSystemOperationContext> context,
352     const fileapi::FileSystemURL& url,
353     const CreateSnapshotFileCallback& callback,
354     bool valid_parse) {
355   if (!valid_parse) {
356     if (!callback.is_null()) {
357       base::PlatformFileInfo file_info;
358       base::FilePath platform_path;
359       scoped_refptr<webkit_blob::ShareableFileReference> file_ref;
360       content::BrowserThread::PostTask(
361           content::BrowserThread::IO,
362           FROM_HERE,
363           base::Bind(callback, base::PLATFORM_FILE_ERROR_IO, file_info,
364                      platform_path, file_ref));
365     }
366     return;
367   }
368   NativeMediaFileUtil::CreateSnapshotFileOnTaskRunnerThread(context.Pass(), url,
369                                                             callback);
370 }
371
372 ITunesDataProvider* ITunesFileUtil::GetDataProvider() {
373   if (!imported_registry_)
374     imported_registry_ = ImportedMediaGalleryRegistry::GetInstance();
375   return imported_registry_->ITunesDataProvider();
376 }
377
378 }  // namespace itunes