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 != NULL)
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 != NULL)
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] = NULL;
103 for (
size_t i = 0; CLIENT_STATIC_ADDIN_TABLE[i].name != NULL; 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 != NULL; 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, NULL);
156 hdl = FindFirstFileW(wpath, FindData);
162static FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPCSTR pszName, LPCSTR pszSubsystem,
164 WINPR_ATTR_UNUSED DWORD dwFlags)
169 LPSTR pszPattern = NULL;
170 size_t cchPattern = 0;
171 LPCSTR pszAddinPath = FREERDP_ADDIN_PATH;
172 LPCSTR pszInstallPrefix = FREERDP_INSTALL_PREFIX;
173 LPCSTR pszExtension = NULL;
174 LPSTR pszSearchPath = NULL;
175 size_t cchSearchPath = 0;
176 size_t cchAddinPath = 0;
177 size_t cchInstallPrefix = 0;
180 cchAddinPath = strnlen(pszAddinPath,
sizeof(FREERDP_ADDIN_PATH));
181 cchInstallPrefix = strnlen(pszInstallPrefix,
sizeof(FREERDP_INSTALL_PREFIX));
182 pszExtension = PathGetSharedLibraryExtensionA(0);
183 cchPattern = 128 + strnlen(pszExtension, MAX_PATH) + 2;
184 pszPattern = (LPSTR)malloc(cchPattern + 1);
188 WLog_ERR(TAG,
"malloc failed!");
192 if (pszName && pszSubsystem && pszType)
194 (void)sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX
"%s-client-%s-%s.%s",
195 pszName, pszSubsystem, pszType, pszExtension);
197 else if (pszName && pszType)
199 (void)sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX
"%s-client-?-%s.%s",
200 pszName, pszType, pszExtension);
204 (void)sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX
"%s-client*.%s",
205 pszName, pszExtension);
209 (void)sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX
"?-client*.%s",
213 cchPattern = strnlen(pszPattern, cchPattern);
214 cchSearchPath = cchInstallPrefix + cchAddinPath + cchPattern + 3;
215 pszSearchPath = (LPSTR)calloc(cchSearchPath + 1,
sizeof(
char));
219 WLog_ERR(TAG,
"malloc failed!");
224 CopyMemory(pszSearchPath, pszInstallPrefix, cchInstallPrefix);
225 pszSearchPath[cchInstallPrefix] =
'\0';
226 const HRESULT hr1 = NativePathCchAppendA(pszSearchPath, cchSearchPath + 1, pszAddinPath);
227 const HRESULT hr2 = NativePathCchAppendA(pszSearchPath, cchSearchPath + 1, pszPattern);
230 if (FAILED(hr1) || FAILED(hr2))
236 hFind = FindFirstFileUTF8(pszSearchPath, &FindData);
245 WLog_ERR(TAG,
"calloc failed!");
249 if (hFind == INVALID_HANDLE_VALUE)
254 char* cFileName = NULL;
260 WLog_ERR(TAG,
"calloc failed!");
265 ConvertWCharNToUtf8Alloc(FindData.cFileName, ARRAYSIZE(FindData.cFileName), NULL);
270 for (
size_t index = 0; cFileName[index]; index++)
271 nDashes += (cFileName[index] ==
'-') ? 1 : 0;
279 p[1] = strchr(p[0],
'-');
284 len = (size_t)(p[1] - p[0]);
287 WLog_WARN(TAG,
"Skipping file '%s', invalid format", cFileName);
290 strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
292 pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
293 pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
294 pAddin->dwFlags |= FREERDP_ADDIN_NAME;
295 ppAddins[nAddins++] = pAddin;
299 else if (nDashes == 2)
305 p[1] = strchr(p[0],
'-');
309 p[2] = strchr(p[1],
'-');
313 p[3] = strchr(p[2],
'.');
318 len = (size_t)(p[1] - p[0]);
321 WLog_WARN(TAG,
"Skipping file '%s', invalid format", cFileName);
324 strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
326 len = (size_t)(p[3] - p[2]);
329 WLog_WARN(TAG,
"Skipping file '%s', invalid format", cFileName);
332 strncpy(pAddin->cSubsystem, p[2], MIN(ARRAYSIZE(pAddin->cSubsystem), len - 1));
334 pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
335 pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
336 pAddin->dwFlags |= FREERDP_ADDIN_NAME;
337 pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM;
338 ppAddins[nAddins++] = pAddin;
342 else if (nDashes == 3)
348 p[1] = strchr(p[0],
'-');
352 p[2] = strchr(p[1],
'-');
356 p[3] = strchr(p[2],
'-');
360 p[4] = strchr(p[3],
'.');
365 len = (size_t)(p[1] - p[0]);
368 WLog_WARN(TAG,
"Skipping file '%s', invalid format", cFileName);
371 strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
373 len = (size_t)(p[3] - p[2]);
376 WLog_WARN(TAG,
"Skipping file '%s', invalid format", cFileName);
379 strncpy(pAddin->cSubsystem, p[2], MIN(ARRAYSIZE(pAddin->cSubsystem), len - 1));
381 len = (size_t)(p[4] - p[3]);
384 WLog_WARN(TAG,
"Skipping file '%s', invalid format", cFileName);
387 strncpy(pAddin->cType, p[3], MIN(ARRAYSIZE(pAddin->cType), len - 1));
389 pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
390 pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
391 pAddin->dwFlags |= FREERDP_ADDIN_NAME;
392 pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM;
393 pAddin->dwFlags |= FREERDP_ADDIN_TYPE;
394 ppAddins[nAddins++] = pAddin;
404 }
while (FindNextFileW(hFind, &FindData));
407 ppAddins[nAddins] = NULL;
411 freerdp_channels_addin_list_free(ppAddins);
415FREERDP_ADDIN** freerdp_channels_list_addins(LPCSTR pszName, LPCSTR pszSubsystem, LPCSTR pszType,
418 if (dwFlags & FREERDP_ADDIN_STATIC)
419 return freerdp_channels_list_client_static_addins(pszName, pszSubsystem, pszType, dwFlags);
420 else if (dwFlags & FREERDP_ADDIN_DYNAMIC)
421 return freerdp_channels_list_dynamic_addins(pszName, pszSubsystem, pszType, dwFlags);
426void freerdp_channels_addin_list_free(
FREERDP_ADDIN** ppAddins)
431 for (
size_t index = 0; ppAddins[index] != NULL; index++)
432 free(ppAddins[index]);
434 free((
void*)ppAddins);
437extern const STATIC_ENTRY CLIENT_VirtualChannelEntryEx_TABLE[];
439static BOOL freerdp_channels_is_virtual_channel_entry_ex(LPCSTR pszName)
441 for (
size_t i = 0; CLIENT_VirtualChannelEntryEx_TABLE[i].name != NULL; i++)
443 const STATIC_ENTRY* entry = &CLIENT_VirtualChannelEntryEx_TABLE[i];
445 if (!strncmp(entry->name, pszName, MAX_PATH))
452PVIRTUALCHANNELENTRY freerdp_channels_load_static_addin_entry(LPCSTR pszName, LPCSTR pszSubsystem,
453 LPCSTR pszType, DWORD dwFlags)
456 const char* type = NULL;
461 if (dwFlags & FREERDP_ADDIN_CHANNEL_DYNAMIC)
462 type =
"DVCPluginEntry";
463 else if (dwFlags & FREERDP_ADDIN_CHANNEL_DEVICE)
464 type =
"DeviceServiceEntry";
465 else if (dwFlags & FREERDP_ADDIN_CHANNEL_STATIC)
467 if (dwFlags & FREERDP_ADDIN_CHANNEL_ENTRYEX)
468 type =
"VirtualChannelEntryEx";
470 type =
"VirtualChannelEntry";
473 for (; table->name != NULL; table++)
475 if (strncmp(table->name, pszName, MAX_PATH) == 0)
477 if (type && (strncmp(table->type, type, MAX_PATH) != 0))
480 if (pszSubsystem != NULL)
484 for (; subsystems->name != NULL; subsystems++)
487 if ((strnlen(pszSubsystem, 1) ==
489 (strncmp(subsystems->name, pszSubsystem, MAX_PATH) == 0))
491 static_subsystem_entry_fn_t fkt = subsystems->entry;
495 if (strncmp(subsystems->type, pszType, MAX_PATH) == 0)
496 return WINPR_FUNC_PTR_CAST(fkt, PVIRTUALCHANNELENTRY);
499 return WINPR_FUNC_PTR_CAST(fkt, PVIRTUALCHANNELENTRY);
505 if (dwFlags & FREERDP_ADDIN_CHANNEL_ENTRYEX)
507 if (!freerdp_channels_is_virtual_channel_entry_ex(pszName))
511 return table->entry.csevc;
521 wMessageQueue* queue;
527 MsgHandler msg_handler;
530static DWORD WINAPI channel_client_thread_proc(LPVOID userdata)
532 UINT error = CHANNEL_RC_OK;
534 wMessage message = { 0 };
535 msg_proc_internals* internals = userdata;
537 WINPR_ASSERT(internals);
541 if (!MessageQueue_Wait(internals->queue))
543 WLog_ERR(TAG,
"MessageQueue_Wait failed!");
544 error = ERROR_INTERNAL_ERROR;
547 if (!MessageQueue_Peek(internals->queue, &message, TRUE))
549 WLog_ERR(TAG,
"MessageQueue_Peek failed!");
550 error = ERROR_INTERNAL_ERROR;
554 if (message.id == WMQ_QUIT)
559 data = (
wStream*)message.wParam;
561 if ((error = internals->msg_handler(internals->userdata, data)))
563 WLog_ERR(TAG,
"msg_handler failed with error %" PRIu32
"!", error);
568 if (error && internals->ctx)
571 (void)_snprintf(msg, 127,
572 "%s_virtual_channel_client_thread reported an"
574 internals->channel_name);
575 setChannelError(internals->ctx, error, msg);
581static void free_msg(
void* obj)
583 wMessage* msg = (wMessage*)obj;
585 if (msg && (msg->id == 0))
588 Stream_Free(s, TRUE);
592static void channel_client_handler_free(msg_proc_internals* internals)
597 if (internals->thread)
598 (void)CloseHandle(internals->thread);
599 MessageQueue_Free(internals->queue);
600 Stream_Free(internals->data_in, TRUE);
601 free(internals->channel_name);
606void* channel_client_create_handler(rdpContext* ctx, LPVOID userdata, MsgHandler msg_handler,
607 const char* channel_name)
609 msg_proc_internals* internals = calloc(1,
sizeof(msg_proc_internals));
612 WLog_ERR(TAG,
"calloc failed!");
615 internals->msg_handler = msg_handler;
616 internals->userdata = userdata;
619 internals->channel_name = _strdup(channel_name);
620 if (!internals->channel_name)
624 WINPR_ASSERT(ctx->settings);
625 internals->ctx = ctx;
627 THREADING_FLAGS_DISABLE_THREADS) == 0)
630 obj.fnObjectFree = free_msg;
631 internals->queue = MessageQueue_New(&obj);
632 if (!internals->queue)
634 WLog_ERR(TAG,
"MessageQueue_New failed!");
638 if (!(internals->thread =
639 CreateThread(NULL, 0, channel_client_thread_proc, (
void*)internals, 0, NULL)))
641 WLog_ERR(TAG,
"CreateThread failed!");
648 channel_client_handler_free(internals);
652UINT channel_client_post_message(
void* MsgsHandle, LPVOID pData, UINT32 dataLength,
653 UINT32 totalLength, UINT32 dataFlags)
655 msg_proc_internals* internals = MsgsHandle;
661 return CHANNEL_RC_OK;
664 if ((dataFlags & CHANNEL_FLAG_SUSPEND) || (dataFlags & CHANNEL_FLAG_RESUME))
666 return CHANNEL_RC_OK;
669 if (dataFlags & CHANNEL_FLAG_FIRST)
671 if (internals->data_in)
673 if (!Stream_EnsureCapacity(internals->data_in, totalLength))
674 return CHANNEL_RC_NO_MEMORY;
677 internals->data_in = Stream_New(NULL, totalLength);
680 if (!(data_in = internals->data_in))
682 WLog_ERR(TAG,
"Stream_New failed!");
683 return CHANNEL_RC_NO_MEMORY;
686 if (!Stream_EnsureRemainingCapacity(data_in, dataLength))
688 Stream_Free(internals->data_in, TRUE);
689 internals->data_in = NULL;
690 return CHANNEL_RC_NO_MEMORY;
693 Stream_Write(data_in, pData, dataLength);
695 if (dataFlags & CHANNEL_FLAG_LAST)
697 if (Stream_Capacity(data_in) != Stream_GetPosition(data_in))
700 (void)_snprintf(msg, 127,
"%s_plugin_process_received: read error",
701 internals->channel_name);
703 return ERROR_INTERNAL_ERROR;
706 internals->data_in = NULL;
707 Stream_SealLength(data_in);
708 Stream_SetPosition(data_in, 0);
711 THREADING_FLAGS_DISABLE_THREADS) != 0)
713 UINT error = CHANNEL_RC_OK;
714 if ((error = internals->msg_handler(internals->userdata, data_in)))
717 "msg_handler failed with error"
720 return ERROR_INTERNAL_ERROR;
723 else if (!MessageQueue_Post(internals->queue, NULL, 0, (
void*)data_in, NULL))
725 WLog_ERR(TAG,
"MessageQueue_Post failed!");
726 return ERROR_INTERNAL_ERROR;
729 return CHANNEL_RC_OK;
732UINT channel_client_quit_handler(
void* MsgsHandle)
734 msg_proc_internals* internals = MsgsHandle;
739 return CHANNEL_RC_OK;
742 WINPR_ASSERT(internals->ctx);
743 WINPR_ASSERT(internals->ctx->settings);
746 THREADING_FLAGS_DISABLE_THREADS) == 0)
748 if (internals->queue && internals->thread)
750 if (MessageQueue_PostQuit(internals->queue, 0) &&
751 (WaitForSingleObject(internals->thread, INFINITE) == WAIT_FAILED))
754 WLog_ERR(TAG,
"WaitForSingleObject failed with error %" PRIu32
"", rc);
760 channel_client_handler_free(internals);
761 return CHANNEL_RC_OK;
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.