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 "apps/browser/file_handler_util.h"
8 #include "base/file_util.h"
9 #include "base/files/file.h"
10 #include "base/files/file_path.h"
11 #include "content/public/browser/browser_thread.h"
12 #include "content/public/browser/child_process_security_policy.h"
13 #include "content/public/browser/render_process_host.h"
14 #include "extensions/browser/extension_prefs.h"
15 #include "net/base/mime_util.h"
16 #include "webkit/browser/fileapi/isolated_context.h"
17 #include "webkit/common/fileapi/file_system_mount_option.h"
18 #include "webkit/common/fileapi/file_system_types.h"
20 #if defined(OS_CHROMEOS)
21 #include "chrome/browser/chromeos/drive/file_system_util.h"
24 using apps::file_handler_util::GrantedFileEntry;
26 namespace extensions {
28 namespace app_file_handler_util {
30 const char kInvalidParameters[] = "Invalid parameters";
31 const char kSecurityError[] = "Security error";
35 bool FileHandlerCanHandleFileWithExtension(
36 const FileHandlerInfo& handler,
37 const base::FilePath& path) {
38 for (std::set<std::string>::const_iterator extension =
39 handler.extensions.begin();
40 extension != handler.extensions.end(); ++extension) {
41 if (*extension == "*")
44 if (path.MatchesExtension(
45 base::FilePath::kExtensionSeparator +
46 base::FilePath::FromUTF8Unsafe(*extension).value())) {
50 // Also accept files with no extension for handlers that support an
51 // empty extension, i.e. both "foo" and "foo." match.
52 if (extension->empty() &&
53 path.MatchesExtension(base::FilePath::StringType())) {
60 bool FileHandlerCanHandleFileWithMimeType(
61 const FileHandlerInfo& handler,
62 const std::string& mime_type) {
63 for (std::set<std::string>::const_iterator type = handler.types.begin();
64 type != handler.types.end(); ++type) {
65 if (net::MatchesMimeType(*type, mime_type))
71 bool DoCheckWritableFile(const base::FilePath& path, bool is_directory) {
73 if (base::PathExists(path) && base::IsLink(path))
77 return base::DirectoryExists(path);
79 // Create the file if it doesn't already exist.
80 int creation_flags = base::File::FLAG_CREATE | base::File::FLAG_READ |
81 base::File::FLAG_WRITE;
82 base::File file(path, creation_flags);
85 return file.error_details() == base::File::FILE_ERROR_EXISTS;
88 // Checks whether a list of paths are all OK for writing and calls a provided
89 // on_success or on_failure callback when done. A file is OK for writing if it
90 // is not a symlink, is not in a blacklisted path and can be opened for writing;
91 // files are created if they do not exist.
92 class WritableFileChecker
93 : public base::RefCountedThreadSafe<WritableFileChecker> {
96 const std::vector<base::FilePath>& paths,
99 const base::Closure& on_success,
100 const base::Callback<void(const base::FilePath&)>& on_failure);
105 friend class base::RefCountedThreadSafe<WritableFileChecker>;
106 virtual ~WritableFileChecker();
108 // Called when a work item is completed. If all work items are done, this
109 // calls the success or failure callback.
112 // Reports an error in completing a work item. This may be called more than
113 // once, but only the last message will be retained.
114 void Error(const base::FilePath& error_path);
116 void CheckLocalWritableFiles();
118 #if defined(OS_CHROMEOS)
119 void CheckRemoteWritableFile(const base::FilePath& remote_path,
120 drive::FileError error,
121 const base::FilePath& local_path);
122 void RemoteCheckDone(const base::FilePath& remote_path,
123 drive::FileError error);
126 const std::vector<base::FilePath> paths_;
128 const bool is_directory_;
129 int outstanding_tasks_;
130 base::FilePath error_path_;
131 base::Closure on_success_;
132 base::Callback<void(const base::FilePath&)> on_failure_;
135 WritableFileChecker::WritableFileChecker(
136 const std::vector<base::FilePath>& paths,
139 const base::Closure& on_success,
140 const base::Callback<void(const base::FilePath&)>& on_failure)
143 is_directory_(is_directory),
144 outstanding_tasks_(1),
145 on_success_(on_success),
146 on_failure_(on_failure) {}
148 void WritableFileChecker::Check() {
149 #if defined(OS_CHROMEOS)
150 if (drive::util::IsUnderDriveMountPoint(paths_[0])) {
151 outstanding_tasks_ = paths_.size();
152 for (std::vector<base::FilePath>::const_iterator it = paths_.begin();
155 DCHECK(drive::util::IsUnderDriveMountPoint(*it));
157 drive::util::CheckDirectoryExists(
160 base::Bind(&WritableFileChecker::RemoteCheckDone, this, *it));
162 drive::util::PrepareWritableFileAndRun(
165 base::Bind(&WritableFileChecker::CheckRemoteWritableFile, this,
172 content::BrowserThread::PostTask(
173 content::BrowserThread::FILE,
175 base::Bind(&WritableFileChecker::CheckLocalWritableFiles, this));
178 WritableFileChecker::~WritableFileChecker() {}
180 void WritableFileChecker::TaskDone() {
181 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
182 if (--outstanding_tasks_ == 0) {
183 if (error_path_.empty())
186 on_failure_.Run(error_path_);
190 // Reports an error in completing a work item. This may be called more than
191 // once, but only the last message will be retained.
192 void WritableFileChecker::Error(const base::FilePath& error_path) {
193 DCHECK(!error_path.empty());
194 error_path_ = error_path;
198 void WritableFileChecker::CheckLocalWritableFiles() {
199 DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
201 for (std::vector<base::FilePath>::const_iterator it = paths_.begin();
204 if (!DoCheckWritableFile(*it, is_directory_)) {
205 content::BrowserThread::PostTask(
206 content::BrowserThread::UI,
208 base::Bind(&WritableFileChecker::Error, this, *it));
212 content::BrowserThread::PostTask(
213 content::BrowserThread::UI,
215 base::Bind(&WritableFileChecker::TaskDone, this));
218 #if defined(OS_CHROMEOS)
219 void WritableFileChecker::CheckRemoteWritableFile(
220 const base::FilePath& remote_path,
221 drive::FileError error,
222 const base::FilePath& /* local_path */) {
223 RemoteCheckDone(remote_path, error);
226 void WritableFileChecker::RemoteCheckDone(
227 const base::FilePath& remote_path,
228 drive::FileError error) {
229 if (error == drive::FILE_ERROR_OK) {
230 content::BrowserThread::PostTask(
231 content::BrowserThread::UI,
233 base::Bind(&WritableFileChecker::TaskDone, this));
235 content::BrowserThread::PostTask(
236 content::BrowserThread::UI,
238 base::Bind(&WritableFileChecker::Error, this, remote_path));
245 typedef std::vector<FileHandlerInfo> FileHandlerList;
247 const FileHandlerInfo* FileHandlerForId(const Extension& app,
248 const std::string& handler_id) {
249 const FileHandlerList* file_handlers = FileHandlers::GetFileHandlers(&app);
253 for (FileHandlerList::const_iterator i = file_handlers->begin();
254 i != file_handlers->end(); i++) {
255 if (i->id == handler_id)
261 const FileHandlerInfo* FirstFileHandlerForFile(
262 const Extension& app,
263 const std::string& mime_type,
264 const base::FilePath& path) {
265 const FileHandlerList* file_handlers = FileHandlers::GetFileHandlers(&app);
269 for (FileHandlerList::const_iterator i = file_handlers->begin();
270 i != file_handlers->end(); i++) {
271 if (FileHandlerCanHandleFile(*i, mime_type, path))
277 std::vector<const FileHandlerInfo*> FindFileHandlersForFiles(
278 const Extension& app, const PathAndMimeTypeSet& files) {
279 std::vector<const FileHandlerInfo*> handlers;
283 // Look for file handlers which can handle all the MIME types specified.
284 const FileHandlerList* file_handlers = FileHandlers::GetFileHandlers(&app);
288 for (FileHandlerList::const_iterator data = file_handlers->begin();
289 data != file_handlers->end(); ++data) {
290 bool handles_all_types = true;
291 for (PathAndMimeTypeSet::const_iterator it = files.begin();
292 it != files.end(); ++it) {
293 if (!FileHandlerCanHandleFile(*data, it->second, it->first)) {
294 handles_all_types = false;
298 if (handles_all_types)
299 handlers.push_back(&*data);
304 bool FileHandlerCanHandleFile(
305 const FileHandlerInfo& handler,
306 const std::string& mime_type,
307 const base::FilePath& path) {
308 return FileHandlerCanHandleFileWithMimeType(handler, mime_type) ||
309 FileHandlerCanHandleFileWithExtension(handler, path);
312 GrantedFileEntry CreateFileEntry(
314 const Extension* extension,
316 const base::FilePath& path,
318 GrantedFileEntry result;
319 fileapi::IsolatedContext* isolated_context =
320 fileapi::IsolatedContext::GetInstance();
321 DCHECK(isolated_context);
323 result.filesystem_id = isolated_context->RegisterFileSystemForPath(
324 fileapi::kFileSystemTypeNativeForPlatformApp, path,
325 &result.registered_name);
327 content::ChildProcessSecurityPolicy* policy =
328 content::ChildProcessSecurityPolicy::GetInstance();
329 policy->GrantReadFileSystem(renderer_id, result.filesystem_id);
330 if (HasFileSystemWritePermission(extension)) {
332 policy->GrantCreateReadWriteFileSystem(renderer_id, result.filesystem_id);
334 policy->GrantWriteFileSystem(renderer_id, result.filesystem_id);
335 policy->GrantDeleteFromFileSystem(renderer_id, result.filesystem_id);
339 result.id = result.filesystem_id + ":" + result.registered_name;
343 void CheckWritableFiles(
344 const std::vector<base::FilePath>& paths,
347 const base::Closure& on_success,
348 const base::Callback<void(const base::FilePath&)>& on_failure) {
349 scoped_refptr<WritableFileChecker> checker(new WritableFileChecker(
350 paths, profile, is_directory, on_success, on_failure));
354 bool HasFileSystemWritePermission(const Extension* extension) {
355 return extension->HasAPIPermission(APIPermission::kFileSystemWrite);
358 bool ValidateFileEntryAndGetPath(
359 const std::string& filesystem_name,
360 const std::string& filesystem_path,
361 const content::RenderViewHost* render_view_host,
362 base::FilePath* file_path,
363 std::string* error) {
364 if (filesystem_path.empty()) {
365 *error = kInvalidParameters;
369 std::string filesystem_id;
370 if (!fileapi::CrackIsolatedFileSystemName(filesystem_name, &filesystem_id)) {
371 *error = kInvalidParameters;
375 // Only return the display path if the process has read access to the
377 content::ChildProcessSecurityPolicy* policy =
378 content::ChildProcessSecurityPolicy::GetInstance();
379 if (!policy->CanReadFileSystem(render_view_host->GetProcess()->GetID(),
381 *error = kSecurityError;
385 fileapi::IsolatedContext* context = fileapi::IsolatedContext::GetInstance();
386 base::FilePath relative_path =
387 base::FilePath::FromUTF8Unsafe(filesystem_path);
388 base::FilePath virtual_path = context->CreateVirtualRootPath(filesystem_id)
389 .Append(relative_path);
390 fileapi::FileSystemType type;
391 fileapi::FileSystemMountOption mount_option;
392 if (!context->CrackVirtualPath(
393 virtual_path, &filesystem_id, &type, file_path, &mount_option)) {
394 *error = kInvalidParameters;
398 // The file system API is only intended to operate on file entries that
399 // correspond to a native file, selected by the user so only allow file
400 // systems returned by the file system API or from a drag and drop operation.
401 if (type != fileapi::kFileSystemTypeNativeForPlatformApp &&
402 type != fileapi::kFileSystemTypeDragged) {
403 *error = kInvalidParameters;
410 } // namespace app_file_handler_util
412 } // namespace extensions