- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / common / extensions / manifest_handlers / externally_connectable.cc
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/manifest_handlers/externally_connectable.h"
6
7 #include <algorithm>
8
9 #include "base/stl_util.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "chrome/common/extensions/api/manifest_types.h"
12 #include "chrome/common/extensions/permissions/permissions_data.h"
13 #include "extensions/common/error_utils.h"
14 #include "extensions/common/manifest_constants.h"
15 #include "extensions/common/permissions/api_permission_set.h"
16 #include "extensions/common/url_pattern.h"
17 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
18 #include "url/gurl.h"
19
20 namespace rcd = net::registry_controlled_domains;
21
22 namespace extensions {
23
24 namespace externally_connectable_errors {
25 const char kErrorInvalidMatchPattern[] = "Invalid match pattern '*'";
26 const char kErrorInvalidId[] = "Invalid ID '*'";
27 const char kErrorNothingSpecified[] =
28     "'externally_connectable' specifies neither 'matches' nor 'ids'; "
29     "nothing will be able to connect";
30 const char kErrorTopLevelDomainsNotAllowed[] =
31     "\"*\" is an effective top level domain for which wildcard subdomains such "
32     "as \"*\" are not allowed";
33 const char kErrorWildcardHostsNotAllowed[] =
34     "Wildcard domain patterns such as \"*\" are not allowed";
35 }  // namespace externally_connectable_errors
36
37 namespace keys = extensions::manifest_keys;
38 namespace errors = externally_connectable_errors;
39 using api::manifest_types::ExternallyConnectable;
40
41 namespace {
42
43 const char kAllIds[] = "*";
44
45 template <typename T>
46 std::vector<T> Sorted(const std::vector<T>& in) {
47   std::vector<T> out = in;
48   std::sort(out.begin(), out.end());
49   return out;
50 }
51
52 } // namespace
53
54 ExternallyConnectableHandler::ExternallyConnectableHandler() {}
55
56 ExternallyConnectableHandler::~ExternallyConnectableHandler() {}
57
58 bool ExternallyConnectableHandler::Parse(Extension* extension,
59                                          string16* error) {
60   const base::Value* externally_connectable = NULL;
61   CHECK(extension->manifest()->Get(keys::kExternallyConnectable,
62                                    &externally_connectable));
63   std::vector<InstallWarning> install_warnings;
64   scoped_ptr<ExternallyConnectableInfo> info =
65       ExternallyConnectableInfo::FromValue(*externally_connectable,
66                                            &install_warnings,
67                                            error);
68   if (!info)
69     return false;
70   if (!info->matches.is_empty()) {
71     PermissionsData::GetInitialAPIPermissions(extension)->insert(
72         APIPermission::kWebConnectable);
73   }
74   extension->AddInstallWarnings(install_warnings);
75   extension->SetManifestData(keys::kExternallyConnectable, info.release());
76   return true;
77 }
78
79 const std::vector<std::string> ExternallyConnectableHandler::Keys() const {
80   return SingleKey(keys::kExternallyConnectable);
81 }
82
83 // static
84 ExternallyConnectableInfo* ExternallyConnectableInfo::Get(
85     const Extension* extension) {
86   return static_cast<ExternallyConnectableInfo*>(
87       extension->GetManifestData(keys::kExternallyConnectable));
88 }
89
90 // static
91 scoped_ptr<ExternallyConnectableInfo> ExternallyConnectableInfo::FromValue(
92     const base::Value& value,
93     std::vector<InstallWarning>* install_warnings,
94     string16* error) {
95   scoped_ptr<ExternallyConnectable> externally_connectable =
96       ExternallyConnectable::FromValue(value, error);
97   if (!externally_connectable)
98     return scoped_ptr<ExternallyConnectableInfo>();
99
100   URLPatternSet matches;
101
102   if (externally_connectable->matches) {
103     for (std::vector<std::string>::iterator it =
104              externally_connectable->matches->begin();
105          it != externally_connectable->matches->end(); ++it) {
106       // Safe to use SCHEME_ALL here; externally_connectable gives a page ->
107       // extension communication path, not the other way.
108       URLPattern pattern(URLPattern::SCHEME_ALL);
109       if (pattern.Parse(*it) != URLPattern::PARSE_SUCCESS) {
110         *error = ErrorUtils::FormatErrorMessageUTF16(
111             errors::kErrorInvalidMatchPattern, *it);
112         return scoped_ptr<ExternallyConnectableInfo>();
113       }
114
115       // Wildcard hosts are not allowed.
116       if (pattern.host().empty()) {
117         // Warning not error for forwards compatibility.
118         install_warnings->push_back(InstallWarning(
119             ErrorUtils::FormatErrorMessage(
120                 errors::kErrorWildcardHostsNotAllowed, *it),
121             keys::kExternallyConnectable,
122             *it));
123         continue;
124       }
125
126       // Wildcards on subdomains of a TLD are not allowed.
127       size_t registry_length = rcd::GetRegistryLength(
128           pattern.host(),
129           // This means that things that look like TLDs - the foobar in
130           // http://google.foobar - count as TLDs.
131           rcd::INCLUDE_UNKNOWN_REGISTRIES,
132           // This means that effective TLDs like appspot.com count as TLDs;
133           // codereview.appspot.com and evil.appspot.com are different.
134           rcd::INCLUDE_PRIVATE_REGISTRIES);
135
136       if (registry_length == std::string::npos) {
137         // The URL parsing combined with host().empty() should have caught this.
138         NOTREACHED() << *it;
139         *error = ErrorUtils::FormatErrorMessageUTF16(
140             errors::kErrorInvalidMatchPattern, *it);
141         return scoped_ptr<ExternallyConnectableInfo>();
142       }
143
144       // Broad match patterns like "*.com", "*.co.uk", and even "*.appspot.com"
145       // are not allowed. However just "appspot.com" is ok.
146       if (registry_length == 0 && pattern.match_subdomains()) {
147         // Warning not error for forwards compatibility.
148         install_warnings->push_back(InstallWarning(
149             ErrorUtils::FormatErrorMessage(
150                 errors::kErrorTopLevelDomainsNotAllowed,
151                 pattern.host().c_str(),
152                 *it),
153             keys::kExternallyConnectable,
154             *it));
155         continue;
156       }
157
158       matches.AddPattern(pattern);
159     }
160   }
161
162   std::vector<std::string> ids;
163   bool all_ids = false;
164
165   if (externally_connectable->ids) {
166     for (std::vector<std::string>::iterator it =
167              externally_connectable->ids->begin();
168          it != externally_connectable->ids->end(); ++it) {
169       if (*it == kAllIds) {
170         all_ids = true;
171       } else if (Extension::IdIsValid(*it)) {
172         ids.push_back(*it);
173       } else {
174         *error = ErrorUtils::FormatErrorMessageUTF16(
175             errors::kErrorInvalidId, *it);
176         return scoped_ptr<ExternallyConnectableInfo>();
177       }
178     }
179   }
180
181   if (!externally_connectable->matches &&
182       !externally_connectable->ids) {
183     install_warnings->push_back(InstallWarning(
184         errors::kErrorNothingSpecified,
185         keys::kExternallyConnectable));
186   }
187
188   bool accepts_tls_channel_id =
189       externally_connectable->accepts_tls_channel_id.get() &&
190       *externally_connectable->accepts_tls_channel_id;
191   return make_scoped_ptr(
192       new ExternallyConnectableInfo(matches, ids, all_ids,
193                                     accepts_tls_channel_id));
194 }
195
196 ExternallyConnectableInfo::~ExternallyConnectableInfo() {}
197
198 ExternallyConnectableInfo::ExternallyConnectableInfo(
199     const URLPatternSet& matches,
200     const std::vector<std::string>& ids,
201     bool all_ids,
202     bool accepts_tls_channel_id)
203     : matches(matches), ids(Sorted(ids)), all_ids(all_ids),
204       accepts_tls_channel_id(accepts_tls_channel_id) {}
205
206 bool ExternallyConnectableInfo::IdCanConnect(const std::string& id) {
207   if (all_ids)
208     return true;
209   DCHECK(base::STLIsSorted(ids));
210   return std::binary_search(ids.begin(), ids.end(), id);
211 }
212
213 }   // namespace extensions