21 #include <freerdp/config.h>
28 #include <winpr/crt.h>
29 #include <winpr/assert.h>
30 #include <winpr/stream.h>
32 #include <freerdp/client/channels.h>
33 #include <freerdp/channels/log.h>
34 #include <freerdp/channels/location.h>
36 #include <freerdp/utils/encoded_types.h>
38 #define TAG CHANNELS_TAG("location.client")
46 LocationClientContext context;
58 static BOOL location_read_header(wLog* log,
wStream* s, UINT16* ppduType, UINT32* ppduLength)
62 WINPR_ASSERT(ppduType);
63 WINPR_ASSERT(ppduLength);
65 if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 6))
67 Stream_Read_UINT16(s, *ppduType);
68 Stream_Read_UINT32(s, *ppduLength);
71 WLog_Print(log, WLOG_ERROR,
72 "RDPLOCATION_HEADER::pduLengh=%" PRIu16
" < sizeof(RDPLOCATION_HEADER)[6]",
76 return Stream_CheckAndLogRequiredLengthWLog(log, s, *ppduLength - 6ull);
79 static BOOL location_write_header(
wStream* s, UINT16 pduType, UINT32 pduLength)
81 if (!Stream_EnsureRemainingCapacity(s, 6))
83 Stream_Write_UINT16(s, pduType);
84 Stream_Write_UINT32(s, pduLength + 6);
85 return Stream_EnsureRemainingCapacity(s, pduLength);
88 static BOOL location_read_server_ready_pdu(LOCATION_CALLBACK* callback,
wStream* s, UINT16 pduSize)
93 Stream_Read_UINT32(s, callback->serverVersion);
94 if (pduSize >= 6 + 4 + 4)
95 Stream_Read_UINT32(s, callback->serverFlags);
99 static UINT location_channel_send(IWTSVirtualChannel* channel,
wStream* s)
101 const size_t len = Stream_GetPosition(s);
102 if (len > UINT32_MAX)
103 return ERROR_INTERNAL_ERROR;
105 Stream_SetPosition(s, 2);
106 Stream_Write_UINT32(s, (UINT32)len);
108 WINPR_ASSERT(channel);
109 WINPR_ASSERT(channel->Write);
110 return channel->Write(channel, (UINT32)len, Stream_Buffer(s), NULL);
113 static UINT location_send_client_ready_pdu(
const LOCATION_CALLBACK* callback)
116 BYTE buffer[32] = { 0 };
117 wStream* s = Stream_StaticInit(&sbuffer, buffer,
sizeof(buffer));
120 if (!location_write_header(s, PDUTYPE_CLIENT_READY, 8))
121 return ERROR_OUTOFMEMORY;
123 Stream_Write_UINT32(s, callback->clientVersion);
124 Stream_Write_UINT32(s, callback->clientFlags);
125 return location_channel_send(callback->baseCb.channel, s);
128 static const char* location_version_str(UINT32 version,
char* buffer,
size_t size)
130 const char* str = NULL;
133 case RDPLOCATION_PROTOCOL_VERSION_100:
134 str =
"RDPLOCATION_PROTOCOL_VERSION_100";
136 case RDPLOCATION_PROTOCOL_VERSION_200:
137 str =
"RDPLOCATION_PROTOCOL_VERSION_200";
140 str =
"RDPLOCATION_PROTOCOL_VERSION_UNKNOWN";
144 (void)_snprintf(buffer, size,
"%s [0x%08" PRIx32
"]", str, version);
153 static UINT location_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
wStream* data)
155 LOCATION_CALLBACK* callback = (LOCATION_CALLBACK*)pChannelCallback;
157 WINPR_ASSERT(callback);
159 LOCATION_PLUGIN* plugin = (LOCATION_PLUGIN*)callback->baseCb.plugin;
160 WINPR_ASSERT(plugin);
163 UINT32 pduLength = 0;
164 if (!location_read_header(plugin->baseDynPlugin.log, data, &pduType, &pduLength))
165 return ERROR_INVALID_DATA;
169 case PDUTYPE_SERVER_READY:
170 if (!location_read_server_ready_pdu(callback, data, pduLength))
171 return ERROR_INVALID_DATA;
173 switch (callback->serverVersion)
175 case RDPLOCATION_PROTOCOL_VERSION_200:
176 callback->clientVersion = RDPLOCATION_PROTOCOL_VERSION_200;
178 case RDPLOCATION_PROTOCOL_VERSION_100:
179 callback->clientVersion = RDPLOCATION_PROTOCOL_VERSION_100;
182 callback->clientVersion = RDPLOCATION_PROTOCOL_VERSION_100;
183 if (callback->serverVersion > RDPLOCATION_PROTOCOL_VERSION_200)
184 callback->clientVersion = RDPLOCATION_PROTOCOL_VERSION_200;
188 char cbuffer[64] = { 0 };
189 char sbuffer[64] = { 0 };
190 WLog_Print(plugin->baseDynPlugin.log, WLOG_DEBUG,
191 "Server version %s, client version %s",
192 location_version_str(callback->serverVersion, sbuffer,
sizeof(sbuffer)),
193 location_version_str(callback->clientVersion, cbuffer,
sizeof(cbuffer)));
195 if (!plugin->context.LocationStart)
197 WLog_Print(plugin->baseDynPlugin.log, WLOG_WARN,
198 "LocationStart=NULL, no location data will be sent");
199 return CHANNEL_RC_OK;
202 plugin->context.LocationStart(&plugin->context, callback->clientVersion, 0);
203 if (res != CHANNEL_RC_OK)
205 return location_send_client_ready_pdu(callback);
207 WLog_WARN(TAG,
"invalid pduType=%s");
208 return ERROR_INVALID_DATA;
212 static UINT location_send_base_location3d(IWTSVirtualChannel* channel,
216 BYTE buffer[32] = { 0 };
217 wStream* s = Stream_StaticInit(&sbuffer, buffer,
sizeof(buffer));
219 WINPR_ASSERT(channel);
224 "latitude=%lf, logitude=%lf, altitude=%" PRId32
225 ", speed=%lf, heading=%lf, haccuracy=%lf, source=%" PRIu8,
226 pdu->latitude, pdu->longitude, pdu->altitude, pdu->speed, pdu->heading,
227 pdu->horizontalAccuracy, *pdu->source);
229 WLog_DBG(TAG,
"latitude=%lf, logitude=%lf, altitude=%" PRId32, pdu->latitude,
230 pdu->longitude, pdu->altitude);
232 if (!location_write_header(s, PDUTYPE_BASE_LOCATION3D, pdu->source ? 25 : 12))
233 return ERROR_OUTOFMEMORY;
235 if (!freerdp_write_four_byte_float(s, pdu->latitude) ||
236 !freerdp_write_four_byte_float(s, pdu->longitude) ||
237 !freerdp_write_four_byte_signed_integer(s, pdu->altitude))
238 return ERROR_INTERNAL_ERROR;
242 if (!freerdp_write_four_byte_float(s, *pdu->speed) ||
243 !freerdp_write_four_byte_float(s, *pdu->heading) ||
244 !freerdp_write_four_byte_float(s, *pdu->horizontalAccuracy))
245 return ERROR_INTERNAL_ERROR;
247 Stream_Write_UINT8(s, *pdu->source);
250 return location_channel_send(channel, s);
253 static UINT location_send_location2d_delta(IWTSVirtualChannel* channel,
257 BYTE buffer[32] = { 0 };
258 wStream* s = Stream_StaticInit(&sbuffer, buffer,
sizeof(buffer));
261 WINPR_ASSERT(channel);
264 const BOOL ext = pdu->speedDelta && pdu->headingDelta;
267 WLog_DBG(TAG,
"latitude=%lf, logitude=%lf, speed=%lf, heading=%lf", pdu->latitudeDelta,
268 pdu->longitudeDelta, pdu->speedDelta, pdu->headingDelta);
270 WLog_DBG(TAG,
"latitude=%lf, logitude=%lf", pdu->latitudeDelta, pdu->longitudeDelta);
272 if (!location_write_header(s, PDUTYPE_LOCATION2D_DELTA, ext ? 16 : 8))
273 return ERROR_OUTOFMEMORY;
275 if (!freerdp_write_four_byte_float(s, pdu->latitudeDelta) ||
276 !freerdp_write_four_byte_float(s, pdu->longitudeDelta))
277 return ERROR_INTERNAL_ERROR;
281 if (!freerdp_write_four_byte_float(s, *pdu->speedDelta) ||
282 !freerdp_write_four_byte_float(s, *pdu->headingDelta))
283 return ERROR_INTERNAL_ERROR;
286 return location_channel_send(channel, s);
289 static UINT location_send_location3d_delta(IWTSVirtualChannel* channel,
293 BYTE buffer[32] = { 0 };
294 wStream* s = Stream_StaticInit(&sbuffer, buffer,
sizeof(buffer));
297 WINPR_ASSERT(channel);
300 const BOOL ext = pdu->speedDelta && pdu->headingDelta;
303 WLog_DBG(TAG,
"latitude=%lf, logitude=%lf, altitude=%" PRId32
", speed=%lf, heading=%lf",
304 pdu->latitudeDelta, pdu->longitudeDelta, pdu->altitudeDelta, pdu->speedDelta,
307 WLog_DBG(TAG,
"latitude=%lf, logitude=%lf, altitude=%" PRId32, pdu->latitudeDelta,
308 pdu->longitudeDelta, pdu->altitudeDelta);
310 if (!location_write_header(s, PDUTYPE_LOCATION3D_DELTA, ext ? 20 : 12))
311 return ERROR_OUTOFMEMORY;
313 if (!freerdp_write_four_byte_float(s, pdu->latitudeDelta) ||
314 !freerdp_write_four_byte_float(s, pdu->longitudeDelta) ||
315 !freerdp_write_four_byte_signed_integer(s, pdu->altitudeDelta))
316 return ERROR_INTERNAL_ERROR;
320 if (!freerdp_write_four_byte_float(s, *pdu->speedDelta) ||
321 !freerdp_write_four_byte_float(s, *pdu->headingDelta))
322 return ERROR_INTERNAL_ERROR;
325 return location_channel_send(channel, s);
328 static UINT location_send(LocationClientContext* context, LOCATION_PDUTYPE type,
size_t count, ...)
330 WINPR_ASSERT(context);
332 LOCATION_PLUGIN* loc = context->handle;
338 IWTSVirtualChannel* channel = cb->channel;
339 WINPR_ASSERT(channel);
341 const LOCATION_CALLBACK* callback =
342 (
const LOCATION_CALLBACK*)loc->baseDynPlugin.channel_callbacks;
343 WINPR_ASSERT(callback);
345 UINT32 res = ERROR_INTERNAL_ERROR;
350 case PDUTYPE_BASE_LOCATION3D:
351 if ((count != 3) && (count != 7))
352 res = ERROR_INVALID_PARAMETER;
356 LOCATIONSOURCE source = LOCATIONSOURCE_IP;
357 double speed = FP_NAN;
358 double heading = FP_NAN;
359 double horizontalAccuracy = FP_NAN;
360 pdu.latitude = va_arg(ap,
double);
361 pdu.longitude = va_arg(ap,
double);
362 pdu.altitude = va_arg(ap, INT32);
363 if ((count > 3) && (callback->clientVersion >= RDPLOCATION_PROTOCOL_VERSION_200))
365 speed = va_arg(ap,
double);
366 heading = va_arg(ap,
double);
367 horizontalAccuracy = va_arg(ap,
double);
368 source = va_arg(ap,
int);
370 pdu.heading = &heading;
371 pdu.horizontalAccuracy = &horizontalAccuracy;
372 pdu.source = &source;
374 res = location_send_base_location3d(channel, &pdu);
377 case PDUTYPE_LOCATION2D_DELTA:
378 if ((count != 2) && (count != 4))
379 res = ERROR_INVALID_PARAMETER;
384 pdu.latitudeDelta = va_arg(ap,
double);
385 pdu.longitudeDelta = va_arg(ap,
double);
387 double speedDelta = FP_NAN;
388 double headingDelta = FP_NAN;
389 if ((count > 2) && (callback->clientVersion >= RDPLOCATION_PROTOCOL_VERSION_200))
391 speedDelta = va_arg(ap,
double);
392 headingDelta = va_arg(ap,
double);
393 pdu.speedDelta = &speedDelta;
394 pdu.headingDelta = &headingDelta;
396 res = location_send_location2d_delta(channel, &pdu);
399 case PDUTYPE_LOCATION3D_DELTA:
400 if ((count != 3) && (count != 5))
401 res = ERROR_INVALID_PARAMETER;
405 double speedDelta = FP_NAN;
406 double headingDelta = FP_NAN;
408 pdu.latitudeDelta = va_arg(ap,
double);
409 pdu.longitudeDelta = va_arg(ap,
double);
410 pdu.altitudeDelta = va_arg(ap, INT32);
411 if ((count > 3) && (callback->clientVersion >= RDPLOCATION_PROTOCOL_VERSION_200))
413 speedDelta = va_arg(ap,
double);
414 headingDelta = va_arg(ap,
double);
415 pdu.speedDelta = &speedDelta;
416 pdu.headingDelta = &headingDelta;
418 res = location_send_location3d_delta(channel, &pdu);
422 res = ERROR_INVALID_PARAMETER;
434 static UINT location_on_close(IWTSVirtualChannelCallback* pChannelCallback)
436 UINT res = CHANNEL_RC_OK;
441 LOCATION_PLUGIN* plugin = (LOCATION_PLUGIN*)callback->plugin;
442 WINPR_ASSERT(plugin);
444 res = IFCALLRESULT(CHANNEL_RC_OK, plugin->context.LocationStop, &plugin->context);
451 static UINT location_init(
GENERIC_DYNVC_PLUGIN* plugin, rdpContext* context, rdpSettings* settings)
453 LOCATION_PLUGIN* loc = (LOCATION_PLUGIN*)plugin;
457 loc->context.LocationSend = location_send;
458 loc->context.handle = loc;
459 plugin->iface.pInterface = &loc->context;
460 return CHANNEL_RC_OK;
463 static const IWTSVirtualChannelCallback location_callbacks = { location_on_data_received,
465 location_on_close, NULL };
472 FREERDP_ENTRY_POINT(UINT VCAPITYPE location_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
474 return freerdp_generic_DVCPluginEntry(pEntryPoints, TAG, LOCATION_DVC_CHANNEL_NAME,
475 sizeof(LOCATION_PLUGIN),
sizeof(LOCATION_CALLBACK),
476 &location_callbacks, location_init, NULL);