Upload upstream chromium 69.0.3497
[platform/framework/web/chromium-efl.git] / components / cast_channel / cast_message_util.cc
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 "components/cast_channel/cast_message_util.h"
6
7 #include <memory>
8
9 #include "base/json/json_reader.h"
10 #include "base/json/json_writer.h"
11 #include "base/logging.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_util.h"
14 #include "build/build_config.h"
15 #include "components/cast_channel/cast_auth_util.h"
16 #include "components/cast_channel/proto/cast_channel.pb.h"
17
18 using base::Value;
19
20 namespace cast_channel {
21
22 namespace {
23 // Reserved message namespaces for internal messages.
24 constexpr char kCastInternalNamespacePrefix[] = "urn:x-cast:com.google.cast.";
25 constexpr char kAuthNamespace[] = "urn:x-cast:com.google.cast.tp.deviceauth";
26 constexpr char kHeartbeatNamespace[] =
27     "urn:x-cast:com.google.cast.tp.heartbeat";
28 constexpr char kConnectionNamespace[] =
29     "urn:x-cast:com.google.cast.tp.connection";
30 constexpr char kReceiverNamespace[] = "urn:x-cast:com.google.cast.receiver";
31 constexpr char kBroadcastNamespace[] = "urn:x-cast:com.google.cast.broadcast";
32
33 // Text payload keys.
34 constexpr char kTypeNodeId[] = "type";
35 constexpr char kRequestIdNodeId[] = "requestId";
36
37 // Cast application protocol message types.
38 constexpr char kKeepAlivePingType[] = "PING";
39 constexpr char kKeepAlivePongType[] = "PONG";
40 constexpr char kGetAppAvailabilityRequestType[] = "GET_APP_AVAILABILITY";
41 constexpr char kConnectionRequestType[] = "CONNECT";
42 constexpr char kBroadcastRequestType[] = "APPLICATION_BROADCAST";
43 constexpr char kLaunchRequestType[] = "LAUNCH";
44 constexpr char kReceiverStatusType[] = "RECEIVER_STATUS";
45 constexpr char kLaunchErrorType[] = "LAUNCH_ERROR";
46
47 // The value used for "sdkType" in a virtual connect request. Historically, this
48 // value is used in the Media Router extension, but here it is reused in Chrome.
49 constexpr int kVirtualConnectSdkType = 2;
50
51 // The value used for "connectionType" in a virtual connect request. This value
52 // stands for CONNECTION_TYPE_LOCAL, which is the only type used in Chrome.
53 constexpr int kVirtualConnectTypeLocal = 1;
54
55 void FillCommonCastMessageFields(CastMessage* message,
56                                  const std::string& source_id,
57                                  const std::string& destination_id,
58                                  const std::string& message_namespace) {
59   message->set_protocol_version(CastMessage::CASTV2_1_0);
60   message->set_source_id(source_id);
61   message->set_destination_id(destination_id);
62   message->set_namespace_(message_namespace);
63 }
64
65 CastMessage CreateKeepAliveMessage(const char* keep_alive_type) {
66   base::DictionaryValue type_dict;
67   type_dict.SetString(kTypeNodeId, keep_alive_type);
68   return CreateCastMessage(kHeartbeatNamespace, type_dict, kPlatformSenderId,
69                            kPlatformReceiverId);
70 }
71
72 // Returns the value to be set as the "platform" value in a virtual connect
73 // request. The value is platform-dependent and is taken from the Platform enum
74 // defined in third_party/metrics_proto/cast_logs.proto.
75 int GetVirtualConnectPlatformValue() {
76 #if defined(OS_WIN)
77   return 3;
78 #elif defined(OS_MACOSX)
79   return 4;
80 #elif defined(OS_CHROMEOS)
81   return 5;
82 #elif defined(OS_LINUX)
83   return 6;
84 #else
85   return 0;
86 #endif
87 }
88
89 }  // namespace
90
91 bool IsCastMessageValid(const CastMessage& message_proto) {
92   if (!message_proto.IsInitialized())
93     return false;
94
95   if (message_proto.namespace_().empty() || message_proto.source_id().empty() ||
96       message_proto.destination_id().empty()) {
97     return false;
98   }
99   return (message_proto.payload_type() == CastMessage_PayloadType_STRING &&
100           message_proto.has_payload_utf8()) ||
101          (message_proto.payload_type() == CastMessage_PayloadType_BINARY &&
102           message_proto.has_payload_binary());
103 }
104
105 std::unique_ptr<base::DictionaryValue> GetDictionaryFromCastMessage(
106     const CastMessage& message) {
107   if (!message.has_payload_utf8())
108     return nullptr;
109
110   return base::DictionaryValue::From(
111       base::JSONReader::Read(message.payload_utf8()));
112 }
113
114 bool IsCastInternalNamespace(const std::string& message_namespace) {
115   // Note: any namespace with the prefix is assumed to be reserved for internal
116   // messages.
117   return base::StartsWith(message_namespace, kCastInternalNamespacePrefix,
118                           base::CompareCase::SENSITIVE);
119 }
120
121 CastMessageType ParseMessageType(const CastMessage& message) {
122   std::unique_ptr<base::DictionaryValue> dictionary =
123       GetDictionaryFromCastMessage(message);
124   return dictionary ? ParseMessageTypeFromPayload(*dictionary)
125                     : CastMessageType::kOther;
126 }
127
128 CastMessageType ParseMessageTypeFromPayload(const base::Value& payload) {
129   const Value* type_string =
130       payload.FindKeyOfType(kTypeNodeId, Value::Type::STRING);
131   return type_string ? CastMessageTypeFromString(type_string->GetString())
132                      : CastMessageType::kOther;
133 }
134
135 const char* CastMessageTypeToString(CastMessageType message_type) {
136   switch (message_type) {
137     case CastMessageType::kPing:
138       return kKeepAlivePingType;
139     case CastMessageType::kPong:
140       return kKeepAlivePongType;
141     case CastMessageType::kGetAppAvailability:
142       return kGetAppAvailabilityRequestType;
143     case CastMessageType::kConnect:
144       return kConnectionRequestType;
145     case CastMessageType::kBroadcast:
146       return kBroadcastRequestType;
147     case CastMessageType::kLaunch:
148       return kLaunchRequestType;
149     case CastMessageType::kReceiverStatus:
150       return kReceiverStatusType;
151     case CastMessageType::kLaunchError:
152       return kLaunchErrorType;
153     case CastMessageType::kOther:
154       return "";
155   }
156   NOTREACHED();
157   return "";
158 }
159
160 CastMessageType CastMessageTypeFromString(const std::string& type) {
161   if (type == kKeepAlivePingType)
162     return CastMessageType::kPing;
163   if (type == kKeepAlivePongType)
164     return CastMessageType::kPong;
165   if (type == kGetAppAvailabilityRequestType)
166     return CastMessageType::kGetAppAvailability;
167   if (type == kConnectionRequestType)
168     return CastMessageType::kConnect;
169   if (type == kBroadcastRequestType)
170     return CastMessageType::kBroadcast;
171   if (type == kLaunchRequestType)
172     return CastMessageType::kLaunch;
173   if (type == kReceiverStatusType)
174     return CastMessageType::kReceiverStatus;
175   if (type == kLaunchErrorType)
176     return CastMessageType::kLaunchError;
177
178   DVLOG(1) << "Unknown message type: " << type;
179   return CastMessageType::kOther;
180 }
181
182 std::string CastMessageToString(const CastMessage& message_proto) {
183   std::string out("{");
184   out += "namespace = " + message_proto.namespace_();
185   out += ", sourceId = " + message_proto.source_id();
186   out += ", destId = " + message_proto.destination_id();
187   out += ", type = " + base::IntToString(message_proto.payload_type());
188   out += ", str = \"" + message_proto.payload_utf8() + "\"}";
189   return out;
190 }
191
192 std::string AuthMessageToString(const DeviceAuthMessage& message) {
193   std::string out("{");
194   if (message.has_challenge()) {
195     out += "challenge: {}, ";
196   }
197   if (message.has_response()) {
198     out += "response: {signature: (";
199     out += base::NumberToString(message.response().signature().length());
200     out += " bytes), certificate: (";
201     out += base::NumberToString(
202         message.response().client_auth_certificate().length());
203     out += " bytes)}";
204   }
205   if (message.has_error()) {
206     out += ", error: {";
207     out += base::IntToString(message.error().error_type());
208     out += "}";
209   }
210   out += "}";
211   return out;
212 }
213
214 void CreateAuthChallengeMessage(CastMessage* message_proto,
215                                 const AuthContext& auth_context) {
216   CHECK(message_proto);
217   DeviceAuthMessage auth_message;
218
219   AuthChallenge* challenge = auth_message.mutable_challenge();
220   DCHECK(challenge);
221   challenge->set_sender_nonce(auth_context.nonce());
222   challenge->set_hash_algorithm(SHA256);
223
224   std::string auth_message_string;
225   auth_message.SerializeToString(&auth_message_string);
226
227   FillCommonCastMessageFields(message_proto, kPlatformSenderId,
228                               kPlatformReceiverId, kAuthNamespace);
229   message_proto->set_payload_type(CastMessage_PayloadType_BINARY);
230   message_proto->set_payload_binary(auth_message_string);
231 }
232
233 bool IsAuthMessage(const CastMessage& message) {
234   return message.namespace_() == kAuthNamespace;
235 }
236
237 bool IsReceiverMessage(const CastMessage& message) {
238   return message.namespace_() == kReceiverNamespace;
239 }
240
241 bool IsPlatformSenderMessage(const CastMessage& message) {
242   return message.destination_id() != cast_channel::kPlatformSenderId;
243 }
244
245 CastMessage CreateKeepAlivePingMessage() {
246   return CreateKeepAliveMessage(kKeepAlivePingType);
247 }
248
249 CastMessage CreateKeepAlivePongMessage() {
250   return CreateKeepAliveMessage(kKeepAlivePongType);
251 }
252
253 CastMessage CreateVirtualConnectionRequest(
254     const std::string& source_id,
255     const std::string& destination_id,
256     VirtualConnectionType connection_type,
257     const std::string& user_agent,
258     const std::string& browser_version) {
259   DCHECK(destination_id != kPlatformReceiverId || connection_type == kStrong);
260
261   // Parse system_version from user agent string. It contains platform, OS and
262   // CPU info and is contained in the first set of parentheses of the user
263   // agent string (e.g., X11; Linux x86_64).
264   std::string system_version;
265   size_t start_index = user_agent.find('(');
266   if (start_index != std::string::npos) {
267     size_t end_index = user_agent.find(')', start_index + 1);
268     if (end_index != std::string::npos) {
269       system_version =
270           user_agent.substr(start_index + 1, end_index - start_index - 1);
271     }
272   }
273
274   Value dict(Value::Type::DICTIONARY);
275   dict.SetKey(kTypeNodeId, Value(kConnectionRequestType));
276   dict.SetKey("userAgent", Value(user_agent));
277   dict.SetKey("connType", Value(connection_type));
278   dict.SetKey("origin", Value(Value::Type::DICTIONARY));
279
280   Value sender_info(Value::Type::DICTIONARY);
281   sender_info.SetKey("sdkType", Value(kVirtualConnectSdkType));
282   sender_info.SetKey("version", Value(browser_version));
283   sender_info.SetKey("browserVersion", Value(browser_version));
284   sender_info.SetKey("platform", Value(GetVirtualConnectPlatformValue()));
285   sender_info.SetKey("connectionType", Value(kVirtualConnectTypeLocal));
286   if (!system_version.empty())
287     sender_info.SetKey("systemVersion", Value(system_version));
288
289   dict.SetKey("senderInfo", std::move(sender_info));
290
291   return CreateCastMessage(kConnectionNamespace, dict, source_id,
292                            destination_id);
293 }
294
295 CastMessage CreateGetAppAvailabilityRequest(const std::string& source_id,
296                                             int request_id,
297                                             const std::string& app_id) {
298   Value dict(Value::Type::DICTIONARY);
299   dict.SetKey(kTypeNodeId, Value(kGetAppAvailabilityRequestType));
300   Value app_id_value(Value::Type::LIST);
301   app_id_value.GetList().push_back(Value(app_id));
302   dict.SetKey("appId", std::move(app_id_value));
303   dict.SetKey(kRequestIdNodeId, Value(request_id));
304
305   return CreateCastMessage(kReceiverNamespace, dict, source_id,
306                            kPlatformReceiverId);
307 }
308
309 BroadcastRequest::BroadcastRequest(const std::string& broadcast_namespace,
310                                    const std::string& message)
311     : broadcast_namespace(broadcast_namespace), message(message) {}
312 BroadcastRequest::~BroadcastRequest() = default;
313
314 bool BroadcastRequest::operator==(const BroadcastRequest& other) const {
315   return broadcast_namespace == other.broadcast_namespace &&
316          message == other.message;
317 }
318
319 CastMessage CreateBroadcastRequest(const std::string& source_id,
320                                    int request_id,
321                                    const std::vector<std::string>& app_ids,
322                                    const BroadcastRequest& request) {
323   Value dict(Value::Type::DICTIONARY);
324   dict.SetKey(kTypeNodeId, Value(kBroadcastRequestType));
325   std::vector<Value> app_ids_value;
326   for (const std::string& app_id : app_ids)
327     app_ids_value.push_back(Value(app_id));
328
329   dict.SetKey("appIds", Value(std::move(app_ids_value)));
330   dict.SetKey("namespace", Value(request.broadcast_namespace));
331   dict.SetKey("message", Value(request.message));
332   return CreateCastMessage(kBroadcastNamespace, dict, source_id,
333                            kPlatformReceiverId);
334 }
335
336 CastMessage CreateLaunchRequest(const std::string& source_id,
337                                 int request_id,
338                                 const std::string& app_id,
339                                 const std::string& locale) {
340   Value dict(Value::Type::DICTIONARY);
341   dict.SetKey(kTypeNodeId, Value(kLaunchRequestType));
342   dict.SetKey(kRequestIdNodeId, Value(request_id));
343   dict.SetKey("appId", Value(app_id));
344   dict.SetKey("language", Value(locale));
345
346   return CreateCastMessage(kReceiverNamespace, dict, source_id,
347                            kPlatformReceiverId);
348 }
349
350 CastMessage CreateCastMessage(const std::string& message_namespace,
351                               const base::Value& message,
352                               const std::string& source_id,
353                               const std::string& destination_id) {
354   CastMessage output;
355   FillCommonCastMessageFields(&output, source_id, destination_id,
356                               message_namespace);
357   output.set_payload_type(
358       CastMessage::PayloadType::CastMessage_PayloadType_STRING);
359   CHECK(base::JSONWriter::Write(message, output.mutable_payload_utf8()));
360   return output;
361 }
362
363 const char* GetAppAvailabilityResultToString(GetAppAvailabilityResult result) {
364   switch (result) {
365     case GetAppAvailabilityResult::kAvailable:
366       return "available";
367     case GetAppAvailabilityResult::kUnavailable:
368       return "unavailable";
369     case GetAppAvailabilityResult::kUnknown:
370       return "unknown";
371   }
372 }
373
374 bool GetRequestIdFromResponse(const Value& payload, int* request_id) {
375   DCHECK(request_id);
376   DCHECK(payload.is_dict());
377
378   const Value* request_id_value =
379       payload.FindKeyOfType(kRequestIdNodeId, Value::Type::INTEGER);
380   if (!request_id_value)
381     return false;
382
383   *request_id = request_id_value->GetInt();
384   return true;
385 }
386
387 GetAppAvailabilityResult GetAppAvailabilityResultFromResponse(
388     const Value& payload,
389     const std::string& app_id) {
390   DCHECK(payload.is_dict());
391   const Value* availability_value =
392       payload.FindPathOfType({"availability", app_id}, Value::Type::STRING);
393   if (!availability_value)
394     return GetAppAvailabilityResult::kUnknown;
395
396   if (availability_value->GetString() == "APP_AVAILABLE")
397     return GetAppAvailabilityResult::kAvailable;
398   if (availability_value->GetString() == "APP_UNAVAILABLE")
399     return GetAppAvailabilityResult::kUnavailable;
400
401   return GetAppAvailabilityResult::kUnknown;
402 }
403
404 LaunchSessionResponse::LaunchSessionResponse() {}
405 LaunchSessionResponse::LaunchSessionResponse(LaunchSessionResponse&& other) =
406     default;
407 LaunchSessionResponse::~LaunchSessionResponse() = default;
408
409 LaunchSessionResponse GetLaunchSessionResponse(
410     const base::DictionaryValue& payload) {
411   const Value* type_value =
412       payload.FindKeyOfType(kTypeNodeId, Value::Type::STRING);
413   if (!type_value)
414     return LaunchSessionResponse();
415
416   if (type_value->GetString() != kReceiverStatusType &&
417       type_value->GetString() != kLaunchErrorType)
418     return LaunchSessionResponse();
419
420   LaunchSessionResponse response;
421   if (type_value->GetString() == kLaunchErrorType) {
422     response.result = LaunchSessionResponse::Result::kError;
423     return response;
424   }
425
426   const Value* receiver_status =
427       payload.FindKeyOfType("status", Value::Type::DICTIONARY);
428   if (!receiver_status)
429     return LaunchSessionResponse();
430
431   response.result = LaunchSessionResponse::Result::kOk;
432   response.receiver_status = receiver_status->Clone();
433   return response;
434 }
435
436 }  // namespace cast_channel