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 Stream_SetPosition(s, 2);
107 Stream_Write_UINT32(s, (UINT32)len);
109 WINPR_ASSERT(channel);
110 WINPR_ASSERT(channel->Write);
111 return channel->Write(channel, (UINT32)len, Stream_Buffer(s), NULL);
114static UINT location_send_client_ready_pdu(
const LOCATION_CALLBACK* callback)
117 BYTE buffer[32] = { 0 };
118 wStream* s = Stream_StaticInit(&sbuffer, buffer,
sizeof(buffer));
121 if (!location_write_header(s, PDUTYPE_CLIENT_READY, 8))
122 return ERROR_OUTOFMEMORY;
124 Stream_Write_UINT32(s, callback->clientVersion);
125 Stream_Write_UINT32(s, callback->clientFlags);
126 return location_channel_send(callback->baseCb.channel, s);
129static const char* location_version_str(UINT32 version,
char* buffer,
size_t size)
131 const char* str = NULL;
134 case RDPLOCATION_PROTOCOL_VERSION_100:
135 str =
"RDPLOCATION_PROTOCOL_VERSION_100";
137 case RDPLOCATION_PROTOCOL_VERSION_200:
138 str =
"RDPLOCATION_PROTOCOL_VERSION_200";
141 str =
"RDPLOCATION_PROTOCOL_VERSION_UNKNOWN";
145 (void)_snprintf(buffer, size,
"%s [0x%08" PRIx32
"]", str, version);
154static UINT location_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
wStream* data)
156 LOCATION_CALLBACK* callback = (LOCATION_CALLBACK*)pChannelCallback;
158 WINPR_ASSERT(callback);
160 LOCATION_PLUGIN* plugin = (LOCATION_PLUGIN*)callback->baseCb.plugin;
161 WINPR_ASSERT(plugin);
164 UINT32 pduLength = 0;
165 if (!location_read_header(plugin->baseDynPlugin.log, data, &pduType, &pduLength))
166 return ERROR_INVALID_DATA;
170 case PDUTYPE_SERVER_READY:
171 if (!location_read_server_ready_pdu(callback, data, pduLength))
172 return ERROR_INVALID_DATA;
174 switch (callback->serverVersion)
176 case RDPLOCATION_PROTOCOL_VERSION_200:
177 callback->clientVersion = RDPLOCATION_PROTOCOL_VERSION_200;
179 case RDPLOCATION_PROTOCOL_VERSION_100:
180 callback->clientVersion = RDPLOCATION_PROTOCOL_VERSION_100;
183 callback->clientVersion = RDPLOCATION_PROTOCOL_VERSION_100;
184 if (callback->serverVersion > RDPLOCATION_PROTOCOL_VERSION_200)
185 callback->clientVersion = RDPLOCATION_PROTOCOL_VERSION_200;
190 char cbuffer[64] = { 0 };
191 char sbuffer[64] = { 0 };
192 WLog_Print(plugin->baseDynPlugin.log, WLOG_DEBUG,
193 "Server version %s, client version %s",
194 location_version_str(callback->serverVersion, sbuffer,
sizeof(sbuffer)),
195 location_version_str(callback->clientVersion, cbuffer,
sizeof(cbuffer)));
198 if (!plugin->context.LocationStart)
200 WLog_Print(plugin->baseDynPlugin.log, WLOG_WARN,
201 "LocationStart=NULL, no location data will be sent");
202 return CHANNEL_RC_OK;
207 plugin->context.LocationStart(&plugin->context, callback->clientVersion, 0);
208 if (res != CHANNEL_RC_OK)
211 return location_send_client_ready_pdu(callback);
213 WLog_WARN(TAG,
"invalid pduType=%s");
214 return ERROR_INVALID_DATA;
218static UINT location_send_base_location3d(IWTSVirtualChannel* channel,
222 BYTE buffer[32] = { 0 };
223 wStream* s = Stream_StaticInit(&sbuffer, buffer,
sizeof(buffer));
225 WINPR_ASSERT(channel);
230 "latitude=%lf, longitude=%lf, altitude=%" PRId32
231 ", speed=%lf, heading=%lf, haccuracy=%lf, source=%" PRIu8,
232 pdu->latitude, pdu->longitude, pdu->altitude, pdu->speed, pdu->heading,
233 pdu->horizontalAccuracy, *pdu->source);
235 WLog_DBG(TAG,
"latitude=%lf, longitude=%lf, altitude=%" PRId32, pdu->latitude,
236 pdu->longitude, pdu->altitude);
238 if (!location_write_header(s, PDUTYPE_BASE_LOCATION3D, pdu->source ? 25 : 12))
239 return ERROR_OUTOFMEMORY;
241 if (!freerdp_write_four_byte_float(s, pdu->latitude) ||
242 !freerdp_write_four_byte_float(s, pdu->longitude) ||
243 !freerdp_write_four_byte_signed_integer(s, pdu->altitude))
244 return ERROR_INTERNAL_ERROR;
248 if (!freerdp_write_four_byte_float(s, *pdu->speed) ||
249 !freerdp_write_four_byte_float(s, *pdu->heading) ||
250 !freerdp_write_four_byte_float(s, *pdu->horizontalAccuracy))
251 return ERROR_INTERNAL_ERROR;
253 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(UINT8, *pdu->source));
256 return location_channel_send(channel, s);
259static UINT location_send_location2d_delta(IWTSVirtualChannel* channel,
263 BYTE buffer[32] = { 0 };
264 wStream* s = Stream_StaticInit(&sbuffer, buffer,
sizeof(buffer));
267 WINPR_ASSERT(channel);
270 const BOOL ext = pdu->speedDelta && pdu->headingDelta;
273 WLog_DBG(TAG,
"latitude=%lf, longitude=%lf, speed=%lf, heading=%lf", pdu->latitudeDelta,
274 pdu->longitudeDelta, pdu->speedDelta, pdu->headingDelta);
276 WLog_DBG(TAG,
"latitude=%lf, longitude=%lf", pdu->latitudeDelta, pdu->longitudeDelta);
278 if (!location_write_header(s, PDUTYPE_LOCATION2D_DELTA, ext ? 16 : 8))
279 return ERROR_OUTOFMEMORY;
281 if (!freerdp_write_four_byte_float(s, pdu->latitudeDelta) ||
282 !freerdp_write_four_byte_float(s, pdu->longitudeDelta))
283 return ERROR_INTERNAL_ERROR;
287 if (!freerdp_write_four_byte_float(s, *pdu->speedDelta) ||
288 !freerdp_write_four_byte_float(s, *pdu->headingDelta))
289 return ERROR_INTERNAL_ERROR;
292 return location_channel_send(channel, s);
295static UINT location_send_location3d_delta(IWTSVirtualChannel* channel,
299 BYTE buffer[32] = { 0 };
300 wStream* s = Stream_StaticInit(&sbuffer, buffer,
sizeof(buffer));
303 WINPR_ASSERT(channel);
306 const BOOL ext = pdu->speedDelta && pdu->headingDelta;
309 WLog_DBG(TAG,
"latitude=%lf, longitude=%lf, altitude=%" PRId32
", speed=%lf, heading=%lf",
310 pdu->latitudeDelta, pdu->longitudeDelta, pdu->altitudeDelta, pdu->speedDelta,
313 WLog_DBG(TAG,
"latitude=%lf, longitude=%lf, altitude=%" PRId32, pdu->latitudeDelta,
314 pdu->longitudeDelta, pdu->altitudeDelta);
316 if (!location_write_header(s, PDUTYPE_LOCATION3D_DELTA, ext ? 20 : 12))
317 return ERROR_OUTOFMEMORY;
319 if (!freerdp_write_four_byte_float(s, pdu->latitudeDelta) ||
320 !freerdp_write_four_byte_float(s, pdu->longitudeDelta) ||
321 !freerdp_write_four_byte_signed_integer(s, pdu->altitudeDelta))
322 return ERROR_INTERNAL_ERROR;
326 if (!freerdp_write_four_byte_float(s, *pdu->speedDelta) ||
327 !freerdp_write_four_byte_float(s, *pdu->headingDelta))
328 return ERROR_INTERNAL_ERROR;
331 return location_channel_send(channel, s);
334static UINT location_send(LocationClientContext* context, LOCATION_PDUTYPE type,
size_t count, ...)
336 WINPR_ASSERT(context);
338 LOCATION_PLUGIN* loc = context->handle;
344 IWTSVirtualChannel* channel = cb->channel;
345 WINPR_ASSERT(channel);
347 const LOCATION_CALLBACK* callback =
348 (
const LOCATION_CALLBACK*)loc->baseDynPlugin.channel_callbacks;
349 WINPR_ASSERT(callback);
351 UINT32 res = ERROR_INTERNAL_ERROR;
356 case PDUTYPE_BASE_LOCATION3D:
357 if ((count != 3) && (count != 7))
358 res = ERROR_INVALID_PARAMETER;
362 LOCATIONSOURCE source = LOCATIONSOURCE_IP;
363 double speed = FP_NAN;
364 double heading = FP_NAN;
365 double horizontalAccuracy = FP_NAN;
366 pdu.latitude = va_arg(ap,
double);
367 pdu.longitude = va_arg(ap,
double);
368 pdu.altitude = va_arg(ap, INT32);
369 if ((count > 3) && (callback->clientVersion >= RDPLOCATION_PROTOCOL_VERSION_200))
371 speed = va_arg(ap,
double);
372 heading = va_arg(ap,
double);
373 horizontalAccuracy = va_arg(ap,
double);
374 source = WINPR_ASSERTING_INT_CAST(LOCATIONSOURCE, va_arg(ap,
int));
376 pdu.heading = &heading;
377 pdu.horizontalAccuracy = &horizontalAccuracy;
378 pdu.source = &source;
380 res = location_send_base_location3d(channel, &pdu);
383 case PDUTYPE_LOCATION2D_DELTA:
384 if ((count != 2) && (count != 4))
385 res = ERROR_INVALID_PARAMETER;
390 pdu.latitudeDelta = va_arg(ap,
double);
391 pdu.longitudeDelta = va_arg(ap,
double);
393 double speedDelta = FP_NAN;
394 double headingDelta = FP_NAN;
395 if ((count > 2) && (callback->clientVersion >= RDPLOCATION_PROTOCOL_VERSION_200))
397 speedDelta = va_arg(ap,
double);
398 headingDelta = va_arg(ap,
double);
399 pdu.speedDelta = &speedDelta;
400 pdu.headingDelta = &headingDelta;
402 res = location_send_location2d_delta(channel, &pdu);
405 case PDUTYPE_LOCATION3D_DELTA:
406 if ((count != 3) && (count != 5))
407 res = ERROR_INVALID_PARAMETER;
411 double speedDelta = FP_NAN;
412 double headingDelta = FP_NAN;
414 pdu.latitudeDelta = va_arg(ap,
double);
415 pdu.longitudeDelta = va_arg(ap,
double);
416 pdu.altitudeDelta = va_arg(ap, INT32);
417 if ((count > 3) && (callback->clientVersion >= RDPLOCATION_PROTOCOL_VERSION_200))
419 speedDelta = va_arg(ap,
double);
420 headingDelta = va_arg(ap,
double);
421 pdu.speedDelta = &speedDelta;
422 pdu.headingDelta = &headingDelta;
424 res = location_send_location3d_delta(channel, &pdu);
428 res = ERROR_INVALID_PARAMETER;
440static UINT location_on_close(IWTSVirtualChannelCallback* pChannelCallback)
442 UINT res = CHANNEL_RC_OK;
447 LOCATION_PLUGIN* plugin = (LOCATION_PLUGIN*)callback->plugin;
448 WINPR_ASSERT(plugin);
450 res = IFCALLRESULT(CHANNEL_RC_OK, plugin->context.LocationStop, &plugin->context);
458 WINPR_ATTR_UNUSED rdpSettings* settings)
460 LOCATION_PLUGIN* loc = (LOCATION_PLUGIN*)plugin;
464 loc->context.LocationSend = location_send;
465 loc->context.handle = loc;
466 plugin->iface.pInterface = &loc->context;
467 return CHANNEL_RC_OK;
470static const IWTSVirtualChannelCallback location_callbacks = { location_on_data_received,
472 location_on_close, NULL };
479FREERDP_ENTRY_POINT(UINT VCAPITYPE location_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
481 return freerdp_generic_DVCPluginEntry(pEntryPoints, TAG, LOCATION_DVC_CHANNEL_NAME,
482 sizeof(LOCATION_PLUGIN),
sizeof(LOCATION_CALLBACK),
483 &location_callbacks, location_init, NULL);