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. 4 5 #include "chrome/browser/extensions/api/file_handlers/app_file_handler_util.h" 6 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 "extensions/common/permissions/permissions_data.h" 16 #include "net/base/mime_util.h" 17 #include "webkit/browser/fileapi/isolated_context.h" 18 #include "webkit/common/fileapi/file_system_mount_option.h" 19 #include "webkit/common/fileapi/file_system_types.h" 20 21 #if defined(OS_CHROMEOS) 22 #include "chrome/browser/chromeos/file_manager/filesystem_api_util.h" 23 #endif 24 25 using apps::file_handler_util::GrantedFileEntry; 26 27 namespace extensions { 28 29 namespace app_file_handler_util { 30 31 const char kInvalidParameters[] = "Invalid parameters"; 32 const char kSecurityError[] = "Security error"; 33 34 namespace { 35 36 bool FileHandlerCanHandleFileWithExtension( 37 const FileHandlerInfo& handler, 38 const base::FilePath& path) { 39 for (std::set<std::string>::const_iterator extension = 40 handler.extensions.begin(); 41 extension != handler.extensions.end(); ++extension) { 42 if (*extension == "*") 43 return true; 44 45 if (path.MatchesExtension( 46 base::FilePath::kExtensionSeparator + 47 base::FilePath::FromUTF8Unsafe(*extension).value())) { 48 return true; 49 } 50 51 // Also accept files with no extension for handlers that support an 52 // empty extension, i.e. both "foo" and "foo." match. 53 if (extension->empty() && 54 path.MatchesExtension(base::FilePath::StringType())) { 55 return true; 56 } 57 } 58 return false; 59 } 60 61 bool FileHandlerCanHandleFileWithMimeType( 62 const FileHandlerInfo& handler, 63 const std::string& mime_type) { 64 for (std::set<std::string>::const_iterator type = handler.types.begin(); 65 type != handler.types.end(); ++type) { 66 if (net::MatchesMimeType(*type, mime_type)) 67 return true; 68 } 69 return false; 70 } 71 72 bool DoCheckWritableFile(const base::FilePath& path, bool is_directory) { 73 // Don't allow links. 74 if (base::PathExists(path) && base::IsLink(path)) 75 return false; 76 77 if (is_directory) 78 return base::DirectoryExists(path); 79 80 // Create the file if it doesn't already exist. 81 int creation_flags = base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_READ; 82 base::File file(path, creation_flags); 83 return file.IsValid(); 84 } 85 86 // Checks whether a list of paths are all OK for writing and calls a provided 87 // on_success or on_failure callback when done. A file is OK for writing if it 88 // is not a symlink, is not in a blacklisted path and can be opened for writing; 89 // files are created if they do not exist. 90 class WritableFileChecker 91 : public base::RefCountedThreadSafe<WritableFileChecker> { 92 public: 93 WritableFileChecker( 94 const std::vector<base::FilePath>& paths, 95 Profile* profile, 96 bool is_directory, 97 const base::Closure& on_success, 98 const base::Callback<void(const base::FilePath&)>& on_failure); 99 100 void Check(); 101 102 private: 103 friend class base::RefCountedThreadSafe<WritableFileChecker>; 104 virtual ~WritableFileChecker(); 105 106 // Called when a work item is completed. If all work items are done, this 107 // calls the success or failure callback. 108 void TaskDone(); 109 110 // Reports an error in completing a work item. This may be called more than 111 // once, but only the last message will be retained. 112 void Error(const base::FilePath& error_path); 113 114 void CheckLocalWritableFiles(); 115 116 #if defined(OS_CHROMEOS) 117 void NonNativeLocalPathCheckDone(const base::FilePath& path, bool success); 118 #endif 119 120 const std::vector<base::FilePath> paths_; 121 Profile* profile_; 122 const bool is_directory_; 123 int outstanding_tasks_; 124 base::FilePath error_path_; 125 base::Closure on_success_; 126 base::Callback<void(const base::FilePath&)> on_failure_; 127 }; 128 129 WritableFileChecker::WritableFileChecker( 130 const std::vector<base::FilePath>& paths, 131 Profile* profile, 132 bool is_directory, 133 const base::Closure& on_success, 134 const base::Callback<void(const base::FilePath&)>& on_failure) 135 : paths_(paths), 136 profile_(profile), 137 is_directory_(is_directory), 138 outstanding_tasks_(1), 139 on_success_(on_success), 140 on_failure_(on_failure) {} 141 142 void WritableFileChecker::Check() { 143 #if defined(OS_CHROMEOS) 144 if (file_manager::util::IsUnderNonNativeLocalPath(profile_, paths_[0])) { 145 outstanding_tasks_ = paths_.size(); 146 for (std::vector<base::FilePath>::const_iterator it = paths_.begin(); 147 it != paths_.end(); 148 ++it) { 149 if (is_directory_) { 150 file_manager::util::IsNonNativeLocalPathDirectory( 151 profile_, 152 *it, 153 base::Bind(&WritableFileChecker::NonNativeLocalPathCheckDone, 154 this, *it)); 155 } else { 156 file_manager::util::PrepareNonNativeLocalFileForWritableApp( 157 profile_, 158 *it, 159 base::Bind(&WritableFileChecker::NonNativeLocalPathCheckDone, 160 this, *it)); 161 } 162 } 163 return; 164 } 165 #endif 166 content::BrowserThread::PostTask( 167 content::BrowserThread::FILE, 168 FROM_HERE, 169 base::Bind(&WritableFileChecker::CheckLocalWritableFiles, this)); 170 } 171 172 WritableFileChecker::~WritableFileChecker() {} 173 174 void WritableFileChecker::TaskDone() { 175 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 176 if (--outstanding_tasks_ == 0) { 177 if (error_path_.empty()) 178 on_success_.Run(); 179 else 180 on_failure_.Run(error_path_); 181 } 182 } 183 184 // Reports an error in completing a work item. This may be called more than 185 // once, but only the last message will be retained. 186 void WritableFileChecker::Error(const base::FilePath& error_path) { 187 DCHECK(!error_path.empty()); 188 error_path_ = error_path; 189 TaskDone(); 190 } 191 192 void WritableFileChecker::CheckLocalWritableFiles() { 193 DCHECK_CURRENTLY_ON(content::BrowserThread::FILE); 194 std::string error; 195 for (std::vector<base::FilePath>::const_iterator it = paths_.begin(); 196 it != paths_.end(); 197 ++it) { 198 if (!DoCheckWritableFile(*it, is_directory_)) { 199 content::BrowserThread::PostTask( 200 content::BrowserThread::UI, 201 FROM_HERE, 202 base::Bind(&WritableFileChecker::Error, this, *it)); 203 return; 204 } 205 } 206 content::BrowserThread::PostTask( 207 content::BrowserThread::UI, 208 FROM_HERE, 209 base::Bind(&WritableFileChecker::TaskDone, this)); 210 } 211 212 #if defined(OS_CHROMEOS) 213 void WritableFileChecker::NonNativeLocalPathCheckDone( 214 const base::FilePath& path, 215 bool success) { 216 if (success) 217 TaskDone(); 218 else 219 Error(path); 220 } 221 #endif 222 223 } // namespace 224 225 const FileHandlerInfo* FileHandlerForId(const Extension& app, 226 const std::string& handler_id) { 227 const FileHandlersInfo* file_handlers = FileHandlers::GetFileHandlers(&app); 228 if (!file_handlers) 229 return NULL; 230 231 for (FileHandlersInfo::const_iterator i = file_handlers->begin(); 232 i != file_handlers->end(); i++) { 233 if (i->id == handler_id) 234 return &*i; 235 } 236 return NULL; 237 } 238 239 const FileHandlerInfo* FirstFileHandlerForFile( 240 const Extension& app, 241 const std::string& mime_type, 242 const base::FilePath& path) { 243 const FileHandlersInfo* file_handlers = FileHandlers::GetFileHandlers(&app); 244 if (!file_handlers) 245 return NULL; 246 247 for (FileHandlersInfo::const_iterator i = file_handlers->begin(); 248 i != file_handlers->end(); i++) { 249 if (FileHandlerCanHandleFile(*i, mime_type, path)) 250 return &*i; 251 } 252 return NULL; 253 } 254 255 std::vector<const FileHandlerInfo*> FindFileHandlersForFiles( 256 const Extension& app, const PathAndMimeTypeSet& files) { 257 std::vector<const FileHandlerInfo*> handlers; 258 if (files.empty()) 259 return handlers; 260 261 // Look for file handlers which can handle all the MIME types specified. 262 const FileHandlersInfo* file_handlers = FileHandlers::GetFileHandlers(&app); 263 if (!file_handlers) 264 return handlers; 265 266 for (FileHandlersInfo::const_iterator data = file_handlers->begin(); 267 data != file_handlers->end(); ++data) { 268 bool handles_all_types = true; 269 for (PathAndMimeTypeSet::const_iterator it = files.begin(); 270 it != files.end(); ++it) { 271 if (!FileHandlerCanHandleFile(*data, it->second, it->first)) { 272 handles_all_types = false; 273 break; 274 } 275 } 276 if (handles_all_types) 277 handlers.push_back(&*data); 278 } 279 return handlers; 280 } 281 282 bool FileHandlerCanHandleFile( 283 const FileHandlerInfo& handler, 284 const std::string& mime_type, 285 const base::FilePath& path) { 286 return FileHandlerCanHandleFileWithMimeType(handler, mime_type) || 287 FileHandlerCanHandleFileWithExtension(handler, path); 288 } 289 290 GrantedFileEntry CreateFileEntry( 291 Profile* profile, 292 const Extension* extension, 293 int renderer_id, 294 const base::FilePath& path, 295 bool is_directory) { 296 GrantedFileEntry result; 297 fileapi::IsolatedContext* isolated_context = 298 fileapi::IsolatedContext::GetInstance(); 299 DCHECK(isolated_context); 300 301 result.filesystem_id = isolated_context->RegisterFileSystemForPath( 302 fileapi::kFileSystemTypeNativeForPlatformApp, std::string(), path, 303 &result.registered_name); 304 305 content::ChildProcessSecurityPolicy* policy = 306 content::ChildProcessSecurityPolicy::GetInstance(); 307 policy->GrantReadFileSystem(renderer_id, result.filesystem_id); 308 if (HasFileSystemWritePermission(extension)) { 309 if (is_directory) { 310 policy->GrantCreateReadWriteFileSystem(renderer_id, result.filesystem_id); 311 } else { 312 policy->GrantWriteFileSystem(renderer_id, result.filesystem_id); 313 policy->GrantDeleteFromFileSystem(renderer_id, result.filesystem_id); 314 } 315 } 316 317 result.id = result.filesystem_id + ":" + result.registered_name; 318 return result; 319 } 320 321 void PrepareFilesForWritableApp( 322 const std::vector<base::FilePath>& paths, 323 Profile* profile, 324 bool is_directory, 325 const base::Closure& on_success, 326 const base::Callback<void(const base::FilePath&)>& on_failure) { 327 scoped_refptr<WritableFileChecker> checker(new WritableFileChecker( 328 paths, profile, is_directory, on_success, on_failure)); 329 checker->Check(); 330 } 331 332 bool HasFileSystemWritePermission(const Extension* extension) { 333 return extension->permissions_data()->HasAPIPermission( 334 APIPermission::kFileSystemWrite); 335 } 336 337 bool ValidateFileEntryAndGetPath( 338 const std::string& filesystem_name, 339 const std::string& filesystem_path, 340 const content::RenderViewHost* render_view_host, 341 base::FilePath* file_path, 342 std::string* error) { 343 if (filesystem_path.empty()) { 344 *error = kInvalidParameters; 345 return false; 346 } 347 348 std::string filesystem_id; 349 if (!fileapi::CrackIsolatedFileSystemName(filesystem_name, &filesystem_id)) { 350 *error = kInvalidParameters; 351 return false; 352 } 353 354 // Only return the display path if the process has read access to the 355 // filesystem. 356 content::ChildProcessSecurityPolicy* policy = 357 content::ChildProcessSecurityPolicy::GetInstance(); 358 if (!policy->CanReadFileSystem(render_view_host->GetProcess()->GetID(), 359 filesystem_id)) { 360 *error = kSecurityError; 361 return false; 362 } 363 364 fileapi::IsolatedContext* context = fileapi::IsolatedContext::GetInstance(); 365 base::FilePath relative_path = 366 base::FilePath::FromUTF8Unsafe(filesystem_path); 367 base::FilePath virtual_path = context->CreateVirtualRootPath(filesystem_id) 368 .Append(relative_path); 369 fileapi::FileSystemType type; 370 fileapi::FileSystemMountOption mount_option; 371 std::string cracked_id; 372 if (!context->CrackVirtualPath( 373 virtual_path, &filesystem_id, &type, &cracked_id, file_path, 374 &mount_option)) { 375 *error = kInvalidParameters; 376 return false; 377 } 378 379 // The file system API is only intended to operate on file entries that 380 // correspond to a native file, selected by the user so only allow file 381 // systems returned by the file system API or from a drag and drop operation. 382 if (type != fileapi::kFileSystemTypeNativeForPlatformApp && 383 type != fileapi::kFileSystemTypeDragged) { 384 *error = kInvalidParameters; 385 return false; 386 } 387 388 return true; 389 } 390 391 } // namespace app_file_handler_util 392 393 } // namespace extensions 394