Coverage Report

Created: 2026-03-04 06:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/channels/rail/rail_common.c
Line
Count
Source
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * RAIL common functions
4
 *
5
 * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 * Copyright 2011 Roman Barabanov <romanbarabanov@gmail.com>
7
 * Copyright 2011 Vic Lee
8
 * Copyright 2015 Thincast Technologies GmbH
9
 * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
10
 *
11
 * Licensed under the Apache License, Version 2.0 (the "License");
12
 * you may not use this file except in compliance with the License.
13
 * You may obtain a copy of the License at
14
 *
15
 *     http://www.apache.org/licenses/LICENSE-2.0
16
 *
17
 * Unless required by applicable law or agreed to in writing, software
18
 * distributed under the License is distributed on an "AS IS" BASIS,
19
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
 * See the License for the specific language governing permissions and
21
 * limitations under the License.
22
 */
23
#include "rail_common.h"
24
25
#include <winpr/crt.h>
26
#include <freerdp/channels/log.h>
27
28
#define TAG CHANNELS_TAG("rail.common")
29
30
const char* rail_get_order_type_string(UINT16 orderType)
31
0
{
32
0
  switch (orderType)
33
0
  {
34
0
    case TS_RAIL_ORDER_EXEC:
35
0
      return "TS_RAIL_ORDER_EXEC";
36
0
    case TS_RAIL_ORDER_ACTIVATE:
37
0
      return "TS_RAIL_ORDER_ACTIVATE";
38
0
    case TS_RAIL_ORDER_SYSPARAM:
39
0
      return "TS_RAIL_ORDER_SYSPARAM";
40
0
    case TS_RAIL_ORDER_SYSCOMMAND:
41
0
      return "TS_RAIL_ORDER_SYSCOMMAND";
42
0
    case TS_RAIL_ORDER_HANDSHAKE:
43
0
      return "TS_RAIL_ORDER_HANDSHAKE";
44
0
    case TS_RAIL_ORDER_NOTIFY_EVENT:
45
0
      return "TS_RAIL_ORDER_NOTIFY_EVENT";
46
0
    case TS_RAIL_ORDER_WINDOWMOVE:
47
0
      return "TS_RAIL_ORDER_WINDOWMOVE";
48
0
    case TS_RAIL_ORDER_LOCALMOVESIZE:
49
0
      return "TS_RAIL_ORDER_LOCALMOVESIZE";
50
0
    case TS_RAIL_ORDER_MINMAXINFO:
51
0
      return "TS_RAIL_ORDER_MINMAXINFO";
52
0
    case TS_RAIL_ORDER_CLIENTSTATUS:
53
0
      return "TS_RAIL_ORDER_CLIENTSTATUS";
54
0
    case TS_RAIL_ORDER_SYSMENU:
55
0
      return "TS_RAIL_ORDER_SYSMENU";
56
0
    case TS_RAIL_ORDER_LANGBARINFO:
57
0
      return "TS_RAIL_ORDER_LANGBARINFO";
58
0
    case TS_RAIL_ORDER_GET_APPID_REQ:
59
0
      return "TS_RAIL_ORDER_GET_APPID_REQ";
60
0
    case TS_RAIL_ORDER_GET_APPID_RESP:
61
0
      return "TS_RAIL_ORDER_GET_APPID_RESP";
62
0
    case TS_RAIL_ORDER_TASKBARINFO:
63
0
      return "TS_RAIL_ORDER_TASKBARINFO";
64
0
    case TS_RAIL_ORDER_LANGUAGEIMEINFO:
65
0
      return "TS_RAIL_ORDER_LANGUAGEIMEINFO";
66
0
    case TS_RAIL_ORDER_COMPARTMENTINFO:
67
0
      return "TS_RAIL_ORDER_COMPARTMENTINFO";
68
0
    case TS_RAIL_ORDER_HANDSHAKE_EX:
69
0
      return "TS_RAIL_ORDER_HANDSHAKE_EX";
70
0
    case TS_RAIL_ORDER_ZORDER_SYNC:
71
0
      return "TS_RAIL_ORDER_ZORDER_SYNC";
72
0
    case TS_RAIL_ORDER_CLOAK:
73
0
      return "TS_RAIL_ORDER_CLOAK";
74
0
    case TS_RAIL_ORDER_POWER_DISPLAY_REQUEST:
75
0
      return "TS_RAIL_ORDER_POWER_DISPLAY_REQUEST";
76
0
    case TS_RAIL_ORDER_SNAP_ARRANGE:
77
0
      return "TS_RAIL_ORDER_SNAP_ARRANGE";
78
0
    case TS_RAIL_ORDER_GET_APPID_RESP_EX:
79
0
      return "TS_RAIL_ORDER_GET_APPID_RESP_EX";
80
0
    case TS_RAIL_ORDER_EXEC_RESULT:
81
0
      return "TS_RAIL_ORDER_EXEC_RESULT";
82
0
    case TS_RAIL_ORDER_TEXTSCALEINFO:
83
0
      return "TS_RAIL_ORDER_TEXTSCALEINFO";
84
0
    case TS_RAIL_ORDER_CARETBLINKINFO:
85
0
      return "TS_RAIL_ORDER_CARETBLINKINFO";
86
0
    default:
87
0
      return "TS_RAIL_ORDER_UNKNOWN";
88
0
  }
89
0
}
90
91
const char* rail_get_order_type_string_full(UINT16 orderType, char* buffer, size_t length)
92
0
{
93
0
  (void)_snprintf(buffer, length, "%s[0x%04" PRIx16 "]", rail_get_order_type_string(orderType),
94
0
                  orderType);
95
0
  return buffer;
96
0
}
97
98
/**
99
 * Function description
100
 *
101
 * @return 0 on success, otherwise a Win32 error code
102
 */
103
UINT rail_read_pdu_header(wStream* s, UINT16* orderType, UINT16* orderLength)
104
0
{
105
0
  if (!s || !orderType || !orderLength)
106
0
    return ERROR_INVALID_PARAMETER;
107
108
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
109
0
    return ERROR_INVALID_DATA;
110
111
0
  Stream_Read_UINT16(s, *orderType);   /* orderType (2 bytes) */
112
0
  Stream_Read_UINT16(s, *orderLength); /* orderLength (2 bytes) */
113
0
  return CHANNEL_RC_OK;
114
0
}
115
116
BOOL rail_write_pdu_header(wStream* s, UINT16 orderType, UINT16 orderLength)
117
0
{
118
0
  if (!Stream_EnsureRemainingCapacity(s, 4))
119
0
    return FALSE;
120
0
  Stream_Write_UINT16(s, orderType);   /* orderType (2 bytes) */
121
0
  Stream_Write_UINT16(s, orderLength); /* orderLength (2 bytes) */
122
0
  return TRUE;
123
0
}
124
125
wStream* rail_pdu_init(size_t length)
126
0
{
127
0
  wStream* s = Stream_New(nullptr, length + RAIL_PDU_HEADER_LENGTH);
128
129
0
  if (!s)
130
0
    return nullptr;
131
132
0
  Stream_Seek(s, RAIL_PDU_HEADER_LENGTH);
133
0
  return s;
134
0
}
135
136
/**
137
 * Function description
138
 *
139
 * @return 0 on success, otherwise a Win32 error code
140
 */
141
UINT rail_read_handshake_order(wStream* s, RAIL_HANDSHAKE_ORDER* handshake)
142
0
{
143
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
144
0
    return ERROR_INVALID_DATA;
145
146
0
  Stream_Read_UINT32(s, handshake->buildNumber); /* buildNumber (4 bytes) */
147
0
  return CHANNEL_RC_OK;
148
0
}
149
150
void rail_write_handshake_order(wStream* s, const RAIL_HANDSHAKE_ORDER* handshake)
151
0
{
152
0
  WINPR_ASSERT(s);
153
0
  WINPR_ASSERT(handshake);
154
0
  WINPR_ASSERT(Stream_EnsureRemainingCapacity(s, 4));
155
0
  Stream_Write_UINT32(s, handshake->buildNumber); /* buildNumber (4 bytes) */
156
0
}
157
158
/**
159
 * Function description
160
 *
161
 * @return 0 on success, otherwise a Win32 error code
162
 */
163
UINT rail_read_handshake_ex_order(wStream* s, RAIL_HANDSHAKE_EX_ORDER* handshakeEx)
164
0
{
165
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
166
0
    return ERROR_INVALID_DATA;
167
168
0
  Stream_Read_UINT32(s, handshakeEx->buildNumber);        /* buildNumber (4 bytes) */
169
0
  Stream_Read_UINT32(s, handshakeEx->railHandshakeFlags); /* railHandshakeFlags (4 bytes) */
170
0
  return CHANNEL_RC_OK;
171
0
}
172
173
void rail_write_handshake_ex_order(wStream* s, const RAIL_HANDSHAKE_EX_ORDER* handshakeEx)
174
0
{
175
0
  WINPR_ASSERT(s);
176
0
  WINPR_ASSERT(handshakeEx);
177
0
  WINPR_ASSERT(Stream_EnsureRemainingCapacity(s, 8));
178
179
0
  Stream_Write_UINT32(s, handshakeEx->buildNumber);        /* buildNumber (4 bytes) */
180
0
  Stream_Write_UINT32(s, handshakeEx->railHandshakeFlags); /* railHandshakeFlags (4 bytes) */
181
0
}
182
183
/**
184
 * Function description
185
 *
186
 * @return 0 on success, otherwise a Win32 error code
187
 */
188
UINT rail_write_unicode_string(wStream* s, const RAIL_UNICODE_STRING* unicode_string)
189
0
{
190
0
  if (!s || !unicode_string)
191
0
    return ERROR_INVALID_PARAMETER;
192
193
0
  if (!Stream_EnsureRemainingCapacity(s, 2 + unicode_string->length))
194
0
  {
195
0
    WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
196
0
    return CHANNEL_RC_NO_MEMORY;
197
0
  }
198
199
0
  Stream_Write_UINT16(s, unicode_string->length);                  /* cbString (2 bytes) */
200
0
  Stream_Write(s, unicode_string->string, unicode_string->length); /* string */
201
0
  return CHANNEL_RC_OK;
202
0
}
203
204
/**
205
 * Function description
206
 *
207
 * @return 0 on success, otherwise a Win32 error code
208
 */
209
UINT rail_write_unicode_string_value(wStream* s, const RAIL_UNICODE_STRING* unicode_string)
210
0
{
211
0
  size_t length = 0;
212
213
0
  if (!s || !unicode_string)
214
0
    return ERROR_INVALID_PARAMETER;
215
216
0
  length = unicode_string->length;
217
218
0
  if (length > 0)
219
0
  {
220
0
    if (!Stream_EnsureRemainingCapacity(s, length))
221
0
    {
222
0
      WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
223
0
      return CHANNEL_RC_NO_MEMORY;
224
0
    }
225
226
0
    Stream_Write(s, unicode_string->string, length); /* string */
227
0
  }
228
229
0
  return CHANNEL_RC_OK;
230
0
}
231
232
/**
233
 * Function description
234
 *
235
 * @return 0 on success, otherwise a Win32 error code
236
 */
237
static UINT rail_read_high_contrast(wStream* s, RAIL_HIGH_CONTRAST* highContrast)
238
0
{
239
0
  if (!s || !highContrast)
240
0
    return ERROR_INVALID_PARAMETER;
241
242
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
243
0
    return ERROR_INVALID_DATA;
244
245
0
  Stream_Read_UINT32(s, highContrast->flags);             /* flags (4 bytes) */
246
0
  Stream_Read_UINT32(s, highContrast->colorSchemeLength); /* colorSchemeLength (4 bytes) */
247
248
0
  if (!rail_read_unicode_string(s, &highContrast->colorScheme)) /* colorScheme */
249
0
    return ERROR_INTERNAL_ERROR;
250
0
  return CHANNEL_RC_OK;
251
0
}
252
253
/**
254
 * Function description
255
 *
256
 * @return 0 on success, otherwise a Win32 error code
257
 */
258
static UINT rail_write_high_contrast(wStream* s, const RAIL_HIGH_CONTRAST* highContrast)
259
0
{
260
0
  UINT32 colorSchemeLength = 0;
261
262
0
  if (!s || !highContrast)
263
0
    return ERROR_INVALID_PARAMETER;
264
265
0
  if (!Stream_EnsureRemainingCapacity(s, 8))
266
0
    return CHANNEL_RC_NO_MEMORY;
267
268
0
  colorSchemeLength = highContrast->colorScheme.length + 2;
269
0
  Stream_Write_UINT32(s, highContrast->flags); /* flags (4 bytes) */
270
0
  Stream_Write_UINT32(s, colorSchemeLength);   /* colorSchemeLength (4 bytes) */
271
0
  return rail_write_unicode_string(s, &highContrast->colorScheme); /* colorScheme */
272
0
}
273
274
/**
275
 * Function description
276
 *
277
 * @return 0 on success, otherwise a Win32 error code
278
 */
279
static UINT rail_read_filterkeys(wStream* s, TS_FILTERKEYS* filterKeys)
280
0
{
281
0
  if (!s || !filterKeys)
282
0
    return ERROR_INVALID_PARAMETER;
283
284
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 20))
285
0
    return ERROR_INVALID_DATA;
286
287
0
  Stream_Read_UINT32(s, filterKeys->Flags);
288
0
  Stream_Read_UINT32(s, filterKeys->WaitTime);
289
0
  Stream_Read_UINT32(s, filterKeys->DelayTime);
290
0
  Stream_Read_UINT32(s, filterKeys->RepeatTime);
291
0
  Stream_Read_UINT32(s, filterKeys->BounceTime);
292
0
  return CHANNEL_RC_OK;
293
0
}
294
295
/**
296
 * Function description
297
 *
298
 * @return 0 on success, otherwise a Win32 error code
299
 */
300
static UINT rail_write_filterkeys(wStream* s, const TS_FILTERKEYS* filterKeys)
301
0
{
302
0
  if (!s || !filterKeys)
303
0
    return ERROR_INVALID_PARAMETER;
304
305
0
  if (!Stream_EnsureRemainingCapacity(s, 20))
306
0
    return CHANNEL_RC_NO_MEMORY;
307
308
0
  Stream_Write_UINT32(s, filterKeys->Flags);
309
0
  Stream_Write_UINT32(s, filterKeys->WaitTime);
310
0
  Stream_Write_UINT32(s, filterKeys->DelayTime);
311
0
  Stream_Write_UINT32(s, filterKeys->RepeatTime);
312
0
  Stream_Write_UINT32(s, filterKeys->BounceTime);
313
0
  return CHANNEL_RC_OK;
314
0
}
315
316
/**
317
 * Function description
318
 *
319
 * @return 0 on success, otherwise a Win32 error code
320
 */
321
UINT rail_read_sysparam_order(wStream* s, RAIL_SYSPARAM_ORDER* sysparam, BOOL extendedSpiSupported)
322
0
{
323
0
  BYTE body = 0;
324
0
  UINT error = CHANNEL_RC_OK;
325
326
0
  if (!s || !sysparam)
327
0
    return ERROR_INVALID_PARAMETER;
328
329
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 5))
330
0
    return ERROR_INVALID_DATA;
331
332
0
  Stream_Read_UINT32(s, sysparam->param); /* systemParam (4 bytes) */
333
334
0
  sysparam->params = 0; /* bitflags of received params */
335
336
0
  switch (sysparam->param)
337
0
  {
338
    /* Client sysparams */
339
0
    case SPI_SET_DRAG_FULL_WINDOWS:
340
0
      sysparam->params |= SPI_MASK_SET_DRAG_FULL_WINDOWS;
341
0
      Stream_Read_UINT8(s, body); /* body (1 byte) */
342
0
      sysparam->dragFullWindows = body != 0;
343
0
      break;
344
345
0
    case SPI_SET_KEYBOARD_CUES:
346
0
      sysparam->params |= SPI_MASK_SET_KEYBOARD_CUES;
347
0
      Stream_Read_UINT8(s, body); /* body (1 byte) */
348
0
      sysparam->keyboardCues = body != 0;
349
0
      break;
350
351
0
    case SPI_SET_KEYBOARD_PREF:
352
0
      sysparam->params |= SPI_MASK_SET_KEYBOARD_PREF;
353
0
      Stream_Read_UINT8(s, body); /* body (1 byte) */
354
0
      sysparam->keyboardPref = body != 0;
355
0
      break;
356
357
0
    case SPI_SET_MOUSE_BUTTON_SWAP:
358
0
      sysparam->params |= SPI_MASK_SET_MOUSE_BUTTON_SWAP;
359
0
      Stream_Read_UINT8(s, body); /* body (1 byte) */
360
0
      sysparam->mouseButtonSwap = body != 0;
361
0
      break;
362
363
0
    case SPI_SET_WORK_AREA:
364
0
      sysparam->params |= SPI_MASK_SET_WORK_AREA;
365
366
0
      if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
367
0
        return ERROR_INVALID_DATA;
368
369
0
      Stream_Read_UINT16(s, sysparam->workArea.left);   /* left (2 bytes) */
370
0
      Stream_Read_UINT16(s, sysparam->workArea.top);    /* top (2 bytes) */
371
0
      Stream_Read_UINT16(s, sysparam->workArea.right);  /* right (2 bytes) */
372
0
      Stream_Read_UINT16(s, sysparam->workArea.bottom); /* bottom (2 bytes) */
373
0
      break;
374
375
0
    case SPI_DISPLAY_CHANGE:
376
0
      sysparam->params |= SPI_MASK_DISPLAY_CHANGE;
377
378
0
      if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
379
0
        return ERROR_INVALID_DATA;
380
381
0
      Stream_Read_UINT16(s, sysparam->displayChange.left);   /* left (2 bytes) */
382
0
      Stream_Read_UINT16(s, sysparam->displayChange.top);    /* top (2 bytes) */
383
0
      Stream_Read_UINT16(s, sysparam->displayChange.right);  /* right (2 bytes) */
384
0
      Stream_Read_UINT16(s, sysparam->displayChange.bottom); /* bottom (2 bytes) */
385
0
      break;
386
387
0
    case SPI_TASKBAR_POS:
388
0
      sysparam->params |= SPI_MASK_TASKBAR_POS;
389
390
0
      if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
391
0
        return ERROR_INVALID_DATA;
392
393
0
      Stream_Read_UINT16(s, sysparam->taskbarPos.left);   /* left (2 bytes) */
394
0
      Stream_Read_UINT16(s, sysparam->taskbarPos.top);    /* top (2 bytes) */
395
0
      Stream_Read_UINT16(s, sysparam->taskbarPos.right);  /* right (2 bytes) */
396
0
      Stream_Read_UINT16(s, sysparam->taskbarPos.bottom); /* bottom (2 bytes) */
397
0
      break;
398
399
0
    case SPI_SET_HIGH_CONTRAST:
400
0
      sysparam->params |= SPI_MASK_SET_HIGH_CONTRAST;
401
0
      if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
402
0
        return ERROR_INVALID_DATA;
403
404
0
      error = rail_read_high_contrast(s, &sysparam->highContrast);
405
0
      break;
406
407
0
    case SPI_SETCARETWIDTH:
408
0
      sysparam->params |= SPI_MASK_SET_CARET_WIDTH;
409
410
0
      if (!extendedSpiSupported)
411
0
        return ERROR_INVALID_DATA;
412
413
0
      if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
414
0
        return ERROR_INVALID_DATA;
415
416
0
      Stream_Read_UINT32(s, sysparam->caretWidth);
417
418
0
      if (sysparam->caretWidth < 0x0001)
419
0
        return ERROR_INVALID_DATA;
420
421
0
      break;
422
423
0
    case SPI_SETSTICKYKEYS:
424
0
      sysparam->params |= SPI_MASK_SET_STICKY_KEYS;
425
426
0
      if (!extendedSpiSupported)
427
0
        return ERROR_INVALID_DATA;
428
429
0
      if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
430
0
        return ERROR_INVALID_DATA;
431
432
0
      Stream_Read_UINT32(s, sysparam->stickyKeys);
433
0
      break;
434
435
0
    case SPI_SETTOGGLEKEYS:
436
0
      sysparam->params |= SPI_MASK_SET_TOGGLE_KEYS;
437
438
0
      if (!extendedSpiSupported)
439
0
        return ERROR_INVALID_DATA;
440
441
0
      if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
442
0
        return ERROR_INVALID_DATA;
443
444
0
      Stream_Read_UINT32(s, sysparam->toggleKeys);
445
0
      break;
446
447
0
    case SPI_SETFILTERKEYS:
448
0
      sysparam->params |= SPI_MASK_SET_FILTER_KEYS;
449
450
0
      if (!extendedSpiSupported)
451
0
        return ERROR_INVALID_DATA;
452
453
0
      if (!Stream_CheckAndLogRequiredLength(TAG, s, 20))
454
0
        return ERROR_INVALID_DATA;
455
456
0
      error = rail_read_filterkeys(s, &sysparam->filterKeys);
457
0
      break;
458
459
    /* Server sysparams */
460
0
    case SPI_SETSCREENSAVEACTIVE:
461
0
      sysparam->params |= SPI_MASK_SET_SCREEN_SAVE_ACTIVE;
462
463
0
      Stream_Read_UINT8(s, body); /* body (1 byte) */
464
0
      sysparam->setScreenSaveActive = body != 0;
465
0
      break;
466
467
0
    case SPI_SETSCREENSAVESECURE:
468
0
      sysparam->params |= SPI_MASK_SET_SET_SCREEN_SAVE_SECURE;
469
470
0
      Stream_Read_UINT8(s, body); /* body (1 byte) */
471
0
      sysparam->setScreenSaveSecure = body != 0;
472
0
      break;
473
474
0
    default:
475
0
      break;
476
0
  }
477
478
0
  return error;
479
0
}
480
481
UINT rail_write_sysparam_order(wStream* s, const RAIL_SYSPARAM_ORDER* sysparam,
482
                               BOOL extendedSpiSupported)
483
0
{
484
0
  BYTE body = 0;
485
0
  UINT error = CHANNEL_RC_OK;
486
487
0
  if (!s || !sysparam)
488
0
    return ERROR_INVALID_PARAMETER;
489
490
0
  if (!Stream_EnsureRemainingCapacity(s, 12))
491
0
    return CHANNEL_RC_NO_MEMORY;
492
493
0
  Stream_Write_UINT32(s, sysparam->param); /* systemParam (4 bytes) */
494
495
0
  switch (sysparam->param)
496
0
  {
497
    /* Client sysparams */
498
0
    case SPI_SET_DRAG_FULL_WINDOWS:
499
0
      body = sysparam->dragFullWindows ? 1 : 0;
500
0
      Stream_Write_UINT8(s, body);
501
0
      break;
502
503
0
    case SPI_SET_KEYBOARD_CUES:
504
0
      body = sysparam->keyboardCues ? 1 : 0;
505
0
      Stream_Write_UINT8(s, body);
506
0
      break;
507
508
0
    case SPI_SET_KEYBOARD_PREF:
509
0
      body = sysparam->keyboardPref ? 1 : 0;
510
0
      Stream_Write_UINT8(s, body);
511
0
      break;
512
513
0
    case SPI_SET_MOUSE_BUTTON_SWAP:
514
0
      body = sysparam->mouseButtonSwap ? 1 : 0;
515
0
      Stream_Write_UINT8(s, body);
516
0
      break;
517
518
0
    case SPI_SET_WORK_AREA:
519
0
      Stream_Write_UINT16(s, sysparam->workArea.left);   /* left (2 bytes) */
520
0
      Stream_Write_UINT16(s, sysparam->workArea.top);    /* top (2 bytes) */
521
0
      Stream_Write_UINT16(s, sysparam->workArea.right);  /* right (2 bytes) */
522
0
      Stream_Write_UINT16(s, sysparam->workArea.bottom); /* bottom (2 bytes) */
523
0
      break;
524
525
0
    case SPI_DISPLAY_CHANGE:
526
0
      Stream_Write_UINT16(s, sysparam->displayChange.left);   /* left (2 bytes) */
527
0
      Stream_Write_UINT16(s, sysparam->displayChange.top);    /* top (2 bytes) */
528
0
      Stream_Write_UINT16(s, sysparam->displayChange.right);  /* right (2 bytes) */
529
0
      Stream_Write_UINT16(s, sysparam->displayChange.bottom); /* bottom (2 bytes) */
530
0
      break;
531
532
0
    case SPI_TASKBAR_POS:
533
0
      Stream_Write_UINT16(s, sysparam->taskbarPos.left);   /* left (2 bytes) */
534
0
      Stream_Write_UINT16(s, sysparam->taskbarPos.top);    /* top (2 bytes) */
535
0
      Stream_Write_UINT16(s, sysparam->taskbarPos.right);  /* right (2 bytes) */
536
0
      Stream_Write_UINT16(s, sysparam->taskbarPos.bottom); /* bottom (2 bytes) */
537
0
      break;
538
539
0
    case SPI_SET_HIGH_CONTRAST:
540
0
      error = rail_write_high_contrast(s, &sysparam->highContrast);
541
0
      break;
542
543
0
    case SPI_SETCARETWIDTH:
544
0
      if (!extendedSpiSupported)
545
0
        return ERROR_INVALID_DATA;
546
547
0
      if (sysparam->caretWidth < 0x0001)
548
0
        return ERROR_INVALID_DATA;
549
550
0
      Stream_Write_UINT32(s, sysparam->caretWidth);
551
0
      break;
552
553
0
    case SPI_SETSTICKYKEYS:
554
0
      if (!extendedSpiSupported)
555
0
        return ERROR_INVALID_DATA;
556
557
0
      Stream_Write_UINT32(s, sysparam->stickyKeys);
558
0
      break;
559
560
0
    case SPI_SETTOGGLEKEYS:
561
0
      if (!extendedSpiSupported)
562
0
        return ERROR_INVALID_DATA;
563
564
0
      Stream_Write_UINT32(s, sysparam->toggleKeys);
565
0
      break;
566
567
0
    case SPI_SETFILTERKEYS:
568
0
      if (!extendedSpiSupported)
569
0
        return ERROR_INVALID_DATA;
570
571
0
      error = rail_write_filterkeys(s, &sysparam->filterKeys);
572
0
      break;
573
574
    /* Server sysparams */
575
0
    case SPI_SETSCREENSAVEACTIVE:
576
0
      body = sysparam->setScreenSaveActive ? 1 : 0;
577
0
      Stream_Write_UINT8(s, body);
578
0
      break;
579
580
0
    case SPI_SETSCREENSAVESECURE:
581
0
      body = sysparam->setScreenSaveSecure ? 1 : 0;
582
0
      Stream_Write_UINT8(s, body);
583
0
      break;
584
585
0
    default:
586
0
      return ERROR_INVALID_PARAMETER;
587
0
  }
588
589
0
  return error;
590
0
}
591
592
BOOL rail_is_extended_spi_supported(UINT32 channelFlags)
593
0
{
594
0
  return (channelFlags & TS_RAIL_ORDER_HANDSHAKE_EX_FLAGS_EXTENDED_SPI_SUPPORTED) != 0;
595
0
}
596
597
const char* rail_handshake_ex_flags_to_string(UINT32 flags, char* buffer, size_t len)
598
0
{
599
0
  if (len < 1)
600
0
    return nullptr;
601
602
0
  (void)_snprintf(buffer, len, "{");
603
0
  char* fbuffer = &buffer[1];
604
0
  len--;
605
606
0
  if (flags & TS_RAIL_ORDER_HANDSHAKEEX_FLAGS_HIDEF)
607
0
    winpr_str_append("HIDEF", fbuffer, len, "|");
608
0
  if (flags & TS_RAIL_ORDER_HANDSHAKE_EX_FLAGS_EXTENDED_SPI_SUPPORTED)
609
0
    winpr_str_append("EXTENDED_SPI_SUPPORTED", fbuffer, len, "|");
610
0
  if (flags & TS_RAIL_ORDER_HANDSHAKE_EX_FLAGS_SNAP_ARRANGE_SUPPORTED)
611
0
    winpr_str_append("SNAP_ARRANGE_SUPPORTED", fbuffer, len, "|");
612
0
  if (flags & TS_RAIL_ORDER_HANDSHAKE_EX_FLAGS_TEXT_SCALE_SUPPORTED)
613
0
    winpr_str_append("TEXT_SCALE_SUPPORTED", fbuffer, len, "|");
614
0
  if (flags & TS_RAIL_ORDER_HANDSHAKE_EX_FLAGS_CARET_BLINK_SUPPORTED)
615
0
    winpr_str_append("CARET_BLINK_SUPPORTED", fbuffer, len, "|");
616
0
  if (flags & TS_RAIL_ORDER_HANDSHAKE_EX_FLAGS_EXTENDED_SPI_2_SUPPORTED)
617
0
    winpr_str_append("EXTENDED_SPI_2_SUPPORTED", fbuffer, len, "|");
618
619
0
  char number[16] = WINPR_C_ARRAY_INIT;
620
0
  (void)_snprintf(number, sizeof(number), "[0x%08" PRIx32 "]", flags);
621
0
  winpr_str_append(number, buffer, len, "}");
622
0
  return buffer;
623
0
}