• 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/metrics/histogram.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_util.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 "components/gcm_driver/gcm_driver.h"
20 #include "extensions/browser/event_router.h"
21 #include "extensions/common/extension.h"
22 
23 namespace {
24 
25 const size_t kMaximumMessageSize = 4096;  // in bytes.
26 const char kCollapseKey[] = "collapse_key";
27 const char kGoogDotRestrictedPrefix[] = "goog.";
28 const char kGoogleRestrictedPrefix[] = "google";
29 
30 // Error messages.
31 const char kInvalidParameter[] =
32     "Function was called with invalid parameters.";
33 const char kGCMDisabled[] = "GCM is currently disabled.";
34 const char kNotSignedIn[] = "Profile was not signed in.";
35 const char kAsyncOperationPending[] =
36     "Asynchronous operation is pending.";
37 const char kNetworkError[] = "Network error occurred.";
38 const char kServerError[] = "Server error occurred.";
39 const char kTtlExceeded[] = "Time-to-live exceeded.";
40 const char kUnknownError[] = "Unknown error occurred.";
41 
GcmResultToError(gcm::GCMClient::Result result)42 const char* GcmResultToError(gcm::GCMClient::Result result) {
43   switch (result) {
44     case gcm::GCMClient::SUCCESS:
45       return "";
46     case gcm::GCMClient::INVALID_PARAMETER:
47       return kInvalidParameter;
48     case gcm::GCMClient::GCM_DISABLED:
49       return kGCMDisabled;
50     case gcm::GCMClient::NOT_SIGNED_IN:
51       return kNotSignedIn;
52     case gcm::GCMClient::ASYNC_OPERATION_PENDING:
53       return kAsyncOperationPending;
54     case gcm::GCMClient::NETWORK_ERROR:
55       return kNetworkError;
56     case gcm::GCMClient::SERVER_ERROR:
57       return kServerError;
58     case gcm::GCMClient::TTL_EXCEEDED:
59       return kTtlExceeded;
60     case gcm::GCMClient::UNKNOWN_ERROR:
61       return kUnknownError;
62     default:
63       NOTREACHED() << "Unexpected value of result cannot be converted: "
64                    << result;
65   }
66 
67   // Never reached, but prevents missing return statement warning.
68   return "";
69 }
70 
IsMessageKeyValid(const std::string & key)71 bool IsMessageKeyValid(const std::string& key) {
72   std::string lower = base::StringToLowerASCII(key);
73   return !key.empty() &&
74          key.compare(0, arraysize(kCollapseKey) - 1, kCollapseKey) != 0 &&
75          lower.compare(0,
76                        arraysize(kGoogleRestrictedPrefix) - 1,
77                        kGoogleRestrictedPrefix) != 0 &&
78          lower.compare(0,
79                        arraysize(kGoogDotRestrictedPrefix),
80                        kGoogDotRestrictedPrefix) != 0;
81 }
82 
83 }  // namespace
84 
85 namespace extensions {
86 
RunAsync()87 bool GcmApiFunction::RunAsync() {
88   if (!IsGcmApiEnabled())
89     return false;
90 
91   return DoWork();
92 }
93 
IsGcmApiEnabled() const94 bool GcmApiFunction::IsGcmApiEnabled() const {
95   Profile* profile = Profile::FromBrowserContext(browser_context());
96 
97   // GCM is not supported in incognito mode.
98   if (profile->IsOffTheRecord())
99     return false;
100 
101   return gcm::GCMProfileService::IsGCMEnabled(profile);
102 }
103 
GetGCMDriver() const104 gcm::GCMDriver* GcmApiFunction::GetGCMDriver() const {
105   return gcm::GCMProfileServiceFactory::GetForProfile(
106       Profile::FromBrowserContext(browser_context()))->driver();
107 }
108 
GcmRegisterFunction()109 GcmRegisterFunction::GcmRegisterFunction() {}
110 
~GcmRegisterFunction()111 GcmRegisterFunction::~GcmRegisterFunction() {}
112 
DoWork()113 bool GcmRegisterFunction::DoWork() {
114   scoped_ptr<api::gcm::Register::Params> params(
115       api::gcm::Register::Params::Create(*args_));
116   EXTENSION_FUNCTION_VALIDATE(params.get());
117 
118   GetGCMDriver()->Register(
119       extension()->id(),
120       params->sender_ids,
121       base::Bind(&GcmRegisterFunction::CompleteFunctionWithResult, this));
122 
123   return true;
124 }
125 
CompleteFunctionWithResult(const std::string & registration_id,gcm::GCMClient::Result result)126 void GcmRegisterFunction::CompleteFunctionWithResult(
127     const std::string& registration_id,
128     gcm::GCMClient::Result result) {
129   SetResult(new base::StringValue(registration_id));
130   SetError(GcmResultToError(result));
131   SendResponse(gcm::GCMClient::SUCCESS == result);
132 }
133 
GcmUnregisterFunction()134 GcmUnregisterFunction::GcmUnregisterFunction() {}
135 
~GcmUnregisterFunction()136 GcmUnregisterFunction::~GcmUnregisterFunction() {}
137 
DoWork()138 bool GcmUnregisterFunction::DoWork() {
139   UMA_HISTOGRAM_BOOLEAN("GCM.APICallUnregister", true);
140 
141   GetGCMDriver()->Unregister(
142       extension()->id(),
143       base::Bind(&GcmUnregisterFunction::CompleteFunctionWithResult, this));
144 
145   return true;
146 }
147 
CompleteFunctionWithResult(gcm::GCMClient::Result result)148 void GcmUnregisterFunction::CompleteFunctionWithResult(
149     gcm::GCMClient::Result result) {
150   SetError(GcmResultToError(result));
151   SendResponse(gcm::GCMClient::SUCCESS == result);
152 }
153 
GcmSendFunction()154 GcmSendFunction::GcmSendFunction() {}
155 
~GcmSendFunction()156 GcmSendFunction::~GcmSendFunction() {}
157 
DoWork()158 bool GcmSendFunction::DoWork() {
159   scoped_ptr<api::gcm::Send::Params> params(
160       api::gcm::Send::Params::Create(*args_));
161   EXTENSION_FUNCTION_VALIDATE(params.get());
162   EXTENSION_FUNCTION_VALIDATE(
163       ValidateMessageData(params->message.data.additional_properties));
164 
165   gcm::GCMClient::OutgoingMessage outgoing_message;
166   outgoing_message.id = params->message.message_id;
167   outgoing_message.data = params->message.data.additional_properties;
168   if (params->message.time_to_live.get())
169     outgoing_message.time_to_live = *params->message.time_to_live;
170 
171   GetGCMDriver()->Send(
172       extension()->id(),
173       params->message.destination_id,
174       outgoing_message,
175       base::Bind(&GcmSendFunction::CompleteFunctionWithResult, this));
176 
177   return true;
178 }
179 
CompleteFunctionWithResult(const std::string & message_id,gcm::GCMClient::Result result)180 void GcmSendFunction::CompleteFunctionWithResult(
181     const std::string& message_id,
182     gcm::GCMClient::Result result) {
183   SetResult(new base::StringValue(message_id));
184   SetError(GcmResultToError(result));
185   SendResponse(gcm::GCMClient::SUCCESS == result);
186 }
187 
ValidateMessageData(const gcm::GCMClient::MessageData & data) const188 bool GcmSendFunction::ValidateMessageData(
189     const gcm::GCMClient::MessageData& data) const {
190   size_t total_size = 0u;
191   for (std::map<std::string, std::string>::const_iterator iter = data.begin();
192        iter != data.end(); ++iter) {
193     total_size += iter->first.size() + iter->second.size();
194 
195     if (!IsMessageKeyValid(iter->first) ||
196         kMaximumMessageSize < iter->first.size() ||
197         kMaximumMessageSize < iter->second.size() ||
198         kMaximumMessageSize < total_size)
199       return false;
200   }
201 
202   return total_size != 0;
203 }
204 
GcmJsEventRouter(Profile * profile)205 GcmJsEventRouter::GcmJsEventRouter(Profile* profile) : profile_(profile) {
206 }
207 
~GcmJsEventRouter()208 GcmJsEventRouter::~GcmJsEventRouter() {
209 }
210 
OnMessage(const std::string & app_id,const gcm::GCMClient::IncomingMessage & message)211 void GcmJsEventRouter::OnMessage(
212     const std::string& app_id,
213     const gcm::GCMClient::IncomingMessage& message) {
214   api::gcm::OnMessage::Message message_arg;
215   message_arg.data.additional_properties = message.data;
216   if (!message.collapse_key.empty())
217     message_arg.collapse_key.reset(new std::string(message.collapse_key));
218 
219   scoped_ptr<Event> event(new Event(
220       api::gcm::OnMessage::kEventName,
221       api::gcm::OnMessage::Create(message_arg).Pass(),
222       profile_));
223   EventRouter::Get(profile_)->DispatchEventToExtension(app_id, event.Pass());
224 }
225 
OnMessagesDeleted(const std::string & app_id)226 void GcmJsEventRouter::OnMessagesDeleted(const std::string& app_id) {
227   scoped_ptr<Event> event(new Event(
228       api::gcm::OnMessagesDeleted::kEventName,
229       api::gcm::OnMessagesDeleted::Create().Pass(),
230       profile_));
231   EventRouter::Get(profile_)->DispatchEventToExtension(app_id, event.Pass());
232 }
233 
OnSendError(const std::string & app_id,const gcm::GCMClient::SendErrorDetails & send_error_details)234 void GcmJsEventRouter::OnSendError(
235     const std::string& app_id,
236     const gcm::GCMClient::SendErrorDetails& send_error_details) {
237   api::gcm::OnSendError::Error error;
238   error.message_id.reset(new std::string(send_error_details.message_id));
239   error.error_message = GcmResultToError(send_error_details.result);
240   error.details.additional_properties = send_error_details.additional_data;
241 
242   scoped_ptr<Event> event(new Event(
243       api::gcm::OnSendError::kEventName,
244       api::gcm::OnSendError::Create(error).Pass(),
245       profile_));
246   EventRouter::Get(profile_)->DispatchEventToExtension(app_id, event.Pass());
247 }
248 
249 }  // namespace extensions
250