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