blob: 455d1dad308645537a992deba9d7cbd06caa0ac5 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "extensions/common/api/sockets/sockets_manifest_permission.h"
#include "base/memory/scoped_ptr.h"
#include "base/stl_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "extensions/common/api/extensions_manifest_types.h"
#include "extensions/common/api/sockets/sockets_manifest_data.h"
#include "extensions/common/error_utils.h"
#include "extensions/common/extension_messages.h"
#include "extensions/common/manifest_constants.h"
#include "grit/extensions_strings.h"
#include "ipc/ipc_message.h"
#include "ui/base/l10n/l10n_util.h"
namespace extensions {
namespace sockets_errors {
const char kErrorInvalidHostPattern[] = "Invalid host:port pattern '*'";
}
namespace errors = sockets_errors;
using core_api::extensions_manifest_types::Sockets;
using core_api::extensions_manifest_types::SocketHostPatterns;
using content::SocketPermissionRequest;
namespace {
static bool ParseHostPattern(
SocketsManifestPermission* permission,
content::SocketPermissionRequest::OperationType operation_type,
const std::string& host_pattern,
base::string16* error) {
SocketPermissionEntry entry;
if (!SocketPermissionEntry::ParseHostPattern(
operation_type, host_pattern, &entry)) {
*error = ErrorUtils::FormatErrorMessageUTF16(
errors::kErrorInvalidHostPattern, host_pattern);
return false;
}
permission->AddPermission(entry);
return true;
}
static bool ParseHostPatterns(
SocketsManifestPermission* permission,
content::SocketPermissionRequest::OperationType operation_type,
const scoped_ptr<SocketHostPatterns>& host_patterns,
base::string16* error) {
if (!host_patterns)
return true;
if (host_patterns->as_string) {
return ParseHostPattern(
permission, operation_type, *host_patterns->as_string, error);
}
CHECK(host_patterns->as_strings);
for (std::vector<std::string>::const_iterator it =
host_patterns->as_strings->begin();
it != host_patterns->as_strings->end();
++it) {
if (!ParseHostPattern(permission, operation_type, *it, error)) {
return false;
}
}
return true;
}
static void SetHostPatterns(
scoped_ptr<SocketHostPatterns>& host_patterns,
const SocketsManifestPermission* permission,
content::SocketPermissionRequest::OperationType operation_type) {
host_patterns.reset(new SocketHostPatterns());
host_patterns->as_strings.reset(new std::vector<std::string>());
for (SocketsManifestPermission::SocketPermissionEntrySet::const_iterator it =
permission->entries().begin();
it != permission->entries().end();
++it) {
if (it->pattern().type == operation_type) {
host_patterns->as_strings->push_back(it->GetHostPatternAsString());
}
}
}
} // namespace
SocketsManifestPermission::SocketsManifestPermission() {}
SocketsManifestPermission::~SocketsManifestPermission() {}
// static
scoped_ptr<SocketsManifestPermission> SocketsManifestPermission::FromValue(
const base::Value& value,
base::string16* error) {
scoped_ptr<Sockets> sockets = Sockets::FromValue(value, error);
if (!sockets)
return scoped_ptr<SocketsManifestPermission>();
scoped_ptr<SocketsManifestPermission> result(new SocketsManifestPermission());
if (sockets->udp) {
if (!ParseHostPatterns(result.get(),
SocketPermissionRequest::UDP_BIND,
sockets->udp->bind,
error)) {
return scoped_ptr<SocketsManifestPermission>();
}
if (!ParseHostPatterns(result.get(),
SocketPermissionRequest::UDP_SEND_TO,
sockets->udp->send,
error)) {
return scoped_ptr<SocketsManifestPermission>();
}
if (!ParseHostPatterns(result.get(),
SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP,
sockets->udp->multicast_membership,
error)) {
return scoped_ptr<SocketsManifestPermission>();
}
}
if (sockets->tcp) {
if (!ParseHostPatterns(result.get(),
SocketPermissionRequest::TCP_CONNECT,
sockets->tcp->connect,
error)) {
return scoped_ptr<SocketsManifestPermission>();
}
}
if (sockets->tcp_server) {
if (!ParseHostPatterns(result.get(),
SocketPermissionRequest::TCP_LISTEN,
sockets->tcp_server->listen,
error)) {
return scoped_ptr<SocketsManifestPermission>();
}
}
return result.Pass();
}
bool SocketsManifestPermission::CheckRequest(
const Extension* extension,
const SocketPermissionRequest& request) const {
for (SocketPermissionEntrySet::const_iterator it = permissions_.begin();
it != permissions_.end();
++it) {
if (it->Check(request))
return true;
}
return false;
}
std::string SocketsManifestPermission::name() const {
return manifest_keys::kSockets;
}
std::string SocketsManifestPermission::id() const { return name(); }
bool SocketsManifestPermission::HasMessages() const {
bool is_empty = permissions_.empty();
return !is_empty;
}
PermissionMessages SocketsManifestPermission::GetMessages() const {
// TODO(rpaquay): This function and callees is (almost) a copy/paste
// from extensions::SocketPermissiona.
PermissionMessages result;
if (!AddAnyHostMessage(result)) {
AddSpecificHostMessage(result);
AddSubdomainHostMessage(result);
}
AddNetworkListMessage(result);
return result;
}
bool SocketsManifestPermission::FromValue(const base::Value* value) {
if (!value)
return false;
base::string16 error;
scoped_ptr<SocketsManifestPermission> manifest_permission(
SocketsManifestPermission::FromValue(*value, &error));
if (!manifest_permission)
return false;
permissions_ = manifest_permission->permissions_;
return true;
}
scoped_ptr<base::Value> SocketsManifestPermission::ToValue() const {
Sockets sockets;
sockets.udp.reset(new Sockets::Udp());
SetHostPatterns(sockets.udp->bind, this, SocketPermissionRequest::UDP_BIND);
SetHostPatterns(
sockets.udp->send, this, SocketPermissionRequest::UDP_SEND_TO);
SetHostPatterns(sockets.udp->multicast_membership,
this,
SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP);
if (sockets.udp->bind->as_strings->size() == 0 &&
sockets.udp->send->as_strings->size() == 0 &&
sockets.udp->multicast_membership->as_strings->size() == 0) {
sockets.udp.reset(NULL);
}
sockets.tcp.reset(new Sockets::Tcp());
SetHostPatterns(
sockets.tcp->connect, this, SocketPermissionRequest::TCP_CONNECT);
if (sockets.tcp->connect->as_strings->size() == 0) {
sockets.tcp.reset(NULL);
}
sockets.tcp_server.reset(new Sockets::TcpServer());
SetHostPatterns(
sockets.tcp_server->listen, this, SocketPermissionRequest::TCP_LISTEN);
if (sockets.tcp_server->listen->as_strings->size() == 0) {
sockets.tcp_server.reset(NULL);
}
return scoped_ptr<base::Value>(sockets.ToValue().release()).Pass();
}
ManifestPermission* SocketsManifestPermission::Diff(
const ManifestPermission* rhs) const {
const SocketsManifestPermission* other =
static_cast<const SocketsManifestPermission*>(rhs);
scoped_ptr<SocketsManifestPermission> result(new SocketsManifestPermission());
result->permissions_ = base::STLSetDifference<SocketPermissionEntrySet>(
permissions_, other->permissions_);
return result.release();
}
ManifestPermission* SocketsManifestPermission::Union(
const ManifestPermission* rhs) const {
const SocketsManifestPermission* other =
static_cast<const SocketsManifestPermission*>(rhs);
scoped_ptr<SocketsManifestPermission> result(new SocketsManifestPermission());
result->permissions_ = base::STLSetUnion<SocketPermissionEntrySet>(
permissions_, other->permissions_);
return result.release();
}
ManifestPermission* SocketsManifestPermission::Intersect(
const ManifestPermission* rhs) const {
const SocketsManifestPermission* other =
static_cast<const SocketsManifestPermission*>(rhs);
scoped_ptr<SocketsManifestPermission> result(new SocketsManifestPermission());
result->permissions_ = base::STLSetIntersection<SocketPermissionEntrySet>(
permissions_, other->permissions_);
return result.release();
}
void SocketsManifestPermission::AddPermission(
const SocketPermissionEntry& entry) {
permissions_.insert(entry);
}
bool SocketsManifestPermission::AddAnyHostMessage(
PermissionMessages& messages) const {
for (SocketPermissionEntrySet::const_iterator it = permissions_.begin();
it != permissions_.end();
++it) {
if (it->IsAddressBoundType() &&
it->GetHostType() == SocketPermissionEntry::ANY_HOST) {
messages.push_back(
PermissionMessage(PermissionMessage::kSocketAnyHost,
l10n_util::GetStringUTF16(
IDS_EXTENSION_PROMPT_WARNING_SOCKET_ANY_HOST)));
return true;
}
}
return false;
}
void SocketsManifestPermission::AddSubdomainHostMessage(
PermissionMessages& messages) const {
std::set<base::string16> domains;
for (SocketPermissionEntrySet::const_iterator it = permissions_.begin();
it != permissions_.end();
++it) {
if (it->GetHostType() == SocketPermissionEntry::HOSTS_IN_DOMAINS)
domains.insert(base::UTF8ToUTF16(it->pattern().host));
}
if (!domains.empty()) {
int id = (domains.size() == 1)
? IDS_EXTENSION_PROMPT_WARNING_SOCKET_HOSTS_IN_DOMAIN
: IDS_EXTENSION_PROMPT_WARNING_SOCKET_HOSTS_IN_DOMAINS;
messages.push_back(PermissionMessage(
PermissionMessage::kSocketDomainHosts,
l10n_util::GetStringFUTF16(
id,
JoinString(
std::vector<base::string16>(domains.begin(), domains.end()),
' '))));
}
}
void SocketsManifestPermission::AddSpecificHostMessage(
PermissionMessages& messages) const {
std::set<base::string16> hostnames;
for (SocketPermissionEntrySet::const_iterator it = permissions_.begin();
it != permissions_.end();
++it) {
if (it->GetHostType() == SocketPermissionEntry::SPECIFIC_HOSTS)
hostnames.insert(base::UTF8ToUTF16(it->pattern().host));
}
if (!hostnames.empty()) {
int id = (hostnames.size() == 1)
? IDS_EXTENSION_PROMPT_WARNING_SOCKET_SPECIFIC_HOST
: IDS_EXTENSION_PROMPT_WARNING_SOCKET_SPECIFIC_HOSTS;
messages.push_back(PermissionMessage(
PermissionMessage::kSocketSpecificHosts,
l10n_util::GetStringFUTF16(
id,
JoinString(
std::vector<base::string16>(hostnames.begin(), hostnames.end()),
' '))));
}
}
void SocketsManifestPermission::AddNetworkListMessage(
PermissionMessages& messages) const {
for (SocketPermissionEntrySet::const_iterator it = permissions_.begin();
it != permissions_.end();
++it) {
if (it->pattern().type == SocketPermissionRequest::NETWORK_STATE) {
messages.push_back(
PermissionMessage(PermissionMessage::kNetworkState,
l10n_util::GetStringUTF16(
IDS_EXTENSION_PROMPT_WARNING_NETWORK_STATE)));
}
}
}
} // namespace extensions