22 #include <freerdp/config.h>
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>
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")
47 static 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++];
65 void* 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++];
85 static FREERDP_ADDIN** freerdp_channels_list_client_static_addins(LPCSTR pszName,
87 LPCSTR pszType, 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);
147 static 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);
162 static FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPCSTR pszName, LPCSTR pszSubsystem,
163 LPCSTR pszType, DWORD dwFlags)
168 LPSTR pszPattern = NULL;
169 size_t cchPattern = 0;
170 LPCSTR pszAddinPath = FREERDP_ADDIN_PATH;
171 LPCSTR pszInstallPrefix = FREERDP_INSTALL_PREFIX;
172 LPCSTR pszExtension = NULL;
173 LPSTR pszSearchPath = NULL;
174 size_t cchSearchPath = 0;
175 size_t cchAddinPath = 0;
176 size_t cchInstallPrefix = 0;
179 cchAddinPath = strnlen(pszAddinPath,
sizeof(FREERDP_ADDIN_PATH));
180 cchInstallPrefix = strnlen(pszInstallPrefix,
sizeof(FREERDP_INSTALL_PREFIX));
181 pszExtension = PathGetSharedLibraryExtensionA(0);
182 cchPattern = 128 + strnlen(pszExtension, MAX_PATH) + 2;
183 pszPattern = (LPSTR)malloc(cchPattern + 1);
187 WLog_ERR(TAG,
"malloc failed!");
191 if (pszName && pszSubsystem && pszType)
193 (void)sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX
"%s-client-%s-%s.%s",
194 pszName, pszSubsystem, pszType, pszExtension);
196 else if (pszName && pszType)
198 (void)sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX
"%s-client-?-%s.%s",
199 pszName, pszType, pszExtension);
203 (void)sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX
"%s-client*.%s",
204 pszName, pszExtension);
208 (void)sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX
"?-client*.%s",
212 cchPattern = strnlen(pszPattern, cchPattern);
213 cchSearchPath = cchInstallPrefix + cchAddinPath + cchPattern + 3;
214 pszSearchPath = (LPSTR)calloc(cchSearchPath + 1,
sizeof(
char));
218 WLog_ERR(TAG,
"malloc failed!");
223 CopyMemory(pszSearchPath, pszInstallPrefix, cchInstallPrefix);
224 pszSearchPath[cchInstallPrefix] =
'\0';
225 const HRESULT hr1 = NativePathCchAppendA(pszSearchPath, cchSearchPath + 1, pszAddinPath);
226 const HRESULT hr2 = NativePathCchAppendA(pszSearchPath, cchSearchPath + 1, pszPattern);
229 if (FAILED(hr1) || FAILED(hr2))
235 hFind = FindFirstFileUTF8(pszSearchPath, &FindData);
244 WLog_ERR(TAG,
"calloc failed!");
248 if (hFind == INVALID_HANDLE_VALUE)
253 char* cFileName = NULL;
259 WLog_ERR(TAG,
"calloc failed!");
264 ConvertWCharNToUtf8Alloc(FindData.cFileName, ARRAYSIZE(FindData.cFileName), NULL);
269 for (
size_t index = 0; cFileName[index]; index++)
270 nDashes += (cFileName[index] ==
'-') ? 1 : 0;
278 p[1] = strchr(p[0],
'-');
283 len = (size_t)(p[1] - p[0]);
286 WLog_WARN(TAG,
"Skipping file '%s', invalid format", cFileName);
289 strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
291 pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
292 pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
293 pAddin->dwFlags |= FREERDP_ADDIN_NAME;
294 ppAddins[nAddins++] = pAddin;
298 else if (nDashes == 2)
304 p[1] = strchr(p[0],
'-');
308 p[2] = strchr(p[1],
'-');
312 p[3] = strchr(p[2],
'.');
317 len = (size_t)(p[1] - p[0]);
320 WLog_WARN(TAG,
"Skipping file '%s', invalid format", cFileName);
323 strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
325 len = (size_t)(p[3] - p[2]);
328 WLog_WARN(TAG,
"Skipping file '%s', invalid format", cFileName);
331 strncpy(pAddin->cSubsystem, p[2], MIN(ARRAYSIZE(pAddin->cSubsystem), len - 1));
333 pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
334 pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
335 pAddin->dwFlags |= FREERDP_ADDIN_NAME;
336 pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM;
337 ppAddins[nAddins++] = pAddin;
341 else if (nDashes == 3)
347 p[1] = strchr(p[0],
'-');
351 p[2] = strchr(p[1],
'-');
355 p[3] = strchr(p[2],
'-');
359 p[4] = strchr(p[3],
'.');
364 len = (size_t)(p[1] - p[0]);
367 WLog_WARN(TAG,
"Skipping file '%s', invalid format", cFileName);
370 strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
372 len = (size_t)(p[3] - p[2]);
375 WLog_WARN(TAG,
"Skipping file '%s', invalid format", cFileName);
378 strncpy(pAddin->cSubsystem, p[2], MIN(ARRAYSIZE(pAddin->cSubsystem), len - 1));
380 len = (size_t)(p[4] - p[3]);
383 WLog_WARN(TAG,
"Skipping file '%s', invalid format", cFileName);
386 strncpy(pAddin->cType, p[3], MIN(ARRAYSIZE(pAddin->cType), len - 1));
388 pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
389 pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
390 pAddin->dwFlags |= FREERDP_ADDIN_NAME;
391 pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM;
392 pAddin->dwFlags |= FREERDP_ADDIN_TYPE;
393 ppAddins[nAddins++] = pAddin;
403 }
while (FindNextFileW(hFind, &FindData));
406 ppAddins[nAddins] = NULL;
410 freerdp_channels_addin_list_free(ppAddins);
414 FREERDP_ADDIN** freerdp_channels_list_addins(LPCSTR pszName, LPCSTR pszSubsystem, LPCSTR pszType,
417 if (dwFlags & FREERDP_ADDIN_STATIC)
418 return freerdp_channels_list_client_static_addins(pszName, pszSubsystem, pszType, dwFlags);
419 else if (dwFlags & FREERDP_ADDIN_DYNAMIC)
420 return freerdp_channels_list_dynamic_addins(pszName, pszSubsystem, pszType, dwFlags);
425 void freerdp_channels_addin_list_free(
FREERDP_ADDIN** ppAddins)
430 for (
size_t index = 0; ppAddins[index] != NULL; index++)
431 free(ppAddins[index]);
436 extern const STATIC_ENTRY CLIENT_VirtualChannelEntryEx_TABLE[];
438 static BOOL freerdp_channels_is_virtual_channel_entry_ex(LPCSTR pszName)
440 for (
size_t i = 0; CLIENT_VirtualChannelEntryEx_TABLE[i].name != NULL; i++)
442 const STATIC_ENTRY* entry = &CLIENT_VirtualChannelEntryEx_TABLE[i];
444 if (!strncmp(entry->name, pszName, MAX_PATH))
451 PVIRTUALCHANNELENTRY freerdp_channels_load_static_addin_entry(LPCSTR pszName, LPCSTR pszSubsystem,
452 LPCSTR pszType, DWORD dwFlags)
455 const char* type = NULL;
460 if (dwFlags & FREERDP_ADDIN_CHANNEL_DYNAMIC)
461 type =
"DVCPluginEntry";
462 else if (dwFlags & FREERDP_ADDIN_CHANNEL_DEVICE)
463 type =
"DeviceServiceEntry";
464 else if (dwFlags & FREERDP_ADDIN_CHANNEL_STATIC)
466 if (dwFlags & FREERDP_ADDIN_CHANNEL_ENTRYEX)
467 type =
"VirtualChannelEntryEx";
469 type =
"VirtualChannelEntry";
472 for (; table->name != NULL; table++)
474 if (strncmp(table->name, pszName, MAX_PATH) == 0)
476 if (type && (strncmp(table->type, type, MAX_PATH) != 0))
479 if (pszSubsystem != NULL)
483 for (; subsystems->name != NULL; subsystems++)
486 if ((strnlen(pszSubsystem, 1) ==
488 (strncmp(subsystems->name, pszSubsystem, MAX_PATH) == 0))
490 static_subsystem_entry_fn_t fkt = subsystems->entry;
494 if (strncmp(subsystems->type, pszType, MAX_PATH) == 0)
495 return WINPR_FUNC_PTR_CAST(fkt, PVIRTUALCHANNELENTRY);
498 return WINPR_FUNC_PTR_CAST(fkt, PVIRTUALCHANNELENTRY);
504 if (dwFlags & FREERDP_ADDIN_CHANNEL_ENTRYEX)
506 if (!freerdp_channels_is_virtual_channel_entry_ex(pszName))
510 return table->entry.csevc;
520 wMessageQueue* queue;
526 MsgHandler msg_handler;
527 } msg_proc_internals;
529 static DWORD WINAPI channel_client_thread_proc(LPVOID userdata)
531 UINT error = CHANNEL_RC_OK;
533 wMessage message = { 0 };
534 msg_proc_internals* internals = userdata;
536 WINPR_ASSERT(internals);
540 if (!MessageQueue_Wait(internals->queue))
542 WLog_ERR(TAG,
"MessageQueue_Wait failed!");
543 error = ERROR_INTERNAL_ERROR;
546 if (!MessageQueue_Peek(internals->queue, &message, TRUE))
548 WLog_ERR(TAG,
"MessageQueue_Peek failed!");
549 error = ERROR_INTERNAL_ERROR;
553 if (message.id == WMQ_QUIT)
558 data = (
wStream*)message.wParam;
560 if ((error = internals->msg_handler(internals->userdata, data)))
562 WLog_ERR(TAG,
"msg_handler failed with error %" PRIu32
"!", error);
567 if (error && internals->ctx)
570 (void)_snprintf(msg, 127,
571 "%s_virtual_channel_client_thread reported an"
573 internals->channel_name);
574 setChannelError(internals->ctx, error, msg);
580 static void free_msg(
void* obj)
582 wMessage* msg = (wMessage*)obj;
584 if (msg && (msg->id == 0))
587 Stream_Free(s, TRUE);
591 static void channel_client_handler_free(msg_proc_internals* internals)
596 if (internals->thread)
597 (void)CloseHandle(internals->thread);
598 MessageQueue_Free(internals->queue);
599 Stream_Free(internals->data_in, TRUE);
600 free(internals->channel_name);
605 void* channel_client_create_handler(rdpContext* ctx, LPVOID userdata, MsgHandler msg_handler,
606 const char* channel_name)
608 msg_proc_internals* internals = calloc(1,
sizeof(msg_proc_internals));
611 WLog_ERR(TAG,
"calloc failed!");
614 internals->msg_handler = msg_handler;
615 internals->userdata = userdata;
618 internals->channel_name = _strdup(channel_name);
619 if (!internals->channel_name)
623 WINPR_ASSERT(ctx->settings);
624 internals->ctx = ctx;
626 THREADING_FLAGS_DISABLE_THREADS) == 0)
629 obj.fnObjectFree = free_msg;
630 internals->queue = MessageQueue_New(&obj);
631 if (!internals->queue)
633 WLog_ERR(TAG,
"MessageQueue_New failed!");
637 if (!(internals->thread =
638 CreateThread(NULL, 0, channel_client_thread_proc, (
void*)internals, 0, NULL)))
640 WLog_ERR(TAG,
"CreateThread failed!");
647 channel_client_handler_free(internals);
651 UINT channel_client_post_message(
void* MsgsHandle, LPVOID pData, UINT32 dataLength,
652 UINT32 totalLength, UINT32 dataFlags)
654 msg_proc_internals* internals = MsgsHandle;
660 return CHANNEL_RC_OK;
663 if ((dataFlags & CHANNEL_FLAG_SUSPEND) || (dataFlags & CHANNEL_FLAG_RESUME))
665 return CHANNEL_RC_OK;
668 if (dataFlags & CHANNEL_FLAG_FIRST)
670 if (internals->data_in)
672 if (!Stream_EnsureCapacity(internals->data_in, totalLength))
673 return CHANNEL_RC_NO_MEMORY;
676 internals->data_in = Stream_New(NULL, totalLength);
679 if (!(data_in = internals->data_in))
681 WLog_ERR(TAG,
"Stream_New failed!");
682 return CHANNEL_RC_NO_MEMORY;
685 if (!Stream_EnsureRemainingCapacity(data_in, dataLength))
687 Stream_Free(internals->data_in, TRUE);
688 internals->data_in = NULL;
689 return CHANNEL_RC_NO_MEMORY;
692 Stream_Write(data_in, pData, dataLength);
694 if (dataFlags & CHANNEL_FLAG_LAST)
696 if (Stream_Capacity(data_in) != Stream_GetPosition(data_in))
699 (void)_snprintf(msg, 127,
"%s_plugin_process_received: read error",
700 internals->channel_name);
702 return ERROR_INTERNAL_ERROR;
705 internals->data_in = NULL;
706 Stream_SealLength(data_in);
707 Stream_SetPosition(data_in, 0);
710 THREADING_FLAGS_DISABLE_THREADS) != 0)
712 UINT error = CHANNEL_RC_OK;
713 if ((error = internals->msg_handler(internals->userdata, data_in)))
716 "msg_handler failed with error"
719 return ERROR_INTERNAL_ERROR;
722 else if (!MessageQueue_Post(internals->queue, NULL, 0, (
void*)data_in, NULL))
724 WLog_ERR(TAG,
"MessageQueue_Post failed!");
725 return ERROR_INTERNAL_ERROR;
728 return CHANNEL_RC_OK;
731 UINT channel_client_quit_handler(
void* MsgsHandle)
733 msg_proc_internals* internals = MsgsHandle;
738 return CHANNEL_RC_OK;
741 WINPR_ASSERT(internals->ctx);
742 WINPR_ASSERT(internals->ctx->settings);
745 THREADING_FLAGS_DISABLE_THREADS) == 0)
747 if (internals->queue && internals->thread)
749 if (MessageQueue_PostQuit(internals->queue, 0) &&
750 (WaitForSingleObject(internals->thread, INFINITE) == WAIT_FAILED))
753 WLog_ERR(TAG,
"WaitForSingleObject failed with error %" PRIu32
"", rc);
759 channel_client_handler_free(internals);
760 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.