22#include <freerdp/config.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>
33#include <freerdp/freerdp.h>
34#include <freerdp/addin.h>
35#include <freerdp/build-config.h>
36#include <freerdp/client/channels.h>
42#include <freerdp/channels/log.h>
43#define TAG CHANNELS_TAG("addin")
47static void* freerdp_channels_find_static_entry_in_table(
const STATIC_ENTRY_TABLE* table,
48 const char* identifier)
53 while (pEntry->entry !=
nullptr)
55 static_entry_fn_t fkt = pEntry->entry;
56 if (strcmp(pEntry->name, identifier) == 0)
57 return WINPR_FUNC_PTR_CAST(fkt,
void*);
59 pEntry = &table->table.cse[index++];
65void* freerdp_channels_client_find_static_entry(
const char* name,
const char* identifier)
70 while (pEntry->table.cse !=
nullptr)
72 if (strcmp(pEntry->name, name) == 0)
74 return freerdp_channels_find_static_entry_in_table(pEntry, identifier);
77 pEntry = &CLIENT_STATIC_ENTRY_TABLES[index++];
85static 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)
97 WLog_ERR(TAG,
"calloc failed!");
101 ppAddins[nAddins] =
nullptr;
103 for (
size_t i = 0; CLIENT_STATIC_ADDIN_TABLE[i].name !=
nullptr; i++)
109 WLog_ERR(TAG,
"calloc failed!");
113 (void)sprintf_s(pAddin->cName, ARRAYSIZE(pAddin->cName),
"%s", table->name);
114 pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
115 pAddin->dwFlags |= FREERDP_ADDIN_STATIC;
116 pAddin->dwFlags |= FREERDP_ADDIN_NAME;
117 ppAddins[nAddins++] = pAddin;
118 subsystems = table->table;
120 for (
size_t j = 0; subsystems[j].name !=
nullptr; j++)
126 WLog_ERR(TAG,
"calloc failed!");
130 (void)sprintf_s(pAddin->cName, ARRAYSIZE(pAddin->cName),
"%s", table->name);
131 (void)sprintf_s(pAddin->cSubsystem, ARRAYSIZE(pAddin->cSubsystem),
"%s",
133 pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
134 pAddin->dwFlags |= FREERDP_ADDIN_STATIC;
135 pAddin->dwFlags |= FREERDP_ADDIN_NAME;
136 pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM;
137 ppAddins[nAddins++] = pAddin;
143 freerdp_channels_addin_list_free(ppAddins);
147static HANDLE FindFirstFileUTF8(LPCSTR pszSearchPath,
WIN32_FIND_DATAW* FindData)
149 HANDLE hdl = INVALID_HANDLE_VALUE;
152 WCHAR* wpath = ConvertUtf8ToWCharAlloc(pszSearchPath,
nullptr);
156 hdl = FindFirstFileW(wpath, FindData);
162static FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPCSTR pszName, LPCSTR pszSubsystem,
164 WINPR_ATTR_UNUSED DWORD dwFlags)
167 LPCSTR pszAddinPath = FREERDP_ADDIN_PATH;
168 LPCSTR pszInstallPrefix = FREERDP_INSTALL_PREFIX;
169 const size_t cchAddinPath = strnlen(pszAddinPath,
sizeof(FREERDP_ADDIN_PATH));
170 const size_t cchInstallPrefix = strnlen(pszInstallPrefix,
sizeof(FREERDP_INSTALL_PREFIX));
171 const char* pszExtension = PathGetSharedLibraryExtensionA(0);
172 const size_t cchPattern = 128 + strnlen(pszExtension, MAX_PATH) + 2;
173 char* pszPattern = calloc(cchPattern + 1,
sizeof(
char));
177 WLog_ERR(TAG,
"malloc failed!");
181 if (pszName && pszSubsystem && pszType)
183 (void)sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX
"%s-client-%s-%s.%s",
184 pszName, pszSubsystem, pszType, pszExtension);
186 else if (pszName && pszType)
188 (void)sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX
"%s-client-?-%s.%s",
189 pszName, pszType, pszExtension);
193 (void)sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX
"%s-client*.%s",
194 pszName, pszExtension);
198 (void)sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX
"?-client*.%s",
202 const size_t cchPattern2 = strnlen(pszPattern, cchPattern);
203 const size_t cchSearchPath = cchInstallPrefix + cchAddinPath + cchPattern2 + 3;
204 char* pszSearchPath = calloc(cchSearchPath + 1,
sizeof(
char));
208 WLog_ERR(TAG,
"malloc failed!");
213 CopyMemory(pszSearchPath, pszInstallPrefix, cchInstallPrefix);
214 pszSearchPath[cchInstallPrefix] =
'\0';
216 char* result = GetCombinedPathV(pszSearchPath,
"%s%c%s", pszAddinPath,
217 PathGetSeparatorA(PATH_STYLE_NATIVE), pszPattern);
225 HANDLE hFind = FindFirstFileUTF8(result, &FindData);
234 WLog_ERR(TAG,
"calloc failed!");
238 if (hFind == INVALID_HANDLE_VALUE)
243 char* cFileName =
nullptr;
249 WLog_ERR(TAG,
"calloc failed!");
254 ConvertWCharNToUtf8Alloc(FindData.cFileName, ARRAYSIZE(FindData.cFileName),
nullptr);
259 for (
size_t index = 0; cFileName[index]; index++)
260 nDashes += (cFileName[index] ==
'-') ? 1 : 0;
265 char* p[2] = WINPR_C_ARRAY_INIT;
268 p[1] = strchr(p[0],
'-');
273 len = (size_t)(p[1] - p[0]);
276 WLog_WARN(TAG,
"Skipping file '%s', invalid format", cFileName);
279 strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
281 pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
282 pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
283 pAddin->dwFlags |= FREERDP_ADDIN_NAME;
284 ppAddins[nAddins++] = pAddin;
288 else if (nDashes == 2)
291 char* p[4] = WINPR_C_ARRAY_INIT;
294 p[1] = strchr(p[0],
'-');
298 p[2] = strchr(p[1],
'-');
302 p[3] = strchr(p[2],
'.');
307 len = (size_t)(p[1] - p[0]);
310 WLog_WARN(TAG,
"Skipping file '%s', invalid format", cFileName);
313 strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
315 len = (size_t)(p[3] - p[2]);
318 WLog_WARN(TAG,
"Skipping file '%s', invalid format", cFileName);
321 strncpy(pAddin->cSubsystem, p[2], MIN(ARRAYSIZE(pAddin->cSubsystem), len - 1));
323 pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
324 pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
325 pAddin->dwFlags |= FREERDP_ADDIN_NAME;
326 pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM;
327 ppAddins[nAddins++] = pAddin;
331 else if (nDashes == 3)
334 char* p[5] = WINPR_C_ARRAY_INIT;
337 p[1] = strchr(p[0],
'-');
341 p[2] = strchr(p[1],
'-');
345 p[3] = strchr(p[2],
'-');
349 p[4] = strchr(p[3],
'.');
354 len = (size_t)(p[1] - p[0]);
357 WLog_WARN(TAG,
"Skipping file '%s', invalid format", cFileName);
360 strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
362 len = (size_t)(p[3] - p[2]);
365 WLog_WARN(TAG,
"Skipping file '%s', invalid format", cFileName);
368 strncpy(pAddin->cSubsystem, p[2], MIN(ARRAYSIZE(pAddin->cSubsystem), len - 1));
370 len = (size_t)(p[4] - p[3]);
373 WLog_WARN(TAG,
"Skipping file '%s', invalid format", cFileName);
376 strncpy(pAddin->cType, p[3], MIN(ARRAYSIZE(pAddin->cType), len - 1));
378 pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
379 pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
380 pAddin->dwFlags |= FREERDP_ADDIN_NAME;
381 pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM;
382 pAddin->dwFlags |= FREERDP_ADDIN_TYPE;
383 ppAddins[nAddins++] = pAddin;
393 }
while (FindNextFileW(hFind, &FindData));
396 ppAddins[nAddins] =
nullptr;
400 freerdp_channels_addin_list_free(ppAddins);
404FREERDP_ADDIN** freerdp_channels_list_addins(LPCSTR pszName, LPCSTR pszSubsystem, LPCSTR pszType,
407 if (dwFlags & FREERDP_ADDIN_STATIC)
408 return freerdp_channels_list_client_static_addins(pszName, pszSubsystem, pszType, dwFlags);
409 else if (dwFlags & FREERDP_ADDIN_DYNAMIC)
410 return freerdp_channels_list_dynamic_addins(pszName, pszSubsystem, pszType, dwFlags);
415void freerdp_channels_addin_list_free(
FREERDP_ADDIN** ppAddins)
420 for (
size_t index = 0; ppAddins[index] !=
nullptr; index++)
421 free(ppAddins[index]);
423 free((
void*)ppAddins);
426extern const STATIC_ENTRY CLIENT_VirtualChannelEntryEx_TABLE[];
428static BOOL freerdp_channels_is_virtual_channel_entry_ex(LPCSTR pszName)
430 for (
size_t i = 0; CLIENT_VirtualChannelEntryEx_TABLE[i].name !=
nullptr; i++)
432 const STATIC_ENTRY* entry = &CLIENT_VirtualChannelEntryEx_TABLE[i];
434 if (!strncmp(entry->name, pszName, MAX_PATH))
441PVIRTUALCHANNELENTRY freerdp_channels_load_static_addin_entry(LPCSTR pszName, LPCSTR pszSubsystem,
442 LPCSTR pszType, DWORD dwFlags)
445 const char* type =
nullptr;
450 if (dwFlags & FREERDP_ADDIN_CHANNEL_DYNAMIC)
451 type =
"DVCPluginEntry";
452 else if (dwFlags & FREERDP_ADDIN_CHANNEL_DEVICE)
453 type =
"DeviceServiceEntry";
454 else if (dwFlags & FREERDP_ADDIN_CHANNEL_STATIC)
456 if (dwFlags & FREERDP_ADDIN_CHANNEL_ENTRYEX)
457 type =
"VirtualChannelEntryEx";
459 type =
"VirtualChannelEntry";
462 for (; table->name !=
nullptr; table++)
464 if (strncmp(table->name, pszName, MAX_PATH) == 0)
466 if (type && (strncmp(table->type, type, MAX_PATH) != 0))
469 if (pszSubsystem !=
nullptr)
473 for (; subsystems->name !=
nullptr; subsystems++)
476 if ((strnlen(pszSubsystem, 1) ==
478 (strncmp(subsystems->name, pszSubsystem, MAX_PATH) == 0))
480 static_subsystem_entry_fn_t fkt = subsystems->entry;
484 if (strncmp(subsystems->type, pszType, MAX_PATH) == 0)
485 return WINPR_FUNC_PTR_CAST(fkt, PVIRTUALCHANNELENTRY);
488 return WINPR_FUNC_PTR_CAST(fkt, PVIRTUALCHANNELENTRY);
494 if (dwFlags & FREERDP_ADDIN_CHANNEL_ENTRYEX)
496 if (!freerdp_channels_is_virtual_channel_entry_ex(pszName))
500 return table->entry.csevc;
510 wMessageQueue* queue;
516 MsgHandler msg_handler;
519static DWORD WINAPI channel_client_thread_proc(LPVOID userdata)
521 UINT error = CHANNEL_RC_OK;
523 wMessage message = WINPR_C_ARRAY_INIT;
524 msg_proc_internals* internals = userdata;
526 WINPR_ASSERT(internals);
530 if (!MessageQueue_Wait(internals->queue))
532 WLog_ERR(TAG,
"MessageQueue_Wait failed!");
533 error = ERROR_INTERNAL_ERROR;
536 if (!MessageQueue_Peek(internals->queue, &message, TRUE))
538 WLog_ERR(TAG,
"MessageQueue_Peek failed!");
539 error = ERROR_INTERNAL_ERROR;
543 if (message.id == WMQ_QUIT)
548 data = (
wStream*)message.wParam;
550 if ((error = internals->msg_handler(internals->userdata, data)))
552 WLog_ERR(TAG,
"msg_handler failed with error %" PRIu32
"!", error);
557 if (error && internals->ctx)
560 (void)_snprintf(msg, 127,
561 "%s_virtual_channel_client_thread reported an"
563 internals->channel_name);
564 setChannelError(internals->ctx, error, msg);
570static void free_msg(
void* obj)
572 wMessage* msg = (wMessage*)obj;
574 if (msg && (msg->id == 0))
577 Stream_Free(s, TRUE);
581static void channel_client_handler_free(msg_proc_internals* internals)
586 if (internals->thread)
587 (void)CloseHandle(internals->thread);
588 MessageQueue_Free(internals->queue);
589 Stream_Free(internals->data_in, TRUE);
590 free(internals->channel_name);
595void* channel_client_create_handler(rdpContext* ctx, LPVOID userdata, MsgHandler msg_handler,
596 const char* channel_name)
598 msg_proc_internals* internals = calloc(1,
sizeof(msg_proc_internals));
601 WLog_ERR(TAG,
"calloc failed!");
604 internals->msg_handler = msg_handler;
605 internals->userdata = userdata;
608 internals->channel_name = _strdup(channel_name);
609 if (!internals->channel_name)
613 WINPR_ASSERT(ctx->settings);
614 internals->ctx = ctx;
616 THREADING_FLAGS_DISABLE_THREADS) == 0)
618 wObject obj = WINPR_C_ARRAY_INIT;
620 internals->queue = MessageQueue_New(&obj);
621 if (!internals->queue)
623 WLog_ERR(TAG,
"MessageQueue_New failed!");
627 if (!(internals->thread = CreateThread(
nullptr, 0, channel_client_thread_proc,
628 (
void*)internals, 0,
nullptr)))
630 WLog_ERR(TAG,
"CreateThread failed!");
637 channel_client_handler_free(internals);
641UINT channel_client_post_message(
void* MsgsHandle, LPVOID pData, UINT32 dataLength,
642 UINT32 totalLength, UINT32 dataFlags)
644 msg_proc_internals* internals = MsgsHandle;
650 return CHANNEL_RC_OK;
653 if ((dataFlags & CHANNEL_FLAG_SUSPEND) || (dataFlags & CHANNEL_FLAG_RESUME))
655 return CHANNEL_RC_OK;
658 if (dataFlags & CHANNEL_FLAG_FIRST)
660 if (internals->data_in)
662 if (!Stream_EnsureCapacity(internals->data_in, totalLength))
663 return CHANNEL_RC_NO_MEMORY;
666 internals->data_in = Stream_New(
nullptr, totalLength);
669 if (!(data_in = internals->data_in))
671 WLog_ERR(TAG,
"Stream_New failed!");
672 return CHANNEL_RC_NO_MEMORY;
675 if (!Stream_EnsureRemainingCapacity(data_in, dataLength))
677 Stream_Free(internals->data_in, TRUE);
678 internals->data_in =
nullptr;
679 return CHANNEL_RC_NO_MEMORY;
682 Stream_Write(data_in, pData, dataLength);
684 if (dataFlags & CHANNEL_FLAG_LAST)
686 if (Stream_Capacity(data_in) != Stream_GetPosition(data_in))
688 WLog_ERR(TAG,
"%s_plugin_process_received: read error", internals->channel_name);
689 return ERROR_INTERNAL_ERROR;
692 internals->data_in =
nullptr;
693 Stream_SealLength(data_in);
694 Stream_ResetPosition(data_in);
697 THREADING_FLAGS_DISABLE_THREADS) != 0)
699 UINT error = CHANNEL_RC_OK;
700 if ((error = internals->msg_handler(internals->userdata, data_in)))
703 "msg_handler failed with error"
706 return ERROR_INTERNAL_ERROR;
709 else if (!MessageQueue_Post(internals->queue,
nullptr, 0, (
void*)data_in,
nullptr))
711 WLog_ERR(TAG,
"MessageQueue_Post failed!");
712 return ERROR_INTERNAL_ERROR;
715 return CHANNEL_RC_OK;
718UINT channel_client_quit_handler(
void* MsgsHandle)
720 msg_proc_internals* internals = MsgsHandle;
725 return CHANNEL_RC_OK;
728 WINPR_ASSERT(internals->ctx);
729 WINPR_ASSERT(internals->ctx->settings);
732 THREADING_FLAGS_DISABLE_THREADS) == 0)
734 if (internals->queue && internals->thread)
736 if (MessageQueue_PostQuit(internals->queue, 0) &&
737 (WaitForSingleObject(internals->thread, INFINITE) == WAIT_FAILED))
740 WLog_ERR(TAG,
"WaitForSingleObject failed with error %" PRIu32
"", rc);
746 channel_client_handler_free(internals);
747 return CHANNEL_RC_OK;
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
This struct contains function pointer to initialize/free objects.
OBJECT_FREE_FN fnObjectFree