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