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 "content/public/common/url_constants.h"
14#include "extensions/common/error_utils.h"
15#include "extensions/common/manifest.h"
16#include "extensions/common/manifest_constants.h"
17#include "extensions/common/url_pattern.h"
18
19namespace keys = extensions::manifest_keys;
20namespace errors = extensions::manifest_errors;
21
22namespace {
23
24const char kReadAccessString[] = "read";
25const char kReadWriteAccessString[] = "read-write";
26const char kCreateAccessString[] = "create";
27
28unsigned int kPermissionsNotDefined = 0;
29unsigned int kReadPermission = 1;
30unsigned int kWritePermission = 1 << 1;
31unsigned int kCreatePermission = 1 << 2;
32unsigned int kInvalidPermission = 1 << 3;
33
34unsigned 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.
45struct FileBrowserHandlerInfo : public extensions::Extension::ManifestData {
46  FileBrowserHandler::List file_browser_handlers;
47
48  FileBrowserHandlerInfo();
49  virtual ~FileBrowserHandlerInfo();
50};
51
52FileBrowserHandlerInfo::FileBrowserHandlerInfo() {
53}
54
55FileBrowserHandlerInfo::~FileBrowserHandlerInfo() {
56}
57
58}  // namespace
59
60FileBrowserHandler::FileBrowserHandler()
61    : file_access_permission_flags_(kPermissionsNotDefined) {
62}
63
64FileBrowserHandler::~FileBrowserHandler() {
65}
66
67void FileBrowserHandler::AddPattern(const URLPattern& pattern) {
68  url_set_.AddPattern(pattern);
69}
70
71void FileBrowserHandler::ClearPatterns() {
72  url_set_.ClearPatterns();
73}
74
75bool FileBrowserHandler::MatchesURL(const GURL& url) const {
76  return url_set_.MatchesURL(url);
77}
78
79bool FileBrowserHandler::AddFileAccessPermission(
80    const std::string& access) {
81  file_access_permission_flags_ |= GetAccessPermissionFlagFromString(access);
82  return (file_access_permission_flags_ & kInvalidPermission) != 0U;
83}
84
85bool 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
100bool FileBrowserHandler::CanRead() const {
101  DCHECK(!(file_access_permission_flags_ & kInvalidPermission));
102  return (file_access_permission_flags_ & kReadPermission) != 0;
103}
104
105bool FileBrowserHandler::CanWrite() const {
106  DCHECK(!(file_access_permission_flags_ & kInvalidPermission));
107  return (file_access_permission_flags_ & kWritePermission) != 0;
108}
109
110bool FileBrowserHandler::HasCreateAccessPermission() const {
111  DCHECK(!(file_access_permission_flags_ & kInvalidPermission));
112  return (file_access_permission_flags_ & kCreatePermission) != 0;
113}
114
115// static
116FileBrowserHandler::List*
117FileBrowserHandler::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
125FileBrowserHandlerParser::FileBrowserHandlerParser() {
126}
127
128FileBrowserHandlerParser::~FileBrowserHandlerParser() {
129}
130
131namespace {
132
133FileBrowserHandler* LoadFileBrowserHandler(
134    const std::string& extension_id,
135    const 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 = 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 = ASCIIToUTF16(errors::kInvalidPageActionDefaultTitle);
154    return NULL;
155  }
156  result->set_title(title);
157
158  // Initialize access permissions (optional).
159  const 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 = 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 = 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 ListValue* file_filters = NULL;
186    if (!file_browser_handler->HasKey(keys::kFileFilters) ||
187        !file_browser_handler->GetList(keys::kFileFilters, &file_filters)) {
188      *error = 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      StringToLowerASCII(&filter);
199      if (!StartsWithASCII(filter,
200                           std::string(chrome::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 = 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|.
244bool LoadFileBrowserHandlers(
245    const std::string& extension_id,
246    const ListValue* extension_actions,
247    FileBrowserHandler::List* result,
248    base::string16* error) {
249  for (ListValue::const_iterator iter = extension_actions->begin();
250       iter != extension_actions->end();
251       ++iter) {
252    if (!(*iter)->IsType(Value::TYPE_DICTIONARY)) {
253      *error = ASCIIToUTF16(errors::kInvalidFileBrowserHandler);
254      return false;
255    }
256    scoped_ptr<FileBrowserHandler> action(
257        LoadFileBrowserHandler(
258            extension_id, reinterpret_cast<DictionaryValue*>(*iter), error));
259    if (!action.get())
260      return false;  // Failed to parse file browser action definition.
261    result->push_back(linked_ptr<FileBrowserHandler>(action.release()));
262  }
263  return true;
264}
265
266}  // namespace
267
268bool FileBrowserHandlerParser::Parse(extensions::Extension* extension,
269                                     base::string16* error) {
270  const ListValue* file_browser_handlers_value = NULL;
271  if (!extension->manifest()->GetList(keys::kFileBrowserHandlers,
272                                      &file_browser_handlers_value)) {
273    *error = ASCIIToUTF16(errors::kInvalidFileBrowserHandler);
274    return false;
275  }
276  scoped_ptr<FileBrowserHandlerInfo> info(new FileBrowserHandlerInfo);
277  if (!LoadFileBrowserHandlers(extension->id(),
278                               file_browser_handlers_value,
279                               &info->file_browser_handlers,
280                               error)) {
281    return false;  // Failed to parse file browser actions definition.
282  }
283
284  extension->SetManifestData(keys::kFileBrowserHandlers, info.release());
285  return true;
286}
287
288const std::vector<std::string> FileBrowserHandlerParser::Keys() const {
289  return SingleKey(keys::kFileBrowserHandlers);
290}
291