OLD | NEW |
| (Empty) |
1 // Copyright 2014 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 "extensions/browser/api/copresence_endpoints/copresence_endpoints_api.h
" | |
6 | |
7 #include "base/base64.h" | |
8 #include "base/json/json_reader.h" | |
9 #include "base/json/json_writer.h" | |
10 #include "base/lazy_instance.h" | |
11 #include "base/strings/string_number_conversions.h" | |
12 #include "base/strings/string_piece.h" | |
13 #include "base/strings/string_util.h" | |
14 #include "components/copresence_endpoints/public/copresence_endpoint.h" | |
15 #include "content/public/browser/browser_context.h" | |
16 #include "extensions/browser/api/copresence_endpoints/copresence_endpoint_resour
ce.h" | |
17 #include "extensions/browser/event_router.h" | |
18 #include "extensions/common/api/copresence_endpoints.h" | |
19 #include "net/base/io_buffer.h" | |
20 | |
21 using copresence_endpoints::CopresenceEndpoint; | |
22 | |
23 namespace extensions { | |
24 | |
25 namespace { | |
26 | |
27 const size_t kSizeBytes = 2; | |
28 const char kToField[] = "to"; | |
29 const char kDataField[] = "data"; | |
30 const char kReplyToField[] = "replyTo"; | |
31 | |
32 bool Base64DecodeWithoutPadding(const std::string& data, std::string* out) { | |
33 std::string ret = data; | |
34 while (ret.size() % 4) | |
35 ret.push_back('='); | |
36 | |
37 if (!base::Base64Decode(ret, &ret)) | |
38 return false; | |
39 | |
40 out->swap(ret); | |
41 return true; | |
42 } | |
43 | |
44 std::string Base64EncodeWithoutPadding(const std::string& data) { | |
45 std::string ret = data; | |
46 base::Base64Encode(ret, &ret); | |
47 while (*(ret.end() - 1) == '=') | |
48 ret.erase(ret.end() - 1); | |
49 return ret; | |
50 } | |
51 | |
52 // Create a message to send to another endpoint. | |
53 std::string CreateMessage(const std::vector<char>& data, | |
54 const std::string& local_endpoint_locator, | |
55 int remote_endpoint_id) { | |
56 base::DictionaryValue dict; | |
57 dict.SetString(kToField, base::IntToString(remote_endpoint_id)); | |
58 dict.SetString(kDataField, Base64EncodeWithoutPadding( | |
59 std::string(data.begin(), data.end()))); | |
60 dict.SetString(kReplyToField, | |
61 Base64EncodeWithoutPadding(local_endpoint_locator)); | |
62 | |
63 std::string json; | |
64 base::JSONWriter::Write(&dict, &json); | |
65 | |
66 std::string message; | |
67 message.push_back(static_cast<unsigned char>(json.size() & 0xff)); | |
68 message.push_back(static_cast<unsigned char>(json.size() >> 8)); | |
69 | |
70 message.append(json); | |
71 return message; | |
72 } | |
73 | |
74 bool IsMessageComplete(const std::string& message, size_t* length) { | |
75 if (message.size() < kSizeBytes) | |
76 return false; | |
77 | |
78 // message[1] = upper order 8 bits. | |
79 // message[0] = lower order 8 bits. | |
80 size_t message_length = (static_cast<size_t>(message[1]) << 8) | | |
81 (static_cast<size_t>(message[0]) & 0xff); | |
82 | |
83 if (message.size() >= kSizeBytes + message_length) { | |
84 *length = message_length; | |
85 return true; | |
86 } | |
87 | |
88 return false; | |
89 } | |
90 | |
91 bool ExtractEndpointId(const std::string& endpoint_locator, int* id) { | |
92 std::vector<std::string> tokens; | |
93 if (Tokenize(endpoint_locator, ".", &tokens) <= 1) | |
94 return false; | |
95 | |
96 if (!base::StringToInt(tokens[0], id)) | |
97 return false; | |
98 | |
99 return true; | |
100 } | |
101 | |
102 // Parse a message received from another endpoint. | |
103 bool ParseReceivedMessage(const std::string& message, | |
104 std::string* data, | |
105 int* remote_endpoint_id) { | |
106 scoped_ptr<base::Value> value(base::JSONReader::Read(message)); | |
107 | |
108 // Check to see that we have a valid dictionary. | |
109 base::DictionaryValue* dict = nullptr; | |
110 if (!value || !value->GetAsDictionary(&dict) || !dict->HasKey(kDataField)) { | |
111 LOG(WARNING) << "Invalid message: " << message; | |
112 return false; | |
113 } | |
114 | |
115 // The fields in the json string are, | |
116 // to: Endpoint Id this message is meant for (unused atm, we only support one | |
117 // local endpoint). TODO(rkc): Fix this to support multiple endpoints. | |
118 // data: Data content of the message. | |
119 // replyTo: Sender of this message (in the locator data format). We only need | |
120 // the endpoint id that we have to reply to, but currently we get | |
121 // the full locator. TODO(rkc): Fix this once other platforms change | |
122 // over to only sending us the ID. | |
123 if (!dict->GetStringASCII(kDataField, data)) | |
124 return false; | |
125 if (!Base64DecodeWithoutPadding(*data, data)) | |
126 return false; | |
127 | |
128 std::string endpoint_locator; | |
129 if (!dict->GetStringASCII(kReplyToField, &endpoint_locator)) | |
130 return false; | |
131 if (!Base64DecodeWithoutPadding(endpoint_locator, &endpoint_locator)) | |
132 return false; | |
133 | |
134 if (!ExtractEndpointId(endpoint_locator, remote_endpoint_id)) | |
135 return false; | |
136 | |
137 VLOG(3) << "Valid message parsed."; | |
138 return true; | |
139 } | |
140 | |
141 } // namespace | |
142 | |
143 // CopresenceEndpointFunction public methods: | |
144 | |
145 CopresenceEndpointFunction::CopresenceEndpointFunction() | |
146 : endpoints_manager_(nullptr) { | |
147 } | |
148 | |
149 void CopresenceEndpointFunction::DispatchOnConnectedEvent(int endpoint_id) { | |
150 // Send the messages to the client app. | |
151 scoped_ptr<Event> event(new Event( | |
152 core_api::copresence_endpoints::OnConnected::kEventName, | |
153 core_api::copresence_endpoints::OnConnected::Create(endpoint_id), | |
154 browser_context())); | |
155 EventRouter::Get(browser_context()) | |
156 ->DispatchEventToExtension(extension_id(), event.Pass()); | |
157 VLOG(2) << "Dispatched OnConnected event: endpointId = " << endpoint_id; | |
158 } | |
159 | |
160 // CopresenceEndpointFunction protected methods: | |
161 | |
162 int CopresenceEndpointFunction::AddEndpoint( | |
163 CopresenceEndpointResource* endpoint) { | |
164 return endpoints_manager_->Add(endpoint); | |
165 } | |
166 | |
167 void CopresenceEndpointFunction::ReplaceEndpoint( | |
168 const std::string& extension_id, | |
169 int endpoint_id, | |
170 CopresenceEndpointResource* endpoint) { | |
171 endpoints_manager_->Replace(extension_id, endpoint_id, endpoint); | |
172 } | |
173 | |
174 CopresenceEndpointResource* CopresenceEndpointFunction::GetEndpoint( | |
175 int endpoint_id) { | |
176 return endpoints_manager_->Get(extension_id(), endpoint_id); | |
177 } | |
178 | |
179 void CopresenceEndpointFunction::RemoveEndpoint(int endpoint_id) { | |
180 endpoints_manager_->Remove(extension_id(), endpoint_id); | |
181 } | |
182 | |
183 ExtensionFunction::ResponseAction CopresenceEndpointFunction::Run() { | |
184 Initialize(); | |
185 return Execute(); | |
186 } | |
187 | |
188 // CopresenceEndpointFunction private methods: | |
189 | |
190 CopresenceEndpointFunction::~CopresenceEndpointFunction() { | |
191 } | |
192 | |
193 void CopresenceEndpointFunction::Initialize() { | |
194 endpoints_manager_ = | |
195 ApiResourceManager<CopresenceEndpointResource>::Get(browser_context()); | |
196 } | |
197 | |
198 void CopresenceEndpointFunction::OnDataReceived( | |
199 int local_endpoint_id, | |
200 const scoped_refptr<net::IOBuffer>& buffer, | |
201 int size) { | |
202 CopresenceEndpointResource* local_endpoint = GetEndpoint(local_endpoint_id); | |
203 if (!local_endpoint) { | |
204 VLOG(2) << "Receiving endpoint not found. ID = " << local_endpoint_id; | |
205 return; | |
206 } | |
207 | |
208 std::string& packet = local_endpoint->packet(); | |
209 packet.append(std::string(buffer->data(), size)); | |
210 size_t message_length; | |
211 if (IsMessageComplete(packet, &message_length)) { | |
212 std::string message_data; | |
213 int remote_endpoint_id; | |
214 if (ParseReceivedMessage(packet.substr(kSizeBytes, message_length), | |
215 &message_data, &remote_endpoint_id)) { | |
216 DispatchOnReceiveEvent(local_endpoint_id, remote_endpoint_id, | |
217 message_data); | |
218 } else { | |
219 LOG(WARNING) << "Invalid message received: " | |
220 << packet.substr(kSizeBytes, message_length) | |
221 << " of length: " << message_length; | |
222 } | |
223 | |
224 if (packet.size() > message_length + kSizeBytes) { | |
225 packet = packet.substr(message_length + kSizeBytes); | |
226 } else { | |
227 packet.clear(); | |
228 } | |
229 } | |
230 } | |
231 | |
232 void CopresenceEndpointFunction::DispatchOnReceiveEvent( | |
233 int local_endpoint_id, | |
234 int remote_endpoint_id, | |
235 const std::string& data) { | |
236 core_api::copresence_endpoints::ReceiveInfo info; | |
237 info.local_endpoint_id = local_endpoint_id; | |
238 info.remote_endpoint_id = remote_endpoint_id; | |
239 info.data.assign(data.begin(), data.end()); | |
240 // Send the data to the client app. | |
241 scoped_ptr<Event> event( | |
242 new Event(core_api::copresence_endpoints::OnReceive::kEventName, | |
243 core_api::copresence_endpoints::OnReceive::Create(info), | |
244 browser_context())); | |
245 EventRouter::Get(browser_context()) | |
246 ->DispatchEventToExtension(extension_id(), event.Pass()); | |
247 VLOG(2) << "Dispatched OnReceive event: localEndpointId = " | |
248 << local_endpoint_id << ", remoteEndpointId = " << remote_endpoint_id | |
249 << " and data = " << data; | |
250 } | |
251 | |
252 // CopresenceEndpointsCreateLocalEndpointFunction implementation: | |
253 ExtensionFunction::ResponseAction | |
254 CopresenceEndpointsCreateLocalEndpointFunction::Execute() { | |
255 // Add an empty endpoint to create a placeholder endpoint_id. We will need to | |
256 // bind | |
257 // this id to the OnConnected event dispatcher, so we need it before we | |
258 // create the actual endpoint. Once we have the endpoint created, we'll | |
259 // replace the | |
260 // placeholder with the actual endpoint object. | |
261 int endpoint_id = | |
262 AddEndpoint(new CopresenceEndpointResource(extension_id(), nullptr)); | |
263 | |
264 scoped_ptr<CopresenceEndpoint> endpoint = | |
265 make_scoped_ptr(new CopresenceEndpoint( | |
266 endpoint_id, | |
267 base::Bind(&CopresenceEndpointsCreateLocalEndpointFunction::OnCreated, | |
268 this, endpoint_id), | |
269 base::Bind(&CopresenceEndpointFunction::DispatchOnConnectedEvent, | |
270 this, endpoint_id), | |
271 base::Bind(&CopresenceEndpointFunction::OnDataReceived, this, | |
272 endpoint_id))); | |
273 | |
274 ReplaceEndpoint( | |
275 extension_id(), endpoint_id, | |
276 new CopresenceEndpointResource(extension_id(), endpoint.Pass())); | |
277 | |
278 return RespondLater(); | |
279 } | |
280 | |
281 void CopresenceEndpointsCreateLocalEndpointFunction::OnCreated( | |
282 int endpoint_id, | |
283 const std::string& locator) { | |
284 core_api::copresence_endpoints::EndpointInfo endpoint_info; | |
285 endpoint_info.endpoint_id = endpoint_id; | |
286 endpoint_info.locator = locator; | |
287 Respond(ArgumentList( | |
288 core_api::copresence_endpoints::CreateLocalEndpoint::Results::Create( | |
289 endpoint_info))); | |
290 } | |
291 | |
292 // CopresenceEndpointsDestroyEndpointFunction implementation: | |
293 ExtensionFunction::ResponseAction | |
294 CopresenceEndpointsDestroyEndpointFunction::Execute() { | |
295 scoped_ptr<core_api::copresence_endpoints::DestroyEndpoint::Params> params( | |
296 core_api::copresence_endpoints::DestroyEndpoint::Params::Create(*args_)); | |
297 EXTENSION_FUNCTION_VALIDATE(params.get()); | |
298 | |
299 RemoveEndpoint(params->endpoint_id); | |
300 return RespondNow(NoArguments()); | |
301 } | |
302 | |
303 // CopresenceEndpointsSendFunction implementation: | |
304 ExtensionFunction::ResponseAction CopresenceEndpointsSendFunction::Execute() { | |
305 scoped_ptr<core_api::copresence_endpoints::Send::Params> params( | |
306 core_api::copresence_endpoints::Send::Params::Create(*args_)); | |
307 EXTENSION_FUNCTION_VALIDATE(params.get()); | |
308 | |
309 CopresenceEndpointResource* endpoint = GetEndpoint(params->local_endpoint_id); | |
310 if (!endpoint) { | |
311 VLOG(1) << "Endpoint not found. ID = " << params->local_endpoint_id; | |
312 return RespondNow( | |
313 ArgumentList(core_api::copresence_endpoints::Send::Results::Create( | |
314 core_api::copresence_endpoints:: | |
315 ENDPOINT_STATUS_INVALID_LOCAL_ENDPOINT))); | |
316 } | |
317 DCHECK(endpoint->endpoint()); | |
318 | |
319 const std::string& message = | |
320 CreateMessage(params->data, endpoint->endpoint()->GetLocator(), | |
321 params->remote_endpoint_id); | |
322 VLOG(3) << "Sending message to remote_endpoint_id = " | |
323 << params->remote_endpoint_id | |
324 << " from local_endpoint_id = " << params->local_endpoint_id | |
325 << " with data[0] = " << static_cast<int>(message[0]) | |
326 << ", data[1] = " << static_cast<int>(message[1]) << ", data[2:] = " | |
327 << std::string((message.c_str() + 2), message.size() - 2); | |
328 | |
329 endpoint->endpoint()->Send(new net::StringIOBuffer(message), message.size()); | |
330 | |
331 return RespondNow( | |
332 ArgumentList(core_api::copresence_endpoints::Send::Results::Create( | |
333 core_api::copresence_endpoints::ENDPOINT_STATUS_NO_ERROR))); | |
334 } | |
335 | |
336 } // namespace extensions | |
OLD | NEW |