Coverage Report

Created: 2026-10-15 06:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/channels/client/addin.c
Line
Count
Source
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * Channel Addins
4
 *
5
 * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 * Copyright 2015 Thincast Technologies GmbH
7
 * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
8
 *
9
 * Licensed under the Apache License, Version 2.0 (the "License");
10
 * you may not use this file except in compliance with the License.
11
 * You may obtain a copy of the License at
12
 *
13
 *     http://www.apache.org/licenses/LICENSE-2.0
14
 *
15
 * Unless required by applicable law or agreed to in writing, software
16
 * distributed under the License is distributed on an "AS IS" BASIS,
17
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
 * See the License for the specific language governing permissions and
19
 * limitations under the License.
20
 */
21
22
#include <freerdp/config.h>
23
24
#include <winpr/crt.h>
25
#include <winpr/assert.h>
26
#include <winpr/path.h>
27
#include <winpr/string.h>
28
#include <winpr/file.h>
29
#include <winpr/synch.h>
30
#include <winpr/library.h>
31
#include <winpr/collections.h>
32
33
#include <freerdp/freerdp.h>
34
#include <freerdp/addin.h>
35
#include <freerdp/build-config.h>
36
#include <freerdp/client/channels.h>
37
38
#include "tables.h"
39
40
#include "addin.h"
41
42
#include <freerdp/channels/log.h>
43
#define TAG CHANNELS_TAG("addin")
44
45
extern const STATIC_ENTRY_TABLE CLIENT_STATIC_ENTRY_TABLES[];
46
47
static void* freerdp_channels_find_static_entry_in_table(const STATIC_ENTRY_TABLE* table,
48
                                                         const char* identifier)
49
0
{
50
0
  size_t index = 0;
51
0
  const STATIC_ENTRY* pEntry = &table->table.cse[index++];
52
53
0
  while (pEntry->entry != nullptr)
54
0
  {
55
0
    static_entry_fn_t fkt = pEntry->entry;
56
0
    if (strcmp(pEntry->name, identifier) == 0)
57
0
      return WINPR_FUNC_PTR_CAST(fkt, void*);
58
59
0
    pEntry = &table->table.cse[index++];
60
0
  }
61
62
0
  return nullptr;
63
0
}
64
65
void* freerdp_channels_client_find_static_entry(const char* name, const char* identifier)
66
0
{
67
0
  size_t index = 0;
68
0
  const STATIC_ENTRY_TABLE* pEntry = &CLIENT_STATIC_ENTRY_TABLES[index++];
69
70
0
  while (pEntry->table.cse != nullptr)
71
0
  {
72
0
    if (strcmp(pEntry->name, name) == 0)
73
0
    {
74
0
      return freerdp_channels_find_static_entry_in_table(pEntry, identifier);
75
0
    }
76
77
0
    pEntry = &CLIENT_STATIC_ENTRY_TABLES[index++];
78
0
  }
79
80
0
  return nullptr;
81
0
}
82
83
extern const STATIC_ADDIN_TABLE CLIENT_STATIC_ADDIN_TABLE[];
84
85
static FREERDP_ADDIN** freerdp_channels_list_client_static_addins(
86
    WINPR_ATTR_UNUSED LPCSTR pszName, WINPR_ATTR_UNUSED LPCSTR pszSubsystem,
87
    WINPR_ATTR_UNUSED LPCSTR pszType, WINPR_ATTR_UNUSED DWORD dwFlags)
88
0
{
89
0
  DWORD nAddins = 0;
90
0
  FREERDP_ADDIN** ppAddins = nullptr;
91
0
  const STATIC_SUBSYSTEM_ENTRY* subsystems = nullptr;
92
0
  nAddins = 0;
93
0
  ppAddins = (FREERDP_ADDIN**)calloc(128, sizeof(FREERDP_ADDIN*));
94
95
0
  if (!ppAddins)
96
0
  {
97
0
    WLog_ERR(TAG, "calloc failed!");
98
0
    return nullptr;
99
0
  }
100
101
0
  ppAddins[nAddins] = nullptr;
102
103
0
  for (size_t i = 0; CLIENT_STATIC_ADDIN_TABLE[i].name != nullptr; i++)
104
0
  {
105
0
    FREERDP_ADDIN* pAddin = (FREERDP_ADDIN*)calloc(1, sizeof(FREERDP_ADDIN));
106
0
    const STATIC_ADDIN_TABLE* table = &CLIENT_STATIC_ADDIN_TABLE[i];
107
0
    if (!pAddin)
108
0
    {
109
0
      WLog_ERR(TAG, "calloc failed!");
110
0
      goto error_out;
111
0
    }
112
113
0
    (void)sprintf_s(pAddin->cName, ARRAYSIZE(pAddin->cName), "%s", table->name);
114
0
    pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
115
0
    pAddin->dwFlags |= FREERDP_ADDIN_STATIC;
116
0
    pAddin->dwFlags |= FREERDP_ADDIN_NAME;
117
0
    ppAddins[nAddins++] = pAddin;
118
0
    subsystems = table->table;
119
120
0
    for (size_t j = 0; subsystems[j].name != nullptr; j++)
121
0
    {
122
0
      pAddin = (FREERDP_ADDIN*)calloc(1, sizeof(FREERDP_ADDIN));
123
124
0
      if (!pAddin)
125
0
      {
126
0
        WLog_ERR(TAG, "calloc failed!");
127
0
        goto error_out;
128
0
      }
129
130
0
      (void)sprintf_s(pAddin->cName, ARRAYSIZE(pAddin->cName), "%s", table->name);
131
0
      (void)sprintf_s(pAddin->cSubsystem, ARRAYSIZE(pAddin->cSubsystem), "%s",
132
0
                      subsystems[j].name);
133
0
      pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
134
0
      pAddin->dwFlags |= FREERDP_ADDIN_STATIC;
135
0
      pAddin->dwFlags |= FREERDP_ADDIN_NAME;
136
0
      pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM;
137
0
      ppAddins[nAddins++] = pAddin;
138
0
    }
139
0
  }
140
141
0
  return ppAddins;
142
0
error_out:
143
0
  freerdp_channels_addin_list_free(ppAddins);
144
0
  return nullptr;
145
0
}
146
147
static HANDLE FindFirstFileUTF8(LPCSTR pszSearchPath, WIN32_FIND_DATAW* FindData)
148
0
{
149
0
  HANDLE hdl = INVALID_HANDLE_VALUE;
150
0
  if (!pszSearchPath)
151
0
    return hdl;
152
0
  WCHAR* wpath = ConvertUtf8ToWCharAlloc(pszSearchPath, nullptr);
153
0
  if (!wpath)
154
0
    return hdl;
155
156
0
  hdl = FindFirstFileW(wpath, FindData);
157
0
  free(wpath);
158
159
0
  return hdl;
160
0
}
161
162
static FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPCSTR pszName, LPCSTR pszSubsystem,
163
                                                            LPCSTR pszType,
164
                                                            WINPR_ATTR_UNUSED DWORD dwFlags)
165
0
{
166
0
  int nDashes = 0;
167
0
  LPCSTR pszAddinPath = FREERDP_ADDIN_PATH;
168
0
  LPCSTR pszInstallPrefix = FREERDP_INSTALL_PREFIX;
169
0
  const size_t cchAddinPath = strnlen(pszAddinPath, sizeof(FREERDP_ADDIN_PATH));
170
0
  const size_t cchInstallPrefix = strnlen(pszInstallPrefix, sizeof(FREERDP_INSTALL_PREFIX));
171
0
  const char* pszExtension = PathGetSharedLibraryExtensionA(0);
172
0
  const size_t cchPattern = 128 + strnlen(pszExtension, MAX_PATH) + 2;
173
0
  char* pszPattern = calloc(cchPattern + 1, sizeof(char));
174
175
0
  if (!pszPattern)
176
0
  {
177
0
    WLog_ERR(TAG, "malloc failed!");
178
0
    return nullptr;
179
0
  }
180
181
0
  if (pszName && pszSubsystem && pszType)
182
0
  {
183
0
    (void)sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX "%s-client-%s-%s.%s",
184
0
                    pszName, pszSubsystem, pszType, pszExtension);
185
0
  }
186
0
  else if (pszName && pszType)
187
0
  {
188
0
    (void)sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX "%s-client-?-%s.%s",
189
0
                    pszName, pszType, pszExtension);
190
0
  }
191
0
  else if (pszName)
192
0
  {
193
0
    (void)sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX "%s-client*.%s",
194
0
                    pszName, pszExtension);
195
0
  }
196
0
  else
197
0
  {
198
0
    (void)sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX "?-client*.%s",
199
0
                    pszExtension);
200
0
  }
201
202
0
  const size_t cchPattern2 = strnlen(pszPattern, cchPattern);
203
0
  const size_t cchSearchPath = cchInstallPrefix + cchAddinPath + cchPattern2 + 3;
204
0
  char* pszSearchPath = calloc(cchSearchPath + 1, sizeof(char));
205
206
0
  if (!pszSearchPath)
207
0
  {
208
0
    WLog_ERR(TAG, "malloc failed!");
209
0
    free(pszPattern);
210
0
    return nullptr;
211
0
  }
212
213
0
  CopyMemory(pszSearchPath, pszInstallPrefix, cchInstallPrefix);
214
0
  pszSearchPath[cchInstallPrefix] = '\0';
215
216
0
  char* result = GetCombinedPathV(pszSearchPath, "%s%c%s", pszAddinPath,
217
0
                                  PathGetSeparatorA(PATH_STYLE_NATIVE), pszPattern);
218
0
  free(pszPattern);
219
0
  free(pszSearchPath);
220
221
0
  if (!result)
222
0
    return nullptr;
223
224
0
  WIN32_FIND_DATAW FindData = WINPR_C_ARRAY_INIT;
225
0
  HANDLE hFind = FindFirstFileUTF8(result, &FindData);
226
0
  free(result);
227
228
0
  DWORD nAddins = 0;
229
0
  FREERDP_ADDIN** ppAddins = (FREERDP_ADDIN**)calloc(128, sizeof(FREERDP_ADDIN*));
230
231
0
  if (!ppAddins)
232
0
  {
233
0
    FindClose(hFind);
234
0
    WLog_ERR(TAG, "calloc failed!");
235
0
    return nullptr;
236
0
  }
237
238
0
  if (hFind == INVALID_HANDLE_VALUE)
239
0
    return ppAddins;
240
241
0
  do
242
0
  {
243
0
    char* cFileName = nullptr;
244
0
    BOOL used = FALSE;
245
0
    FREERDP_ADDIN* pAddin = (FREERDP_ADDIN*)calloc(1, sizeof(FREERDP_ADDIN));
246
247
0
    if (!pAddin)
248
0
    {
249
0
      WLog_ERR(TAG, "calloc failed!");
250
0
      goto error_out;
251
0
    }
252
253
0
    cFileName =
254
0
        ConvertWCharNToUtf8Alloc(FindData.cFileName, ARRAYSIZE(FindData.cFileName), nullptr);
255
0
    if (!cFileName)
256
0
      goto skip;
257
258
0
    nDashes = 0;
259
0
    for (size_t index = 0; cFileName[index]; index++)
260
0
      nDashes += (cFileName[index] == '-') ? 1 : 0;
261
262
0
    if (nDashes == 1)
263
0
    {
264
0
      size_t len = 0;
265
0
      char* p[2] = WINPR_C_ARRAY_INIT;
266
      /* <name>-client.<extension> */
267
0
      p[0] = cFileName;
268
0
      p[1] = strchr(p[0], '-');
269
0
      if (!p[1])
270
0
        goto skip;
271
0
      p[1] += 1;
272
273
0
      len = (size_t)(p[1] - p[0]);
274
0
      if (len < 1)
275
0
      {
276
0
        WLog_WARN(TAG, "Skipping file '%s', invalid format", cFileName);
277
0
        goto skip;
278
0
      }
279
0
      strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
280
281
0
      pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
282
0
      pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
283
0
      pAddin->dwFlags |= FREERDP_ADDIN_NAME;
284
0
      ppAddins[nAddins++] = pAddin;
285
286
0
      used = TRUE;
287
0
    }
288
0
    else if (nDashes == 2)
289
0
    {
290
0
      size_t len = 0;
291
0
      char* p[4] = WINPR_C_ARRAY_INIT;
292
      /* <name>-client-<subsystem>.<extension> */
293
0
      p[0] = cFileName;
294
0
      p[1] = strchr(p[0], '-');
295
0
      if (!p[1])
296
0
        goto skip;
297
0
      p[1] += 1;
298
0
      p[2] = strchr(p[1], '-');
299
0
      if (!p[2])
300
0
        goto skip;
301
0
      p[2] += 1;
302
0
      p[3] = strchr(p[2], '.');
303
0
      if (!p[3])
304
0
        goto skip;
305
0
      p[3] += 1;
306
307
0
      len = (size_t)(p[1] - p[0]);
308
0
      if (len < 1)
309
0
      {
310
0
        WLog_WARN(TAG, "Skipping file '%s', invalid format", cFileName);
311
0
        goto skip;
312
0
      }
313
0
      strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
314
315
0
      len = (size_t)(p[3] - p[2]);
316
0
      if (len < 1)
317
0
      {
318
0
        WLog_WARN(TAG, "Skipping file '%s', invalid format", cFileName);
319
0
        goto skip;
320
0
      }
321
0
      strncpy(pAddin->cSubsystem, p[2], MIN(ARRAYSIZE(pAddin->cSubsystem), len - 1));
322
323
0
      pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
324
0
      pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
325
0
      pAddin->dwFlags |= FREERDP_ADDIN_NAME;
326
0
      pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM;
327
0
      ppAddins[nAddins++] = pAddin;
328
329
0
      used = TRUE;
330
0
    }
331
0
    else if (nDashes == 3)
332
0
    {
333
0
      size_t len = 0;
334
0
      char* p[5] = WINPR_C_ARRAY_INIT;
335
      /* <name>-client-<subsystem>-<type>.<extension> */
336
0
      p[0] = cFileName;
337
0
      p[1] = strchr(p[0], '-');
338
0
      if (!p[1])
339
0
        goto skip;
340
0
      p[1] += 1;
341
0
      p[2] = strchr(p[1], '-');
342
0
      if (!p[2])
343
0
        goto skip;
344
0
      p[2] += 1;
345
0
      p[3] = strchr(p[2], '-');
346
0
      if (!p[3])
347
0
        goto skip;
348
0
      p[3] += 1;
349
0
      p[4] = strchr(p[3], '.');
350
0
      if (!p[4])
351
0
        goto skip;
352
0
      p[4] += 1;
353
354
0
      len = (size_t)(p[1] - p[0]);
355
0
      if (len < 1)
356
0
      {
357
0
        WLog_WARN(TAG, "Skipping file '%s', invalid format", cFileName);
358
0
        goto skip;
359
0
      }
360
0
      strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
361
362
0
      len = (size_t)(p[3] - p[2]);
363
0
      if (len < 1)
364
0
      {
365
0
        WLog_WARN(TAG, "Skipping file '%s', invalid format", cFileName);
366
0
        goto skip;
367
0
      }
368
0
      strncpy(pAddin->cSubsystem, p[2], MIN(ARRAYSIZE(pAddin->cSubsystem), len - 1));
369
370
0
      len = (size_t)(p[4] - p[3]);
371
0
      if (len < 1)
372
0
      {
373
0
        WLog_WARN(TAG, "Skipping file '%s', invalid format", cFileName);
374
0
        goto skip;
375
0
      }
376
0
      strncpy(pAddin->cType, p[3], MIN(ARRAYSIZE(pAddin->cType), len - 1));
377
378
0
      pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
379
0
      pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
380
0
      pAddin->dwFlags |= FREERDP_ADDIN_NAME;
381
0
      pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM;
382
0
      pAddin->dwFlags |= FREERDP_ADDIN_TYPE;
383
0
      ppAddins[nAddins++] = pAddin;
384
385
0
      used = TRUE;
386
0
    }
387
388
0
  skip:
389
0
    free(cFileName);
390
0
    if (!used)
391
0
      free(pAddin);
392
393
0
  } while (FindNextFileW(hFind, &FindData));
394
395
0
  FindClose(hFind);
396
0
  ppAddins[nAddins] = nullptr;
397
0
  return ppAddins;
398
0
error_out:
399
0
  FindClose(hFind);
400
0
  freerdp_channels_addin_list_free(ppAddins);
401
0
  return nullptr;
402
0
}
403
404
FREERDP_ADDIN** freerdp_channels_list_addins(LPCSTR pszName, LPCSTR pszSubsystem, LPCSTR pszType,
405
                                             DWORD dwFlags)
406
0
{
407
0
  if (dwFlags & FREERDP_ADDIN_STATIC)
408
0
    return freerdp_channels_list_client_static_addins(pszName, pszSubsystem, pszType, dwFlags);
409
0
  else if (dwFlags & FREERDP_ADDIN_DYNAMIC)
410
0
    return freerdp_channels_list_dynamic_addins(pszName, pszSubsystem, pszType, dwFlags);
411
412
0
  return nullptr;
413
0
}
414
415
void freerdp_channels_addin_list_free(FREERDP_ADDIN** ppAddins)
416
0
{
417
0
  if (!ppAddins)
418
0
    return;
419
420
0
  for (size_t index = 0; ppAddins[index] != nullptr; index++)
421
0
    free(ppAddins[index]);
422
423
0
  free((void*)ppAddins);
424
0
}
425
426
extern const STATIC_ENTRY CLIENT_VirtualChannelEntryEx_TABLE[];
427
428
static BOOL freerdp_channels_is_virtual_channel_entry_ex(LPCSTR pszName)
429
0
{
430
0
  for (size_t i = 0; CLIENT_VirtualChannelEntryEx_TABLE[i].name != nullptr; i++)
431
0
  {
432
0
    const STATIC_ENTRY* entry = &CLIENT_VirtualChannelEntryEx_TABLE[i];
433
434
0
    if (!strncmp(entry->name, pszName, MAX_PATH))
435
0
      return TRUE;
436
0
  }
437
438
0
  return FALSE;
439
0
}
440
441
PVIRTUALCHANNELENTRY freerdp_channels_load_static_addin_entry(LPCSTR pszName, LPCSTR pszSubsystem,
442
                                                              LPCSTR pszType, DWORD dwFlags)
443
2.65k
{
444
2.65k
  const STATIC_ADDIN_TABLE* table = CLIENT_STATIC_ADDIN_TABLE;
445
2.65k
  const char* type = nullptr;
446
447
2.65k
  if (!pszName)
448
0
    return nullptr;
449
450
2.65k
  if (dwFlags & FREERDP_ADDIN_CHANNEL_DYNAMIC)
451
2.65k
    type = "DVCPluginEntry";
452
0
  else if (dwFlags & FREERDP_ADDIN_CHANNEL_DEVICE)
453
0
    type = "DeviceServiceEntry";
454
0
  else if (dwFlags & FREERDP_ADDIN_CHANNEL_STATIC)
455
0
  {
456
0
    if (dwFlags & FREERDP_ADDIN_CHANNEL_ENTRYEX)
457
0
      type = "VirtualChannelEntryEx";
458
0
    else
459
0
      type = "VirtualChannelEntry";
460
0
  }
461
462
23.9k
  for (; table->name != nullptr; table++)
463
23.9k
  {
464
23.9k
    if (strncmp(table->name, pszName, MAX_PATH) == 0)
465
2.65k
    {
466
2.65k
      if (type && (strncmp(table->type, type, MAX_PATH) != 0))
467
0
        continue;
468
469
2.65k
      if (pszSubsystem != nullptr)
470
0
      {
471
0
        const STATIC_SUBSYSTEM_ENTRY* subsystems = table->table;
472
473
0
        for (; subsystems->name != nullptr; subsystems++)
474
0
        {
475
          /* If the pszSubsystem is an empty string use the default backend. */
476
0
          if ((strnlen(pszSubsystem, 1) ==
477
0
               0) || /* we only want to know if strnlen is > 0 */
478
0
              (strncmp(subsystems->name, pszSubsystem, MAX_PATH) == 0))
479
0
          {
480
0
            static_subsystem_entry_fn_t fkt = subsystems->entry;
481
482
0
            if (pszType)
483
0
            {
484
0
              if (strncmp(subsystems->type, pszType, MAX_PATH) == 0)
485
0
                return WINPR_FUNC_PTR_CAST(fkt, PVIRTUALCHANNELENTRY);
486
0
            }
487
0
            else
488
0
              return WINPR_FUNC_PTR_CAST(fkt, PVIRTUALCHANNELENTRY);
489
0
          }
490
0
        }
491
0
      }
492
2.65k
      else
493
2.65k
      {
494
2.65k
        if (dwFlags & FREERDP_ADDIN_CHANNEL_ENTRYEX)
495
0
        {
496
0
          if (!freerdp_channels_is_virtual_channel_entry_ex(pszName))
497
0
            return nullptr;
498
0
        }
499
500
2.65k
        return table->entry.csevc;
501
2.65k
      }
502
2.65k
    }
503
23.9k
  }
504
505
0
  return nullptr;
506
2.65k
}
507
508
typedef struct
509
{
510
  wMessageQueue* queue;
511
  wStream* data_in;
512
  HANDLE thread;
513
  char* channel_name;
514
  rdpContext* ctx;
515
  LPVOID userdata;
516
  MsgHandler msg_handler;
517
  BOOL firstFlagReceived;
518
  UINT32 totalLength;
519
} msg_proc_internals;
520
521
static DWORD WINAPI channel_client_thread_proc(LPVOID userdata)
522
0
{
523
0
  UINT error = CHANNEL_RC_OK;
524
0
  wStream* data = nullptr;
525
0
  wMessage message = WINPR_C_ARRAY_INIT;
526
0
  msg_proc_internals* internals = userdata;
527
528
0
  WINPR_ASSERT(internals);
529
530
0
  while (1)
531
0
  {
532
0
    if (!MessageQueue_Wait(internals->queue))
533
0
    {
534
0
      WLog_ERR(TAG, "MessageQueue_Wait failed!");
535
0
      error = ERROR_INTERNAL_ERROR;
536
0
      break;
537
0
    }
538
0
    if (!MessageQueue_Peek(internals->queue, &message, TRUE))
539
0
    {
540
0
      WLog_ERR(TAG, "MessageQueue_Peek failed!");
541
0
      error = ERROR_INTERNAL_ERROR;
542
0
      break;
543
0
    }
544
545
0
    if (message.id == WMQ_QUIT)
546
0
      break;
547
548
0
    if (message.id == 0)
549
0
    {
550
0
      data = (wStream*)message.wParam;
551
552
0
      if ((error = internals->msg_handler(internals->userdata, data)))
553
0
      {
554
0
        WLog_ERR(TAG, "msg_handler failed with error %" PRIu32 "!", error);
555
0
        break;
556
0
      }
557
0
    }
558
0
  }
559
0
  if (error && internals->ctx)
560
0
  {
561
0
    char msg[128];
562
0
    (void)_snprintf(msg, 127,
563
0
                    "%s_virtual_channel_client_thread reported an"
564
0
                    " error",
565
0
                    internals->channel_name);
566
0
    setChannelError(internals->ctx, error, msg);
567
0
  }
568
0
  ExitThread(error);
569
0
  return error;
570
0
}
571
572
static void free_msg(void* obj)
573
0
{
574
0
  wMessage* msg = (wMessage*)obj;
575
576
0
  if (msg && (msg->id == 0))
577
0
  {
578
0
    wStream* s = (wStream*)msg->wParam;
579
0
    Stream_Free(s, TRUE);
580
0
  }
581
0
}
582
583
static void channel_client_handler_free(msg_proc_internals* internals)
584
0
{
585
0
  if (!internals)
586
0
    return;
587
588
0
  if (internals->thread)
589
0
    (void)CloseHandle(internals->thread);
590
0
  MessageQueue_Free(internals->queue);
591
0
  Stream_Free(internals->data_in, TRUE);
592
0
  free(internals->channel_name);
593
0
  free(internals);
594
0
}
595
596
/*  Create message queue and thread or not, depending on settings */
597
void* channel_client_create_handler(rdpContext* ctx, LPVOID userdata, MsgHandler msg_handler,
598
                                    const char* channel_name)
599
0
{
600
0
  msg_proc_internals* internals = calloc(1, sizeof(msg_proc_internals));
601
0
  if (!internals)
602
0
  {
603
0
    WLog_ERR(TAG, "calloc failed!");
604
0
    return nullptr;
605
0
  }
606
0
  internals->msg_handler = msg_handler;
607
0
  internals->userdata = userdata;
608
0
  if (channel_name)
609
0
  {
610
0
    internals->channel_name = _strdup(channel_name);
611
0
    if (!internals->channel_name)
612
0
      goto fail;
613
0
  }
614
0
  WINPR_ASSERT(ctx);
615
0
  WINPR_ASSERT(ctx->settings);
616
0
  internals->ctx = ctx;
617
0
  if ((freerdp_settings_get_uint32(ctx->settings, FreeRDP_ThreadingFlags) &
618
0
       THREADING_FLAGS_DISABLE_THREADS) == 0)
619
0
  {
620
0
    wObject obj = WINPR_C_ARRAY_INIT;
621
0
    obj.fnObjectFree = free_msg;
622
0
    internals->queue = MessageQueue_New(&obj);
623
0
    if (!internals->queue)
624
0
    {
625
0
      WLog_ERR(TAG, "MessageQueue_New failed!");
626
0
      goto fail;
627
0
    }
628
629
0
    if (!(internals->thread = CreateThread(nullptr, 0, channel_client_thread_proc,
630
0
                                           (void*)internals, 0, nullptr)))
631
0
    {
632
0
      WLog_ERR(TAG, "CreateThread failed!");
633
0
      goto fail;
634
0
    }
635
0
  }
636
0
  return internals;
637
638
0
fail:
639
0
  channel_client_handler_free(internals);
640
0
  return nullptr;
641
0
}
642
/* post a message in the queue or directly call the processing handler */
643
UINT channel_client_post_message(void* MsgsHandle, LPVOID pData, UINT32 dataLength,
644
                                 UINT32 totalLength, UINT32 dataFlags)
645
0
{
646
0
  msg_proc_internals* internals = MsgsHandle;
647
0
  wStream* data_in = nullptr;
648
649
0
  if (!internals)
650
0
  {
651
    /* TODO: return some error here */
652
0
    return CHANNEL_RC_OK;
653
0
  }
654
655
0
  if ((dataFlags & CHANNEL_FLAG_SUSPEND) || (dataFlags & CHANNEL_FLAG_RESUME))
656
0
  {
657
0
    return CHANNEL_RC_OK;
658
0
  }
659
660
0
  if (dataFlags & CHANNEL_FLAG_FIRST)
661
0
  {
662
0
    if (internals->firstFlagReceived)
663
0
      return ERROR_INVALID_DATA;
664
0
    internals->firstFlagReceived = TRUE;
665
666
0
    if (internals->data_in)
667
0
    {
668
0
      if (!Stream_EnsureCapacity(internals->data_in, dataLength))
669
0
        return CHANNEL_RC_NO_MEMORY;
670
0
    }
671
0
    else
672
0
      internals->data_in = Stream_New(nullptr, dataLength);
673
0
    internals->totalLength = totalLength;
674
0
  }
675
676
0
  if (!(data_in = internals->data_in))
677
0
  {
678
0
    WLog_ERR(TAG, "Stream_New failed!");
679
0
    return CHANNEL_RC_NO_MEMORY;
680
0
  }
681
682
0
  if (!Stream_EnsureRemainingCapacity(data_in, dataLength))
683
0
  {
684
0
    Stream_Free(internals->data_in, TRUE);
685
0
    internals->data_in = nullptr;
686
0
    return CHANNEL_RC_NO_MEMORY;
687
0
  }
688
689
0
  Stream_Write(data_in, pData, dataLength);
690
691
0
  if ((Stream_GetPosition(data_in) > totalLength) || (internals->totalLength != totalLength))
692
0
  {
693
0
    Stream_Free(internals->data_in, TRUE);
694
0
    internals->data_in = nullptr;
695
0
    return ERROR_INVALID_DATA;
696
0
  }
697
698
0
  if (dataFlags & CHANNEL_FLAG_LAST)
699
0
  {
700
0
    if (!internals->firstFlagReceived)
701
0
      return ERROR_INVALID_DATA;
702
0
    internals->firstFlagReceived = FALSE;
703
704
0
    if (!data_in)
705
0
      return ERROR_INVALID_DATA;
706
707
0
    if (internals->totalLength != Stream_GetPosition(data_in))
708
0
    {
709
0
      WLog_ERR(TAG, "%s_plugin_process_received: read error", internals->channel_name);
710
0
      return ERROR_INTERNAL_ERROR;
711
0
    }
712
713
0
    internals->totalLength = 0;
714
0
    internals->data_in = nullptr;
715
0
    Stream_SealLength(data_in);
716
0
    Stream_ResetPosition(data_in);
717
718
0
    if ((freerdp_settings_get_uint32(internals->ctx->settings, FreeRDP_ThreadingFlags) &
719
0
         THREADING_FLAGS_DISABLE_THREADS) != 0)
720
0
    {
721
0
      UINT error = CHANNEL_RC_OK;
722
0
      if ((error = internals->msg_handler(internals->userdata, data_in)))
723
0
      {
724
0
        WLog_ERR(TAG,
725
0
                 "msg_handler failed with error"
726
0
                 " %" PRIu32 "!",
727
0
                 error);
728
0
        return ERROR_INTERNAL_ERROR;
729
0
      }
730
0
    }
731
0
    else if (!MessageQueue_Post(internals->queue, nullptr, 0, (void*)data_in, nullptr))
732
0
    {
733
0
      WLog_ERR(TAG, "MessageQueue_Post failed!");
734
0
      return ERROR_INTERNAL_ERROR;
735
0
    }
736
0
  }
737
0
  return CHANNEL_RC_OK;
738
0
}
739
/* Tear down queue and thread */
740
UINT channel_client_quit_handler(void* MsgsHandle)
741
0
{
742
0
  msg_proc_internals* internals = MsgsHandle;
743
0
  UINT rc = 0;
744
0
  if (!internals)
745
0
  {
746
    /* TODO: return some error here */
747
0
    return CHANNEL_RC_OK;
748
0
  }
749
750
0
  WINPR_ASSERT(internals->ctx);
751
0
  WINPR_ASSERT(internals->ctx->settings);
752
753
0
  if ((freerdp_settings_get_uint32(internals->ctx->settings, FreeRDP_ThreadingFlags) &
754
0
       THREADING_FLAGS_DISABLE_THREADS) == 0)
755
0
  {
756
0
    if (internals->queue && internals->thread)
757
0
    {
758
0
      if (MessageQueue_PostQuit(internals->queue, 0) &&
759
0
          (WaitForSingleObject(internals->thread, INFINITE) == WAIT_FAILED))
760
0
      {
761
0
        rc = GetLastError();
762
0
        WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", rc);
763
0
        return rc;
764
0
      }
765
0
    }
766
0
  }
767
768
0
  channel_client_handler_free(internals);
769
0
  return CHANNEL_RC_OK;
770
0
}