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_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 "net/base/mime_util.h"
13 #include "webkit/browser/fileapi/isolated_context.h"
14 #include "webkit/common/fileapi/file_system_types.h"
16 #if defined(OS_CHROMEOS)
17 #include "chrome/browser/chromeos/drive/file_system_util.h"
20 namespace extensions {
22 namespace app_file_handler_util {
26 bool FileHandlerCanHandleFileWithExtension(
27 const FileHandlerInfo& handler,
28 const base::FilePath& path) {
29 for (std::set<std::string>::const_iterator extension =
30 handler.extensions.begin();
31 extension != handler.extensions.end(); ++extension) {
32 if (*extension == "*")
35 if (path.MatchesExtension(
36 base::FilePath::kExtensionSeparator +
37 base::FilePath::FromUTF8Unsafe(*extension).value())) {
41 // Also accept files with no extension for handlers that support an
42 // empty extension, i.e. both "foo" and "foo." match.
43 if (extension->empty() &&
44 path.MatchesExtension(base::FilePath::StringType())) {
51 bool FileHandlerCanHandleFileWithMimeType(
52 const FileHandlerInfo& handler,
53 const std::string& mime_type) {
54 for (std::set<std::string>::const_iterator type = handler.types.begin();
55 type != handler.types.end(); ++type) {
56 if (net::MatchesMimeType(*type, mime_type))
62 bool DoCheckWritableFile(const base::FilePath& path, bool is_directory) {
64 if (base::PathExists(path) && file_util::IsLink(path))
68 return base::DirectoryExists(path);
70 // Create the file if it doesn't already exist.
71 base::PlatformFileError error = base::PLATFORM_FILE_OK;
72 int creation_flags = base::PLATFORM_FILE_CREATE |
73 base::PLATFORM_FILE_READ |
74 base::PLATFORM_FILE_WRITE;
75 base::PlatformFile file = base::CreatePlatformFile(path, creation_flags,
77 // Close the file so we don't keep a lock open.
78 if (file != base::kInvalidPlatformFileValue)
79 base::ClosePlatformFile(file);
80 if (error != base::PLATFORM_FILE_OK &&
81 error != base::PLATFORM_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);
124 const std::vector<base::FilePath> paths_;
126 const bool is_directory_;
127 int outstanding_tasks_;
128 base::FilePath error_path_;
129 base::Closure on_success_;
130 base::Callback<void(const base::FilePath&)> on_failure_;
133 WritableFileChecker::WritableFileChecker(
134 const std::vector<base::FilePath>& paths,
137 const base::Closure& on_success,
138 const base::Callback<void(const base::FilePath&)>& on_failure)
141 is_directory_(is_directory),
142 outstanding_tasks_(1),
143 on_success_(on_success),
144 on_failure_(on_failure) {}
146 void WritableFileChecker::Check() {
147 #if defined(OS_CHROMEOS)
148 if (drive::util::IsUnderDriveMountPoint(paths_[0])) {
149 outstanding_tasks_ = paths_.size();
150 for (std::vector<base::FilePath>::const_iterator it = paths_.begin();
153 DCHECK(drive::util::IsUnderDriveMountPoint(*it));
154 drive::util::PrepareWritableFileAndRun(
157 base::Bind(&WritableFileChecker::CheckRemoteWritableFile, this, *it));
162 content::BrowserThread::PostTask(
163 content::BrowserThread::FILE,
165 base::Bind(&WritableFileChecker::CheckLocalWritableFiles, this));
168 WritableFileChecker::~WritableFileChecker() {}
170 void WritableFileChecker::TaskDone() {
171 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
172 if (--outstanding_tasks_ == 0) {
173 if (error_path_.empty())
176 on_failure_.Run(error_path_);
180 // Reports an error in completing a work item. This may be called more than
181 // once, but only the last message will be retained.
182 void WritableFileChecker::Error(const base::FilePath& error_path) {
183 DCHECK(!error_path.empty());
184 error_path_ = error_path;
188 void WritableFileChecker::CheckLocalWritableFiles() {
189 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
191 for (std::vector<base::FilePath>::const_iterator it = paths_.begin();
194 if (!DoCheckWritableFile(*it, is_directory_)) {
195 content::BrowserThread::PostTask(
196 content::BrowserThread::UI,
198 base::Bind(&WritableFileChecker::Error, this, *it));
202 content::BrowserThread::PostTask(
203 content::BrowserThread::UI,
205 base::Bind(&WritableFileChecker::TaskDone, this));
208 #if defined(OS_CHROMEOS)
209 void WritableFileChecker::CheckRemoteWritableFile(
210 const base::FilePath& remote_path,
211 drive::FileError error,
212 const base::FilePath& /* local_path */) {
213 if (error == drive::FILE_ERROR_OK) {
214 content::BrowserThread::PostTask(
215 content::BrowserThread::UI,
217 base::Bind(&WritableFileChecker::TaskDone, this));
219 content::BrowserThread::PostTask(
220 content::BrowserThread::UI,
222 base::Bind(&WritableFileChecker::Error, this, remote_path));
229 typedef std::vector<FileHandlerInfo> FileHandlerList;
231 const FileHandlerInfo* FileHandlerForId(const Extension& app,
232 const std::string& handler_id) {
233 const FileHandlerList* file_handlers = FileHandlers::GetFileHandlers(&app);
237 for (FileHandlerList::const_iterator i = file_handlers->begin();
238 i != file_handlers->end(); i++) {
239 if (i->id == handler_id)
245 const FileHandlerInfo* FirstFileHandlerForFile(
246 const Extension& app,
247 const std::string& mime_type,
248 const base::FilePath& path) {
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 (FileHandlerCanHandleFile(*i, mime_type, path))
261 std::vector<const FileHandlerInfo*> FindFileHandlersForFiles(
262 const Extension& app, const PathAndMimeTypeSet& files) {
263 std::vector<const FileHandlerInfo*> handlers;
267 // Look for file handlers which can handle all the MIME types specified.
268 const FileHandlerList* file_handlers = FileHandlers::GetFileHandlers(&app);
272 for (FileHandlerList::const_iterator data = file_handlers->begin();
273 data != file_handlers->end(); ++data) {
274 bool handles_all_types = true;
275 for (PathAndMimeTypeSet::const_iterator it = files.begin();
276 it != files.end(); ++it) {
277 if (!FileHandlerCanHandleFile(*data, it->second, it->first)) {
278 handles_all_types = false;
282 if (handles_all_types)
283 handlers.push_back(&*data);
288 bool FileHandlerCanHandleFile(
289 const FileHandlerInfo& handler,
290 const std::string& mime_type,
291 const base::FilePath& path) {
292 return FileHandlerCanHandleFileWithMimeType(handler, mime_type) ||
293 FileHandlerCanHandleFileWithExtension(handler, path);
296 GrantedFileEntry CreateFileEntry(
298 const Extension* extension,
300 const base::FilePath& path,
302 GrantedFileEntry result;
303 fileapi::IsolatedContext* isolated_context =
304 fileapi::IsolatedContext::GetInstance();
305 DCHECK(isolated_context);
307 result.filesystem_id = isolated_context->RegisterFileSystemForPath(
308 fileapi::kFileSystemTypeNativeForPlatformApp, path,
309 &result.registered_name);
311 content::ChildProcessSecurityPolicy* policy =
312 content::ChildProcessSecurityPolicy::GetInstance();
313 policy->GrantReadFileSystem(renderer_id, result.filesystem_id);
314 if (HasFileSystemWritePermission(extension)) {
315 policy->GrantWriteFileSystem(renderer_id, result.filesystem_id);
316 policy->GrantDeleteFromFileSystem(renderer_id, result.filesystem_id);
318 policy->GrantCreateFileForFileSystem(renderer_id, result.filesystem_id);
321 result.id = result.filesystem_id + ":" + result.registered_name;
325 void CheckWritableFiles(
326 const std::vector<base::FilePath>& paths,
329 const base::Closure& on_success,
330 const base::Callback<void(const base::FilePath&)>& on_failure) {
331 scoped_refptr<WritableFileChecker> checker(new WritableFileChecker(
332 paths, profile, is_directory, on_success, on_failure));
336 GrantedFileEntry::GrantedFileEntry() {}
338 bool HasFileSystemWritePermission(const Extension* extension) {
339 return extension->HasAPIPermission(APIPermission::kFileSystemWrite);
342 } // namespace app_file_handler_util
344 } // namespace extensions