Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / extensions / api / file_handlers / app_file_handler_util.cc
blob07214116d643137af3bdca7f5ef18832513fa466
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/files/file.h"
8 #include "base/files/file_path.h"
9 #include "base/files/file_util.h"
10 #include "content/public/browser/browser_thread.h"
11 #include "content/public/browser/child_process_security_policy.h"
12 #include "extensions/browser/extension_prefs.h"
13 #include "extensions/browser/granted_file_entry.h"
14 #include "extensions/common/permissions/permissions_data.h"
15 #include "net/base/mime_util.h"
16 #include "storage/browser/fileapi/isolated_context.h"
17 #include "storage/common/fileapi/file_system_mount_option.h"
18 #include "storage/common/fileapi/file_system_types.h"
20 #if defined(OS_CHROMEOS)
21 #include "chrome/browser/chromeos/file_manager/filesystem_api_util.h"
22 #endif
24 namespace extensions {
26 namespace app_file_handler_util {
28 const char kInvalidParameters[] = "Invalid parameters";
29 const char kSecurityError[] = "Security error";
31 namespace {
33 bool FileHandlerCanHandleFileWithExtension(
34 const FileHandlerInfo& handler,
35 const base::FilePath& path) {
36 for (std::set<std::string>::const_iterator extension =
37 handler.extensions.begin();
38 extension != handler.extensions.end(); ++extension) {
39 if (*extension == "*")
40 return true;
42 // Accept files whose extension or combined extension (e.g. ".tar.gz")
43 // match the supported extensions of file handler.
44 base::FilePath::StringType handler_extention(
45 base::FilePath::kExtensionSeparator +
46 base::FilePath::FromUTF8Unsafe(*extension).value());
47 if (base::FilePath::CompareEqualIgnoreCase(
48 handler_extention, path.Extension()) ||
49 base::FilePath::CompareEqualIgnoreCase(
50 handler_extention, path.FinalExtension())) {
51 return true;
54 // Also accept files with no extension for handlers that support an
55 // empty extension, i.e. both "foo" and "foo." match.
56 if (extension->empty() &&
57 path.MatchesExtension(base::FilePath::StringType())) {
58 return true;
61 return false;
64 bool FileHandlerCanHandleFileWithMimeType(
65 const FileHandlerInfo& handler,
66 const std::string& mime_type) {
67 for (std::set<std::string>::const_iterator type = handler.types.begin();
68 type != handler.types.end(); ++type) {
69 if (net::MatchesMimeType(*type, mime_type))
70 return true;
72 return false;
75 bool PrepareNativeLocalFileForWritableApp(const base::FilePath& path,
76 bool is_directory) {
77 DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
79 // Don't allow links.
80 if (base::PathExists(path) && base::IsLink(path))
81 return false;
83 if (is_directory)
84 return base::DirectoryExists(path);
86 // Create the file if it doesn't already exist.
87 int creation_flags = base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_READ;
88 base::File file(path, creation_flags);
90 return file.IsValid();
93 // Checks whether a list of paths are all OK for writing and calls a provided
94 // on_success or on_failure callback when done. A file is OK for writing if it
95 // is not a symlink, is not in a blacklisted path and can be opened for writing;
96 // files are created if they do not exist.
97 class WritableFileChecker
98 : public base::RefCountedThreadSafe<WritableFileChecker> {
99 public:
100 WritableFileChecker(
101 const std::vector<base::FilePath>& paths,
102 Profile* profile,
103 bool is_directory,
104 const base::Closure& on_success,
105 const base::Callback<void(const base::FilePath&)>& on_failure);
107 void Check();
109 private:
110 friend class base::RefCountedThreadSafe<WritableFileChecker>;
111 virtual ~WritableFileChecker();
113 // Called when a work item is completed. If all work items are done, this
114 // calls the success or failure callback.
115 void TaskDone();
117 // Reports an error in completing a work item. This may be called more than
118 // once, but only the last message will be retained.
119 void Error(const base::FilePath& error_path);
121 void CheckLocalWritableFiles();
123 // Called when processing a file is completed with either a success or an
124 // error.
125 void OnPrepareFileDone(const base::FilePath& path, bool success);
127 const std::vector<base::FilePath> paths_;
128 Profile* profile_;
129 const bool is_directory_;
130 int outstanding_tasks_;
131 base::FilePath error_path_;
132 base::Closure on_success_;
133 base::Callback<void(const base::FilePath&)> on_failure_;
136 WritableFileChecker::WritableFileChecker(
137 const std::vector<base::FilePath>& paths,
138 Profile* profile,
139 bool is_directory,
140 const base::Closure& on_success,
141 const base::Callback<void(const base::FilePath&)>& on_failure)
142 : paths_(paths),
143 profile_(profile),
144 is_directory_(is_directory),
145 outstanding_tasks_(1),
146 on_success_(on_success),
147 on_failure_(on_failure) {}
149 void WritableFileChecker::Check() {
150 outstanding_tasks_ = paths_.size();
151 for (const auto& path : paths_) {
152 #if defined(OS_CHROMEOS)
153 if (file_manager::util::IsUnderNonNativeLocalPath(profile_, path)) {
154 if (is_directory_) {
155 file_manager::util::IsNonNativeLocalPathDirectory(
156 profile_, path,
157 base::Bind(&WritableFileChecker::OnPrepareFileDone, this, path));
158 } else {
159 file_manager::util::PrepareNonNativeLocalFileForWritableApp(
160 profile_, path,
161 base::Bind(&WritableFileChecker::OnPrepareFileDone, this, path));
163 continue;
165 #endif
166 content::BrowserThread::PostTaskAndReplyWithResult(
167 content::BrowserThread::FILE, FROM_HERE,
168 base::Bind(&PrepareNativeLocalFileForWritableApp, path,
169 is_directory_),
170 base::Bind(&WritableFileChecker::OnPrepareFileDone, this, path));
174 WritableFileChecker::~WritableFileChecker() {}
176 void WritableFileChecker::TaskDone() {
177 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
178 if (--outstanding_tasks_ == 0) {
179 if (error_path_.empty())
180 on_success_.Run();
181 else
182 on_failure_.Run(error_path_);
186 // Reports an error in completing a work item. This may be called more than
187 // once, but only the last message will be retained.
188 void WritableFileChecker::Error(const base::FilePath& error_path) {
189 DCHECK(!error_path.empty());
190 error_path_ = error_path;
191 TaskDone();
194 void WritableFileChecker::OnPrepareFileDone(const base::FilePath& path,
195 bool success) {
196 if (success)
197 TaskDone();
198 else
199 Error(path);
202 } // namespace
204 const FileHandlerInfo* FileHandlerForId(const Extension& app,
205 const std::string& handler_id) {
206 const FileHandlersInfo* file_handlers = FileHandlers::GetFileHandlers(&app);
207 if (!file_handlers)
208 return NULL;
210 for (FileHandlersInfo::const_iterator i = file_handlers->begin();
211 i != file_handlers->end(); i++) {
212 if (i->id == handler_id)
213 return &*i;
215 return NULL;
218 const FileHandlerInfo* FirstFileHandlerForFile(
219 const Extension& app,
220 const std::string& mime_type,
221 const base::FilePath& path) {
222 const FileHandlersInfo* file_handlers = FileHandlers::GetFileHandlers(&app);
223 if (!file_handlers)
224 return NULL;
226 for (FileHandlersInfo::const_iterator i = file_handlers->begin();
227 i != file_handlers->end(); i++) {
228 if (FileHandlerCanHandleFile(*i, mime_type, path))
229 return &*i;
231 return NULL;
234 std::vector<const FileHandlerInfo*> FindFileHandlersForFiles(
235 const Extension& app, const PathAndMimeTypeSet& files) {
236 std::vector<const FileHandlerInfo*> handlers;
237 if (files.empty())
238 return handlers;
240 // Look for file handlers which can handle all the MIME types specified.
241 const FileHandlersInfo* file_handlers = FileHandlers::GetFileHandlers(&app);
242 if (!file_handlers)
243 return handlers;
245 for (FileHandlersInfo::const_iterator data = file_handlers->begin();
246 data != file_handlers->end(); ++data) {
247 bool handles_all_types = true;
248 for (PathAndMimeTypeSet::const_iterator it = files.begin();
249 it != files.end(); ++it) {
250 if (!FileHandlerCanHandleFile(*data, it->second, it->first)) {
251 handles_all_types = false;
252 break;
255 if (handles_all_types)
256 handlers.push_back(&*data);
258 return handlers;
261 bool FileHandlerCanHandleFile(
262 const FileHandlerInfo& handler,
263 const std::string& mime_type,
264 const base::FilePath& path) {
265 return FileHandlerCanHandleFileWithMimeType(handler, mime_type) ||
266 FileHandlerCanHandleFileWithExtension(handler, path);
269 GrantedFileEntry CreateFileEntry(
270 Profile* profile,
271 const Extension* extension,
272 int renderer_id,
273 const base::FilePath& path,
274 bool is_directory) {
275 GrantedFileEntry result;
276 storage::IsolatedContext* isolated_context =
277 storage::IsolatedContext::GetInstance();
278 DCHECK(isolated_context);
280 result.filesystem_id = isolated_context->RegisterFileSystemForPath(
281 storage::kFileSystemTypeNativeForPlatformApp,
282 std::string(),
283 path,
284 &result.registered_name);
286 content::ChildProcessSecurityPolicy* policy =
287 content::ChildProcessSecurityPolicy::GetInstance();
288 policy->GrantReadFileSystem(renderer_id, result.filesystem_id);
289 if (HasFileSystemWritePermission(extension)) {
290 if (is_directory) {
291 policy->GrantCreateReadWriteFileSystem(renderer_id, result.filesystem_id);
292 } else {
293 policy->GrantWriteFileSystem(renderer_id, result.filesystem_id);
294 policy->GrantDeleteFromFileSystem(renderer_id, result.filesystem_id);
298 result.id = result.filesystem_id + ":" + result.registered_name;
299 return result;
302 void PrepareFilesForWritableApp(
303 const std::vector<base::FilePath>& paths,
304 Profile* profile,
305 bool is_directory,
306 const base::Closure& on_success,
307 const base::Callback<void(const base::FilePath&)>& on_failure) {
308 scoped_refptr<WritableFileChecker> checker(new WritableFileChecker(
309 paths, profile, is_directory, on_success, on_failure));
310 checker->Check();
313 bool HasFileSystemWritePermission(const Extension* extension) {
314 return extension->permissions_data()->HasAPIPermission(
315 APIPermission::kFileSystemWrite);
318 bool ValidateFileEntryAndGetPath(const std::string& filesystem_name,
319 const std::string& filesystem_path,
320 int render_process_id,
321 base::FilePath* file_path,
322 std::string* error) {
323 if (filesystem_path.empty()) {
324 *error = kInvalidParameters;
325 return false;
328 std::string filesystem_id;
329 if (!storage::CrackIsolatedFileSystemName(filesystem_name, &filesystem_id)) {
330 *error = kInvalidParameters;
331 return false;
334 // Only return the display path if the process has read access to the
335 // filesystem.
336 content::ChildProcessSecurityPolicy* policy =
337 content::ChildProcessSecurityPolicy::GetInstance();
338 if (!policy->CanReadFileSystem(render_process_id, filesystem_id)) {
339 *error = kSecurityError;
340 return false;
343 storage::IsolatedContext* context = storage::IsolatedContext::GetInstance();
344 base::FilePath relative_path =
345 base::FilePath::FromUTF8Unsafe(filesystem_path);
346 base::FilePath virtual_path = context->CreateVirtualRootPath(filesystem_id)
347 .Append(relative_path);
348 storage::FileSystemType type;
349 storage::FileSystemMountOption mount_option;
350 std::string cracked_id;
351 if (!context->CrackVirtualPath(
352 virtual_path, &filesystem_id, &type, &cracked_id, file_path,
353 &mount_option)) {
354 *error = kInvalidParameters;
355 return false;
358 // The file system API is only intended to operate on file entries that
359 // correspond to a native file, selected by the user so only allow file
360 // systems returned by the file system API or from a drag and drop operation.
361 if (type != storage::kFileSystemTypeNativeForPlatformApp &&
362 type != storage::kFileSystemTypeDragged) {
363 *error = kInvalidParameters;
364 return false;
367 return true;
370 } // namespace app_file_handler_util
372 } // namespace extensions