- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / file_handlers / app_file_handler_util.cc
1 // Copyright (c) 2012 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/extensions/api/file_handlers/app_file_handler_util.h"
6
7 #include "base/file_util.h"
8 #include "base/files/file_path.h"
9 #include "chrome/browser/extensions/extension_prefs.h"
10 #include "content/public/browser/browser_thread.h"
11 #include "content/public/browser/child_process_security_policy.h"
12 #include "net/base/mime_util.h"
13 #include "webkit/browser/fileapi/isolated_context.h"
14 #include "webkit/common/fileapi/file_system_types.h"
15
16 #if defined(OS_CHROMEOS)
17 #include "chrome/browser/chromeos/drive/file_system_util.h"
18 #endif
19
20 namespace extensions {
21
22 namespace app_file_handler_util {
23
24 namespace {
25
26 bool FileHandlerCanHandleFileWithExtension(
27     const FileHandlerInfo& handler,
28     const base::FilePath& path) {
29   for (std::set<std::string>::const_iterator extension =
30        handler.extensions.begin();
31        extension != handler.extensions.end(); ++extension) {
32     if (*extension == "*")
33       return true;
34
35     if (path.MatchesExtension(
36         base::FilePath::kExtensionSeparator +
37         base::FilePath::FromUTF8Unsafe(*extension).value())) {
38       return true;
39     }
40
41     // Also accept files with no extension for handlers that support an
42     // empty extension, i.e. both "foo" and "foo." match.
43     if (extension->empty() &&
44         path.MatchesExtension(base::FilePath::StringType())) {
45       return true;
46     }
47   }
48   return false;
49 }
50
51 bool FileHandlerCanHandleFileWithMimeType(
52     const FileHandlerInfo& handler,
53     const std::string& mime_type) {
54   for (std::set<std::string>::const_iterator type = handler.types.begin();
55        type != handler.types.end(); ++type) {
56     if (net::MatchesMimeType(*type, mime_type))
57       return true;
58   }
59   return false;
60 }
61
62 bool DoCheckWritableFile(const base::FilePath& path, bool is_directory) {
63   // Don't allow links.
64   if (base::PathExists(path) && file_util::IsLink(path))
65     return false;
66
67   if (is_directory)
68     return base::DirectoryExists(path);
69
70   // Create the file if it doesn't already exist.
71   base::PlatformFileError error = base::PLATFORM_FILE_OK;
72   int creation_flags = base::PLATFORM_FILE_CREATE |
73                        base::PLATFORM_FILE_READ |
74                        base::PLATFORM_FILE_WRITE;
75   base::PlatformFile file = base::CreatePlatformFile(path, creation_flags,
76                                                      NULL, &error);
77   // Close the file so we don't keep a lock open.
78   if (file != base::kInvalidPlatformFileValue)
79     base::ClosePlatformFile(file);
80   if (error != base::PLATFORM_FILE_OK &&
81       error != base::PLATFORM_FILE_ERROR_EXISTS) {
82     return false;
83   }
84
85   return true;
86 }
87
88 // Checks whether a list of paths are all OK for writing and calls a provided
89 // on_success or on_failure callback when done. A file is OK for writing if it
90 // is not a symlink, is not in a blacklisted path and can be opened for writing;
91 // files are created if they do not exist.
92 class WritableFileChecker
93     : public base::RefCountedThreadSafe<WritableFileChecker> {
94  public:
95   WritableFileChecker(
96       const std::vector<base::FilePath>& paths,
97       Profile* profile,
98       bool is_directory,
99       const base::Closure& on_success,
100       const base::Callback<void(const base::FilePath&)>& on_failure);
101
102   void Check();
103
104  private:
105   friend class base::RefCountedThreadSafe<WritableFileChecker>;
106   virtual ~WritableFileChecker();
107
108   // Called when a work item is completed. If all work items are done, this
109   // calls the success or failure callback.
110   void TaskDone();
111
112   // Reports an error in completing a work item. This may be called more than
113   // once, but only the last message will be retained.
114   void Error(const base::FilePath& error_path);
115
116   void CheckLocalWritableFiles();
117
118 #if defined(OS_CHROMEOS)
119   void CheckRemoteWritableFile(const base::FilePath& remote_path,
120                                drive::FileError error,
121                                const base::FilePath& local_path);
122 #endif
123
124   const std::vector<base::FilePath> paths_;
125   Profile* profile_;
126   const bool is_directory_;
127   int outstanding_tasks_;
128   base::FilePath error_path_;
129   base::Closure on_success_;
130   base::Callback<void(const base::FilePath&)> on_failure_;
131 };
132
133 WritableFileChecker::WritableFileChecker(
134     const std::vector<base::FilePath>& paths,
135     Profile* profile,
136     bool is_directory,
137     const base::Closure& on_success,
138     const base::Callback<void(const base::FilePath&)>& on_failure)
139     : paths_(paths),
140       profile_(profile),
141       is_directory_(is_directory),
142       outstanding_tasks_(1),
143       on_success_(on_success),
144       on_failure_(on_failure) {}
145
146 void WritableFileChecker::Check() {
147 #if defined(OS_CHROMEOS)
148   if (drive::util::IsUnderDriveMountPoint(paths_[0])) {
149     outstanding_tasks_ = paths_.size();
150     for (std::vector<base::FilePath>::const_iterator it = paths_.begin();
151          it != paths_.end();
152          ++it) {
153       DCHECK(drive::util::IsUnderDriveMountPoint(*it));
154       drive::util::PrepareWritableFileAndRun(
155           profile_,
156           *it,
157           base::Bind(&WritableFileChecker::CheckRemoteWritableFile, this, *it));
158     }
159     return;
160   }
161 #endif
162   content::BrowserThread::PostTask(
163       content::BrowserThread::FILE,
164       FROM_HERE,
165       base::Bind(&WritableFileChecker::CheckLocalWritableFiles, this));
166 }
167
168 WritableFileChecker::~WritableFileChecker() {}
169
170 void WritableFileChecker::TaskDone() {
171   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
172   if (--outstanding_tasks_ == 0) {
173     if (error_path_.empty())
174       on_success_.Run();
175     else
176       on_failure_.Run(error_path_);
177   }
178 }
179
180 // Reports an error in completing a work item. This may be called more than
181 // once, but only the last message will be retained.
182 void WritableFileChecker::Error(const base::FilePath& error_path) {
183   DCHECK(!error_path.empty());
184   error_path_ = error_path;
185   TaskDone();
186 }
187
188 void WritableFileChecker::CheckLocalWritableFiles() {
189   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
190   std::string error;
191   for (std::vector<base::FilePath>::const_iterator it = paths_.begin();
192        it != paths_.end();
193        ++it) {
194     if (!DoCheckWritableFile(*it, is_directory_)) {
195       content::BrowserThread::PostTask(
196           content::BrowserThread::UI,
197           FROM_HERE,
198           base::Bind(&WritableFileChecker::Error, this, *it));
199       return;
200     }
201   }
202   content::BrowserThread::PostTask(
203       content::BrowserThread::UI,
204       FROM_HERE,
205       base::Bind(&WritableFileChecker::TaskDone, this));
206 }
207
208 #if defined(OS_CHROMEOS)
209 void WritableFileChecker::CheckRemoteWritableFile(
210     const base::FilePath& remote_path,
211     drive::FileError error,
212     const base::FilePath& /* local_path */) {
213   if (error == drive::FILE_ERROR_OK) {
214     content::BrowserThread::PostTask(
215         content::BrowserThread::UI,
216         FROM_HERE,
217         base::Bind(&WritableFileChecker::TaskDone, this));
218   } else {
219     content::BrowserThread::PostTask(
220         content::BrowserThread::UI,
221         FROM_HERE,
222         base::Bind(&WritableFileChecker::Error, this, remote_path));
223   }
224 }
225 #endif
226
227 }  // namespace
228
229 typedef std::vector<FileHandlerInfo> FileHandlerList;
230
231 const FileHandlerInfo* FileHandlerForId(const Extension& app,
232                                         const std::string& handler_id) {
233   const FileHandlerList* file_handlers = FileHandlers::GetFileHandlers(&app);
234   if (!file_handlers)
235     return NULL;
236
237   for (FileHandlerList::const_iterator i = file_handlers->begin();
238        i != file_handlers->end(); i++) {
239     if (i->id == handler_id)
240       return &*i;
241   }
242   return NULL;
243 }
244
245 const FileHandlerInfo* FirstFileHandlerForFile(
246     const Extension& app,
247     const std::string& mime_type,
248     const base::FilePath& path) {
249   const FileHandlerList* file_handlers = FileHandlers::GetFileHandlers(&app);
250   if (!file_handlers)
251     return NULL;
252
253   for (FileHandlerList::const_iterator i = file_handlers->begin();
254        i != file_handlers->end(); i++) {
255     if (FileHandlerCanHandleFile(*i, mime_type, path))
256       return &*i;
257   }
258   return NULL;
259 }
260
261 std::vector<const FileHandlerInfo*> FindFileHandlersForFiles(
262     const Extension& app, const PathAndMimeTypeSet& files) {
263   std::vector<const FileHandlerInfo*> handlers;
264   if (files.empty())
265     return handlers;
266
267   // Look for file handlers which can handle all the MIME types specified.
268   const FileHandlerList* file_handlers = FileHandlers::GetFileHandlers(&app);
269   if (!file_handlers)
270     return handlers;
271
272   for (FileHandlerList::const_iterator data = file_handlers->begin();
273        data != file_handlers->end(); ++data) {
274     bool handles_all_types = true;
275     for (PathAndMimeTypeSet::const_iterator it = files.begin();
276          it != files.end(); ++it) {
277       if (!FileHandlerCanHandleFile(*data, it->second, it->first)) {
278         handles_all_types = false;
279         break;
280       }
281     }
282     if (handles_all_types)
283       handlers.push_back(&*data);
284   }
285   return handlers;
286 }
287
288 bool FileHandlerCanHandleFile(
289     const FileHandlerInfo& handler,
290     const std::string& mime_type,
291     const base::FilePath& path) {
292   return FileHandlerCanHandleFileWithMimeType(handler, mime_type) ||
293       FileHandlerCanHandleFileWithExtension(handler, path);
294 }
295
296 GrantedFileEntry CreateFileEntry(
297     Profile* profile,
298     const Extension* extension,
299     int renderer_id,
300     const base::FilePath& path,
301     bool is_directory) {
302   GrantedFileEntry result;
303   fileapi::IsolatedContext* isolated_context =
304       fileapi::IsolatedContext::GetInstance();
305   DCHECK(isolated_context);
306
307   result.filesystem_id = isolated_context->RegisterFileSystemForPath(
308       fileapi::kFileSystemTypeNativeForPlatformApp, path,
309       &result.registered_name);
310
311   content::ChildProcessSecurityPolicy* policy =
312       content::ChildProcessSecurityPolicy::GetInstance();
313   policy->GrantReadFileSystem(renderer_id, result.filesystem_id);
314   if (HasFileSystemWritePermission(extension)) {
315     policy->GrantWriteFileSystem(renderer_id, result.filesystem_id);
316     policy->GrantDeleteFromFileSystem(renderer_id, result.filesystem_id);
317     if (is_directory)
318       policy->GrantCreateFileForFileSystem(renderer_id, result.filesystem_id);
319   }
320
321   result.id = result.filesystem_id + ":" + result.registered_name;
322   return result;
323 }
324
325 void CheckWritableFiles(
326     const std::vector<base::FilePath>& paths,
327     Profile* profile,
328     bool is_directory,
329     const base::Closure& on_success,
330     const base::Callback<void(const base::FilePath&)>& on_failure) {
331   scoped_refptr<WritableFileChecker> checker(new WritableFileChecker(
332       paths, profile, is_directory, on_success, on_failure));
333   checker->Check();
334 }
335
336 GrantedFileEntry::GrantedFileEntry() {}
337
338 bool HasFileSystemWritePermission(const Extension* extension) {
339   return extension->HasAPIPermission(APIPermission::kFileSystemWrite);
340 }
341
342 }  // namespace app_file_handler_util
343
344 }  // namespace extensions