12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved. 22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file. 42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/extensions/api/file_handlers/app_file_handler_util.h" 62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)#include "base/files/file.h" 8424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)#include "base/files/file_path.h" 91320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/files/file_util.h" 10424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)#include "content/public/browser/browser_thread.h" 112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/browser/child_process_security_policy.h" 12a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "content/public/browser/render_process_host.h" 135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/browser/extension_prefs.h" 14116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "extensions/browser/granted_file_entry.h" 1546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "extensions/common/permissions/permissions_data.h" 162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "net/base/mime_util.h" 171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "storage/browser/fileapi/isolated_context.h" 181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "storage/common/fileapi/file_system_mount_option.h" 191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "storage/common/fileapi/file_system_types.h" 202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 21424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)#if defined(OS_CHROMEOS) 22cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "chrome/browser/chromeos/file_manager/filesystem_api_util.h" 23424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)#endif 24424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace extensions { 262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace app_file_handler_util { 282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 29a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)const char kInvalidParameters[] = "Invalid parameters"; 30a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)const char kSecurityError[] = "Security error"; 31a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 32c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)namespace { 33c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 34c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)bool FileHandlerCanHandleFileWithExtension( 35c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) const FileHandlerInfo& handler, 36c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) const base::FilePath& path) { 37c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) for (std::set<std::string>::const_iterator extension = 38c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) handler.extensions.begin(); 39c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) extension != handler.extensions.end(); ++extension) { 40c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (*extension == "*") 41c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) return true; 42c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 43c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (path.MatchesExtension( 44c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) base::FilePath::kExtensionSeparator + 45c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) base::FilePath::FromUTF8Unsafe(*extension).value())) { 46c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) return true; 47c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 48c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 49c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // Also accept files with no extension for handlers that support an 50c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // empty extension, i.e. both "foo" and "foo." match. 51c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (extension->empty() && 52c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) path.MatchesExtension(base::FilePath::StringType())) { 53c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) return true; 54c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 55c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 56c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) return false; 57c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)} 58c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 59b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)bool FileHandlerCanHandleFileWithMimeType( 60b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) const FileHandlerInfo& handler, 61b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) const std::string& mime_type) { 62b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) for (std::set<std::string>::const_iterator type = handler.types.begin(); 63b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) type != handler.types.end(); ++type) { 64b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if (net::MatchesMimeType(*type, mime_type)) 65b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return true; 66b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) } 67b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return false; 68b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)} 69b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 70424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)bool DoCheckWritableFile(const base::FilePath& path, bool is_directory) { 71424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) // Don't allow links. 72a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) if (base::PathExists(path) && base::IsLink(path)) 73424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) return false; 74424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 75424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) if (is_directory) 76424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) return base::DirectoryExists(path); 77424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 78424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) // Create the file if it doesn't already exist. 7946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) int creation_flags = base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_READ; 8023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) base::File file(path, creation_flags); 8146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) return file.IsValid(); 82424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)} 83424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 84424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)// Checks whether a list of paths are all OK for writing and calls a provided 85424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)// on_success or on_failure callback when done. A file is OK for writing if it 86424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)// is not a symlink, is not in a blacklisted path and can be opened for writing; 87424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)// files are created if they do not exist. 88424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)class WritableFileChecker 89424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) : public base::RefCountedThreadSafe<WritableFileChecker> { 90424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) public: 91424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) WritableFileChecker( 92424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) const std::vector<base::FilePath>& paths, 93424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) Profile* profile, 94424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) bool is_directory, 95424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) const base::Closure& on_success, 96424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) const base::Callback<void(const base::FilePath&)>& on_failure); 97424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 98424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) void Check(); 99424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 100424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) private: 101424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) friend class base::RefCountedThreadSafe<WritableFileChecker>; 102424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) virtual ~WritableFileChecker(); 103424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 104424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) // Called when a work item is completed. If all work items are done, this 105424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) // calls the success or failure callback. 106424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) void TaskDone(); 107424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 108424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) // Reports an error in completing a work item. This may be called more than 109424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) // once, but only the last message will be retained. 110424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) void Error(const base::FilePath& error_path); 111424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 112424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) void CheckLocalWritableFiles(); 113424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 114424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)#if defined(OS_CHROMEOS) 115cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) void NonNativeLocalPathCheckDone(const base::FilePath& path, bool success); 116424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)#endif 117424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 118424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) const std::vector<base::FilePath> paths_; 119424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) Profile* profile_; 120424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) const bool is_directory_; 121424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) int outstanding_tasks_; 122424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) base::FilePath error_path_; 123424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) base::Closure on_success_; 124424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) base::Callback<void(const base::FilePath&)> on_failure_; 125424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)}; 126424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 127424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)WritableFileChecker::WritableFileChecker( 128424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) const std::vector<base::FilePath>& paths, 129424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) Profile* profile, 130424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) bool is_directory, 131424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) const base::Closure& on_success, 132424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) const base::Callback<void(const base::FilePath&)>& on_failure) 133424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) : paths_(paths), 134424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) profile_(profile), 135424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) is_directory_(is_directory), 136424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) outstanding_tasks_(1), 137424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) on_success_(on_success), 138424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) on_failure_(on_failure) {} 139424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 140424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)void WritableFileChecker::Check() { 141424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)#if defined(OS_CHROMEOS) 142cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (file_manager::util::IsUnderNonNativeLocalPath(profile_, paths_[0])) { 143424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) outstanding_tasks_ = paths_.size(); 144424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) for (std::vector<base::FilePath>::const_iterator it = paths_.begin(); 145424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) it != paths_.end(); 146424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) ++it) { 1475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (is_directory_) { 148cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) file_manager::util::IsNonNativeLocalPathDirectory( 1495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) profile_, 1505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) *it, 151cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) base::Bind(&WritableFileChecker::NonNativeLocalPathCheckDone, 152cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this, *it)); 1535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } else { 15446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) file_manager::util::PrepareNonNativeLocalFileForWritableApp( 1555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) profile_, 1565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) *it, 157cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) base::Bind(&WritableFileChecker::NonNativeLocalPathCheckDone, 158cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this, *it)); 1595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 160424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) } 161424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) return; 162424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) } 163424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)#endif 164424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) content::BrowserThread::PostTask( 165424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) content::BrowserThread::FILE, 166424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) FROM_HERE, 167424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) base::Bind(&WritableFileChecker::CheckLocalWritableFiles, this)); 168424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)} 169424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 170424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)WritableFileChecker::~WritableFileChecker() {} 171424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 172424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)void WritableFileChecker::TaskDone() { 173effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 174424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) if (--outstanding_tasks_ == 0) { 175424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) if (error_path_.empty()) 176424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) on_success_.Run(); 177424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) else 178424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) on_failure_.Run(error_path_); 179424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) } 180424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)} 181424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 182424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)// Reports an error in completing a work item. This may be called more than 183424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)// once, but only the last message will be retained. 184424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)void WritableFileChecker::Error(const base::FilePath& error_path) { 185424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) DCHECK(!error_path.empty()); 186424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) error_path_ = error_path; 187424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) TaskDone(); 188424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)} 189424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 190424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)void WritableFileChecker::CheckLocalWritableFiles() { 191effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch DCHECK_CURRENTLY_ON(content::BrowserThread::FILE); 192424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) std::string error; 193424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) for (std::vector<base::FilePath>::const_iterator it = paths_.begin(); 194424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) it != paths_.end(); 195424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) ++it) { 196424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) if (!DoCheckWritableFile(*it, is_directory_)) { 197424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) content::BrowserThread::PostTask( 198424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) content::BrowserThread::UI, 199424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) FROM_HERE, 200424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) base::Bind(&WritableFileChecker::Error, this, *it)); 201424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) return; 202424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) } 203424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) } 204424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) content::BrowserThread::PostTask( 205424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) content::BrowserThread::UI, 206424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) FROM_HERE, 207424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) base::Bind(&WritableFileChecker::TaskDone, this)); 208424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)} 209424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 210424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)#if defined(OS_CHROMEOS) 211cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void WritableFileChecker::NonNativeLocalPathCheckDone( 212cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const base::FilePath& path, 213cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) bool success) { 214cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (success) 215cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) TaskDone(); 216cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) else 217cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) Error(path); 218424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)} 219424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)#endif 220424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 221c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)} // namespace 222c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 2232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const FileHandlerInfo* FileHandlerForId(const Extension& app, 2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const std::string& handler_id) { 225cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const FileHandlersInfo* file_handlers = FileHandlers::GetFileHandlers(&app); 2262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!file_handlers) 2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return NULL; 2282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 229cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for (FileHandlersInfo::const_iterator i = file_handlers->begin(); 2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) i != file_handlers->end(); i++) { 2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (i->id == handler_id) 2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return &*i; 2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 2342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return NULL; 2352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 2362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 237c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const FileHandlerInfo* FirstFileHandlerForFile( 238c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) const Extension& app, 239c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) const std::string& mime_type, 240c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) const base::FilePath& path) { 241cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const FileHandlersInfo* file_handlers = FileHandlers::GetFileHandlers(&app); 2422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!file_handlers) 2432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return NULL; 2442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 245cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for (FileHandlersInfo::const_iterator i = file_handlers->begin(); 2462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) i != file_handlers->end(); i++) { 247c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (FileHandlerCanHandleFile(*i, mime_type, path)) 248c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) return &*i; 2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 2502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return NULL; 2512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 2522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 253b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)std::vector<const FileHandlerInfo*> FindFileHandlersForFiles( 254b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) const Extension& app, const PathAndMimeTypeSet& files) { 2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) std::vector<const FileHandlerInfo*> handlers; 256b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if (files.empty()) 2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return handlers; 2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Look for file handlers which can handle all the MIME types specified. 260cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const FileHandlersInfo* file_handlers = FileHandlers::GetFileHandlers(&app); 2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!file_handlers) 2622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return handlers; 2632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 264cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for (FileHandlersInfo::const_iterator data = file_handlers->begin(); 2652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) data != file_handlers->end(); ++data) { 2662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) bool handles_all_types = true; 267b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) for (PathAndMimeTypeSet::const_iterator it = files.begin(); 268b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) it != files.end(); ++it) { 269b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if (!FileHandlerCanHandleFile(*data, it->second, it->first)) { 2702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) handles_all_types = false; 2712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) break; 2722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 2732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 2742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (handles_all_types) 2752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) handlers.push_back(&*data); 2762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 2772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return handlers; 2782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 280c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)bool FileHandlerCanHandleFile( 281c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) const FileHandlerInfo& handler, 282c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) const std::string& mime_type, 283c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) const base::FilePath& path) { 284c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) return FileHandlerCanHandleFileWithMimeType(handler, mime_type) || 285c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) FileHandlerCanHandleFileWithExtension(handler, path); 286c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)} 287c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 2882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)GrantedFileEntry CreateFileEntry( 2892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) Profile* profile, 290424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) const Extension* extension, 2912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) int renderer_id, 2922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const base::FilePath& path, 293424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) bool is_directory) { 2942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) GrantedFileEntry result; 29503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) storage::IsolatedContext* isolated_context = 29603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) storage::IsolatedContext::GetInstance(); 2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) DCHECK(isolated_context); 2982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) result.filesystem_id = isolated_context->RegisterFileSystemForPath( 30003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) storage::kFileSystemTypeNativeForPlatformApp, 30103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) std::string(), 30203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) path, 3032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) &result.registered_name); 3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 3052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) content::ChildProcessSecurityPolicy* policy = 3062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) content::ChildProcessSecurityPolicy::GetInstance(); 3072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) policy->GrantReadFileSystem(renderer_id, result.filesystem_id); 308424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) if (HasFileSystemWritePermission(extension)) { 3095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (is_directory) { 3105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) policy->GrantCreateReadWriteFileSystem(renderer_id, result.filesystem_id); 3115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } else { 3125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) policy->GrantWriteFileSystem(renderer_id, result.filesystem_id); 3135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) policy->GrantDeleteFromFileSystem(renderer_id, result.filesystem_id); 3145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 315424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) } 3162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 3172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) result.id = result.filesystem_id + ":" + result.registered_name; 3182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return result; 3192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 3202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 32146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)void PrepareFilesForWritableApp( 322424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) const std::vector<base::FilePath>& paths, 323424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) Profile* profile, 324424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) bool is_directory, 325424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) const base::Closure& on_success, 326424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) const base::Callback<void(const base::FilePath&)>& on_failure) { 327424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) scoped_refptr<WritableFileChecker> checker(new WritableFileChecker( 328424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) paths, profile, is_directory, on_success, on_failure)); 329424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) checker->Check(); 330424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)} 331424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 332424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)bool HasFileSystemWritePermission(const Extension* extension) { 33346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) return extension->permissions_data()->HasAPIPermission( 33446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) APIPermission::kFileSystemWrite); 335424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)} 336424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 337a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)bool ValidateFileEntryAndGetPath( 338a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) const std::string& filesystem_name, 339a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) const std::string& filesystem_path, 340a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) const content::RenderViewHost* render_view_host, 341a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) base::FilePath* file_path, 342a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) std::string* error) { 3435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (filesystem_path.empty()) { 3445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) *error = kInvalidParameters; 3455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return false; 3465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 3475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 348a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) std::string filesystem_id; 34903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) if (!storage::CrackIsolatedFileSystemName(filesystem_name, &filesystem_id)) { 350a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *error = kInvalidParameters; 351a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) return false; 352a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) } 353a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 354a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // Only return the display path if the process has read access to the 355a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // filesystem. 356a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) content::ChildProcessSecurityPolicy* policy = 357a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) content::ChildProcessSecurityPolicy::GetInstance(); 358a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) if (!policy->CanReadFileSystem(render_view_host->GetProcess()->GetID(), 359a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) filesystem_id)) { 360a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *error = kSecurityError; 361a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) return false; 362a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) } 363a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 36403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) storage::IsolatedContext* context = storage::IsolatedContext::GetInstance(); 365a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) base::FilePath relative_path = 366a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) base::FilePath::FromUTF8Unsafe(filesystem_path); 367a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) base::FilePath virtual_path = context->CreateVirtualRootPath(filesystem_id) 368a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) .Append(relative_path); 36903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) storage::FileSystemType type; 37003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) storage::FileSystemMountOption mount_option; 3716d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles) std::string cracked_id; 372a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) if (!context->CrackVirtualPath( 3736d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles) virtual_path, &filesystem_id, &type, &cracked_id, file_path, 3746d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles) &mount_option)) { 375a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *error = kInvalidParameters; 376a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) return false; 377a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) } 378a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 379a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // The file system API is only intended to operate on file entries that 380a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // correspond to a native file, selected by the user so only allow file 381a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // systems returned by the file system API or from a drag and drop operation. 38203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) if (type != storage::kFileSystemTypeNativeForPlatformApp && 38303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) type != storage::kFileSystemTypeDragged) { 384a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *error = kInvalidParameters; 385a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) return false; 386a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) } 387a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 388a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) return true; 389a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)} 390a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 3912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} // namespace app_file_handler_util 3922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 3932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} // namespace extensions 394