1 // Copyright 2019 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 "cast/common/channel/message_util.h"
6 
7 #include <sstream>
8 #include <utility>
9 
10 #include "cast/common/channel/virtual_connection.h"
11 #include "util/json/json_serialization.h"
12 #include "util/json/json_value.h"
13 #include "util/osp_logging.h"
14 
15 #if defined(__APPLE__) || defined(__MACH__)
16 #include <TargetConditionals.h>
17 #endif
18 
19 namespace openscreen {
20 namespace cast {
21 namespace {
22 
23 using ::cast::channel::CastMessage;
24 
25 // The value used for "sdkType" in a virtual CONNECT request. Historically, this
26 // value was used in Chrome's C++ impl even though "2" refers to the Media
27 // Router Extension.
28 constexpr int kVirtualConnectSdkType = 2;
29 
30 // The value used for "connectionType" in a virtual CONNECT request. This value
31 // stands for CONNECTION_TYPE_LOCAL.
32 constexpr int kVirtualConnectTypeLocal = 1;
33 
34 // The value to be set as the "platform" value in a virtual CONNECT request.
35 // Source (in Chromium source tree):
36 // src/third_party/metrics_proto/cast_logs.proto
37 enum VirtualConnectPlatformValue {
38   kOtherPlatform = 0,
39   kAndroid = 1,
40   kIOS = 2,
41   kWindows = 3,
42   kMacOSX = 4,
43   kChromeOS = 5,
44   kLinux = 6,
45   kCastDevice = 7,
46 };
47 
48 #if defined(__APPLE__) || defined(__MACH__)
GetVirtualConnectPlatformMacFlavor()49 constexpr VirtualConnectPlatformValue GetVirtualConnectPlatformMacFlavor() {
50 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
51   return kIOS;
52 #else
53   return kMacOSX;
54 #endif
55 }
56 #endif
57 
GetVirtualConnectPlatform()58 constexpr VirtualConnectPlatformValue GetVirtualConnectPlatform() {
59   // Based on //build/build_config.h in the Chromium project. The order of these
60   // matters!
61 #if defined(__ANDROID__)
62   return kAndroid;
63 #elif defined(__APPLE__) || defined(__MACH__)
64   return GetVirtualConnectPlatformMacFlavor();
65 #elif defined(_WIN32) || defined(_WIN64)
66   return kWindows;
67 #elif defined(OS_CHROMEOS)
68   // Note: OS_CHROMEOS is defined via the compiler's command line in Chromium
69   // embedder builds by Chromium's //build/config/linux:runtime_library config.
70   return kChromeOS;
71 #elif defined(__linux__)
72   return kLinux;
73 #else
74   return kOtherPlatform;
75 #endif
76 }
77 
MakeConnectionMessage(const std::string & source_id,const std::string & destination_id)78 CastMessage MakeConnectionMessage(const std::string& source_id,
79                                   const std::string& destination_id) {
80   CastMessage connect_message;
81   connect_message.set_protocol_version(kDefaultOutgoingMessageVersion);
82   connect_message.set_source_id(source_id);
83   connect_message.set_destination_id(destination_id);
84   connect_message.set_namespace_(kConnectionNamespace);
85   return connect_message;
86 }
87 
88 }  // namespace
89 
ToString(AppAvailabilityResult availability)90 std::string ToString(AppAvailabilityResult availability) {
91   switch (availability) {
92     case AppAvailabilityResult::kAvailable:
93       return "Available";
94     case AppAvailabilityResult::kUnavailable:
95       return "Unavailable";
96     case AppAvailabilityResult::kUnknown:
97       return "Unknown";
98     default:
99       OSP_NOTREACHED();
100   }
101 }
102 
MakeSimpleUTF8Message(const std::string & namespace_,std::string payload)103 CastMessage MakeSimpleUTF8Message(const std::string& namespace_,
104                                   std::string payload) {
105   CastMessage message;
106   message.set_protocol_version(kDefaultOutgoingMessageVersion);
107   message.set_namespace_(namespace_);
108   message.set_payload_type(::cast::channel::CastMessage_PayloadType_STRING);
109   message.set_payload_utf8(std::move(payload));
110   return message;
111 }
112 
MakeConnectMessage(const std::string & source_id,const std::string & destination_id)113 CastMessage MakeConnectMessage(const std::string& source_id,
114                                const std::string& destination_id) {
115   CastMessage connect_message =
116       MakeConnectionMessage(source_id, destination_id);
117   connect_message.set_payload_type(
118       ::cast::channel::CastMessage_PayloadType_STRING);
119 
120   // Historically, the CONNECT message was meant to come from a Chrome browser.
121   // However, this library could be embedded in any app. So, properties like
122   // user agent, application version, etc. are not known here.
123   static constexpr char kUnknownVersion[] = "Unknown (Open Screen)";
124 
125   Json::Value message(Json::objectValue);
126   message[kMessageKeyType] = CastMessageTypeToString(CastMessageType::kConnect);
127   for (int i = 0; i <= 3; ++i) {
128     message[kMessageKeyProtocolVersionList][i] =
129         ::cast::channel::CastMessage_ProtocolVersion_CASTV2_1_0 + i;
130   }
131   message[kMessageKeyUserAgent] = kUnknownVersion;
132   message[kMessageKeyConnType] =
133       static_cast<int>(VirtualConnection::Type::kStrong);
134   message[kMessageKeyOrigin] = Json::Value(Json::objectValue);
135 
136   Json::Value sender_info(Json::objectValue);
137   sender_info[kMessageKeySdkType] = kVirtualConnectSdkType;
138   sender_info[kMessageKeyVersion] = kUnknownVersion;
139   sender_info[kMessageKeyBrowserVersion] = kUnknownVersion;
140   sender_info[kMessageKeyPlatform] = GetVirtualConnectPlatform();
141   sender_info[kMessageKeyConnectionType] = kVirtualConnectTypeLocal;
142   message[kMessageKeySenderInfo] = std::move(sender_info);
143 
144   connect_message.set_payload_utf8(json::Stringify(std::move(message)).value());
145   return connect_message;
146 }
147 
MakeCloseMessage(const std::string & source_id,const std::string & destination_id)148 CastMessage MakeCloseMessage(const std::string& source_id,
149                              const std::string& destination_id) {
150   CastMessage close_message = MakeConnectionMessage(source_id, destination_id);
151   close_message.set_payload_type(
152       ::cast::channel::CastMessage_PayloadType_STRING);
153   close_message.set_payload_utf8(R"!({"type": "CLOSE"})!");
154   return close_message;
155 }
156 
MakeUniqueSessionId(const char * prefix)157 std::string MakeUniqueSessionId(const char* prefix) {
158   static int next_id = 10000;
159 
160   std::ostringstream oss;
161   oss << prefix << '-' << (next_id++);
162   return oss.str();
163 }
164 
165 }  // namespace cast
166 }  // namespace openscreen
167