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 base::StringToLowerASCII(&filter
);
203 if (!StartsWithASCII(filter
,
204 std::string(url::kFileSystemScheme
) + ':',
206 *error
= extensions::ErrorUtils::FormatErrorMessageUTF16(
207 errors::kInvalidURLPatternError
, filter
);
210 // The user inputs filesystem:*; we don't actually implement scheme
211 // wildcards in URLPattern, so transform to what will match correctly.
212 filter
.replace(0, 11, "chrome-extension://*/");
213 URLPattern
pattern(URLPattern::SCHEME_EXTENSION
);
214 if (pattern
.Parse(filter
) != URLPattern::PARSE_SUCCESS
) {
215 *error
= extensions::ErrorUtils::FormatErrorMessageUTF16(
216 errors::kInvalidURLPatternError
, filter
);
219 std::string path
= pattern
.path();
220 bool allowed
= path
== "/*" || path
== "/*.*" ||
221 (path
.compare(0, 3, "/*.") == 0 &&
222 path
.find_first_of('*', 3) == std::string::npos
);
224 *error
= extensions::ErrorUtils::FormatErrorMessageUTF16(
225 errors::kInvalidURLPatternError
, filter
);
228 result
->AddPattern(pattern
);
232 std::string default_icon
;
233 // Read the file browser action |default_icon| (optional).
234 if (file_browser_handler
->HasKey(keys::kPageActionDefaultIcon
)) {
235 if (!file_browser_handler
->GetString(
236 keys::kPageActionDefaultIcon
, &default_icon
) ||
237 default_icon
.empty()) {
238 *error
= base::ASCIIToUTF16(errors::kInvalidPageActionIconPath
);
241 result
->set_icon_path(default_icon
);
244 return result
.release();
247 // Loads FileBrowserHandlers from |extension_actions| into a list in |result|.
248 bool LoadFileBrowserHandlers(
249 const std::string
& extension_id
,
250 const base::ListValue
* extension_actions
,
251 FileBrowserHandler::List
* result
,
252 base::string16
* error
) {
253 for (base::ListValue::const_iterator iter
= extension_actions
->begin();
254 iter
!= extension_actions
->end();
256 if (!(*iter
)->IsType(base::Value::TYPE_DICTIONARY
)) {
257 *error
= base::ASCIIToUTF16(errors::kInvalidFileBrowserHandler
);
260 scoped_ptr
<FileBrowserHandler
> action(
261 LoadFileBrowserHandler(
263 reinterpret_cast<base::DictionaryValue
*>(*iter
), error
));
265 return false; // Failed to parse file browser action definition.
266 result
->push_back(linked_ptr
<FileBrowserHandler
>(action
.release()));
273 bool FileBrowserHandlerParser::Parse(extensions::Extension
* extension
,
274 base::string16
* error
) {
275 const base::Value
* file_browser_handlers_value
= nullptr;
276 if (!extension
->manifest()->Get(keys::kFileBrowserHandlers
,
277 &file_browser_handlers_value
)) {
281 if (!extensions::PermissionsParser::HasAPIPermission(
282 extension
, extensions::APIPermission::ID::kFileBrowserHandler
)) {
283 extension
->AddInstallWarning(extensions::InstallWarning(
284 errors::kInvalidFileBrowserHandlerMissingPermission
));
288 const base::ListValue
* file_browser_handlers_list_value
= nullptr;
289 if (!file_browser_handlers_value
->GetAsList(
290 &file_browser_handlers_list_value
)) {
291 *error
= base::ASCIIToUTF16(errors::kInvalidFileBrowserHandler
);
295 scoped_ptr
<FileBrowserHandlerInfo
> info(new FileBrowserHandlerInfo
);
296 if (!LoadFileBrowserHandlers(extension
->id(),
297 file_browser_handlers_list_value
,
298 &info
->file_browser_handlers
, error
)) {
299 return false; // Failed to parse file browser actions definition.
302 extension
->SetManifestData(keys::kFileBrowserHandlers
, info
.release());
306 const std::vector
<std::string
> FileBrowserHandlerParser::Keys() const {
307 return SingleKey(keys::kFileBrowserHandlers
);