diff options
Diffstat (limited to 'chromium/components/gcm_driver/gcm_driver.cc')
-rw-r--r-- | chromium/components/gcm_driver/gcm_driver.cc | 373 |
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 |