Coverage Report

Created: 2026-07-30 07:06

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
304
{
32
304
  switch (orderType)
33
304
  {
34
2
    case TS_RAIL_ORDER_EXEC:
35
2
      return "TS_RAIL_ORDER_EXEC";
36
2
    case TS_RAIL_ORDER_ACTIVATE:
37
2
      return "TS_RAIL_ORDER_ACTIVATE";
38
54
    case TS_RAIL_ORDER_SYSPARAM:
39
54
      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
2
    case TS_RAIL_ORDER_NOTIFY_EVENT:
45
2
      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
1
    case TS_RAIL_ORDER_CLIENTSTATUS:
53
1
      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
70
    case TS_RAIL_ORDER_GET_APPID_RESP_EX:
79
70
      return "TS_RAIL_ORDER_GET_APPID_RESP_EX";
80
149
    case TS_RAIL_ORDER_EXEC_RESULT:
81
149
      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
4
    default:
87
4
      return "TS_RAIL_ORDER_UNKNOWN";
88
304
  }
89
304
}
90
91
const char* rail_get_order_type_string_full(UINT16 orderType, char* buffer, size_t length)
92
304
{
93
304
  (void)_snprintf(buffer, length, "%s[0x%04" PRIx16 "]", rail_get_order_type_string(orderType),
94
304
                  orderType);
95
304
  return buffer;
96
304
}
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.34k
{
105
1.34k
  if (!s || !orderType || !orderLength)
106
0
    return ERROR_INVALID_PARAMETER;
107
108
1.34k
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
109
5
    return ERROR_INVALID_DATA;
110
111
1.33k
  Stream_Read_UINT16(s, *orderType);   /* orderType (2 bytes) */
112
1.33k
  Stream_Read_UINT16(s, *orderLength); /* orderLength (2 bytes) */
113
1.33k
  if (*orderLength < RAIL_PDU_HEADER_LENGTH)
114
8
  {
115
8
    WLog_ERR(TAG, "invalid order length %" PRIu16 " < RAIL_PDU_HEADER_LENGTH(%u)", *orderLength,
116
8
             RAIL_PDU_HEADER_LENGTH);
117
8
    return ERROR_INVALID_DATA;
118
8
  }
119
120
1.32k
  if (!Stream_CheckAndLogRequiredLength(TAG, s, *orderLength - RAIL_PDU_HEADER_LENGTH))
121
29
    return ERROR_INVALID_DATA;
122
123
1.29k
  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.29k
  return CHANNEL_RC_OK;
131
1.29k
}
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
76
{
207
76
  if (!s || !highContrast)
208
0
    return ERROR_INVALID_PARAMETER;
209
210
76
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
211
0
    return ERROR_INVALID_DATA;
212
213
76
  Stream_Read_UINT32(s, highContrast->flags);             /* flags (4 bytes) */
214
76
  Stream_Read_UINT32(s, highContrast->colorSchemeLength); /* colorSchemeLength (4 bytes) */
215
216
76
  if (!rail_read_unicode_string(s, &highContrast->colorScheme)) /* colorScheme */
217
44
    return ERROR_INTERNAL_ERROR;
218
32
  return CHANNEL_RC_OK;
219
76
}
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
319
{
291
319
  BYTE body = 0;
292
319
  UINT error = CHANNEL_RC_OK;
293
294
319
  if (!s || !sysparam)
295
0
    return ERROR_INVALID_PARAMETER;
296
297
319
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 5))
298
1
    return ERROR_INVALID_DATA;
299
300
318
  Stream_Read_UINT32(s, sysparam->param); /* systemParam (4 bytes) */
301
302
318
  sysparam->params = 0; /* bitflags of received params */
303
304
318
  switch (sysparam->param)
305
318
  {
306
    /* Client sysparams */
307
12
    case SPI_SET_DRAG_FULL_WINDOWS:
308
12
      sysparam->params |= SPI_MASK_SET_DRAG_FULL_WINDOWS;
309
12
      Stream_Read_UINT8(s, body); /* body (1 byte) */
310
12
      sysparam->dragFullWindows = body != 0;
311
12
      break;
312
313
14
    case SPI_SET_KEYBOARD_CUES:
314
14
      sysparam->params |= SPI_MASK_SET_KEYBOARD_CUES;
315
14
      Stream_Read_UINT8(s, body); /* body (1 byte) */
316
14
      sysparam->keyboardCues = body != 0;
317
14
      break;
318
319
4
    case SPI_SET_KEYBOARD_PREF:
320
4
      sysparam->params |= SPI_MASK_SET_KEYBOARD_PREF;
321
4
      Stream_Read_UINT8(s, body); /* body (1 byte) */
322
4
      sysparam->keyboardPref = body != 0;
323
4
      break;
324
325
15
    case SPI_SET_MOUSE_BUTTON_SWAP:
326
15
      sysparam->params |= SPI_MASK_SET_MOUSE_BUTTON_SWAP;
327
15
      Stream_Read_UINT8(s, body); /* body (1 byte) */
328
15
      sysparam->mouseButtonSwap = body != 0;
329
15
      break;
330
331
13
    case SPI_SET_WORK_AREA:
332
13
      sysparam->params |= SPI_MASK_SET_WORK_AREA;
333
334
13
      if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
335
1
        return ERROR_INVALID_DATA;
336
337
12
      Stream_Read_UINT16(s, sysparam->workArea.left);   /* left (2 bytes) */
338
12
      Stream_Read_UINT16(s, sysparam->workArea.top);    /* top (2 bytes) */
339
12
      Stream_Read_UINT16(s, sysparam->workArea.right);  /* right (2 bytes) */
340
12
      Stream_Read_UINT16(s, sysparam->workArea.bottom); /* bottom (2 bytes) */
341
12
      break;
342
343
18
    case SPI_DISPLAY_CHANGE:
344
18
      sysparam->params |= SPI_MASK_DISPLAY_CHANGE;
345
346
18
      if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
347
1
        return ERROR_INVALID_DATA;
348
349
17
      Stream_Read_UINT16(s, sysparam->displayChange.left);   /* left (2 bytes) */
350
17
      Stream_Read_UINT16(s, sysparam->displayChange.top);    /* top (2 bytes) */
351
17
      Stream_Read_UINT16(s, sysparam->displayChange.right);  /* right (2 bytes) */
352
17
      Stream_Read_UINT16(s, sysparam->displayChange.bottom); /* bottom (2 bytes) */
353
17
      break;
354
355
10
    case SPI_TASKBAR_POS:
356
10
      sysparam->params |= SPI_MASK_TASKBAR_POS;
357
358
10
      if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
359
2
        return ERROR_INVALID_DATA;
360
361
8
      Stream_Read_UINT16(s, sysparam->taskbarPos.left);   /* left (2 bytes) */
362
8
      Stream_Read_UINT16(s, sysparam->taskbarPos.top);    /* top (2 bytes) */
363
8
      Stream_Read_UINT16(s, sysparam->taskbarPos.right);  /* right (2 bytes) */
364
8
      Stream_Read_UINT16(s, sysparam->taskbarPos.bottom); /* bottom (2 bytes) */
365
8
      break;
366
367
77
    case SPI_SET_HIGH_CONTRAST:
368
77
      sysparam->params |= SPI_MASK_SET_HIGH_CONTRAST;
369
77
      if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
370
1
        return ERROR_INVALID_DATA;
371
372
76
      error = rail_read_high_contrast(s, &sysparam->highContrast);
373
76
      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
19
    case SPI_SETSCREENSAVEACTIVE:
429
19
      sysparam->params |= SPI_MASK_SET_SCREEN_SAVE_ACTIVE;
430
431
19
      Stream_Read_UINT8(s, body); /* body (1 byte) */
432
19
      sysparam->setScreenSaveActive = body != 0;
433
19
      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
121
    default:
443
121
      break;
444
318
  }
445
446
309
  return error;
447
318
}
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
319
{
562
319
  return (channelFlags & TS_RAIL_ORDER_HANDSHAKE_EX_FLAGS_EXTENDED_SPI_SUPPORTED) != 0;
563
319
}
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
}