Coverage Report

Created: 2026-07-16 07:09

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