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.
5 #include "chrome/common/extensions/api/file_browser_handlers/file_browser_handler.h"
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/install_warning.h"
15 #include "extensions/common/manifest.h"
16 #include "extensions/common/manifest_constants.h"
17 #include "extensions/common/manifest_handlers/permissions_parser.h"
18 #include "extensions/common/permissions/api_permission.h"
19 #include "extensions/common/url_pattern.h"
20 #include "url/url_constants.h"
22 namespace keys
= extensions::manifest_keys
;
23 namespace errors
= extensions::manifest_errors
;
27 const char kReadAccessString
[] = "read";
28 const char kReadWriteAccessString
[] = "read-write";
29 const char kCreateAccessString
[] = "create";
31 unsigned int kPermissionsNotDefined
= 0;
32 unsigned int kReadPermission
= 1;
33 unsigned int kWritePermission
= 1 << 1;
34 unsigned int kCreatePermission
= 1 << 2;
35 unsigned int kInvalidPermission
= 1 << 3;
37 unsigned int GetAccessPermissionFlagFromString(const std::string
& access_str
) {
38 if (access_str
== kReadAccessString
)
39 return kReadPermission
;
40 if (access_str
== kReadWriteAccessString
)
41 return kReadPermission
| kWritePermission
;
42 if (access_str
== kCreateAccessString
)
43 return kCreatePermission
;
44 return kInvalidPermission
;
47 // Stored on the Extension.
48 struct FileBrowserHandlerInfo
: public extensions::Extension::ManifestData
{
49 FileBrowserHandler::List file_browser_handlers
;
51 FileBrowserHandlerInfo();
52 ~FileBrowserHandlerInfo() override
;
55 FileBrowserHandlerInfo::FileBrowserHandlerInfo() {
58 FileBrowserHandlerInfo::~FileBrowserHandlerInfo() {
63 FileBrowserHandler::FileBrowserHandler()
64 : file_access_permission_flags_(kPermissionsNotDefined
) {
67 FileBrowserHandler::~FileBrowserHandler() {
70 void FileBrowserHandler::AddPattern(const URLPattern
& pattern
) {
71 url_set_
.AddPattern(pattern
);
74 void FileBrowserHandler::ClearPatterns() {
75 url_set_
.ClearPatterns();
78 bool FileBrowserHandler::MatchesURL(const GURL
& url
) const {
79 return url_set_
.MatchesURL(url
);
82 bool FileBrowserHandler::AddFileAccessPermission(
83 const std::string
& access
) {
84 file_access_permission_flags_
|= GetAccessPermissionFlagFromString(access
);
85 return (file_access_permission_flags_
& kInvalidPermission
) != 0U;
88 bool FileBrowserHandler::ValidateFileAccessPermissions() {
89 bool is_invalid
= (file_access_permission_flags_
& kInvalidPermission
) != 0U;
90 bool can_create
= (file_access_permission_flags_
& kCreatePermission
) != 0U;
91 bool can_read_or_write
= (file_access_permission_flags_
&
92 (kReadPermission
| kWritePermission
)) != 0U;
93 if (is_invalid
|| (can_create
&& can_read_or_write
)) {
94 file_access_permission_flags_
= kInvalidPermission
;
98 if (file_access_permission_flags_
== kPermissionsNotDefined
)
99 file_access_permission_flags_
= kReadPermission
| kWritePermission
;
103 bool FileBrowserHandler::CanRead() const {
104 DCHECK(!(file_access_permission_flags_
& kInvalidPermission
));
105 return (file_access_permission_flags_
& kReadPermission
) != 0;
108 bool FileBrowserHandler::CanWrite() const {
109 DCHECK(!(file_access_permission_flags_
& kInvalidPermission
));
110 return (file_access_permission_flags_
& kWritePermission
) != 0;
113 bool FileBrowserHandler::HasCreateAccessPermission() const {
114 DCHECK(!(file_access_permission_flags_
& kInvalidPermission
));
115 return (file_access_permission_flags_
& kCreatePermission
) != 0;
119 FileBrowserHandler::List
*
120 FileBrowserHandler::GetHandlers(const extensions::Extension
* extension
) {
121 FileBrowserHandlerInfo
* const info
= static_cast<FileBrowserHandlerInfo
*>(
122 extension
->GetManifestData(keys::kFileBrowserHandlers
));
126 return &info
->file_browser_handlers
;
129 FileBrowserHandlerParser::FileBrowserHandlerParser() {
132 FileBrowserHandlerParser::~FileBrowserHandlerParser() {
137 FileBrowserHandler
* LoadFileBrowserHandler(
138 const std::string
& extension_id
,
139 const base::DictionaryValue
* file_browser_handler
,
140 base::string16
* error
) {
141 scoped_ptr
<FileBrowserHandler
> result(new FileBrowserHandler());
142 result
->set_extension_id(extension_id
);
144 std::string handler_id
;
145 // Read the file action |id| (mandatory).
146 if (!file_browser_handler
->HasKey(keys::kPageActionId
) ||
147 !file_browser_handler
->GetString(keys::kPageActionId
, &handler_id
)) {
148 *error
= base::ASCIIToUTF16(errors::kInvalidPageActionId
);
151 result
->set_id(handler_id
);
153 // Read the page action title from |default_title| (mandatory).
155 if (!file_browser_handler
->HasKey(keys::kPageActionDefaultTitle
) ||
156 !file_browser_handler
->GetString(keys::kPageActionDefaultTitle
, &title
)) {
157 *error
= base::ASCIIToUTF16(errors::kInvalidPageActionDefaultTitle
);
160 result
->set_title(title
);
162 // Initialize access permissions (optional).
163 const base::ListValue
* access_list_value
= NULL
;
164 if (file_browser_handler
->HasKey(keys::kFileAccessList
)) {
165 if (!file_browser_handler
->GetList(keys::kFileAccessList
,
166 &access_list_value
) ||
167 access_list_value
->empty()) {
168 *error
= base::ASCIIToUTF16(errors::kInvalidFileAccessList
);
171 for (size_t i
= 0; i
< access_list_value
->GetSize(); ++i
) {
173 if (!access_list_value
->GetString(i
, &access
) ||
174 result
->AddFileAccessPermission(access
)) {
175 *error
= extensions::ErrorUtils::FormatErrorMessageUTF16(
176 errors::kInvalidFileAccessValue
, base::IntToString(i
));
181 if (!result
->ValidateFileAccessPermissions()) {
182 *error
= base::ASCIIToUTF16(errors::kInvalidFileAccessList
);
186 // Initialize file filters (mandatory, unless "create" access is specified,
187 // in which case is ignored). The list can be empty.
188 if (!result
->HasCreateAccessPermission()) {
189 const base::ListValue
* file_filters
= NULL
;
190 if (!file_browser_handler
->HasKey(keys::kFileFilters
) ||
191 !file_browser_handler
->GetList(keys::kFileFilters
, &file_filters
)) {
192 *error
= base::ASCIIToUTF16(errors::kInvalidFileFiltersList
);
195 for (size_t i
= 0; i
< file_filters
->GetSize(); ++i
) {
197 if (!file_filters
->GetString(i
, &filter
)) {
198 *error
= extensions::ErrorUtils::FormatErrorMessageUTF16(
199 errors::kInvalidFileFilterValue
, base::IntToString(i
));
202 filter
= base::ToLowerASCII(filter
);
203 if (!base::StartsWith(filter
, std::string(url::kFileSystemScheme
) + ':',
204 base::CompareCase::SENSITIVE
)) {
205 *error
= extensions::ErrorUtils::FormatErrorMessageUTF16(
206 errors::kInvalidURLPatternError
, filter
);
209 // The user inputs filesystem:*; we don't actually implement scheme
210 // wildcards in URLPattern, so transform to what will match correctly.
211 filter
.replace(0, 11, "chrome-extension://*/");
212 URLPattern
pattern(URLPattern::SCHEME_EXTENSION
);
213 if (pattern
.Parse(filter
) != URLPattern::PARSE_SUCCESS
) {
214 *error
= extensions::ErrorUtils::FormatErrorMessageUTF16(
215 errors::kInvalidURLPatternError
, filter
);
218 std::string path
= pattern
.path();
219 bool allowed
= path
== "/*" || path
== "/*.*" ||
220 (path
.compare(0, 3, "/*.") == 0 &&
221 path
.find_first_of('*', 3) == std::string::npos
);
223 *error
= extensions::ErrorUtils::FormatErrorMessageUTF16(
224 errors::kInvalidURLPatternError
, filter
);
227 result
->AddPattern(pattern
);
231 std::string default_icon
;
232 // Read the file browser action |default_icon| (optional).
233 if (file_browser_handler
->HasKey(keys::kPageActionDefaultIcon
)) {
234 if (!file_browser_handler
->GetString(
235 keys::kPageActionDefaultIcon
, &default_icon
) ||
236 default_icon
.empty()) {
237 *error
= base::ASCIIToUTF16(errors::kInvalidPageActionIconPath
);
240 result
->set_icon_path(default_icon
);
243 return result
.release();
246 // Loads FileBrowserHandlers from |extension_actions| into a list in |result|.
247 bool LoadFileBrowserHandlers(
248 const std::string
& extension_id
,
249 const base::ListValue
* extension_actions
,
250 FileBrowserHandler::List
* result
,
251 base::string16
* error
) {
252 for (base::ListValue::const_iterator iter
= extension_actions
->begin();
253 iter
!= extension_actions
->end();
255 if (!(*iter
)->IsType(base::Value::TYPE_DICTIONARY
)) {
256 *error
= base::ASCIIToUTF16(errors::kInvalidFileBrowserHandler
);
259 scoped_ptr
<FileBrowserHandler
> action(
260 LoadFileBrowserHandler(
262 reinterpret_cast<base::DictionaryValue
*>(*iter
), error
));
264 return false; // Failed to parse file browser action definition.
265 result
->push_back(linked_ptr
<FileBrowserHandler
>(action
.release()));
272 bool FileBrowserHandlerParser::Parse(extensions::Extension
* extension
,
273 base::string16
* error
) {
274 const base::Value
* file_browser_handlers_value
= nullptr;
275 if (!extension
->manifest()->Get(keys::kFileBrowserHandlers
,
276 &file_browser_handlers_value
)) {
280 if (!extensions::PermissionsParser::HasAPIPermission(
281 extension
, extensions::APIPermission::ID::kFileBrowserHandler
)) {
282 extension
->AddInstallWarning(extensions::InstallWarning(
283 errors::kInvalidFileBrowserHandlerMissingPermission
));
287 const base::ListValue
* file_browser_handlers_list_value
= nullptr;
288 if (!file_browser_handlers_value
->GetAsList(
289 &file_browser_handlers_list_value
)) {
290 *error
= base::ASCIIToUTF16(errors::kInvalidFileBrowserHandler
);
294 scoped_ptr
<FileBrowserHandlerInfo
> info(new FileBrowserHandlerInfo
);
295 if (!LoadFileBrowserHandlers(extension
->id(),
296 file_browser_handlers_list_value
,
297 &info
->file_browser_handlers
, error
)) {
298 return false; // Failed to parse file browser actions definition.
301 extension
->SetManifestData(keys::kFileBrowserHandlers
, info
.release());
305 const std::vector
<std::string
> FileBrowserHandlerParser::Keys() const {
306 return SingleKey(keys::kFileBrowserHandlers
);