summaryrefslogtreecommitdiff
path: root/chromium/components/gcm_driver/gcm_driver.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/components/gcm_driver/gcm_driver.cc')
-rw-r--r--chromium/components/gcm_driver/gcm_driver.cc373
1 files changed, 373 insertions, 0 deletions
diff --git a/chromium/components/gcm_driver/gcm_driver.cc b/chromium/components/gcm_driver/gcm_driver.cc
new file mode 100644
index 00000000000..35960120e38
--- /dev/null
+++ b/chromium/components/gcm_driver/gcm_driver.cc
@@ -0,0 +1,373 @@
+// 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 "components/gcm_driver/gcm_driver.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
+#include "components/gcm_driver/crypto/gcm_decryption_result.h"
+#include "components/gcm_driver/crypto/gcm_encryption_result.h"
+#include "components/gcm_driver/gcm_app_handler.h"
+
+namespace gcm {
+
+InstanceIDHandler::InstanceIDHandler() = default;
+
+InstanceIDHandler::~InstanceIDHandler() = default;
+
+void InstanceIDHandler::DeleteAllTokensForApp(const std::string& app_id,
+ DeleteTokenCallback callback) {
+ DeleteToken(app_id, "*", "*", std::move(callback));
+}
+
+GCMDriver::GCMDriver(
+ const base::FilePath& store_path,
+ const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner) {
+ // The |blocking_task_runner| can be nullptr for tests that do not need the
+ // encryption capabilities of the GCMDriver class.
+ if (blocking_task_runner)
+ encryption_provider_.Init(store_path, blocking_task_runner);
+}
+
+GCMDriver::~GCMDriver() = default;
+
+void GCMDriver::Register(const std::string& app_id,
+ const std::vector<std::string>& sender_ids,
+ RegisterCallback callback) {
+ DCHECK(!app_id.empty());
+ DCHECK(!sender_ids.empty() && sender_ids.size() <= kMaxSenders);
+ DCHECK(!callback.is_null());
+
+ GCMClient::Result result = EnsureStarted(GCMClient::IMMEDIATE_START);
+ if (result != GCMClient::SUCCESS) {
+ std::move(callback).Run(std::string(), result);
+ return;
+ }
+
+ // If previous register operation is still in progress, bail out.
+ if (register_callbacks_.find(app_id) != register_callbacks_.end()) {
+ std::move(callback).Run(std::string(), GCMClient::ASYNC_OPERATION_PENDING);
+ return;
+ }
+
+ // Normalize the sender IDs by making them sorted.
+ std::vector<std::string> normalized_sender_ids = sender_ids;
+ std::sort(normalized_sender_ids.begin(), normalized_sender_ids.end());
+
+ register_callbacks_[app_id] = std::move(callback);
+
+ // If previous unregister operation is still in progress, wait until it
+ // finishes. We don't want to throw ASYNC_OPERATION_PENDING when the user
+ // uninstalls an app (ungistering) and then reinstalls the app again
+ // (registering).
+ auto unregister_iter = unregister_callbacks_.find(app_id);
+ if (unregister_iter != unregister_callbacks_.end()) {
+ // Replace the original unregister callback with an intermediate callback
+ // that will invoke the original unregister callback and trigger the pending
+ // registration after the unregistration finishes.
+ // Note that some parameters to RegisterAfterUnregister are specified here
+ // when the callback is created (base::Bind supports the partial binding
+ // of parameters).
+ unregister_iter->second = base::BindOnce(
+ &GCMDriver::RegisterAfterUnregister, weak_ptr_factory_.GetWeakPtr(),
+ app_id, normalized_sender_ids, std::move(unregister_iter->second));
+ return;
+ }
+
+ RegisterImpl(app_id, normalized_sender_ids);
+}
+
+void GCMDriver::Unregister(const std::string& app_id,
+ UnregisterCallback callback) {
+ UnregisterInternal(app_id, nullptr /* sender_id */, std::move(callback));
+}
+
+void GCMDriver::UnregisterWithSenderId(const std::string& app_id,
+ const std::string& sender_id,
+ UnregisterCallback callback) {
+ DCHECK(!sender_id.empty());
+ UnregisterInternal(app_id, &sender_id, std::move(callback));
+}
+
+void GCMDriver::UnregisterInternal(const std::string& app_id,
+ const std::string* sender_id,
+ UnregisterCallback callback) {
+ DCHECK(!app_id.empty());
+ DCHECK(!callback.is_null());
+
+ GCMClient::Result result = EnsureStarted(GCMClient::IMMEDIATE_START);
+ if (result != GCMClient::SUCCESS) {
+ std::move(callback).Run(result);
+ return;
+ }
+
+ // If previous un/register operation is still in progress, bail out.
+ if (register_callbacks_.find(app_id) != register_callbacks_.end() ||
+ unregister_callbacks_.find(app_id) != unregister_callbacks_.end()) {
+ std::move(callback).Run(GCMClient::ASYNC_OPERATION_PENDING);
+ return;
+ }
+
+ unregister_callbacks_[app_id] = std::move(callback);
+
+ if (sender_id)
+ UnregisterWithSenderIdImpl(app_id, *sender_id);
+ else
+ UnregisterImpl(app_id);
+}
+
+void GCMDriver::Send(const std::string& app_id,
+ const std::string& receiver_id,
+ const OutgoingMessage& message,
+ SendCallback callback) {
+ DCHECK(!app_id.empty());
+ DCHECK(!receiver_id.empty());
+ DCHECK(!callback.is_null());
+
+ GCMClient::Result result = EnsureStarted(GCMClient::IMMEDIATE_START);
+ if (result != GCMClient::SUCCESS) {
+ std::move(callback).Run(std::string(), result);
+ return;
+ }
+
+ // If the message with send ID is still in progress, bail out.
+ std::pair<std::string, std::string> key(app_id, message.id);
+ if (send_callbacks_.find(key) != send_callbacks_.end()) {
+ std::move(callback).Run(message.id, GCMClient::INVALID_PARAMETER);
+ return;
+ }
+
+ send_callbacks_[key] = std::move(callback);
+
+ SendImpl(app_id, receiver_id, message);
+}
+
+void GCMDriver::GetEncryptionInfo(const std::string& app_id,
+ GetEncryptionInfoCallback callback) {
+ encryption_provider_.GetEncryptionInfo(app_id, "" /* authorized_entity */,
+ std::move(callback));
+}
+
+void GCMDriver::UnregisterWithSenderIdImpl(const std::string& app_id,
+ const std::string& sender_id) {
+ NOTREACHED();
+}
+
+void GCMDriver::RegisterFinished(const std::string& app_id,
+ const std::string& registration_id,
+ GCMClient::Result result) {
+ auto callback_iter = register_callbacks_.find(app_id);
+ if (callback_iter == register_callbacks_.end()) {
+ // The callback could have been removed when the app is uninstalled.
+ return;
+ }
+
+ RegisterCallback callback = std::move(callback_iter->second);
+ register_callbacks_.erase(callback_iter);
+ std::move(callback).Run(registration_id, result);
+}
+
+void GCMDriver::RemoveEncryptionInfoAfterUnregister(const std::string& app_id,
+ GCMClient::Result result) {
+ encryption_provider_.RemoveEncryptionInfo(
+ app_id, "" /* authorized_entity */,
+ base::BindOnce(&GCMDriver::UnregisterFinished,
+ weak_ptr_factory_.GetWeakPtr(), app_id, result));
+}
+
+void GCMDriver::UnregisterFinished(const std::string& app_id,
+ GCMClient::Result result) {
+ auto callback_iter = unregister_callbacks_.find(app_id);
+ if (callback_iter == unregister_callbacks_.end())
+ return;
+
+ UnregisterCallback callback = std::move(callback_iter->second);
+ unregister_callbacks_.erase(callback_iter);
+ std::move(callback).Run(result);
+}
+
+void GCMDriver::SendFinished(const std::string& app_id,
+ const std::string& message_id,
+ GCMClient::Result result) {
+ auto callback_iter = send_callbacks_.find(
+ std::pair<std::string, std::string>(app_id, message_id));
+ if (callback_iter == send_callbacks_.end()) {
+ // The callback could have been removed when the app is uninstalled.
+ return;
+ }
+
+ SendCallback callback = std::move(callback_iter->second);
+ send_callbacks_.erase(callback_iter);
+ std::move(callback).Run(message_id, result);
+}
+
+void GCMDriver::Shutdown() {
+ for (GCMAppHandlerMap::const_iterator iter = app_handlers_.begin();
+ iter != app_handlers_.end(); ++iter) {
+ DVLOG(1) << "Calling ShutdownHandler for: " << iter->first;
+ iter->second->ShutdownHandler();
+ }
+ app_handlers_.clear();
+}
+
+void GCMDriver::AddAppHandler(const std::string& app_id,
+ GCMAppHandler* handler) {
+ DCHECK(!app_id.empty());
+ DCHECK(handler);
+ DCHECK_EQ(app_handlers_.count(app_id), 0u);
+ app_handlers_[app_id] = handler;
+ DVLOG(1) << "App handler added for: " << app_id;
+}
+
+void GCMDriver::RemoveAppHandler(const std::string& app_id) {
+ DCHECK(!app_id.empty());
+ app_handlers_.erase(app_id);
+ DVLOG(1) << "App handler removed for: " << app_id;
+}
+
+GCMAppHandler* GCMDriver::GetAppHandler(const std::string& app_id) {
+ // Look for exact match.
+ GCMAppHandlerMap::const_iterator iter = app_handlers_.find(app_id);
+ if (iter != app_handlers_.end())
+ return iter->second;
+
+ // Ask the handlers whether they know how to handle it.
+ for (iter = app_handlers_.begin(); iter != app_handlers_.end(); ++iter) {
+ if (iter->second->CanHandle(app_id))
+ return iter->second;
+ }
+
+ return nullptr;
+}
+
+GCMEncryptionProvider* GCMDriver::GetEncryptionProviderInternal() {
+ return &encryption_provider_;
+}
+
+bool GCMDriver::HasRegisterCallback(const std::string& app_id) {
+ return register_callbacks_.find(app_id) != register_callbacks_.end();
+}
+
+void GCMDriver::ClearCallbacks() {
+ register_callbacks_.clear();
+ unregister_callbacks_.clear();
+ send_callbacks_.clear();
+}
+
+void GCMDriver::DispatchMessage(const std::string& app_id,
+ const IncomingMessage& message) {
+ encryption_provider_.DecryptMessage(
+ app_id, message,
+ base::BindOnce(&GCMDriver::DispatchMessageInternal,
+ weak_ptr_factory_.GetWeakPtr(), app_id));
+}
+
+void GCMDriver::DispatchMessageInternal(const std::string& app_id,
+ GCMDecryptionResult result,
+ IncomingMessage message) {
+ UMA_HISTOGRAM_ENUMERATION("GCM.Crypto.DecryptMessageResult", result,
+ GCMDecryptionResult::ENUM_SIZE);
+
+ switch (result) {
+ case GCMDecryptionResult::UNENCRYPTED:
+ case GCMDecryptionResult::DECRYPTED_DRAFT_03:
+ case GCMDecryptionResult::DECRYPTED_DRAFT_08: {
+ GCMAppHandler* handler = GetAppHandler(app_id);
+ UMA_HISTOGRAM_BOOLEAN("GCM.DeliveredToAppHandler", !!handler);
+
+ if (handler)
+ handler->OnMessage(app_id, message);
+
+ // TODO(peter/harkness): Surface unavailable app handlers on
+ // chrome://gcm-internals and send a delivery receipt.
+ return;
+ }
+ case GCMDecryptionResult::INVALID_ENCRYPTION_HEADER:
+ case GCMDecryptionResult::INVALID_CRYPTO_KEY_HEADER:
+ case GCMDecryptionResult::NO_KEYS:
+ case GCMDecryptionResult::INVALID_SHARED_SECRET:
+ case GCMDecryptionResult::INVALID_PAYLOAD:
+ case GCMDecryptionResult::INVALID_BINARY_HEADER_PAYLOAD_LENGTH:
+ case GCMDecryptionResult::INVALID_BINARY_HEADER_RECORD_SIZE:
+ case GCMDecryptionResult::INVALID_BINARY_HEADER_PUBLIC_KEY_LENGTH:
+ case GCMDecryptionResult::INVALID_BINARY_HEADER_PUBLIC_KEY_FORMAT: {
+ RecordDecryptionFailure(app_id, result);
+ GCMAppHandler* handler = GetAppHandler(app_id);
+ if (handler) {
+ handler->OnMessageDecryptionFailed(
+ app_id, message.message_id,
+ ToGCMDecryptionResultDetailsString(result));
+ }
+ return;
+ }
+ case GCMDecryptionResult::ENUM_SIZE:
+ break; // deliberate fall-through
+ }
+
+ NOTREACHED();
+}
+
+void GCMDriver::RegisterAfterUnregister(
+ const std::string& app_id,
+ const std::vector<std::string>& normalized_sender_ids,
+ UnregisterCallback unregister_callback,
+ GCMClient::Result result) {
+ // Invoke the original unregister callback.
+ std::move(unregister_callback).Run(result);
+
+ // Trigger the pending registration.
+ DCHECK(register_callbacks_.find(app_id) != register_callbacks_.end());
+ RegisterImpl(app_id, normalized_sender_ids);
+}
+
+void GCMDriver::EncryptMessage(const std::string& app_id,
+ const std::string& authorized_entity,
+ const std::string& p256dh,
+ const std::string& auth_secret,
+ const std::string& message,
+ EncryptMessageCallback callback) {
+ encryption_provider_.EncryptMessage(
+ app_id, authorized_entity, p256dh, auth_secret, message,
+ base::BindOnce(&GCMDriver::OnMessageEncrypted,
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void GCMDriver::OnMessageEncrypted(EncryptMessageCallback callback,
+ GCMEncryptionResult result,
+ std::string message) {
+ UMA_HISTOGRAM_ENUMERATION("GCM.Crypto.EncryptMessageResult", result,
+ GCMEncryptionResult::ENUM_SIZE);
+ std::move(callback).Run(result, std::move(message));
+}
+
+void GCMDriver::DecryptMessage(const std::string& app_id,
+ const std::string& authorized_entity,
+ const std::string& message,
+ DecryptMessageCallback callback) {
+ IncomingMessage incoming_message;
+ incoming_message.sender_id = authorized_entity;
+ incoming_message.raw_data = message;
+ incoming_message.data[GCMEncryptionProvider::kContentEncodingProperty] =
+ GCMEncryptionProvider::kContentCodingAes128Gcm;
+ encryption_provider_.DecryptMessage(
+ app_id, incoming_message,
+ base::BindOnce(&GCMDriver::OnMessageDecrypted,
+ weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void GCMDriver::OnMessageDecrypted(DecryptMessageCallback callback,
+ GCMDecryptionResult result,
+ IncomingMessage message) {
+ UMA_HISTOGRAM_ENUMERATION("GCM.Crypto.DecryptMessageResult", result,
+ GCMDecryptionResult::ENUM_SIZE);
+ std::move(callback).Run(result, std::move(message.raw_data));
+}
+
+} // namespace gcm