Coverage Report

Created: 2024-05-20 06:11

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