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