Add ENABLE_MEDIA_ROUTER define to builds other than Android and iOS.
[chromium-blink-merge.git] / chrome / browser / extensions / api / file_handlers / app_file_handler_util.cc
blob74a13478d1ff8443bd13a124e21ee169781b70e1
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 "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 "storage/browser/fileapi/isolated_context.h"
18 #include "storage/common/fileapi/file_system_mount_option.h"
19 #include "storage/common/fileapi/file_system_types.h"
21 #if defined(OS_CHROMEOS)
22 #include "chrome/browser/chromeos/file_manager/filesystem_api_util.h"
23 #endif
25 namespace extensions {
27 namespace app_file_handler_util {
29 const char kInvalidParameters[] = "Invalid parameters";
30 const char kSecurityError[] = "Security error";
32 namespace {
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 == "*")
41 return true;
43 // Accept files whose extension or combined extension (e.g. ".tar.gz")
44 // match the supported extensions of file handler.
45 base::FilePath::StringType handler_extention(
46 base::FilePath::kExtensionSeparator +
47 base::FilePath::FromUTF8Unsafe(*extension).value());
48 if (base::FilePath::CompareEqualIgnoreCase(
49 handler_extention, path.Extension()) ||
50 base::FilePath::CompareEqualIgnoreCase(
51 handler_extention, path.FinalExtension())) {
52 return true;
55 // Also accept files with no extension for handlers that support an
56 // empty extension, i.e. both "foo" and "foo." match.
57 if (extension->empty() &&
58 path.MatchesExtension(base::FilePath::StringType())) {
59 return true;
62 return false;
65 bool FileHandlerCanHandleFileWithMimeType(
66 const FileHandlerInfo& handler,
67 const std::string& mime_type) {
68 for (std::set<std::string>::const_iterator type = handler.types.begin();
69 type != handler.types.end(); ++type) {
70 if (net::MatchesMimeType(*type, mime_type))
71 return true;
73 return false;
76 bool PrepareNativeLocalFileForWritableApp(const base::FilePath& path,
77 bool is_directory) {
78 DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
80 // Don't allow links.
81 if (base::PathExists(path) && base::IsLink(path))
82 return false;
84 if (is_directory)
85 return base::DirectoryExists(path);
87 // Create the file if it doesn't already exist.
88 int creation_flags = base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_READ;
89 base::File file(path, creation_flags);
91 return file.IsValid();
94 // Checks whether a list of paths are all OK for writing and calls a provided
95 // on_success or on_failure callback when done. A file is OK for writing if it
96 // is not a symlink, is not in a blacklisted path and can be opened for writing;
97 // files are created if they do not exist.
98 class WritableFileChecker
99 : public base::RefCountedThreadSafe<WritableFileChecker> {
100 public:
101 WritableFileChecker(
102 const std::vector<base::FilePath>& paths,
103 Profile* profile,
104 bool is_directory,
105 const base::Closure& on_success,
106 const base::Callback<void(const base::FilePath&)>& on_failure);
108 void Check();
110 private:
111 friend class base::RefCountedThreadSafe<WritableFileChecker>;
112 virtual ~WritableFileChecker();
114 // Called when a work item is completed. If all work items are done, this
115 // calls the success or failure callback.
116 void TaskDone();
118 // Reports an error in completing a work item. This may be called more than
119 // once, but only the last message will be retained.
120 void Error(const base::FilePath& error_path);
122 void CheckLocalWritableFiles();
124 // Called when processing a file is completed with either a success or an
125 // error.
126 void OnPrepareFileDone(const base::FilePath& path, bool success);
128 const std::vector<base::FilePath> paths_;
129 Profile* profile_;
130 const bool is_directory_;
131 int outstanding_tasks_;
132 base::FilePath error_path_;
133 base::Closure on_success_;
134 base::Callback<void(const base::FilePath&)> on_failure_;
137 WritableFileChecker::WritableFileChecker(
138 const std::vector<base::FilePath>& paths,
139 Profile* profile,
140 bool is_directory,
141 const base::Closure& on_success,
142 const base::Callback<void(const base::FilePath&)>& on_failure)
143 : paths_(paths),
144 profile_(profile),
145 is_directory_(is_directory),
146 outstanding_tasks_(1),
147 on_success_(on_success),
148 on_failure_(on_failure) {}
150 void WritableFileChecker::Check() {
151 outstanding_tasks_ = paths_.size();
152 for (const auto& path : paths_) {
153 #if defined(OS_CHROMEOS)
154 if (file_manager::util::IsUnderNonNativeLocalPath(profile_, path)) {
155 if (is_directory_) {
156 file_manager::util::IsNonNativeLocalPathDirectory(
157 profile_, path,
158 base::Bind(&WritableFileChecker::OnPrepareFileDone, this, path));
159 } else {
160 file_manager::util::PrepareNonNativeLocalFileForWritableApp(
161 profile_, path,
162 base::Bind(&WritableFileChecker::OnPrepareFileDone, this, path));
164 continue;
166 #endif
167 content::BrowserThread::PostTaskAndReplyWithResult(
168 content::BrowserThread::FILE, FROM_HERE,
169 base::Bind(&PrepareNativeLocalFileForWritableApp, path,
170 is_directory_),
171 base::Bind(&WritableFileChecker::OnPrepareFileDone, this, path));
175 WritableFileChecker::~WritableFileChecker() {}
177 void WritableFileChecker::TaskDone() {
178 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
179 if (--outstanding_tasks_ == 0) {
180 if (error_path_.empty())
181 on_success_.Run();
182 else
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;
192 TaskDone();
195 void WritableFileChecker::OnPrepareFileDone(const base::FilePath& path,
196 bool success) {
197 if (success)
198 TaskDone();
199 else
200 Error(path);
203 } // namespace
205 const FileHandlerInfo* FileHandlerForId(const Extension& app,
206 const std::string& handler_id) {
207 const FileHandlersInfo* file_handlers = FileHandlers::GetFileHandlers(&app);
208 if (!file_handlers)
209 return NULL;
211 for (FileHandlersInfo::const_iterator i = file_handlers->begin();
212 i != file_handlers->end(); i++) {
213 if (i->id == handler_id)
214 return &*i;
216 return NULL;
219 const FileHandlerInfo* FirstFileHandlerForFile(
220 const Extension& app,
221 const std::string& mime_type,
222 const base::FilePath& path) {
223 const FileHandlersInfo* file_handlers = FileHandlers::GetFileHandlers(&app);
224 if (!file_handlers)
225 return NULL;
227 for (FileHandlersInfo::const_iterator i = file_handlers->begin();
228 i != file_handlers->end(); i++) {
229 if (FileHandlerCanHandleFile(*i, mime_type, path))
230 return &*i;
232 return NULL;
235 std::vector<const FileHandlerInfo*> FindFileHandlersForFiles(
236 const Extension& app, const PathAndMimeTypeSet& files) {
237 std::vector<const FileHandlerInfo*> handlers;
238 if (files.empty())
239 return handlers;
241 // Look for file handlers which can handle all the MIME types specified.
242 const FileHandlersInfo* file_handlers = FileHandlers::GetFileHandlers(&app);
243 if (!file_handlers)
244 return handlers;
246 for (FileHandlersInfo::const_iterator data = file_handlers->begin();
247 data != file_handlers->end(); ++data) {
248 bool handles_all_types = true;
249 for (PathAndMimeTypeSet::const_iterator it = files.begin();
250 it != files.end(); ++it) {
251 if (!FileHandlerCanHandleFile(*data, it->second, it->first)) {
252 handles_all_types = false;
253 break;
256 if (handles_all_types)
257 handlers.push_back(&*data);
259 return handlers;
262 bool FileHandlerCanHandleFile(
263 const FileHandlerInfo& handler,
264 const std::string& mime_type,
265 const base::FilePath& path) {
266 return FileHandlerCanHandleFileWithMimeType(handler, mime_type) ||
267 FileHandlerCanHandleFileWithExtension(handler, path);
270 GrantedFileEntry CreateFileEntry(
271 Profile* profile,
272 const Extension* extension,
273 int renderer_id,
274 const base::FilePath& path,
275 bool is_directory) {
276 GrantedFileEntry result;
277 storage::IsolatedContext* isolated_context =
278 storage::IsolatedContext::GetInstance();
279 DCHECK(isolated_context);
281 result.filesystem_id = isolated_context->RegisterFileSystemForPath(
282 storage::kFileSystemTypeNativeForPlatformApp,
283 std::string(),
284 path,
285 &result.registered_name);
287 content::ChildProcessSecurityPolicy* policy =
288 content::ChildProcessSecurityPolicy::GetInstance();
289 policy->GrantReadFileSystem(renderer_id, result.filesystem_id);
290 if (HasFileSystemWritePermission(extension)) {
291 if (is_directory) {
292 policy->GrantCreateReadWriteFileSystem(renderer_id, result.filesystem_id);
293 } else {
294 policy->GrantWriteFileSystem(renderer_id, result.filesystem_id);
295 policy->GrantDeleteFromFileSystem(renderer_id, result.filesystem_id);
299 result.id = result.filesystem_id + ":" + result.registered_name;
300 return result;
303 void PrepareFilesForWritableApp(
304 const std::vector<base::FilePath>& paths,
305 Profile* profile,
306 bool is_directory,
307 const base::Closure& on_success,
308 const base::Callback<void(const base::FilePath&)>& on_failure) {
309 scoped_refptr<WritableFileChecker> checker(new WritableFileChecker(
310 paths, profile, is_directory, on_success, on_failure));
311 checker->Check();
314 bool HasFileSystemWritePermission(const Extension* extension) {
315 return extension->permissions_data()->HasAPIPermission(
316 APIPermission::kFileSystemWrite);
319 bool ValidateFileEntryAndGetPath(
320 const std::string& filesystem_name,
321 const std::string& filesystem_path,
322 const content::RenderViewHost* render_view_host,
323 base::FilePath* file_path,
324 std::string* error) {
325 if (filesystem_path.empty()) {
326 *error = kInvalidParameters;
327 return false;
330 std::string filesystem_id;
331 if (!storage::CrackIsolatedFileSystemName(filesystem_name, &filesystem_id)) {
332 *error = kInvalidParameters;
333 return false;
336 // Only return the display path if the process has read access to the
337 // filesystem.
338 content::ChildProcessSecurityPolicy* policy =
339 content::ChildProcessSecurityPolicy::GetInstance();
340 if (!policy->CanReadFileSystem(render_view_host->GetProcess()->GetID(),
341 filesystem_id)) {
342 *error = kSecurityError;
343 return false;
346 storage::IsolatedContext* context = storage::IsolatedContext::GetInstance();
347 base::FilePath relative_path =
348 base::FilePath::FromUTF8Unsafe(filesystem_path);
349 base::FilePath virtual_path = context->CreateVirtualRootPath(filesystem_id)
350 .Append(relative_path);
351 storage::FileSystemType type;
352 storage::FileSystemMountOption mount_option;
353 std::string cracked_id;
354 if (!context->CrackVirtualPath(
355 virtual_path, &filesystem_id, &type, &cracked_id, file_path,
356 &mount_option)) {
357 *error = kInvalidParameters;
358 return false;
361 // The file system API is only intended to operate on file entries that
362 // correspond to a native file, selected by the user so only allow file
363 // systems returned by the file system API or from a drag and drop operation.
364 if (type != storage::kFileSystemTypeNativeForPlatformApp &&
365 type != storage::kFileSystemTypeDragged) {
366 *error = kInvalidParameters;
367 return false;
370 return true;
373 } // namespace app_file_handler_util
375 } // namespace extensions