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 "net/base/mime_util.h"
15 #include "webkit/browser/fileapi/isolated_context.h"
16 #include "webkit/common/fileapi/file_system_mount_option.h"
17 #include "webkit/common/fileapi/file_system_types.h"
19 #if defined(OS_CHROMEOS)
20 #include "chrome/browser/chromeos/drive/file_system_util.h"
23 namespace extensions {
25 namespace app_file_handler_util {
27 const char kInvalidParameters[] = "Invalid parameters";
28 const char kSecurityError[] = "Security error";
32 bool FileHandlerCanHandleFileWithExtension(
33 const FileHandlerInfo& handler,
34 const base::FilePath& path) {
35 for (std::set<std::string>::const_iterator extension =
36 handler.extensions.begin();
37 extension != handler.extensions.end(); ++extension) {
38 if (*extension == "*")
41 if (path.MatchesExtension(
42 base::FilePath::kExtensionSeparator +
43 base::FilePath::FromUTF8Unsafe(*extension).value())) {
47 // Also accept files with no extension for handlers that support an
48 // empty extension, i.e. both "foo" and "foo." match.
49 if (extension->empty() &&
50 path.MatchesExtension(base::FilePath::StringType())) {
57 bool FileHandlerCanHandleFileWithMimeType(
58 const FileHandlerInfo& handler,
59 const std::string& mime_type) {
60 for (std::set<std::string>::const_iterator type = handler.types.begin();
61 type != handler.types.end(); ++type) {
62 if (net::MatchesMimeType(*type, mime_type))
68 bool DoCheckWritableFile(const base::FilePath& path, bool is_directory) {
70 if (base::PathExists(path) && base::IsLink(path))
74 return base::DirectoryExists(path);
76 // Create the file if it doesn't already exist.
77 int creation_flags = base::File::FLAG_CREATE | base::File::FLAG_READ |
78 base::File::FLAG_WRITE;
79 base::File file(path, creation_flags);
82 return file.error_details() == base::File::FILE_ERROR_EXISTS;
85 // Checks whether a list of paths are all OK for writing and calls a provided
86 // on_success or on_failure callback when done. A file is OK for writing if it
87 // is not a symlink, is not in a blacklisted path and can be opened for writing;
88 // files are created if they do not exist.
89 class WritableFileChecker
90 : public base::RefCountedThreadSafe<WritableFileChecker> {
93 const std::vector<base::FilePath>& paths,
96 const base::Closure& on_success,
97 const base::Callback<void(const base::FilePath&)>& on_failure);
102 friend class base::RefCountedThreadSafe<WritableFileChecker>;
103 virtual ~WritableFileChecker();
105 // Called when a work item is completed. If all work items are done, this
106 // calls the success or failure callback.
109 // Reports an error in completing a work item. This may be called more than
110 // once, but only the last message will be retained.
111 void Error(const base::FilePath& error_path);
113 void CheckLocalWritableFiles();
115 #if defined(OS_CHROMEOS)
116 void CheckRemoteWritableFile(const base::FilePath& remote_path,
117 drive::FileError error,
118 const base::FilePath& local_path);
119 void RemoteCheckDone(const base::FilePath& remote_path,
120 drive::FileError error);
123 const std::vector<base::FilePath> paths_;
125 const bool is_directory_;
126 int outstanding_tasks_;
127 base::FilePath error_path_;
128 base::Closure on_success_;
129 base::Callback<void(const base::FilePath&)> on_failure_;
132 WritableFileChecker::WritableFileChecker(
133 const std::vector<base::FilePath>& paths,
136 const base::Closure& on_success,
137 const base::Callback<void(const base::FilePath&)>& on_failure)
140 is_directory_(is_directory),
141 outstanding_tasks_(1),
142 on_success_(on_success),
143 on_failure_(on_failure) {}
145 void WritableFileChecker::Check() {
146 #if defined(OS_CHROMEOS)
147 if (drive::util::IsUnderDriveMountPoint(paths_[0])) {
148 outstanding_tasks_ = paths_.size();
149 for (std::vector<base::FilePath>::const_iterator it = paths_.begin();
152 DCHECK(drive::util::IsUnderDriveMountPoint(*it));
154 drive::util::CheckDirectoryExists(
157 base::Bind(&WritableFileChecker::RemoteCheckDone, this, *it));
159 drive::util::PrepareWritableFileAndRun(
162 base::Bind(&WritableFileChecker::CheckRemoteWritableFile, this,
169 content::BrowserThread::PostTask(
170 content::BrowserThread::FILE,
172 base::Bind(&WritableFileChecker::CheckLocalWritableFiles, this));
175 WritableFileChecker::~WritableFileChecker() {}
177 void WritableFileChecker::TaskDone() {
178 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
179 if (--outstanding_tasks_ == 0) {
180 if (error_path_.empty())
183 on_failure_.Run(error_path_);
187 // Reports an error in completing a work item. This may be called more than
188 // once, but only the last message will be retained.
189 void WritableFileChecker::Error(const base::FilePath& error_path) {
190 DCHECK(!error_path.empty());
191 error_path_ = error_path;
195 void WritableFileChecker::CheckLocalWritableFiles() {
196 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
198 for (std::vector<base::FilePath>::const_iterator it = paths_.begin();
201 if (!DoCheckWritableFile(*it, is_directory_)) {
202 content::BrowserThread::PostTask(
203 content::BrowserThread::UI,
205 base::Bind(&WritableFileChecker::Error, this, *it));
209 content::BrowserThread::PostTask(
210 content::BrowserThread::UI,
212 base::Bind(&WritableFileChecker::TaskDone, this));
215 #if defined(OS_CHROMEOS)
216 void WritableFileChecker::CheckRemoteWritableFile(
217 const base::FilePath& remote_path,
218 drive::FileError error,
219 const base::FilePath& /* local_path */) {
220 RemoteCheckDone(remote_path, error);
223 void WritableFileChecker::RemoteCheckDone(
224 const base::FilePath& remote_path,
225 drive::FileError error) {
226 if (error == drive::FILE_ERROR_OK) {
227 content::BrowserThread::PostTask(
228 content::BrowserThread::UI,
230 base::Bind(&WritableFileChecker::TaskDone, this));
232 content::BrowserThread::PostTask(
233 content::BrowserThread::UI,
235 base::Bind(&WritableFileChecker::Error, this, remote_path));
242 typedef std::vector<FileHandlerInfo> FileHandlerList;
244 const FileHandlerInfo* FileHandlerForId(const Extension& app,
245 const std::string& handler_id) {
246 const FileHandlerList* file_handlers = FileHandlers::GetFileHandlers(&app);
250 for (FileHandlerList::const_iterator i = file_handlers->begin();
251 i != file_handlers->end(); i++) {
252 if (i->id == handler_id)
258 const FileHandlerInfo* FirstFileHandlerForFile(
259 const Extension& app,
260 const std::string& mime_type,
261 const base::FilePath& path) {
262 const FileHandlerList* file_handlers = FileHandlers::GetFileHandlers(&app);
266 for (FileHandlerList::const_iterator i = file_handlers->begin();
267 i != file_handlers->end(); i++) {
268 if (FileHandlerCanHandleFile(*i, mime_type, path))
274 std::vector<const FileHandlerInfo*> FindFileHandlersForFiles(
275 const Extension& app, const PathAndMimeTypeSet& files) {
276 std::vector<const FileHandlerInfo*> handlers;
280 // Look for file handlers which can handle all the MIME types specified.
281 const FileHandlerList* file_handlers = FileHandlers::GetFileHandlers(&app);
285 for (FileHandlerList::const_iterator data = file_handlers->begin();
286 data != file_handlers->end(); ++data) {
287 bool handles_all_types = true;
288 for (PathAndMimeTypeSet::const_iterator it = files.begin();
289 it != files.end(); ++it) {
290 if (!FileHandlerCanHandleFile(*data, it->second, it->first)) {
291 handles_all_types = false;
295 if (handles_all_types)
296 handlers.push_back(&*data);
301 bool FileHandlerCanHandleFile(
302 const FileHandlerInfo& handler,
303 const std::string& mime_type,
304 const base::FilePath& path) {
305 return FileHandlerCanHandleFileWithMimeType(handler, mime_type) ||
306 FileHandlerCanHandleFileWithExtension(handler, path);
309 GrantedFileEntry CreateFileEntry(
311 const Extension* extension,
313 const base::FilePath& path,
315 GrantedFileEntry result;
316 fileapi::IsolatedContext* isolated_context =
317 fileapi::IsolatedContext::GetInstance();
318 DCHECK(isolated_context);
320 result.filesystem_id = isolated_context->RegisterFileSystemForPath(
321 fileapi::kFileSystemTypeNativeForPlatformApp, path,
322 &result.registered_name);
324 content::ChildProcessSecurityPolicy* policy =
325 content::ChildProcessSecurityPolicy::GetInstance();
326 policy->GrantReadFileSystem(renderer_id, result.filesystem_id);
327 if (HasFileSystemWritePermission(extension)) {
329 policy->GrantCreateReadWriteFileSystem(renderer_id, result.filesystem_id);
331 policy->GrantWriteFileSystem(renderer_id, result.filesystem_id);
332 policy->GrantDeleteFromFileSystem(renderer_id, result.filesystem_id);
336 result.id = result.filesystem_id + ":" + result.registered_name;
340 void CheckWritableFiles(
341 const std::vector<base::FilePath>& paths,
344 const base::Closure& on_success,
345 const base::Callback<void(const base::FilePath&)>& on_failure) {
346 scoped_refptr<WritableFileChecker> checker(new WritableFileChecker(
347 paths, profile, is_directory, on_success, on_failure));
351 GrantedFileEntry::GrantedFileEntry() {}
353 bool HasFileSystemWritePermission(const Extension* extension) {
354 return extension->HasAPIPermission(APIPermission::kFileSystemWrite);
357 bool ValidateFileEntryAndGetPath(
358 const std::string& filesystem_name,
359 const std::string& filesystem_path,
360 const content::RenderViewHost* render_view_host,
361 base::FilePath* file_path,
362 std::string* error) {
363 if (filesystem_path.empty()) {
364 *error = kInvalidParameters;
368 std::string filesystem_id;
369 if (!fileapi::CrackIsolatedFileSystemName(filesystem_name, &filesystem_id)) {
370 *error = kInvalidParameters;
374 // Only return the display path if the process has read access to the
376 content::ChildProcessSecurityPolicy* policy =
377 content::ChildProcessSecurityPolicy::GetInstance();
378 if (!policy->CanReadFileSystem(render_view_host->GetProcess()->GetID(),
380 *error = kSecurityError;
384 fileapi::IsolatedContext* context = fileapi::IsolatedContext::GetInstance();
385 base::FilePath relative_path =
386 base::FilePath::FromUTF8Unsafe(filesystem_path);
387 base::FilePath virtual_path = context->CreateVirtualRootPath(filesystem_id)
388 .Append(relative_path);
389 fileapi::FileSystemType type;
390 fileapi::FileSystemMountOption mount_option;
391 if (!context->CrackVirtualPath(
392 virtual_path, &filesystem_id, &type, file_path, &mount_option)) {
393 *error = kInvalidParameters;
397 // The file system API is only intended to operate on file entries that
398 // correspond to a native file, selected by the user so only allow file
399 // systems returned by the file system API or from a drag and drop operation.
400 if (type != fileapi::kFileSystemTypeNativeForPlatformApp &&
401 type != fileapi::kFileSystemTypeDragged) {
402 *error = kInvalidParameters;
409 } // namespace app_file_handler_util
411 } // namespace extensions