21 #include <freerdp/config.h>
28 #include <winpr/crt.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;
59 static 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);
80 static 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);
89 static 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);
100 static 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);
114 static 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);
129 static 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);
154 static 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;
189 char cbuffer[64] = { 0 };
190 char sbuffer[64] = { 0 };
191 WLog_Print(plugin->baseDynPlugin.log, WLOG_DEBUG,
192 "Server version %s, client version %s",
193 location_version_str(callback->serverVersion, sbuffer,
sizeof(sbuffer)),
194 location_version_str(callback->clientVersion, cbuffer,
sizeof(cbuffer)));
196 if (!plugin->context.LocationStart)
198 WLog_Print(plugin->baseDynPlugin.log, WLOG_WARN,
199 "LocationStart=NULL, no location data will be sent");
200 return CHANNEL_RC_OK;
203 plugin->context.LocationStart(&plugin->context, callback->clientVersion, 0);
204 if (res != CHANNEL_RC_OK)
206 return location_send_client_ready_pdu(callback);
208 WLog_WARN(TAG,
"invalid pduType=%s");
209 return ERROR_INVALID_DATA;
213 static UINT location_send_base_location3d(IWTSVirtualChannel* channel,
217 BYTE buffer[32] = { 0 };
218 wStream* s = Stream_StaticInit(&sbuffer, buffer,
sizeof(buffer));
220 WINPR_ASSERT(channel);
225 "latitude=%lf, longitude=%lf, altitude=%" PRId32
226 ", speed=%lf, heading=%lf, haccuracy=%lf, source=%" PRIu8,
227 pdu->latitude, pdu->longitude, pdu->altitude, pdu->speed, pdu->heading,
228 pdu->horizontalAccuracy, *pdu->source);
230 WLog_DBG(TAG,
"latitude=%lf, longitude=%lf, altitude=%" PRId32, pdu->latitude,
231 pdu->longitude, pdu->altitude);
233 if (!location_write_header(s, PDUTYPE_BASE_LOCATION3D, pdu->source ? 25 : 12))
234 return ERROR_OUTOFMEMORY;
236 if (!freerdp_write_four_byte_float(s, pdu->latitude) ||
237 !freerdp_write_four_byte_float(s, pdu->longitude) ||
238 !freerdp_write_four_byte_signed_integer(s, pdu->altitude))
239 return ERROR_INTERNAL_ERROR;
243 if (!freerdp_write_four_byte_float(s, *pdu->speed) ||
244 !freerdp_write_four_byte_float(s, *pdu->heading) ||
245 !freerdp_write_four_byte_float(s, *pdu->horizontalAccuracy))
246 return ERROR_INTERNAL_ERROR;
248 Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(UINT8, *pdu->source));
251 return location_channel_send(channel, s);
254 static UINT location_send_location2d_delta(IWTSVirtualChannel* channel,
258 BYTE buffer[32] = { 0 };
259 wStream* s = Stream_StaticInit(&sbuffer, buffer,
sizeof(buffer));
262 WINPR_ASSERT(channel);
265 const BOOL ext = pdu->speedDelta && pdu->headingDelta;
268 WLog_DBG(TAG,
"latitude=%lf, longitude=%lf, speed=%lf, heading=%lf", pdu->latitudeDelta,
269 pdu->longitudeDelta, pdu->speedDelta, pdu->headingDelta);
271 WLog_DBG(TAG,
"latitude=%lf, longitude=%lf", pdu->latitudeDelta, pdu->longitudeDelta);
273 if (!location_write_header(s, PDUTYPE_LOCATION2D_DELTA, ext ? 16 : 8))
274 return ERROR_OUTOFMEMORY;
276 if (!freerdp_write_four_byte_float(s, pdu->latitudeDelta) ||
277 !freerdp_write_four_byte_float(s, pdu->longitudeDelta))
278 return ERROR_INTERNAL_ERROR;
282 if (!freerdp_write_four_byte_float(s, *pdu->speedDelta) ||
283 !freerdp_write_four_byte_float(s, *pdu->headingDelta))
284 return ERROR_INTERNAL_ERROR;
287 return location_channel_send(channel, s);
290 static UINT location_send_location3d_delta(IWTSVirtualChannel* channel,
294 BYTE buffer[32] = { 0 };
295 wStream* s = Stream_StaticInit(&sbuffer, buffer,
sizeof(buffer));
298 WINPR_ASSERT(channel);
301 const BOOL ext = pdu->speedDelta && pdu->headingDelta;
304 WLog_DBG(TAG,
"latitude=%lf, longitude=%lf, altitude=%" PRId32
", speed=%lf, heading=%lf",
305 pdu->latitudeDelta, pdu->longitudeDelta, pdu->altitudeDelta, pdu->speedDelta,
308 WLog_DBG(TAG,
"latitude=%lf, longitude=%lf, altitude=%" PRId32, pdu->latitudeDelta,
309 pdu->longitudeDelta, pdu->altitudeDelta);
311 if (!location_write_header(s, PDUTYPE_LOCATION3D_DELTA, ext ? 20 : 12))
312 return ERROR_OUTOFMEMORY;
314 if (!freerdp_write_four_byte_float(s, pdu->latitudeDelta) ||
315 !freerdp_write_four_byte_float(s, pdu->longitudeDelta) ||
316 !freerdp_write_four_byte_signed_integer(s, pdu->altitudeDelta))
317 return ERROR_INTERNAL_ERROR;
321 if (!freerdp_write_four_byte_float(s, *pdu->speedDelta) ||
322 !freerdp_write_four_byte_float(s, *pdu->headingDelta))
323 return ERROR_INTERNAL_ERROR;
326 return location_channel_send(channel, s);
329 static UINT location_send(LocationClientContext* context, LOCATION_PDUTYPE type,
size_t count, ...)
331 WINPR_ASSERT(context);
333 LOCATION_PLUGIN* loc = context->handle;
339 IWTSVirtualChannel* channel = cb->channel;
340 WINPR_ASSERT(channel);
342 const LOCATION_CALLBACK* callback =
343 (
const LOCATION_CALLBACK*)loc->baseDynPlugin.channel_callbacks;
344 WINPR_ASSERT(callback);
346 UINT32 res = ERROR_INTERNAL_ERROR;
351 case PDUTYPE_BASE_LOCATION3D:
352 if ((count != 3) && (count != 7))
353 res = ERROR_INVALID_PARAMETER;
357 LOCATIONSOURCE source = LOCATIONSOURCE_IP;
358 double speed = FP_NAN;
359 double heading = FP_NAN;
360 double horizontalAccuracy = FP_NAN;
361 pdu.latitude = va_arg(ap,
double);
362 pdu.longitude = va_arg(ap,
double);
363 pdu.altitude = va_arg(ap, INT32);
364 if ((count > 3) && (callback->clientVersion >= RDPLOCATION_PROTOCOL_VERSION_200))
366 speed = va_arg(ap,
double);
367 heading = va_arg(ap,
double);
368 horizontalAccuracy = va_arg(ap,
double);
369 source = WINPR_ASSERTING_INT_CAST(LOCATIONSOURCE, va_arg(ap,
int));
371 pdu.heading = &heading;
372 pdu.horizontalAccuracy = &horizontalAccuracy;
373 pdu.source = &source;
375 res = location_send_base_location3d(channel, &pdu);
378 case PDUTYPE_LOCATION2D_DELTA:
379 if ((count != 2) && (count != 4))
380 res = ERROR_INVALID_PARAMETER;
385 pdu.latitudeDelta = va_arg(ap,
double);
386 pdu.longitudeDelta = va_arg(ap,
double);
388 double speedDelta = FP_NAN;
389 double headingDelta = FP_NAN;
390 if ((count > 2) && (callback->clientVersion >= RDPLOCATION_PROTOCOL_VERSION_200))
392 speedDelta = va_arg(ap,
double);
393 headingDelta = va_arg(ap,
double);
394 pdu.speedDelta = &speedDelta;
395 pdu.headingDelta = &headingDelta;
397 res = location_send_location2d_delta(channel, &pdu);
400 case PDUTYPE_LOCATION3D_DELTA:
401 if ((count != 3) && (count != 5))
402 res = ERROR_INVALID_PARAMETER;
406 double speedDelta = FP_NAN;
407 double headingDelta = FP_NAN;
409 pdu.latitudeDelta = va_arg(ap,
double);
410 pdu.longitudeDelta = va_arg(ap,
double);
411 pdu.altitudeDelta = va_arg(ap, INT32);
412 if ((count > 3) && (callback->clientVersion >= RDPLOCATION_PROTOCOL_VERSION_200))
414 speedDelta = va_arg(ap,
double);
415 headingDelta = va_arg(ap,
double);
416 pdu.speedDelta = &speedDelta;
417 pdu.headingDelta = &headingDelta;
419 res = location_send_location3d_delta(channel, &pdu);
423 res = ERROR_INVALID_PARAMETER;
435 static UINT location_on_close(IWTSVirtualChannelCallback* pChannelCallback)
437 UINT res = CHANNEL_RC_OK;
442 LOCATION_PLUGIN* plugin = (LOCATION_PLUGIN*)callback->plugin;
443 WINPR_ASSERT(plugin);
445 res = IFCALLRESULT(CHANNEL_RC_OK, plugin->context.LocationStop, &plugin->context);
452 static UINT location_init(
GENERIC_DYNVC_PLUGIN* plugin, rdpContext* context, rdpSettings* settings)
454 LOCATION_PLUGIN* loc = (LOCATION_PLUGIN*)plugin;
458 loc->context.LocationSend = location_send;
459 loc->context.handle = loc;
460 plugin->iface.pInterface = &loc->context;
461 return CHANNEL_RC_OK;
464 static const IWTSVirtualChannelCallback location_callbacks = { location_on_data_received,
466 location_on_close, NULL };
473 FREERDP_ENTRY_POINT(UINT VCAPITYPE location_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
475 return freerdp_generic_DVCPluginEntry(pEntryPoints, TAG, LOCATION_DVC_CHANNEL_NAME,
476 sizeof(LOCATION_PLUGIN),
sizeof(LOCATION_CALLBACK),
477 &location_callbacks, location_init, NULL);