Upstream version 7.36.149.0
[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 "apps/browser/file_handler_util.h"
8 #include "base/file_util.h"
9 #include "base/files/file.h"
10 #include "base/files/file_path.h"
11 #include "content/public/browser/browser_thread.h"
12 #include "content/public/browser/child_process_security_policy.h"
13 #include "content/public/browser/render_process_host.h"
14 #include "extensions/browser/extension_prefs.h"
15 #include "net/base/mime_util.h"
16 #include "webkit/browser/fileapi/isolated_context.h"
17 #include "webkit/common/fileapi/file_system_mount_option.h"
18 #include "webkit/common/fileapi/file_system_types.h"
19
20 #if defined(OS_CHROMEOS)
21 #include "chrome/browser/chromeos/drive/file_system_util.h"
22 #endif
23
24 using apps::file_handler_util::GrantedFileEntry;
25
26 namespace extensions {
27
28 namespace app_file_handler_util {
29
30 const char kInvalidParameters[] = "Invalid parameters";
31 const char kSecurityError[] = "Security error";
32
33 namespace {
34
35 bool FileHandlerCanHandleFileWithExtension(
36     const FileHandlerInfo& handler,
37     const base::FilePath& path) {
38   for (std::set<std::string>::const_iterator extension =
39        handler.extensions.begin();
40        extension != handler.extensions.end(); ++extension) {
41     if (*extension == "*")
42       return true;
43
44     if (path.MatchesExtension(
45         base::FilePath::kExtensionSeparator +
46         base::FilePath::FromUTF8Unsafe(*extension).value())) {
47       return true;
48     }
49
50     // Also accept files with no extension for handlers that support an
51     // empty extension, i.e. both "foo" and "foo." match.
52     if (extension->empty() &&
53         path.MatchesExtension(base::FilePath::StringType())) {
54       return true;
55     }
56   }
57   return false;
58 }
59
60 bool FileHandlerCanHandleFileWithMimeType(
61     const FileHandlerInfo& handler,
62     const std::string& mime_type) {
63   for (std::set<std::string>::const_iterator type = handler.types.begin();
64        type != handler.types.end(); ++type) {
65     if (net::MatchesMimeType(*type, mime_type))
66       return true;
67   }
68   return false;
69 }
70
71 bool DoCheckWritableFile(const base::FilePath& path, bool is_directory) {
72   // Don't allow links.
73   if (base::PathExists(path) && base::IsLink(path))
74     return false;
75
76   if (is_directory)
77     return base::DirectoryExists(path);
78
79   // Create the file if it doesn't already exist.
80   int creation_flags = base::File::FLAG_CREATE | base::File::FLAG_READ |
81                        base::File::FLAG_WRITE;
82   base::File file(path, creation_flags);
83   if (file.IsValid())
84     return true;
85   return file.error_details() == base::File::FILE_ERROR_EXISTS;
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   void RemoteCheckDone(const base::FilePath& remote_path,
123                        drive::FileError error);
124 #endif
125
126   const std::vector<base::FilePath> paths_;
127   Profile* profile_;
128   const bool is_directory_;
129   int outstanding_tasks_;
130   base::FilePath error_path_;
131   base::Closure on_success_;
132   base::Callback<void(const base::FilePath&)> on_failure_;
133 };
134
135 WritableFileChecker::WritableFileChecker(
136     const std::vector<base::FilePath>& paths,
137     Profile* profile,
138     bool is_directory,
139     const base::Closure& on_success,
140     const base::Callback<void(const base::FilePath&)>& on_failure)
141     : paths_(paths),
142       profile_(profile),
143       is_directory_(is_directory),
144       outstanding_tasks_(1),
145       on_success_(on_success),
146       on_failure_(on_failure) {}
147
148 void WritableFileChecker::Check() {
149 #if defined(OS_CHROMEOS)
150   if (drive::util::IsUnderDriveMountPoint(paths_[0])) {
151     outstanding_tasks_ = paths_.size();
152     for (std::vector<base::FilePath>::const_iterator it = paths_.begin();
153          it != paths_.end();
154          ++it) {
155       DCHECK(drive::util::IsUnderDriveMountPoint(*it));
156       if (is_directory_) {
157         drive::util::CheckDirectoryExists(
158             profile_,
159             *it,
160             base::Bind(&WritableFileChecker::RemoteCheckDone, this, *it));
161       } else {
162         drive::util::PrepareWritableFileAndRun(
163             profile_,
164             *it,
165             base::Bind(&WritableFileChecker::CheckRemoteWritableFile, this,
166                        *it));
167       }
168     }
169     return;
170   }
171 #endif
172   content::BrowserThread::PostTask(
173       content::BrowserThread::FILE,
174       FROM_HERE,
175       base::Bind(&WritableFileChecker::CheckLocalWritableFiles, this));
176 }
177
178 WritableFileChecker::~WritableFileChecker() {}
179
180 void WritableFileChecker::TaskDone() {
181   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
182   if (--outstanding_tasks_ == 0) {
183     if (error_path_.empty())
184       on_success_.Run();
185     else
186       on_failure_.Run(error_path_);
187   }
188 }
189
190 // Reports an error in completing a work item. This may be called more than
191 // once, but only the last message will be retained.
192 void WritableFileChecker::Error(const base::FilePath& error_path) {
193   DCHECK(!error_path.empty());
194   error_path_ = error_path;
195   TaskDone();
196 }
197
198 void WritableFileChecker::CheckLocalWritableFiles() {
199   DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
200   std::string error;
201   for (std::vector<base::FilePath>::const_iterator it = paths_.begin();
202        it != paths_.end();
203        ++it) {
204     if (!DoCheckWritableFile(*it, is_directory_)) {
205       content::BrowserThread::PostTask(
206           content::BrowserThread::UI,
207           FROM_HERE,
208           base::Bind(&WritableFileChecker::Error, this, *it));
209       return;
210     }
211   }
212   content::BrowserThread::PostTask(
213       content::BrowserThread::UI,
214       FROM_HERE,
215       base::Bind(&WritableFileChecker::TaskDone, this));
216 }
217
218 #if defined(OS_CHROMEOS)
219 void WritableFileChecker::CheckRemoteWritableFile(
220     const base::FilePath& remote_path,
221     drive::FileError error,
222     const base::FilePath& /* local_path */) {
223   RemoteCheckDone(remote_path, error);
224 }
225
226 void WritableFileChecker::RemoteCheckDone(
227     const base::FilePath& remote_path,
228     drive::FileError error) {
229   if (error == drive::FILE_ERROR_OK) {
230     content::BrowserThread::PostTask(
231         content::BrowserThread::UI,
232         FROM_HERE,
233         base::Bind(&WritableFileChecker::TaskDone, this));
234   } else {
235     content::BrowserThread::PostTask(
236         content::BrowserThread::UI,
237         FROM_HERE,
238         base::Bind(&WritableFileChecker::Error, this, remote_path));
239   }
240 }
241 #endif
242
243 }  // namespace
244
245 typedef std::vector<FileHandlerInfo> FileHandlerList;
246
247 const FileHandlerInfo* FileHandlerForId(const Extension& app,
248                                         const std::string& handler_id) {
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 (i->id == handler_id)
256       return &*i;
257   }
258   return NULL;
259 }
260
261 const FileHandlerInfo* FirstFileHandlerForFile(
262     const Extension& app,
263     const std::string& mime_type,
264     const base::FilePath& path) {
265   const FileHandlerList* file_handlers = FileHandlers::GetFileHandlers(&app);
266   if (!file_handlers)
267     return NULL;
268
269   for (FileHandlerList::const_iterator i = file_handlers->begin();
270        i != file_handlers->end(); i++) {
271     if (FileHandlerCanHandleFile(*i, mime_type, path))
272       return &*i;
273   }
274   return NULL;
275 }
276
277 std::vector<const FileHandlerInfo*> FindFileHandlersForFiles(
278     const Extension& app, const PathAndMimeTypeSet& files) {
279   std::vector<const FileHandlerInfo*> handlers;
280   if (files.empty())
281     return handlers;
282
283   // Look for file handlers which can handle all the MIME types specified.
284   const FileHandlerList* file_handlers = FileHandlers::GetFileHandlers(&app);
285   if (!file_handlers)
286     return handlers;
287
288   for (FileHandlerList::const_iterator data = file_handlers->begin();
289        data != file_handlers->end(); ++data) {
290     bool handles_all_types = true;
291     for (PathAndMimeTypeSet::const_iterator it = files.begin();
292          it != files.end(); ++it) {
293       if (!FileHandlerCanHandleFile(*data, it->second, it->first)) {
294         handles_all_types = false;
295         break;
296       }
297     }
298     if (handles_all_types)
299       handlers.push_back(&*data);
300   }
301   return handlers;
302 }
303
304 bool FileHandlerCanHandleFile(
305     const FileHandlerInfo& handler,
306     const std::string& mime_type,
307     const base::FilePath& path) {
308   return FileHandlerCanHandleFileWithMimeType(handler, mime_type) ||
309       FileHandlerCanHandleFileWithExtension(handler, path);
310 }
311
312 GrantedFileEntry CreateFileEntry(
313     Profile* profile,
314     const Extension* extension,
315     int renderer_id,
316     const base::FilePath& path,
317     bool is_directory) {
318   GrantedFileEntry result;
319   fileapi::IsolatedContext* isolated_context =
320       fileapi::IsolatedContext::GetInstance();
321   DCHECK(isolated_context);
322
323   result.filesystem_id = isolated_context->RegisterFileSystemForPath(
324       fileapi::kFileSystemTypeNativeForPlatformApp, path,
325       &result.registered_name);
326
327   content::ChildProcessSecurityPolicy* policy =
328       content::ChildProcessSecurityPolicy::GetInstance();
329   policy->GrantReadFileSystem(renderer_id, result.filesystem_id);
330   if (HasFileSystemWritePermission(extension)) {
331     if (is_directory) {
332       policy->GrantCreateReadWriteFileSystem(renderer_id, result.filesystem_id);
333     } else {
334       policy->GrantWriteFileSystem(renderer_id, result.filesystem_id);
335       policy->GrantDeleteFromFileSystem(renderer_id, result.filesystem_id);
336     }
337   }
338
339   result.id = result.filesystem_id + ":" + result.registered_name;
340   return result;
341 }
342
343 void CheckWritableFiles(
344     const std::vector<base::FilePath>& paths,
345     Profile* profile,
346     bool is_directory,
347     const base::Closure& on_success,
348     const base::Callback<void(const base::FilePath&)>& on_failure) {
349   scoped_refptr<WritableFileChecker> checker(new WritableFileChecker(
350       paths, profile, is_directory, on_success, on_failure));
351   checker->Check();
352 }
353
354 bool HasFileSystemWritePermission(const Extension* extension) {
355   return extension->HasAPIPermission(APIPermission::kFileSystemWrite);
356 }
357
358 bool ValidateFileEntryAndGetPath(
359     const std::string& filesystem_name,
360     const std::string& filesystem_path,
361     const content::RenderViewHost* render_view_host,
362     base::FilePath* file_path,
363     std::string* error) {
364   if (filesystem_path.empty()) {
365     *error = kInvalidParameters;
366     return false;
367   }
368
369   std::string filesystem_id;
370   if (!fileapi::CrackIsolatedFileSystemName(filesystem_name, &filesystem_id)) {
371     *error = kInvalidParameters;
372     return false;
373   }
374
375   // Only return the display path if the process has read access to the
376   // filesystem.
377   content::ChildProcessSecurityPolicy* policy =
378       content::ChildProcessSecurityPolicy::GetInstance();
379   if (!policy->CanReadFileSystem(render_view_host->GetProcess()->GetID(),
380                                  filesystem_id)) {
381     *error = kSecurityError;
382     return false;
383   }
384
385   fileapi::IsolatedContext* context = fileapi::IsolatedContext::GetInstance();
386   base::FilePath relative_path =
387       base::FilePath::FromUTF8Unsafe(filesystem_path);
388   base::FilePath virtual_path = context->CreateVirtualRootPath(filesystem_id)
389       .Append(relative_path);
390   fileapi::FileSystemType type;
391   fileapi::FileSystemMountOption mount_option;
392   if (!context->CrackVirtualPath(
393           virtual_path, &filesystem_id, &type, file_path, &mount_option)) {
394     *error = kInvalidParameters;
395     return false;
396   }
397
398   // The file system API is only intended to operate on file entries that
399   // correspond to a native file, selected by the user so only allow file
400   // systems returned by the file system API or from a drag and drop operation.
401   if (type != fileapi::kFileSystemTypeNativeForPlatformApp &&
402       type != fileapi::kFileSystemTypeDragged) {
403     *error = kInvalidParameters;
404     return false;
405   }
406
407   return true;
408 }
409
410 }  // namespace app_file_handler_util
411
412 }  // namespace extensions