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