21#include <freerdp/config.h>
29#include <winpr/assert.h>
30#include <winpr/cast.h>
31#include <winpr/stream.h>
33#include <freerdp/client/channels.h>
34#include <freerdp/channels/log.h>
35#include <freerdp/channels/location.h>
37#include <freerdp/utils/encoded_types.h>
39#define TAG CHANNELS_TAG("location.client")
47 LocationClientContext context;
59static BOOL location_read_header(wLog* log,
wStream* s, UINT16* ppduType, UINT32* ppduLength)
63 WINPR_ASSERT(ppduType);
64 WINPR_ASSERT(ppduLength);
66 if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 6))
68 Stream_Read_UINT16(s, *ppduType);
69 Stream_Read_UINT32(s, *ppduLength);
72 WLog_Print(log, WLOG_ERROR,
73 "RDPLOCATION_HEADER::pduLengh=%" PRIu16
" < sizeof(RDPLOCATION_HEADER)[6]",
77 return Stream_CheckAndLogRequiredLengthWLog(log, s, *ppduLength - 6ull);
80static BOOL location_write_header(
wStream* s, UINT16 pduType, UINT32 pduLength)
82 if (!Stream_EnsureRemainingCapacity(s, 6))
84 Stream_Write_UINT16(s, pduType);
85 Stream_Write_UINT32(s, pduLength + 6);
86 return Stream_EnsureRemainingCapacity(s, pduLength);
89static BOOL location_read_server_ready_pdu(LOCATION_CALLBACK* callback,
wStream* s, UINT32 pduSize)
94 Stream_Read_UINT32(s, callback->serverVersion);
95 if (pduSize >= 6 + 4 + 4)
96 Stream_Read_UINT32(s, callback->serverFlags);
100static UINT location_channel_send(IWTSVirtualChannel* channel,
wStream* s)
102 const size_t len = Stream_GetPosition(s);
103 if (len > UINT32_MAX)
104 return ERROR_INTERNAL_ERROR;
106 if (!Stream_SetPosition(s, 2))
107 return ERROR_INVALID_DATA;
108 Stream_Write_UINT32(s, (UINT32)len);
110 WINPR_ASSERT(channel);
111 WINPR_ASSERT(channel->Write);
112 return channel->Write(channel, (UINT32)len, Stream_Buffer(s),
nullptr);
115static UINT location_send_client_ready_pdu(
const LOCATION_CALLBACK* callback)
117 wStream sbuffer = WINPR_C_ARRAY_INIT;
118 BYTE buffer[32] = WINPR_C_ARRAY_INIT;
119 wStream* s = Stream_StaticInit(&sbuffer, buffer,
sizeof(buffer));
122 if (!location_write_header(s, PDUTYPE_CLIENT_READY, 8))
123 return ERROR_OUTOFMEMORY;
125 Stream_Write_UINT32(s, callback->clientVersion);
126 Stream_Write_UINT32(s, callback->clientFlags);
127 return location_channel_send(callback->baseCb.channel, s);
130static const char* location_version_str(UINT32 version,
char* buffer,
size_t size)
132 const char* str =
nullptr;
135 case RDPLOCATION_PROTOCOL_VERSION_100:
136 str =
"RDPLOCATION_PROTOCOL_VERSION_100";
138 case RDPLOCATION_PROTOCOL_VERSION_200:
139 str =
"RDPLOCATION_PROTOCOL_VERSION_200";
142 str =
"RDPLOCATION_PROTOCOL_VERSION_UNKNOWN";
146 (void)_snprintf(buffer, size,
"%s [0x%08" PRIx32
"]", str, version);
155static UINT location_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
wStream* data)
157 LOCATION_CALLBACK* callback = (LOCATION_CALLBACK*)pChannelCallback;
159 WINPR_ASSERT(callback);
161 LOCATION_PLUGIN* plugin = (LOCATION_PLUGIN*)callback->baseCb.plugin;
162 WINPR_ASSERT(plugin);
165 UINT32 pduLength = 0;
166 if (!location_read_header(plugin->baseDynPlugin.log, data, &pduType, &pduLength))
167 return ERROR_INVALID_DATA;
171 case PDUTYPE_SERVER_READY:
172 if (!location_read_server_ready_pdu(callback, data, pduLength))
173 return ERROR_INVALID_DATA;
175 switch (callback->serverVersion)
177 case RDPLOCATION_PROTOCOL_VERSION_200:
178 callback->clientVersion = RDPLOCATION_PROTOCOL_VERSION_200;
180 case RDPLOCATION_PROTOCOL_VERSION_100:
181 callback->clientVersion = RDPLOCATION_PROTOCOL_VERSION_100;
184 callback->clientVersion = RDPLOCATION_PROTOCOL_VERSION_100;
185 if (callback->serverVersion > RDPLOCATION_PROTOCOL_VERSION_200)
186 callback->clientVersion = RDPLOCATION_PROTOCOL_VERSION_200;
191 char cbuffer[64] = WINPR_C_ARRAY_INIT;
192 char sbuffer[64] = WINPR_C_ARRAY_INIT;
193 WLog_Print(plugin->baseDynPlugin.log, WLOG_DEBUG,
194 "Server version %s, client version %s",
195 location_version_str(callback->serverVersion, sbuffer,
sizeof(sbuffer)),
196 location_version_str(callback->clientVersion, cbuffer,
sizeof(cbuffer)));
199 if (!plugin->context.LocationStart)
201 WLog_Print(plugin->baseDynPlugin.log, WLOG_WARN,
202 "LocationStart=nullptr, no location data will be sent");
203 return CHANNEL_RC_OK;
208 plugin->context.LocationStart(&plugin->context, callback->clientVersion, 0);
209 if (res != CHANNEL_RC_OK)
212 return location_send_client_ready_pdu(callback);
214 WLog_WARN(TAG,
"invalid pduType=%" PRIu16, pduType);
215 return ERROR_INVALID_DATA;
219static UINT location_send_base_location3d(IWTSVirtualChannel* channel,
222 wStream sbuffer = WINPR_C_ARRAY_INIT;
223 BYTE buffer[32] = WINPR_C_ARRAY_INIT;
224 wStream* s = Stream_StaticInit(&sbuffer, buffer,
sizeof(buffer));
226 WINPR_ASSERT(channel);
231 "latitude=%lf, longitude=%lf, altitude=%" PRId32
232 ", speed=%lf, heading=%lf, haccuracy=%lf, source=%" PRIu8,
233 pdu->latitude, pdu->longitude, pdu->altitude, pdu->speed ? *pdu->speed : FP_NAN,
234 pdu->heading ? *pdu->heading : FP_NAN,
235 pdu->horizontalAccuracy ? *pdu->horizontalAccuracy : FP_NAN, *pdu->source);
237 WLog_DBG(TAG,
"latitude=%lf, longitude=%lf, altitude=%" PRId32, pdu->latitude,
238 pdu->longitude, pdu->altitude);
240 if (!location_write_header(s, PDUTYPE_BASE_LOCATION3D, pdu->source ? 25 : 12))
241 return ERROR_OUTOFMEMORY;
243 if (!freerdp_write_four_byte_float(s, pdu->latitude) ||
244 !freerdp_write_four_byte_float(s, pdu->longitude) ||
245 !freerdp_write_four_byte_signed_integer(s, pdu->altitude))
246 return ERROR_INTERNAL_ERROR;
250 if (!freerdp_write_four_byte_float(s, *pdu->speed) ||
251 !freerdp_write_four_byte_float(s, *pdu->heading) ||
252 !freerdp_write_four_byte_float(s, *pdu->horizontalAccuracy))
253 return ERROR_INTERNAL_ERROR;
255 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(UINT8, *pdu->source));
258 return location_channel_send(channel, s);
261static UINT location_send_location2d_delta(IWTSVirtualChannel* channel,
264 wStream sbuffer = WINPR_C_ARRAY_INIT;
265 BYTE buffer[32] = WINPR_C_ARRAY_INIT;
266 wStream* s = Stream_StaticInit(&sbuffer, buffer,
sizeof(buffer));
269 WINPR_ASSERT(channel);
272 const BOOL ext = pdu->speedDelta && pdu->headingDelta;
275 WLog_DBG(TAG,
"latitude=%lf, longitude=%lf, speed=%lf, heading=%lf", pdu->latitudeDelta,
276 pdu->longitudeDelta, pdu->speedDelta ? *pdu->speedDelta : FP_NAN,
277 pdu->headingDelta ? *pdu->headingDelta : FP_NAN);
279 WLog_DBG(TAG,
"latitude=%lf, longitude=%lf", pdu->latitudeDelta, pdu->longitudeDelta);
281 if (!location_write_header(s, PDUTYPE_LOCATION2D_DELTA, ext ? 16 : 8))
282 return ERROR_OUTOFMEMORY;
284 if (!freerdp_write_four_byte_float(s, pdu->latitudeDelta) ||
285 !freerdp_write_four_byte_float(s, pdu->longitudeDelta))
286 return ERROR_INTERNAL_ERROR;
290 if (!freerdp_write_four_byte_float(s, *pdu->speedDelta) ||
291 !freerdp_write_four_byte_float(s, *pdu->headingDelta))
292 return ERROR_INTERNAL_ERROR;
295 return location_channel_send(channel, s);
298static UINT location_send_location3d_delta(IWTSVirtualChannel* channel,
301 wStream sbuffer = WINPR_C_ARRAY_INIT;
302 BYTE buffer[32] = WINPR_C_ARRAY_INIT;
303 wStream* s = Stream_StaticInit(&sbuffer, buffer,
sizeof(buffer));
306 WINPR_ASSERT(channel);
309 const BOOL ext = pdu->speedDelta && pdu->headingDelta;
312 WLog_DBG(TAG,
"latitude=%lf, longitude=%lf, altitude=%" PRId32
", speed=%lf, heading=%lf",
313 pdu->latitudeDelta, pdu->longitudeDelta, pdu->altitudeDelta,
314 pdu->speedDelta ? *pdu->speedDelta : FP_NAN,
315 pdu->headingDelta ? *pdu->headingDelta : FP_NAN);
317 WLog_DBG(TAG,
"latitude=%lf, longitude=%lf, altitude=%" PRId32, pdu->latitudeDelta,
318 pdu->longitudeDelta, pdu->altitudeDelta);
320 if (!location_write_header(s, PDUTYPE_LOCATION3D_DELTA, ext ? 20 : 12))
321 return ERROR_OUTOFMEMORY;
323 if (!freerdp_write_four_byte_float(s, pdu->latitudeDelta) ||
324 !freerdp_write_four_byte_float(s, pdu->longitudeDelta) ||
325 !freerdp_write_four_byte_signed_integer(s, pdu->altitudeDelta))
326 return ERROR_INTERNAL_ERROR;
330 if (!freerdp_write_four_byte_float(s, *pdu->speedDelta) ||
331 !freerdp_write_four_byte_float(s, *pdu->headingDelta))
332 return ERROR_INTERNAL_ERROR;
335 return location_channel_send(channel, s);
338static UINT location_send(LocationClientContext* context, LOCATION_PDUTYPE type,
size_t count, ...)
340 WINPR_ASSERT(context);
342 LOCATION_PLUGIN* loc = context->handle;
348 IWTSVirtualChannel* channel = cb->channel;
349 WINPR_ASSERT(channel);
351 const LOCATION_CALLBACK* callback =
352 (
const LOCATION_CALLBACK*)loc->baseDynPlugin.channel_callbacks;
353 WINPR_ASSERT(callback);
355 UINT32 res = ERROR_INTERNAL_ERROR;
356 va_list ap = WINPR_C_ARRAY_INIT;
360 case PDUTYPE_BASE_LOCATION3D:
361 if ((count != 3) && (count != 7))
362 res = ERROR_INVALID_PARAMETER;
365 LOCATIONSOURCE source = LOCATIONSOURCE_IP;
366 double speed = FP_NAN;
367 double heading = FP_NAN;
368 double horizontalAccuracy = FP_NAN;
370 .longitude = va_arg(ap,
double),
371 .altitude = va_arg(ap, INT32),
374 .horizontalAccuracy =
nullptr,
377 if ((count > 3) && (callback->clientVersion >= RDPLOCATION_PROTOCOL_VERSION_200))
379 speed = va_arg(ap,
double);
380 heading = va_arg(ap,
double);
381 horizontalAccuracy = va_arg(ap,
double);
382 source = WINPR_ASSERTING_INT_CAST(LOCATIONSOURCE, va_arg(ap,
int));
384 pdu.heading = &heading;
385 pdu.horizontalAccuracy = &horizontalAccuracy;
386 pdu.source = &source;
388 res = location_send_base_location3d(channel, &pdu);
391 case PDUTYPE_LOCATION2D_DELTA:
392 if ((count != 2) && (count != 4))
393 res = ERROR_INVALID_PARAMETER;
397 .longitudeDelta = va_arg(ap,
double),
398 .speedDelta =
nullptr,
399 .headingDelta =
nullptr };
401 double speedDelta = FP_NAN;
402 double headingDelta = FP_NAN;
403 if ((count > 2) && (callback->clientVersion >= RDPLOCATION_PROTOCOL_VERSION_200))
405 speedDelta = va_arg(ap,
double);
406 headingDelta = va_arg(ap,
double);
407 pdu.speedDelta = &speedDelta;
408 pdu.headingDelta = &headingDelta;
410 res = location_send_location2d_delta(channel, &pdu);
413 case PDUTYPE_LOCATION3D_DELTA:
414 if ((count != 3) && (count != 5))
415 res = ERROR_INVALID_PARAMETER;
418 double speedDelta = FP_NAN;
419 double headingDelta = FP_NAN;
422 .longitudeDelta = va_arg(ap,
double),
423 .altitudeDelta = va_arg(ap, INT32),
424 .speedDelta =
nullptr,
425 .headingDelta =
nullptr };
426 if ((count > 3) && (callback->clientVersion >= RDPLOCATION_PROTOCOL_VERSION_200))
428 speedDelta = va_arg(ap,
double);
429 headingDelta = va_arg(ap,
double);
430 pdu.speedDelta = &speedDelta;
431 pdu.headingDelta = &headingDelta;
433 res = location_send_location3d_delta(channel, &pdu);
437 res = ERROR_INVALID_PARAMETER;
449static UINT location_on_close(IWTSVirtualChannelCallback* pChannelCallback)
451 UINT res = CHANNEL_RC_OK;
456 LOCATION_PLUGIN* plugin = (LOCATION_PLUGIN*)callback->plugin;
457 WINPR_ASSERT(plugin);
459 res = IFCALLRESULT(CHANNEL_RC_OK, plugin->context.LocationStop, &plugin->context);
467 WINPR_ATTR_UNUSED rdpSettings* settings)
469 LOCATION_PLUGIN* loc = (LOCATION_PLUGIN*)plugin;
473 loc->context.LocationSend = location_send;
474 loc->context.handle = loc;
475 plugin->iface.pInterface = &loc->context;
476 return CHANNEL_RC_OK;
479static const IWTSVirtualChannelCallback location_callbacks = { location_on_data_received,
481 location_on_close,
nullptr };
488FREERDP_ENTRY_POINT(UINT VCAPITYPE location_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
490 return freerdp_generic_DVCPluginEntry(pEntryPoints, TAG, LOCATION_DVC_CHANNEL_NAME,
491 sizeof(LOCATION_PLUGIN),
sizeof(LOCATION_CALLBACK),
492 &location_callbacks, location_init,
nullptr);