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 "base/files/file.h"
      8 #include "base/files/file_path.h"
      9 #include "base/files/file_util.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 "storage/browser/fileapi/isolated_context.h"
     18 #include "storage/common/fileapi/file_system_mount_option.h"
     19 #include "storage/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   storage::IsolatedContext* isolated_context =
    296       storage::IsolatedContext::GetInstance();
    297   DCHECK(isolated_context);
    298 
    299   result.filesystem_id = isolated_context->RegisterFileSystemForPath(
    300       storage::kFileSystemTypeNativeForPlatformApp,
    301       std::string(),
    302       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 (!storage::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   storage::IsolatedContext* context = storage::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   storage::FileSystemType type;
    370   storage::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 != storage::kFileSystemTypeNativeForPlatformApp &&
    383       type != storage::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