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/browser/extensions/api/gcm/gcm_api.h"
6
7 #include <algorithm>
8 #include <map>
9 #include <vector>
10
11 #include "base/sha1.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "chrome/browser/extensions/extension_system.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/services/gcm/gcm_profile_service.h"
17 #include "chrome/browser/services/gcm/gcm_profile_service_factory.h"
18 #include "chrome/common/extensions/api/gcm.h"
19 #include "extensions/browser/event_router.h"
20 #include "extensions/common/extension.h"
21
22 namespace {
23
24 const size_t kMaximumMessageSize = 4096; // in bytes.
25 const char kGoogDotRestrictedPrefix[] = "goog.";
26 const size_t kGoogDotPrefixLength = arraysize(kGoogDotRestrictedPrefix) - 1;
27 const char kGoogleRestrictedPrefix[] = "google";
28 const size_t kGooglePrefixLength = arraysize(kGoogleRestrictedPrefix) - 1;
29
30 // Error messages.
31 const char kInvalidParameter[] =
32 "Function was called with invalid parameters.";
33 const char kAsyncOperationPending[] =
34 "Asynchronous operation is pending.";
35 const char kNetworkError[] = "Network error occured.";
36 const char kServerError[] = "Server error occured.";
37 const char kTtlExceeded[] = "Time-to-live exceeded.";
38 const char kUnknownError[] = "Unknown error occured.";
39
SHA1HashHexString(const std::string & str)40 std::string SHA1HashHexString(const std::string& str) {
41 std::string hash = base::SHA1HashString(str);
42 return base::HexEncode(hash.data(), hash.size());
43 }
44
GcmResultToError(gcm::GCMClient::Result result)45 const char* GcmResultToError(gcm::GCMClient::Result result) {
46 switch (result) {
47 case gcm::GCMClient::SUCCESS:
48 return "";
49 case gcm::GCMClient::INVALID_PARAMETER:
50 return kInvalidParameter;
51 case gcm::GCMClient::ASYNC_OPERATION_PENDING:
52 return kAsyncOperationPending;
53 case gcm::GCMClient::NETWORK_ERROR:
54 return kNetworkError;
55 case gcm::GCMClient::SERVER_ERROR:
56 return kServerError;
57 case gcm::GCMClient::TTL_EXCEEDED:
58 return kTtlExceeded;
59 case gcm::GCMClient::UNKNOWN_ERROR:
60 return kUnknownError;
61 default:
62 NOTREACHED() << "Unexpected value of result cannot be converted: "
63 << result;
64 }
65
66 // Never reached, but prevents missing return statement warning.
67 return "";
68 }
69
IsMessageKeyValid(const std::string & key)70 bool IsMessageKeyValid(const std::string& key) {
71 return !key.empty() &&
72 key.compare(0, kGooglePrefixLength, kGoogleRestrictedPrefix) != 0 &&
73 key.compare(0, kGoogDotPrefixLength, kGoogDotRestrictedPrefix) != 0;
74 }
75
76 } // namespace
77
78 namespace extensions {
79
RunImpl()80 bool GcmApiFunction::RunImpl() {
81 if (!IsGcmApiEnabled())
82 return false;
83
84 return DoWork();
85 }
86
IsGcmApiEnabled() const87 bool GcmApiFunction::IsGcmApiEnabled() const {
88 return gcm::GCMProfileService::IsGCMEnabled() &&
89 !GetExtension()->public_key().empty();
90 }
91
GCMProfileService() const92 gcm::GCMProfileService* GcmApiFunction::GCMProfileService() const {
93 return gcm::GCMProfileServiceFactory::GetForProfile(
94 Profile::FromBrowserContext(context()));
95 }
96
GcmRegisterFunction()97 GcmRegisterFunction::GcmRegisterFunction() {}
98
~GcmRegisterFunction()99 GcmRegisterFunction::~GcmRegisterFunction() {}
100
DoWork()101 bool GcmRegisterFunction::DoWork() {
102 scoped_ptr<api::gcm::Register::Params> params(
103 api::gcm::Register::Params::Create(*args_));
104 EXTENSION_FUNCTION_VALIDATE(params.get());
105
106 // Caching the values so that it is possbile to safely pass them by reference.
107 sender_ids_ = params->sender_ids;
108 cert_ = SHA1HashHexString(GetExtension()->public_key());
109
110 GCMProfileService()->Register(
111 GetExtension()->id(),
112 sender_ids_,
113 cert_,
114 base::Bind(&GcmRegisterFunction::CompleteFunctionWithResult, this));
115
116 return true;
117 }
118
CompleteFunctionWithResult(const std::string & registration_id,gcm::GCMClient::Result result)119 void GcmRegisterFunction::CompleteFunctionWithResult(
120 const std::string& registration_id,
121 gcm::GCMClient::Result result) {
122 SetResult(base::Value::CreateStringValue(registration_id));
123 SetError(GcmResultToError(result));
124 SendResponse(gcm::GCMClient::SUCCESS == result);
125 }
126
GcmSendFunction()127 GcmSendFunction::GcmSendFunction() {}
128
~GcmSendFunction()129 GcmSendFunction::~GcmSendFunction() {}
130
DoWork()131 bool GcmSendFunction::DoWork() {
132 scoped_ptr<api::gcm::Send::Params> params(
133 api::gcm::Send::Params::Create(*args_));
134 EXTENSION_FUNCTION_VALIDATE(params.get());
135 EXTENSION_FUNCTION_VALIDATE(
136 ValidateMessageData(params->message.data.additional_properties));
137
138 // Caching the values so that it is possbile to safely pass them by reference.
139 outgoing_message_.id = params->message.message_id;
140 outgoing_message_.data = params->message.data.additional_properties;
141 if (params->message.time_to_live.get())
142 outgoing_message_.time_to_live = *params->message.time_to_live;
143 destination_id_ = params->message.destination_id;
144
145 GCMProfileService()->Send(
146 GetExtension()->id(),
147 params->message.destination_id,
148 outgoing_message_,
149 base::Bind(&GcmSendFunction::CompleteFunctionWithResult, this));
150
151 return true;
152 }
153
CompleteFunctionWithResult(const std::string & message_id,gcm::GCMClient::Result result)154 void GcmSendFunction::CompleteFunctionWithResult(
155 const std::string& message_id,
156 gcm::GCMClient::Result result) {
157 SetResult(base::Value::CreateStringValue(message_id));
158 SetError(GcmResultToError(result));
159 SendResponse(gcm::GCMClient::SUCCESS == result);
160 }
161
ValidateMessageData(const gcm::GCMClient::MessageData & data) const162 bool GcmSendFunction::ValidateMessageData(
163 const gcm::GCMClient::MessageData& data) const {
164 size_t total_size = 0u;
165 for (std::map<std::string, std::string>::const_iterator iter = data.begin();
166 iter != data.end(); ++iter) {
167 total_size += iter->first.size() + iter->second.size();
168
169 if (!IsMessageKeyValid(iter->first) ||
170 kMaximumMessageSize < iter->first.size() ||
171 kMaximumMessageSize < iter->second.size() ||
172 kMaximumMessageSize < total_size)
173 return false;
174 }
175
176 return total_size != 0;
177 }
178
GcmJsEventRouter(Profile * profile)179 GcmJsEventRouter::GcmJsEventRouter(Profile* profile) : profile_(profile) {}
180
~GcmJsEventRouter()181 GcmJsEventRouter::~GcmJsEventRouter() {}
182
OnMessage(const std::string & app_id,const gcm::GCMClient::IncomingMessage & message)183 void GcmJsEventRouter::OnMessage(
184 const std::string& app_id,
185 const gcm::GCMClient::IncomingMessage& message) {
186 api::gcm::OnMessage::Message message_arg;
187 message_arg.data.additional_properties = message.data;
188
189 scoped_ptr<Event> event(new Event(
190 api::gcm::OnMessage::kEventName,
191 api::gcm::OnMessage::Create(message_arg).Pass(),
192 profile_));
193 ExtensionSystem::Get(profile_)->event_router()->DispatchEventToExtension(
194 app_id, event.Pass());
195 }
196
OnMessagesDeleted(const std::string & app_id)197 void GcmJsEventRouter::OnMessagesDeleted(const std::string& app_id) {
198 scoped_ptr<Event> event(new Event(
199 api::gcm::OnMessagesDeleted::kEventName,
200 api::gcm::OnMessagesDeleted::Create().Pass(),
201 profile_));
202 ExtensionSystem::Get(profile_)->event_router()->DispatchEventToExtension(
203 app_id, event.Pass());
204 }
205
OnSendError(const std::string & app_id,const std::string & message_id,gcm::GCMClient::Result result)206 void GcmJsEventRouter::OnSendError(const std::string& app_id,
207 const std::string& message_id,
208 gcm::GCMClient::Result result) {
209 api::gcm::OnSendError::Error error;
210 error.message_id.reset(new std::string(message_id));
211 error.error_message = GcmResultToError(result);
212
213 scoped_ptr<Event> event(new Event(
214 api::gcm::OnSendError::kEventName,
215 api::gcm::OnSendError::Create(error).Pass(),
216 profile_));
217 ExtensionSystem::Get(profile_)->event_router()->DispatchEventToExtension(
218 app_id, event.Pass());
219 }
220
221 } // namespace extensions
222