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.
5 #include "chrome/browser/extensions/api/file_handlers/app_file_handler_util.h"
7 #include "base/file_util.h"
8 #include "base/files/file.h"
9 #include "base/files/file_path.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 "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"
21 #if defined(OS_CHROMEOS)
22 #include "chrome/browser/chromeos/file_manager/filesystem_api_util.h"
25 namespace extensions {
27 namespace app_file_handler_util {
29 const char kInvalidParameters[] = "Invalid parameters";
30 const char kSecurityError[] = "Security error";
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 == "*")
43 if (path.MatchesExtension(
44 base::FilePath::kExtensionSeparator +
45 base::FilePath::FromUTF8Unsafe(*extension).value())) {
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())) {
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))
70 bool DoCheckWritableFile(const base::FilePath& path, bool is_directory) {
72 if (base::PathExists(path) && base::IsLink(path))
76 return base::DirectoryExists(path);
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();
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> {
92 const std::vector<base::FilePath>& paths,
95 const base::Closure& on_success,
96 const base::Callback<void(const base::FilePath&)>& on_failure);
101 friend class base::RefCountedThreadSafe<WritableFileChecker>;
102 virtual ~WritableFileChecker();
104 // Called when a work item is completed. If all work items are done, this
105 // calls the success or failure callback.
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);
112 void CheckLocalWritableFiles();
114 #if defined(OS_CHROMEOS)
115 void NonNativeLocalPathCheckDone(const base::FilePath& path, bool success);
118 const std::vector<base::FilePath> paths_;
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_;
127 WritableFileChecker::WritableFileChecker(
128 const std::vector<base::FilePath>& paths,
131 const base::Closure& on_success,
132 const base::Callback<void(const base::FilePath&)>& on_failure)
135 is_directory_(is_directory),
136 outstanding_tasks_(1),
137 on_success_(on_success),
138 on_failure_(on_failure) {}
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();
148 file_manager::util::IsNonNativeLocalPathDirectory(
151 base::Bind(&WritableFileChecker::NonNativeLocalPathCheckDone,
154 file_manager::util::PrepareNonNativeLocalFileForWritableApp(
157 base::Bind(&WritableFileChecker::NonNativeLocalPathCheckDone,
164 content::BrowserThread::PostTask(
165 content::BrowserThread::FILE,
167 base::Bind(&WritableFileChecker::CheckLocalWritableFiles, this));
170 WritableFileChecker::~WritableFileChecker() {}
172 void WritableFileChecker::TaskDone() {
173 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
174 if (--outstanding_tasks_ == 0) {
175 if (error_path_.empty())
178 on_failure_.Run(error_path_);
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;
190 void WritableFileChecker::CheckLocalWritableFiles() {
191 DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
193 for (std::vector<base::FilePath>::const_iterator it = paths_.begin();
196 if (!DoCheckWritableFile(*it, is_directory_)) {
197 content::BrowserThread::PostTask(
198 content::BrowserThread::UI,
200 base::Bind(&WritableFileChecker::Error, this, *it));
204 content::BrowserThread::PostTask(
205 content::BrowserThread::UI,
207 base::Bind(&WritableFileChecker::TaskDone, this));
210 #if defined(OS_CHROMEOS)
211 void WritableFileChecker::NonNativeLocalPathCheckDone(
212 const base::FilePath& path,
223 const FileHandlerInfo* FileHandlerForId(const Extension& app,
224 const std::string& handler_id) {
225 const FileHandlersInfo* file_handlers = FileHandlers::GetFileHandlers(&app);
229 for (FileHandlersInfo::const_iterator i = file_handlers->begin();
230 i != file_handlers->end(); i++) {
231 if (i->id == handler_id)
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);
245 for (FileHandlersInfo::const_iterator i = file_handlers->begin();
246 i != file_handlers->end(); i++) {
247 if (FileHandlerCanHandleFile(*i, mime_type, path))
253 std::vector<const FileHandlerInfo*> FindFileHandlersForFiles(
254 const Extension& app, const PathAndMimeTypeSet& files) {
255 std::vector<const FileHandlerInfo*> handlers;
259 // Look for file handlers which can handle all the MIME types specified.
260 const FileHandlersInfo* file_handlers = FileHandlers::GetFileHandlers(&app);
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;
274 if (handles_all_types)
275 handlers.push_back(&*data);
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);
288 GrantedFileEntry CreateFileEntry(
290 const Extension* extension,
292 const base::FilePath& path,
294 GrantedFileEntry result;
295 fileapi::IsolatedContext* isolated_context =
296 fileapi::IsolatedContext::GetInstance();
297 DCHECK(isolated_context);
299 result.filesystem_id = isolated_context->RegisterFileSystemForPath(
300 fileapi::kFileSystemTypeNativeForPlatformApp, std::string(), path,
301 &result.registered_name);
303 content::ChildProcessSecurityPolicy* policy =
304 content::ChildProcessSecurityPolicy::GetInstance();
305 policy->GrantReadFileSystem(renderer_id, result.filesystem_id);
306 if (HasFileSystemWritePermission(extension)) {
308 policy->GrantCreateReadWriteFileSystem(renderer_id, result.filesystem_id);
310 policy->GrantWriteFileSystem(renderer_id, result.filesystem_id);
311 policy->GrantDeleteFromFileSystem(renderer_id, result.filesystem_id);
315 result.id = result.filesystem_id + ":" + result.registered_name;
319 void PrepareFilesForWritableApp(
320 const std::vector<base::FilePath>& paths,
323 const base::Closure& on_success,
324 const base::Callback<void(const base::FilePath&)>& on_failure) {
325 scoped_refptr<WritableFileChecker> checker(new WritableFileChecker(
326 paths, profile, is_directory, on_success, on_failure));
330 bool HasFileSystemWritePermission(const Extension* extension) {
331 return extension->permissions_data()->HasAPIPermission(
332 APIPermission::kFileSystemWrite);
335 bool ValidateFileEntryAndGetPath(
336 const std::string& filesystem_name,
337 const std::string& filesystem_path,
338 const content::RenderViewHost* render_view_host,
339 base::FilePath* file_path,
340 std::string* error) {
341 if (filesystem_path.empty()) {
342 *error = kInvalidParameters;
346 std::string filesystem_id;
347 if (!fileapi::CrackIsolatedFileSystemName(filesystem_name, &filesystem_id)) {
348 *error = kInvalidParameters;
352 // Only return the display path if the process has read access to the
354 content::ChildProcessSecurityPolicy* policy =
355 content::ChildProcessSecurityPolicy::GetInstance();
356 if (!policy->CanReadFileSystem(render_view_host->GetProcess()->GetID(),
358 *error = kSecurityError;
362 fileapi::IsolatedContext* context = fileapi::IsolatedContext::GetInstance();
363 base::FilePath relative_path =
364 base::FilePath::FromUTF8Unsafe(filesystem_path);
365 base::FilePath virtual_path = context->CreateVirtualRootPath(filesystem_id)
366 .Append(relative_path);
367 fileapi::FileSystemType type;
368 fileapi::FileSystemMountOption mount_option;
369 std::string cracked_id;
370 if (!context->CrackVirtualPath(
371 virtual_path, &filesystem_id, &type, &cracked_id, file_path,
373 *error = kInvalidParameters;
377 // The file system API is only intended to operate on file entries that
378 // correspond to a native file, selected by the user so only allow file
379 // systems returned by the file system API or from a drag and drop operation.
380 if (type != fileapi::kFileSystemTypeNativeForPlatformApp &&
381 type != fileapi::kFileSystemTypeDragged) {
382 *error = kInvalidParameters;
389 } // namespace app_file_handler_util
391 } // namespace extensions