Coverage Report

Created: 2026-08-31 06:29

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
0
{
444
0
  const STATIC_ADDIN_TABLE* table = CLIENT_STATIC_ADDIN_TABLE;
445
0
  const char* type = nullptr;
446
447
0
  if (!pszName)
448
0
    return nullptr;
449
450
0
  if (dwFlags & FREERDP_ADDIN_CHANNEL_DYNAMIC)
451
0
    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
0
  for (; table->name != nullptr; table++)
463
0
  {
464
0
    if (strncmp(table->name, pszName, MAX_PATH) == 0)
465
0
    {
466
0
      if (type && (strncmp(table->type, type, MAX_PATH) != 0))
467
0
        continue;
468
469
0
      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
0
      else
493
0
      {
494
0
        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
0
        return table->entry.csevc;
501
0
      }
502
0
    }
503
0
  }
504
505
0
  return nullptr;
506
0
}
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
} msg_proc_internals;
518
519
static DWORD WINAPI channel_client_thread_proc(LPVOID userdata)
520
0
{
521
0
  UINT error = CHANNEL_RC_OK;
522
0
  wStream* data = nullptr;
523
0
  wMessage message = WINPR_C_ARRAY_INIT;
524
0
  msg_proc_internals* internals = userdata;
525
526
0
  WINPR_ASSERT(internals);
527
528
0
  while (1)
529
0
  {
530
0
    if (!MessageQueue_Wait(internals->queue))
531
0
    {
532
0
      WLog_ERR(TAG, "MessageQueue_Wait failed!");
533
0
      error = ERROR_INTERNAL_ERROR;
534
0
      break;
535
0
    }
536
0
    if (!MessageQueue_Peek(internals->queue, &message, TRUE))
537
0
    {
538
0
      WLog_ERR(TAG, "MessageQueue_Peek failed!");
539
0
      error = ERROR_INTERNAL_ERROR;
540
0
      break;
541
0
    }
542
543
0
    if (message.id == WMQ_QUIT)
544
0
      break;
545
546
0
    if (message.id == 0)
547
0
    {
548
0
      data = (wStream*)message.wParam;
549
550
0
      if ((error = internals->msg_handler(internals->userdata, data)))
551
0
      {
552
0
        WLog_ERR(TAG, "msg_handler failed with error %" PRIu32 "!", error);
553
0
        break;
554
0
      }
555
0
    }
556
0
  }
557
0
  if (error && internals->ctx)
558
0
  {
559
0
    char msg[128];
560
0
    (void)_snprintf(msg, 127,
561
0
                    "%s_virtual_channel_client_thread reported an"
562
0
                    " error",
563
0
                    internals->channel_name);
564
0
    setChannelError(internals->ctx, error, msg);
565
0
  }
566
0
  ExitThread(error);
567
0
  return error;
568
0
}
569
570
static void free_msg(void* obj)
571
0
{
572
0
  wMessage* msg = (wMessage*)obj;
573
574
0
  if (msg && (msg->id == 0))
575
0
  {
576
0
    wStream* s = (wStream*)msg->wParam;
577
0
    Stream_Free(s, TRUE);
578
0
  }
579
0
}
580
581
static void channel_client_handler_free(msg_proc_internals* internals)
582
0
{
583
0
  if (!internals)
584
0
    return;
585
586
0
  if (internals->thread)
587
0
    (void)CloseHandle(internals->thread);
588
0
  MessageQueue_Free(internals->queue);
589
0
  Stream_Free(internals->data_in, TRUE);
590
0
  free(internals->channel_name);
591
0
  free(internals);
592
0
}
593
594
/*  Create message queue and thread or not, depending on settings */
595
void* channel_client_create_handler(rdpContext* ctx, LPVOID userdata, MsgHandler msg_handler,
596
                                    const char* channel_name)
597
0
{
598
0
  msg_proc_internals* internals = calloc(1, sizeof(msg_proc_internals));
599
0
  if (!internals)
600
0
  {
601
0
    WLog_ERR(TAG, "calloc failed!");
602
0
    return nullptr;
603
0
  }
604
0
  internals->msg_handler = msg_handler;
605
0
  internals->userdata = userdata;
606
0
  if (channel_name)
607
0
  {
608
0
    internals->channel_name = _strdup(channel_name);
609
0
    if (!internals->channel_name)
610
0
      goto fail;
611
0
  }
612
0
  WINPR_ASSERT(ctx);
613
0
  WINPR_ASSERT(ctx->settings);
614
0
  internals->ctx = ctx;
615
0
  if ((freerdp_settings_get_uint32(ctx->settings, FreeRDP_ThreadingFlags) &
616
0
       THREADING_FLAGS_DISABLE_THREADS) == 0)
617
0
  {
618
0
    wObject obj = WINPR_C_ARRAY_INIT;
619
0
    obj.fnObjectFree = free_msg;
620
0
    internals->queue = MessageQueue_New(&obj);
621
0
    if (!internals->queue)
622
0
    {
623
0
      WLog_ERR(TAG, "MessageQueue_New failed!");
624
0
      goto fail;
625
0
    }
626
627
0
    if (!(internals->thread = CreateThread(nullptr, 0, channel_client_thread_proc,
628
0
                                           (void*)internals, 0, nullptr)))
629
0
    {
630
0
      WLog_ERR(TAG, "CreateThread failed!");
631
0
      goto fail;
632
0
    }
633
0
  }
634
0
  return internals;
635
636
0
fail:
637
0
  channel_client_handler_free(internals);
638
0
  return nullptr;
639
0
}
640
/* post a message in the queue or directly call the processing handler */
641
UINT channel_client_post_message(void* MsgsHandle, LPVOID pData, UINT32 dataLength,
642
                                 UINT32 totalLength, UINT32 dataFlags)
643
0
{
644
0
  msg_proc_internals* internals = MsgsHandle;
645
0
  wStream* data_in = nullptr;
646
647
0
  if (!internals)
648
0
  {
649
    /* TODO: return some error here */
650
0
    return CHANNEL_RC_OK;
651
0
  }
652
653
0
  if ((dataFlags & CHANNEL_FLAG_SUSPEND) || (dataFlags & CHANNEL_FLAG_RESUME))
654
0
  {
655
0
    return CHANNEL_RC_OK;
656
0
  }
657
658
0
  if (dataFlags & CHANNEL_FLAG_FIRST)
659
0
  {
660
0
    if (internals->data_in)
661
0
    {
662
0
      if (!Stream_EnsureCapacity(internals->data_in, totalLength))
663
0
        return CHANNEL_RC_NO_MEMORY;
664
0
    }
665
0
    else
666
0
      internals->data_in = Stream_New(nullptr, totalLength);
667
0
  }
668
669
0
  if (!(data_in = internals->data_in))
670
0
  {
671
0
    WLog_ERR(TAG, "Stream_New failed!");
672
0
    return CHANNEL_RC_NO_MEMORY;
673
0
  }
674
675
0
  if (!Stream_EnsureRemainingCapacity(data_in, dataLength))
676
0
  {
677
0
    Stream_Free(internals->data_in, TRUE);
678
0
    internals->data_in = nullptr;
679
0
    return CHANNEL_RC_NO_MEMORY;
680
0
  }
681
682
0
  Stream_Write(data_in, pData, dataLength);
683
684
0
  if (dataFlags & CHANNEL_FLAG_LAST)
685
0
  {
686
0
    if (Stream_Capacity(data_in) != Stream_GetPosition(data_in))
687
0
    {
688
0
      WLog_ERR(TAG, "%s_plugin_process_received: read error", internals->channel_name);
689
0
      return ERROR_INTERNAL_ERROR;
690
0
    }
691
692
0
    internals->data_in = nullptr;
693
0
    Stream_SealLength(data_in);
694
0
    Stream_ResetPosition(data_in);
695
696
0
    if ((freerdp_settings_get_uint32(internals->ctx->settings, FreeRDP_ThreadingFlags) &
697
0
         THREADING_FLAGS_DISABLE_THREADS) != 0)
698
0
    {
699
0
      UINT error = CHANNEL_RC_OK;
700
0
      if ((error = internals->msg_handler(internals->userdata, data_in)))
701
0
      {
702
0
        WLog_ERR(TAG,
703
0
                 "msg_handler failed with error"
704
0
                 " %" PRIu32 "!",
705
0
                 error);
706
0
        return ERROR_INTERNAL_ERROR;
707
0
      }
708
0
    }
709
0
    else if (!MessageQueue_Post(internals->queue, nullptr, 0, (void*)data_in, nullptr))
710
0
    {
711
0
      WLog_ERR(TAG, "MessageQueue_Post failed!");
712
0
      return ERROR_INTERNAL_ERROR;
713
0
    }
714
0
  }
715
0
  return CHANNEL_RC_OK;
716
0
}
717
/* Tear down queue and thread */
718
UINT channel_client_quit_handler(void* MsgsHandle)
719
0
{
720
0
  msg_proc_internals* internals = MsgsHandle;
721
0
  UINT rc = 0;
722
0
  if (!internals)
723
0
  {
724
    /* TODO: return some error here */
725
0
    return CHANNEL_RC_OK;
726
0
  }
727
728
0
  WINPR_ASSERT(internals->ctx);
729
0
  WINPR_ASSERT(internals->ctx->settings);
730
731
0
  if ((freerdp_settings_get_uint32(internals->ctx->settings, FreeRDP_ThreadingFlags) &
732
0
       THREADING_FLAGS_DISABLE_THREADS) == 0)
733
0
  {
734
0
    if (internals->queue && internals->thread)
735
0
    {
736
0
      if (MessageQueue_PostQuit(internals->queue, 0) &&
737
0
          (WaitForSingleObject(internals->thread, INFINITE) == WAIT_FAILED))
738
0
      {
739
0
        rc = GetLastError();
740
0
        WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", rc);
741
0
        return rc;
742
0
      }
743
0
    }
744
0
  }
745
746
0
  channel_client_handler_free(internals);
747
0
  return CHANNEL_RC_OK;
748
0
}