Home | History | Annotate | Download | only in file_handlers
      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 
     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 
     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 
     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 
    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 
    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 
    172 WritableFileChecker::~WritableFileChecker() {}
    173 
    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.
    186 void WritableFileChecker::Error(const base::FilePath& error_path) {
    187   DCHECK(!error_path.empty());
    188   error_path_ = error_path;
    189   TaskDone();
    190 }
    191 
    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)
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    332 bool HasFileSystemWritePermission(const Extension* extension) {
    333   return extension->permissions_data()->HasAPIPermission(
    334       APIPermission::kFileSystemWrite);
    335 }
    336 
    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