| 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 |