1 // Copyright 2013 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/common/extensions/api/file_browser_handlers/file_browser_handler.h" 6 7 #include "base/logging.h" 8 #include "base/strings/string_number_conversions.h" 9 #include "base/strings/string_util.h" 10 #include "base/strings/utf_string_conversions.h" 11 #include "base/values.h" 12 #include "chrome/common/extensions/extension_constants.h" 13 #include "extensions/common/error_utils.h" 14 #include "extensions/common/manifest.h" 15 #include "extensions/common/manifest_constants.h" 16 #include "extensions/common/url_pattern.h" 17 #include "url/url_constants.h" 18 19 namespace keys = extensions::manifest_keys; 20 namespace errors = extensions::manifest_errors; 21 22 namespace { 23 24 const char kReadAccessString[] = "read"; 25 const char kReadWriteAccessString[] = "read-write"; 26 const char kCreateAccessString[] = "create"; 27 28 unsigned int kPermissionsNotDefined = 0; 29 unsigned int kReadPermission = 1; 30 unsigned int kWritePermission = 1 << 1; 31 unsigned int kCreatePermission = 1 << 2; 32 unsigned int kInvalidPermission = 1 << 3; 33 34 unsigned int GetAccessPermissionFlagFromString(const std::string& access_str) { 35 if (access_str == kReadAccessString) 36 return kReadPermission; 37 if (access_str == kReadWriteAccessString) 38 return kReadPermission | kWritePermission; 39 if (access_str == kCreateAccessString) 40 return kCreatePermission; 41 return kInvalidPermission; 42 } 43 44 // Stored on the Extension. 45 struct FileBrowserHandlerInfo : public extensions::Extension::ManifestData { 46 FileBrowserHandler::List file_browser_handlers; 47 48 FileBrowserHandlerInfo(); 49 virtual ~FileBrowserHandlerInfo(); 50 }; 51 52 FileBrowserHandlerInfo::FileBrowserHandlerInfo() { 53 } 54 55 FileBrowserHandlerInfo::~FileBrowserHandlerInfo() { 56 } 57 58 } // namespace 59 60 FileBrowserHandler::FileBrowserHandler() 61 : file_access_permission_flags_(kPermissionsNotDefined) { 62 } 63 64 FileBrowserHandler::~FileBrowserHandler() { 65 } 66 67 void FileBrowserHandler::AddPattern(const URLPattern& pattern) { 68 url_set_.AddPattern(pattern); 69 } 70 71 void FileBrowserHandler::ClearPatterns() { 72 url_set_.ClearPatterns(); 73 } 74 75 bool FileBrowserHandler::MatchesURL(const GURL& url) const { 76 return url_set_.MatchesURL(url); 77 } 78 79 bool FileBrowserHandler::AddFileAccessPermission( 80 const std::string& access) { 81 file_access_permission_flags_ |= GetAccessPermissionFlagFromString(access); 82 return (file_access_permission_flags_ & kInvalidPermission) != 0U; 83 } 84 85 bool FileBrowserHandler::ValidateFileAccessPermissions() { 86 bool is_invalid = (file_access_permission_flags_ & kInvalidPermission) != 0U; 87 bool can_create = (file_access_permission_flags_ & kCreatePermission) != 0U; 88 bool can_read_or_write = (file_access_permission_flags_ & 89 (kReadPermission | kWritePermission)) != 0U; 90 if (is_invalid || (can_create && can_read_or_write)) { 91 file_access_permission_flags_ = kInvalidPermission; 92 return false; 93 } 94 95 if (file_access_permission_flags_ == kPermissionsNotDefined) 96 file_access_permission_flags_ = kReadPermission | kWritePermission; 97 return true; 98 } 99 100 bool FileBrowserHandler::CanRead() const { 101 DCHECK(!(file_access_permission_flags_ & kInvalidPermission)); 102 return (file_access_permission_flags_ & kReadPermission) != 0; 103 } 104 105 bool FileBrowserHandler::CanWrite() const { 106 DCHECK(!(file_access_permission_flags_ & kInvalidPermission)); 107 return (file_access_permission_flags_ & kWritePermission) != 0; 108 } 109 110 bool FileBrowserHandler::HasCreateAccessPermission() const { 111 DCHECK(!(file_access_permission_flags_ & kInvalidPermission)); 112 return (file_access_permission_flags_ & kCreatePermission) != 0; 113 } 114 115 // static 116 FileBrowserHandler::List* 117 FileBrowserHandler::GetHandlers(const extensions::Extension* extension) { 118 FileBrowserHandlerInfo* info = static_cast<FileBrowserHandlerInfo*>( 119 extension->GetManifestData(keys::kFileBrowserHandlers)); 120 if (info) 121 return &info->file_browser_handlers; 122 return NULL; 123 } 124 125 FileBrowserHandlerParser::FileBrowserHandlerParser() { 126 } 127 128 FileBrowserHandlerParser::~FileBrowserHandlerParser() { 129 } 130 131 namespace { 132 133 FileBrowserHandler* LoadFileBrowserHandler( 134 const std::string& extension_id, 135 const base::DictionaryValue* file_browser_handler, 136 base::string16* error) { 137 scoped_ptr<FileBrowserHandler> result(new FileBrowserHandler()); 138 result->set_extension_id(extension_id); 139 140 std::string handler_id; 141 // Read the file action |id| (mandatory). 142 if (!file_browser_handler->HasKey(keys::kPageActionId) || 143 !file_browser_handler->GetString(keys::kPageActionId, &handler_id)) { 144 *error = base::ASCIIToUTF16(errors::kInvalidPageActionId); 145 return NULL; 146 } 147 result->set_id(handler_id); 148 149 // Read the page action title from |default_title| (mandatory). 150 std::string title; 151 if (!file_browser_handler->HasKey(keys::kPageActionDefaultTitle) || 152 !file_browser_handler->GetString(keys::kPageActionDefaultTitle, &title)) { 153 *error = base::ASCIIToUTF16(errors::kInvalidPageActionDefaultTitle); 154 return NULL; 155 } 156 result->set_title(title); 157 158 // Initialize access permissions (optional). 159 const base::ListValue* access_list_value = NULL; 160 if (file_browser_handler->HasKey(keys::kFileAccessList)) { 161 if (!file_browser_handler->GetList(keys::kFileAccessList, 162 &access_list_value) || 163 access_list_value->empty()) { 164 *error = base::ASCIIToUTF16(errors::kInvalidFileAccessList); 165 return NULL; 166 } 167 for (size_t i = 0; i < access_list_value->GetSize(); ++i) { 168 std::string access; 169 if (!access_list_value->GetString(i, &access) || 170 result->AddFileAccessPermission(access)) { 171 *error = extensions::ErrorUtils::FormatErrorMessageUTF16( 172 errors::kInvalidFileAccessValue, base::IntToString(i)); 173 return NULL; 174 } 175 } 176 } 177 if (!result->ValidateFileAccessPermissions()) { 178 *error = base::ASCIIToUTF16(errors::kInvalidFileAccessList); 179 return NULL; 180 } 181 182 // Initialize file filters (mandatory, unless "create" access is specified, 183 // in which case is ignored). The list can be empty. 184 if (!result->HasCreateAccessPermission()) { 185 const base::ListValue* file_filters = NULL; 186 if (!file_browser_handler->HasKey(keys::kFileFilters) || 187 !file_browser_handler->GetList(keys::kFileFilters, &file_filters)) { 188 *error = base::ASCIIToUTF16(errors::kInvalidFileFiltersList); 189 return NULL; 190 } 191 for (size_t i = 0; i < file_filters->GetSize(); ++i) { 192 std::string filter; 193 if (!file_filters->GetString(i, &filter)) { 194 *error = extensions::ErrorUtils::FormatErrorMessageUTF16( 195 errors::kInvalidFileFilterValue, base::IntToString(i)); 196 return NULL; 197 } 198 base::StringToLowerASCII(&filter); 199 if (!StartsWithASCII(filter, 200 std::string(url::kFileSystemScheme) + ':', 201 true)) { 202 *error = extensions::ErrorUtils::FormatErrorMessageUTF16( 203 errors::kInvalidURLPatternError, filter); 204 return NULL; 205 } 206 // The user inputs filesystem:*; we don't actually implement scheme 207 // wildcards in URLPattern, so transform to what will match correctly. 208 filter.replace(0, 11, "chrome-extension://*/"); 209 URLPattern pattern(URLPattern::SCHEME_EXTENSION); 210 if (pattern.Parse(filter) != URLPattern::PARSE_SUCCESS) { 211 *error = extensions::ErrorUtils::FormatErrorMessageUTF16( 212 errors::kInvalidURLPatternError, filter); 213 return NULL; 214 } 215 std::string path = pattern.path(); 216 bool allowed = path == "/*" || path == "/*.*" || 217 (path.compare(0, 3, "/*.") == 0 && 218 path.find_first_of('*', 3) == std::string::npos); 219 if (!allowed) { 220 *error = extensions::ErrorUtils::FormatErrorMessageUTF16( 221 errors::kInvalidURLPatternError, filter); 222 return NULL; 223 } 224 result->AddPattern(pattern); 225 } 226 } 227 228 std::string default_icon; 229 // Read the file browser action |default_icon| (optional). 230 if (file_browser_handler->HasKey(keys::kPageActionDefaultIcon)) { 231 if (!file_browser_handler->GetString( 232 keys::kPageActionDefaultIcon, &default_icon) || 233 default_icon.empty()) { 234 *error = base::ASCIIToUTF16(errors::kInvalidPageActionIconPath); 235 return NULL; 236 } 237 result->set_icon_path(default_icon); 238 } 239 240 return result.release(); 241 } 242 243 // Loads FileBrowserHandlers from |extension_actions| into a list in |result|. 244 bool LoadFileBrowserHandlers( 245 const std::string& extension_id, 246 const base::ListValue* extension_actions, 247 FileBrowserHandler::List* result, 248 base::string16* error) { 249 for (base::ListValue::const_iterator iter = extension_actions->begin(); 250 iter != extension_actions->end(); 251 ++iter) { 252 if (!(*iter)->IsType(base::Value::TYPE_DICTIONARY)) { 253 *error = base::ASCIIToUTF16(errors::kInvalidFileBrowserHandler); 254 return false; 255 } 256 scoped_ptr<FileBrowserHandler> action( 257 LoadFileBrowserHandler( 258 extension_id, 259 reinterpret_cast<base::DictionaryValue*>(*iter), error)); 260 if (!action.get()) 261 return false; // Failed to parse file browser action definition. 262 result->push_back(linked_ptr<FileBrowserHandler>(action.release())); 263 } 264 return true; 265 } 266 267 } // namespace 268 269 bool FileBrowserHandlerParser::Parse(extensions::Extension* extension, 270 base::string16* error) { 271 const base::ListValue* file_browser_handlers_value = NULL; 272 if (!extension->manifest()->GetList(keys::kFileBrowserHandlers, 273 &file_browser_handlers_value)) { 274 *error = base::ASCIIToUTF16(errors::kInvalidFileBrowserHandler); 275 return false; 276 } 277 scoped_ptr<FileBrowserHandlerInfo> info(new FileBrowserHandlerInfo); 278 if (!LoadFileBrowserHandlers(extension->id(), 279 file_browser_handlers_value, 280 &info->file_browser_handlers, 281 error)) { 282 return false; // Failed to parse file browser actions definition. 283 } 284 285 extension->SetManifestData(keys::kFileBrowserHandlers, info.release()); 286 return true; 287 } 288 289 const std::vector<std::string> FileBrowserHandlerParser::Keys() const { 290 return SingleKey(keys::kFileBrowserHandlers); 291 } 292