Coverage Report

Created: 2025-07-01 06:46

/src/FreeRDP/channels/client/addin.c
Line
Count
Source (jump to first uncovered line)
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 != NULL)
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 NULL;
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 != NULL)
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 NULL;
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 = NULL;
91
0
  const STATIC_SUBSYSTEM_ENTRY* subsystems = NULL;
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 NULL;
99
0
  }
100
101
0
  ppAddins[nAddins] = NULL;
102
103
0
  for (size_t i = 0; CLIENT_STATIC_ADDIN_TABLE[i].name != NULL; 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 != NULL; 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 NULL;
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, NULL);
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
  HANDLE hFind = NULL;
168
0
  DWORD nAddins = 0;
169
0
  LPSTR pszPattern = NULL;
170
0
  size_t cchPattern = 0;
171
0
  LPCSTR pszAddinPath = FREERDP_ADDIN_PATH;
172
0
  LPCSTR pszInstallPrefix = FREERDP_INSTALL_PREFIX;
173
0
  LPCSTR pszExtension = NULL;
174
0
  LPSTR pszSearchPath = NULL;
175
0
  size_t cchSearchPath = 0;
176
0
  size_t cchAddinPath = 0;
177
0
  size_t cchInstallPrefix = 0;
178
0
  FREERDP_ADDIN** ppAddins = NULL;
179
0
  WIN32_FIND_DATAW FindData = { 0 };
180
0
  cchAddinPath = strnlen(pszAddinPath, sizeof(FREERDP_ADDIN_PATH));
181
0
  cchInstallPrefix = strnlen(pszInstallPrefix, sizeof(FREERDP_INSTALL_PREFIX));
182
0
  pszExtension = PathGetSharedLibraryExtensionA(0);
183
0
  cchPattern = 128 + strnlen(pszExtension, MAX_PATH) + 2;
184
0
  pszPattern = (LPSTR)malloc(cchPattern + 1);
185
186
0
  if (!pszPattern)
187
0
  {
188
0
    WLog_ERR(TAG, "malloc failed!");
189
0
    return NULL;
190
0
  }
191
192
0
  if (pszName && pszSubsystem && pszType)
193
0
  {
194
0
    (void)sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX "%s-client-%s-%s.%s",
195
0
                    pszName, pszSubsystem, pszType, pszExtension);
196
0
  }
197
0
  else if (pszName && pszType)
198
0
  {
199
0
    (void)sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX "%s-client-?-%s.%s",
200
0
                    pszName, pszType, pszExtension);
201
0
  }
202
0
  else if (pszName)
203
0
  {
204
0
    (void)sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX "%s-client*.%s",
205
0
                    pszName, pszExtension);
206
0
  }
207
0
  else
208
0
  {
209
0
    (void)sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX "?-client*.%s",
210
0
                    pszExtension);
211
0
  }
212
213
0
  cchPattern = strnlen(pszPattern, cchPattern);
214
0
  cchSearchPath = cchInstallPrefix + cchAddinPath + cchPattern + 3;
215
0
  pszSearchPath = (LPSTR)calloc(cchSearchPath + 1, sizeof(char));
216
217
0
  if (!pszSearchPath)
218
0
  {
219
0
    WLog_ERR(TAG, "malloc failed!");
220
0
    free(pszPattern);
221
0
    return NULL;
222
0
  }
223
224
0
  CopyMemory(pszSearchPath, pszInstallPrefix, cchInstallPrefix);
225
0
  pszSearchPath[cchInstallPrefix] = '\0';
226
0
  const HRESULT hr1 = NativePathCchAppendA(pszSearchPath, cchSearchPath + 1, pszAddinPath);
227
0
  const HRESULT hr2 = NativePathCchAppendA(pszSearchPath, cchSearchPath + 1, pszPattern);
228
0
  free(pszPattern);
229
230
0
  if (FAILED(hr1) || FAILED(hr2))
231
0
  {
232
0
    free(pszSearchPath);
233
0
    return NULL;
234
0
  }
235
236
0
  hFind = FindFirstFileUTF8(pszSearchPath, &FindData);
237
238
0
  free(pszSearchPath);
239
0
  nAddins = 0;
240
0
  ppAddins = (FREERDP_ADDIN**)calloc(128, sizeof(FREERDP_ADDIN*));
241
242
0
  if (!ppAddins)
243
0
  {
244
0
    FindClose(hFind);
245
0
    WLog_ERR(TAG, "calloc failed!");
246
0
    return NULL;
247
0
  }
248
249
0
  if (hFind == INVALID_HANDLE_VALUE)
250
0
    return ppAddins;
251
252
0
  do
253
0
  {
254
0
    char* cFileName = NULL;
255
0
    BOOL used = FALSE;
256
0
    FREERDP_ADDIN* pAddin = (FREERDP_ADDIN*)calloc(1, sizeof(FREERDP_ADDIN));
257
258
0
    if (!pAddin)
259
0
    {
260
0
      WLog_ERR(TAG, "calloc failed!");
261
0
      goto error_out;
262
0
    }
263
264
0
    cFileName =
265
0
        ConvertWCharNToUtf8Alloc(FindData.cFileName, ARRAYSIZE(FindData.cFileName), NULL);
266
0
    if (!cFileName)
267
0
      goto skip;
268
269
0
    nDashes = 0;
270
0
    for (size_t index = 0; cFileName[index]; index++)
271
0
      nDashes += (cFileName[index] == '-') ? 1 : 0;
272
273
0
    if (nDashes == 1)
274
0
    {
275
0
      size_t len = 0;
276
0
      char* p[2] = { 0 };
277
      /* <name>-client.<extension> */
278
0
      p[0] = cFileName;
279
0
      p[1] = strchr(p[0], '-');
280
0
      if (!p[1])
281
0
        goto skip;
282
0
      p[1] += 1;
283
284
0
      len = (size_t)(p[1] - p[0]);
285
0
      if (len < 1)
286
0
      {
287
0
        WLog_WARN(TAG, "Skipping file '%s', invalid format", cFileName);
288
0
        goto skip;
289
0
      }
290
0
      strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
291
292
0
      pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
293
0
      pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
294
0
      pAddin->dwFlags |= FREERDP_ADDIN_NAME;
295
0
      ppAddins[nAddins++] = pAddin;
296
297
0
      used = TRUE;
298
0
    }
299
0
    else if (nDashes == 2)
300
0
    {
301
0
      size_t len = 0;
302
0
      char* p[4] = { 0 };
303
      /* <name>-client-<subsystem>.<extension> */
304
0
      p[0] = cFileName;
305
0
      p[1] = strchr(p[0], '-');
306
0
      if (!p[1])
307
0
        goto skip;
308
0
      p[1] += 1;
309
0
      p[2] = strchr(p[1], '-');
310
0
      if (!p[2])
311
0
        goto skip;
312
0
      p[2] += 1;
313
0
      p[3] = strchr(p[2], '.');
314
0
      if (!p[3])
315
0
        goto skip;
316
0
      p[3] += 1;
317
318
0
      len = (size_t)(p[1] - p[0]);
319
0
      if (len < 1)
320
0
      {
321
0
        WLog_WARN(TAG, "Skipping file '%s', invalid format", cFileName);
322
0
        goto skip;
323
0
      }
324
0
      strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
325
326
0
      len = (size_t)(p[3] - p[2]);
327
0
      if (len < 1)
328
0
      {
329
0
        WLog_WARN(TAG, "Skipping file '%s', invalid format", cFileName);
330
0
        goto skip;
331
0
      }
332
0
      strncpy(pAddin->cSubsystem, p[2], MIN(ARRAYSIZE(pAddin->cSubsystem), len - 1));
333
334
0
      pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
335
0
      pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
336
0
      pAddin->dwFlags |= FREERDP_ADDIN_NAME;
337
0
      pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM;
338
0
      ppAddins[nAddins++] = pAddin;
339
340
0
      used = TRUE;
341
0
    }
342
0
    else if (nDashes == 3)
343
0
    {
344
0
      size_t len = 0;
345
0
      char* p[5] = { 0 };
346
      /* <name>-client-<subsystem>-<type>.<extension> */
347
0
      p[0] = cFileName;
348
0
      p[1] = strchr(p[0], '-');
349
0
      if (!p[1])
350
0
        goto skip;
351
0
      p[1] += 1;
352
0
      p[2] = strchr(p[1], '-');
353
0
      if (!p[2])
354
0
        goto skip;
355
0
      p[2] += 1;
356
0
      p[3] = strchr(p[2], '-');
357
0
      if (!p[3])
358
0
        goto skip;
359
0
      p[3] += 1;
360
0
      p[4] = strchr(p[3], '.');
361
0
      if (!p[4])
362
0
        goto skip;
363
0
      p[4] += 1;
364
365
0
      len = (size_t)(p[1] - p[0]);
366
0
      if (len < 1)
367
0
      {
368
0
        WLog_WARN(TAG, "Skipping file '%s', invalid format", cFileName);
369
0
        goto skip;
370
0
      }
371
0
      strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
372
373
0
      len = (size_t)(p[3] - p[2]);
374
0
      if (len < 1)
375
0
      {
376
0
        WLog_WARN(TAG, "Skipping file '%s', invalid format", cFileName);
377
0
        goto skip;
378
0
      }
379
0
      strncpy(pAddin->cSubsystem, p[2], MIN(ARRAYSIZE(pAddin->cSubsystem), len - 1));
380
381
0
      len = (size_t)(p[4] - p[3]);
382
0
      if (len < 1)
383
0
      {
384
0
        WLog_WARN(TAG, "Skipping file '%s', invalid format", cFileName);
385
0
        goto skip;
386
0
      }
387
0
      strncpy(pAddin->cType, p[3], MIN(ARRAYSIZE(pAddin->cType), len - 1));
388
389
0
      pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
390
0
      pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
391
0
      pAddin->dwFlags |= FREERDP_ADDIN_NAME;
392
0
      pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM;
393
0
      pAddin->dwFlags |= FREERDP_ADDIN_TYPE;
394
0
      ppAddins[nAddins++] = pAddin;
395
396
0
      used = TRUE;
397
0
    }
398
399
0
  skip:
400
0
    free(cFileName);
401
0
    if (!used)
402
0
      free(pAddin);
403
404
0
  } while (FindNextFileW(hFind, &FindData));
405
406
0
  FindClose(hFind);
407
0
  ppAddins[nAddins] = NULL;
408
0
  return ppAddins;
409
0
error_out:
410
0
  FindClose(hFind);
411
0
  freerdp_channels_addin_list_free(ppAddins);
412
0
  return NULL;
413
0
}
414
415
FREERDP_ADDIN** freerdp_channels_list_addins(LPCSTR pszName, LPCSTR pszSubsystem, LPCSTR pszType,
416
                                             DWORD dwFlags)
417
0
{
418
0
  if (dwFlags & FREERDP_ADDIN_STATIC)
419
0
    return freerdp_channels_list_client_static_addins(pszName, pszSubsystem, pszType, dwFlags);
420
0
  else if (dwFlags & FREERDP_ADDIN_DYNAMIC)
421
0
    return freerdp_channels_list_dynamic_addins(pszName, pszSubsystem, pszType, dwFlags);
422
423
0
  return NULL;
424
0
}
425
426
void freerdp_channels_addin_list_free(FREERDP_ADDIN** ppAddins)
427
0
{
428
0
  if (!ppAddins)
429
0
    return;
430
431
0
  for (size_t index = 0; ppAddins[index] != NULL; index++)
432
0
    free(ppAddins[index]);
433
434
0
  free((void*)ppAddins);
435
0
}
436
437
extern const STATIC_ENTRY CLIENT_VirtualChannelEntryEx_TABLE[];
438
439
static BOOL freerdp_channels_is_virtual_channel_entry_ex(LPCSTR pszName)
440
0
{
441
0
  for (size_t i = 0; CLIENT_VirtualChannelEntryEx_TABLE[i].name != NULL; i++)
442
0
  {
443
0
    const STATIC_ENTRY* entry = &CLIENT_VirtualChannelEntryEx_TABLE[i];
444
445
0
    if (!strncmp(entry->name, pszName, MAX_PATH))
446
0
      return TRUE;
447
0
  }
448
449
0
  return FALSE;
450
0
}
451
452
PVIRTUALCHANNELENTRY freerdp_channels_load_static_addin_entry(LPCSTR pszName, LPCSTR pszSubsystem,
453
                                                              LPCSTR pszType, DWORD dwFlags)
454
0
{
455
0
  const STATIC_ADDIN_TABLE* table = CLIENT_STATIC_ADDIN_TABLE;
456
0
  const char* type = NULL;
457
458
0
  if (!pszName)
459
0
    return NULL;
460
461
0
  if (dwFlags & FREERDP_ADDIN_CHANNEL_DYNAMIC)
462
0
    type = "DVCPluginEntry";
463
0
  else if (dwFlags & FREERDP_ADDIN_CHANNEL_DEVICE)
464
0
    type = "DeviceServiceEntry";
465
0
  else if (dwFlags & FREERDP_ADDIN_CHANNEL_STATIC)
466
0
  {
467
0
    if (dwFlags & FREERDP_ADDIN_CHANNEL_ENTRYEX)
468
0
      type = "VirtualChannelEntryEx";
469
0
    else
470
0
      type = "VirtualChannelEntry";
471
0
  }
472
473
0
  for (; table->name != NULL; table++)
474
0
  {
475
0
    if (strncmp(table->name, pszName, MAX_PATH) == 0)
476
0
    {
477
0
      if (type && (strncmp(table->type, type, MAX_PATH) != 0))
478
0
        continue;
479
480
0
      if (pszSubsystem != NULL)
481
0
      {
482
0
        const STATIC_SUBSYSTEM_ENTRY* subsystems = table->table;
483
484
0
        for (; subsystems->name != NULL; subsystems++)
485
0
        {
486
          /* If the pszSubsystem is an empty string use the default backend. */
487
0
          if ((strnlen(pszSubsystem, 1) ==
488
0
               0) || /* we only want to know if strnlen is > 0 */
489
0
              (strncmp(subsystems->name, pszSubsystem, MAX_PATH) == 0))
490
0
          {
491
0
            static_subsystem_entry_fn_t fkt = subsystems->entry;
492
493
0
            if (pszType)
494
0
            {
495
0
              if (strncmp(subsystems->type, pszType, MAX_PATH) == 0)
496
0
                return WINPR_FUNC_PTR_CAST(fkt, PVIRTUALCHANNELENTRY);
497
0
            }
498
0
            else
499
0
              return WINPR_FUNC_PTR_CAST(fkt, PVIRTUALCHANNELENTRY);
500
0
          }
501
0
        }
502
0
      }
503
0
      else
504
0
      {
505
0
        if (dwFlags & FREERDP_ADDIN_CHANNEL_ENTRYEX)
506
0
        {
507
0
          if (!freerdp_channels_is_virtual_channel_entry_ex(pszName))
508
0
            return NULL;
509
0
        }
510
511
0
        return table->entry.csevc;
512
0
      }
513
0
    }
514
0
  }
515
516
0
  return NULL;
517
0
}
518
519
typedef struct
520
{
521
  wMessageQueue* queue;
522
  wStream* data_in;
523
  HANDLE thread;
524
  char* channel_name;
525
  rdpContext* ctx;
526
  LPVOID userdata;
527
  MsgHandler msg_handler;
528
} msg_proc_internals;
529
530
static DWORD WINAPI channel_client_thread_proc(LPVOID userdata)
531
0
{
532
0
  UINT error = CHANNEL_RC_OK;
533
0
  wStream* data = NULL;
534
0
  wMessage message = { 0 };
535
0
  msg_proc_internals* internals = userdata;
536
537
0
  WINPR_ASSERT(internals);
538
539
0
  while (1)
540
0
  {
541
0
    if (!MessageQueue_Wait(internals->queue))
542
0
    {
543
0
      WLog_ERR(TAG, "MessageQueue_Wait failed!");
544
0
      error = ERROR_INTERNAL_ERROR;
545
0
      break;
546
0
    }
547
0
    if (!MessageQueue_Peek(internals->queue, &message, TRUE))
548
0
    {
549
0
      WLog_ERR(TAG, "MessageQueue_Peek failed!");
550
0
      error = ERROR_INTERNAL_ERROR;
551
0
      break;
552
0
    }
553
554
0
    if (message.id == WMQ_QUIT)
555
0
      break;
556
557
0
    if (message.id == 0)
558
0
    {
559
0
      data = (wStream*)message.wParam;
560
561
0
      if ((error = internals->msg_handler(internals->userdata, data)))
562
0
      {
563
0
        WLog_ERR(TAG, "msg_handler failed with error %" PRIu32 "!", error);
564
0
        break;
565
0
      }
566
0
    }
567
0
  }
568
0
  if (error && internals->ctx)
569
0
  {
570
0
    char msg[128];
571
0
    (void)_snprintf(msg, 127,
572
0
                    "%s_virtual_channel_client_thread reported an"
573
0
                    " error",
574
0
                    internals->channel_name);
575
0
    setChannelError(internals->ctx, error, msg);
576
0
  }
577
0
  ExitThread(error);
578
0
  return error;
579
0
}
580
581
static void free_msg(void* obj)
582
0
{
583
0
  wMessage* msg = (wMessage*)obj;
584
585
0
  if (msg && (msg->id == 0))
586
0
  {
587
0
    wStream* s = (wStream*)msg->wParam;
588
0
    Stream_Free(s, TRUE);
589
0
  }
590
0
}
591
592
static void channel_client_handler_free(msg_proc_internals* internals)
593
0
{
594
0
  if (!internals)
595
0
    return;
596
597
0
  if (internals->thread)
598
0
    (void)CloseHandle(internals->thread);
599
0
  MessageQueue_Free(internals->queue);
600
0
  Stream_Free(internals->data_in, TRUE);
601
0
  free(internals->channel_name);
602
0
  free(internals);
603
0
}
604
605
/*  Create message queue and thread or not, depending on settings */
606
void* channel_client_create_handler(rdpContext* ctx, LPVOID userdata, MsgHandler msg_handler,
607
                                    const char* channel_name)
608
0
{
609
0
  msg_proc_internals* internals = calloc(1, sizeof(msg_proc_internals));
610
0
  if (!internals)
611
0
  {
612
0
    WLog_ERR(TAG, "calloc failed!");
613
0
    return NULL;
614
0
  }
615
0
  internals->msg_handler = msg_handler;
616
0
  internals->userdata = userdata;
617
0
  if (channel_name)
618
0
  {
619
0
    internals->channel_name = _strdup(channel_name);
620
0
    if (!internals->channel_name)
621
0
      goto fail;
622
0
  }
623
0
  WINPR_ASSERT(ctx);
624
0
  WINPR_ASSERT(ctx->settings);
625
0
  internals->ctx = ctx;
626
0
  if ((freerdp_settings_get_uint32(ctx->settings, FreeRDP_ThreadingFlags) &
627
0
       THREADING_FLAGS_DISABLE_THREADS) == 0)
628
0
  {
629
0
    wObject obj = { 0 };
630
0
    obj.fnObjectFree = free_msg;
631
0
    internals->queue = MessageQueue_New(&obj);
632
0
    if (!internals->queue)
633
0
    {
634
0
      WLog_ERR(TAG, "MessageQueue_New failed!");
635
0
      goto fail;
636
0
    }
637
638
0
    if (!(internals->thread =
639
0
              CreateThread(NULL, 0, channel_client_thread_proc, (void*)internals, 0, NULL)))
640
0
    {
641
0
      WLog_ERR(TAG, "CreateThread failed!");
642
0
      goto fail;
643
0
    }
644
0
  }
645
0
  return internals;
646
647
0
fail:
648
0
  channel_client_handler_free(internals);
649
0
  return NULL;
650
0
}
651
/* post a message in the queue or directly call the processing handler */
652
UINT channel_client_post_message(void* MsgsHandle, LPVOID pData, UINT32 dataLength,
653
                                 UINT32 totalLength, UINT32 dataFlags)
654
0
{
655
0
  msg_proc_internals* internals = MsgsHandle;
656
0
  wStream* data_in = NULL;
657
658
0
  if (!internals)
659
0
  {
660
    /* TODO: return some error here */
661
0
    return CHANNEL_RC_OK;
662
0
  }
663
664
0
  if ((dataFlags & CHANNEL_FLAG_SUSPEND) || (dataFlags & CHANNEL_FLAG_RESUME))
665
0
  {
666
0
    return CHANNEL_RC_OK;
667
0
  }
668
669
0
  if (dataFlags & CHANNEL_FLAG_FIRST)
670
0
  {
671
0
    if (internals->data_in)
672
0
    {
673
0
      if (!Stream_EnsureCapacity(internals->data_in, totalLength))
674
0
        return CHANNEL_RC_NO_MEMORY;
675
0
    }
676
0
    else
677
0
      internals->data_in = Stream_New(NULL, totalLength);
678
0
  }
679
680
0
  if (!(data_in = internals->data_in))
681
0
  {
682
0
    WLog_ERR(TAG, "Stream_New failed!");
683
0
    return CHANNEL_RC_NO_MEMORY;
684
0
  }
685
686
0
  if (!Stream_EnsureRemainingCapacity(data_in, dataLength))
687
0
  {
688
0
    Stream_Free(internals->data_in, TRUE);
689
0
    internals->data_in = NULL;
690
0
    return CHANNEL_RC_NO_MEMORY;
691
0
  }
692
693
0
  Stream_Write(data_in, pData, dataLength);
694
695
0
  if (dataFlags & CHANNEL_FLAG_LAST)
696
0
  {
697
0
    if (Stream_Capacity(data_in) != Stream_GetPosition(data_in))
698
0
    {
699
0
      char msg[128];
700
0
      (void)_snprintf(msg, 127, "%s_plugin_process_received: read error",
701
0
                      internals->channel_name);
702
0
      WLog_ERR(TAG, msg);
703
0
      return ERROR_INTERNAL_ERROR;
704
0
    }
705
706
0
    internals->data_in = NULL;
707
0
    Stream_SealLength(data_in);
708
0
    Stream_SetPosition(data_in, 0);
709
710
0
    if ((freerdp_settings_get_uint32(internals->ctx->settings, FreeRDP_ThreadingFlags) &
711
0
         THREADING_FLAGS_DISABLE_THREADS) != 0)
712
0
    {
713
0
      UINT error = CHANNEL_RC_OK;
714
0
      if ((error = internals->msg_handler(internals->userdata, data_in)))
715
0
      {
716
0
        WLog_ERR(TAG,
717
0
                 "msg_handler failed with error"
718
0
                 " %" PRIu32 "!",
719
0
                 error);
720
0
        return ERROR_INTERNAL_ERROR;
721
0
      }
722
0
    }
723
0
    else if (!MessageQueue_Post(internals->queue, NULL, 0, (void*)data_in, NULL))
724
0
    {
725
0
      WLog_ERR(TAG, "MessageQueue_Post failed!");
726
0
      return ERROR_INTERNAL_ERROR;
727
0
    }
728
0
  }
729
0
  return CHANNEL_RC_OK;
730
0
}
731
/* Tear down queue and thread */
732
UINT channel_client_quit_handler(void* MsgsHandle)
733
0
{
734
0
  msg_proc_internals* internals = MsgsHandle;
735
0
  UINT rc = 0;
736
0
  if (!internals)
737
0
  {
738
    /* TODO: return some error here */
739
0
    return CHANNEL_RC_OK;
740
0
  }
741
742
0
  WINPR_ASSERT(internals->ctx);
743
0
  WINPR_ASSERT(internals->ctx->settings);
744
745
0
  if ((freerdp_settings_get_uint32(internals->ctx->settings, FreeRDP_ThreadingFlags) &
746
0
       THREADING_FLAGS_DISABLE_THREADS) == 0)
747
0
  {
748
0
    if (internals->queue && internals->thread)
749
0
    {
750
0
      if (MessageQueue_PostQuit(internals->queue, 0) &&
751
0
          (WaitForSingleObject(internals->thread, INFINITE) == WAIT_FAILED))
752
0
      {
753
0
        rc = GetLastError();
754
0
        WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", rc);
755
0
        return rc;
756
0
      }
757
0
    }
758
0
  }
759
760
0
  channel_client_handler_free(internals);
761
0
  return CHANNEL_RC_OK;
762
0
}