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=%" PRIu16, pduType);
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->speed : FP_NAN,
233 pdu->heading ? *pdu->heading : FP_NAN,
234 pdu->horizontalAccuracy ? *pdu->horizontalAccuracy : FP_NAN, *pdu->source);
236 WLog_DBG(TAG,
"latitude=%lf, longitude=%lf, altitude=%" PRId32, pdu->latitude,
237 pdu->longitude, pdu->altitude);
239 if (!location_write_header(s, PDUTYPE_BASE_LOCATION3D, pdu->source ? 25 : 12))
240 return ERROR_OUTOFMEMORY;
242 if (!freerdp_write_four_byte_float(s, pdu->latitude) ||
243 !freerdp_write_four_byte_float(s, pdu->longitude) ||
244 !freerdp_write_four_byte_signed_integer(s, pdu->altitude))
245 return ERROR_INTERNAL_ERROR;
249 if (!freerdp_write_four_byte_float(s, *pdu->speed) ||
250 !freerdp_write_four_byte_float(s, *pdu->heading) ||
251 !freerdp_write_four_byte_float(s, *pdu->horizontalAccuracy))
252 return ERROR_INTERNAL_ERROR;
254 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(UINT8, *pdu->source));
257 return location_channel_send(channel, s);
260static UINT location_send_location2d_delta(IWTSVirtualChannel* channel,
264 BYTE buffer[32] = { 0 };
265 wStream* s = Stream_StaticInit(&sbuffer, buffer,
sizeof(buffer));
268 WINPR_ASSERT(channel);
271 const BOOL ext = pdu->speedDelta && pdu->headingDelta;
274 WLog_DBG(TAG,
"latitude=%lf, longitude=%lf, speed=%lf, heading=%lf", pdu->latitudeDelta,
275 pdu->longitudeDelta, pdu->speedDelta ? *pdu->speedDelta : FP_NAN,
276 pdu->headingDelta ? *pdu->headingDelta : FP_NAN);
278 WLog_DBG(TAG,
"latitude=%lf, longitude=%lf", pdu->latitudeDelta, pdu->longitudeDelta);
280 if (!location_write_header(s, PDUTYPE_LOCATION2D_DELTA, ext ? 16 : 8))
281 return ERROR_OUTOFMEMORY;
283 if (!freerdp_write_four_byte_float(s, pdu->latitudeDelta) ||
284 !freerdp_write_four_byte_float(s, pdu->longitudeDelta))
285 return ERROR_INTERNAL_ERROR;
289 if (!freerdp_write_four_byte_float(s, *pdu->speedDelta) ||
290 !freerdp_write_four_byte_float(s, *pdu->headingDelta))
291 return ERROR_INTERNAL_ERROR;
294 return location_channel_send(channel, s);
297static UINT location_send_location3d_delta(IWTSVirtualChannel* channel,
301 BYTE buffer[32] = { 0 };
302 wStream* s = Stream_StaticInit(&sbuffer, buffer,
sizeof(buffer));
305 WINPR_ASSERT(channel);
308 const BOOL ext = pdu->speedDelta && pdu->headingDelta;
311 WLog_DBG(TAG,
"latitude=%lf, longitude=%lf, altitude=%" PRId32
", speed=%lf, heading=%lf",
312 pdu->latitudeDelta, pdu->longitudeDelta, pdu->altitudeDelta,
313 pdu->speedDelta ? *pdu->speedDelta : FP_NAN,
314 pdu->headingDelta ? *pdu->headingDelta : FP_NAN);
316 WLog_DBG(TAG,
"latitude=%lf, longitude=%lf, altitude=%" PRId32, pdu->latitudeDelta,
317 pdu->longitudeDelta, pdu->altitudeDelta);
319 if (!location_write_header(s, PDUTYPE_LOCATION3D_DELTA, ext ? 20 : 12))
320 return ERROR_OUTOFMEMORY;
322 if (!freerdp_write_four_byte_float(s, pdu->latitudeDelta) ||
323 !freerdp_write_four_byte_float(s, pdu->longitudeDelta) ||
324 !freerdp_write_four_byte_signed_integer(s, pdu->altitudeDelta))
325 return ERROR_INTERNAL_ERROR;
329 if (!freerdp_write_four_byte_float(s, *pdu->speedDelta) ||
330 !freerdp_write_four_byte_float(s, *pdu->headingDelta))
331 return ERROR_INTERNAL_ERROR;
334 return location_channel_send(channel, s);
337static UINT location_send(LocationClientContext* context, LOCATION_PDUTYPE type,
size_t count, ...)
339 WINPR_ASSERT(context);
341 LOCATION_PLUGIN* loc = context->handle;
347 IWTSVirtualChannel* channel = cb->channel;
348 WINPR_ASSERT(channel);
350 const LOCATION_CALLBACK* callback =
351 (
const LOCATION_CALLBACK*)loc->baseDynPlugin.channel_callbacks;
352 WINPR_ASSERT(callback);
354 UINT32 res = ERROR_INTERNAL_ERROR;
359 case PDUTYPE_BASE_LOCATION3D:
360 if ((count != 3) && (count != 7))
361 res = ERROR_INVALID_PARAMETER;
364 LOCATIONSOURCE source = LOCATIONSOURCE_IP;
365 double speed = FP_NAN;
366 double heading = FP_NAN;
367 double horizontalAccuracy = FP_NAN;
369 .longitude = va_arg(ap,
double),
370 .altitude = va_arg(ap, INT32),
373 .horizontalAccuracy = NULL,
376 if ((count > 3) && (callback->clientVersion >= RDPLOCATION_PROTOCOL_VERSION_200))
378 speed = va_arg(ap,
double);
379 heading = va_arg(ap,
double);
380 horizontalAccuracy = va_arg(ap,
double);
381 source = WINPR_ASSERTING_INT_CAST(LOCATIONSOURCE, va_arg(ap,
int));
383 pdu.heading = &heading;
384 pdu.horizontalAccuracy = &horizontalAccuracy;
385 pdu.source = &source;
387 res = location_send_base_location3d(channel, &pdu);
390 case PDUTYPE_LOCATION2D_DELTA:
391 if ((count != 2) && (count != 4))
392 res = ERROR_INVALID_PARAMETER;
396 .longitudeDelta = va_arg(ap,
double),
398 .headingDelta = NULL };
400 double speedDelta = FP_NAN;
401 double headingDelta = FP_NAN;
402 if ((count > 2) && (callback->clientVersion >= RDPLOCATION_PROTOCOL_VERSION_200))
404 speedDelta = va_arg(ap,
double);
405 headingDelta = va_arg(ap,
double);
406 pdu.speedDelta = &speedDelta;
407 pdu.headingDelta = &headingDelta;
409 res = location_send_location2d_delta(channel, &pdu);
412 case PDUTYPE_LOCATION3D_DELTA:
413 if ((count != 3) && (count != 5))
414 res = ERROR_INVALID_PARAMETER;
417 double speedDelta = FP_NAN;
418 double headingDelta = FP_NAN;
421 .longitudeDelta = va_arg(ap,
double),
422 .altitudeDelta = va_arg(ap, INT32),
424 .headingDelta = NULL };
425 if ((count > 3) && (callback->clientVersion >= RDPLOCATION_PROTOCOL_VERSION_200))
427 speedDelta = va_arg(ap,
double);
428 headingDelta = va_arg(ap,
double);
429 pdu.speedDelta = &speedDelta;
430 pdu.headingDelta = &headingDelta;
432 res = location_send_location3d_delta(channel, &pdu);
436 res = ERROR_INVALID_PARAMETER;
448static UINT location_on_close(IWTSVirtualChannelCallback* pChannelCallback)
450 UINT res = CHANNEL_RC_OK;
455 LOCATION_PLUGIN* plugin = (LOCATION_PLUGIN*)callback->plugin;
456 WINPR_ASSERT(plugin);
458 res = IFCALLRESULT(CHANNEL_RC_OK, plugin->context.LocationStop, &plugin->context);
466 WINPR_ATTR_UNUSED rdpSettings* settings)
468 LOCATION_PLUGIN* loc = (LOCATION_PLUGIN*)plugin;
472 loc->context.LocationSend = location_send;
473 loc->context.handle = loc;
474 plugin->iface.pInterface = &loc->context;
475 return CHANNEL_RC_OK;
478static const IWTSVirtualChannelCallback location_callbacks = { location_on_data_received,
480 location_on_close, NULL };
487FREERDP_ENTRY_POINT(UINT VCAPITYPE location_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
489 return freerdp_generic_DVCPluginEntry(pEntryPoints, TAG, LOCATION_DVC_CHANNEL_NAME,
490 sizeof(LOCATION_PLUGIN),
sizeof(LOCATION_CALLBACK),
491 &location_callbacks, location_init, NULL);