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