• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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