FreeRDP
Loading...
Searching...
No Matches
pf_modules.c
1
23#include <winpr/assert.h>
24
25#include <winpr/file.h>
26#include <winpr/wlog.h>
27#include <winpr/path.h>
28#include <winpr/library.h>
29#include <freerdp/api.h>
30#include <freerdp/build-config.h>
31
32#include <freerdp/server/proxy/proxy_log.h>
33#include <freerdp/server/proxy/proxy_modules_api.h>
34
35#include <freerdp/server/proxy/proxy_context.h>
36#include "proxy_modules.h"
37
38#define TAG PROXY_TAG("modules")
39
40#define MODULE_ENTRY_POINT "proxy_module_entry_point"
41
42struct proxy_module
43{
44 proxyPluginsManager mgr;
45 wArrayList* plugins;
46 wArrayList* handles;
47};
48
49static const char* pf_modules_get_filter_type_string(PF_FILTER_TYPE result)
50{
51 switch (result)
52 {
53 case FILTER_TYPE_KEYBOARD:
54 return "FILTER_TYPE_KEYBOARD";
55 case FILTER_TYPE_UNICODE:
56 return "FILTER_TYPE_UNICODE";
57 case FILTER_TYPE_MOUSE:
58 return "FILTER_TYPE_MOUSE";
59 case FILTER_TYPE_MOUSE_EX:
60 return "FILTER_TYPE_MOUSE_EX";
61 case FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_DATA:
62 return "FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_DATA";
63 case FILTER_TYPE_SERVER_PASSTHROUGH_CHANNEL_DATA:
64 return "FILTER_TYPE_SERVER_PASSTHROUGH_CHANNEL_DATA";
65 case FILTER_TYPE_CLIENT_PASSTHROUGH_DYN_CHANNEL_CREATE:
66 return "FILTER_TYPE_CLIENT_PASSTHROUGH_DYN_CHANNEL_CREATE";
67 case FILTER_TYPE_SERVER_FETCH_TARGET_ADDR:
68 return "FILTER_TYPE_SERVER_FETCH_TARGET_ADDR";
69 case FILTER_TYPE_SERVER_PEER_LOGON:
70 return "FILTER_TYPE_SERVER_PEER_LOGON";
71 case FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_CREATE:
72 return "FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_CREATE";
73 case FILTER_TYPE_STATIC_INTERCEPT_LIST:
74 return "FILTER_TYPE_STATIC_INTERCEPT_LIST";
75 case FILTER_TYPE_DYN_INTERCEPT_LIST:
76 return "FILTER_TYPE_DYN_INTERCEPT_LIST";
77 case FILTER_TYPE_INTERCEPT_CHANNEL:
78 return "FILTER_TYPE_INTERCEPT_CHANNEL";
79 case FILTER_LAST:
80 return "FILTER_LAST";
81 default:
82 return "FILTER_UNKNOWN";
83 }
84}
85
86static const char* pf_modules_get_hook_type_string(PF_HOOK_TYPE result)
87{
88 switch (result)
89 {
90 case HOOK_TYPE_CLIENT_INIT_CONNECT:
91 return "HOOK_TYPE_CLIENT_INIT_CONNECT";
92 case HOOK_TYPE_CLIENT_UNINIT_CONNECT:
93 return "HOOK_TYPE_CLIENT_UNINIT_CONNECT";
94 case HOOK_TYPE_CLIENT_PRE_CONNECT:
95 return "HOOK_TYPE_CLIENT_PRE_CONNECT";
96 case HOOK_TYPE_CLIENT_POST_CONNECT:
97 return "HOOK_TYPE_CLIENT_POST_CONNECT";
98 case HOOK_TYPE_CLIENT_POST_DISCONNECT:
99 return "HOOK_TYPE_CLIENT_POST_DISCONNECT";
100 case HOOK_TYPE_CLIENT_REDIRECT:
101 return "HOOK_TYPE_CLIENT_REDIRECT";
102 case HOOK_TYPE_CLIENT_VERIFY_X509:
103 return "HOOK_TYPE_CLIENT_VERIFY_X509";
104 case HOOK_TYPE_CLIENT_LOGIN_FAILURE:
105 return "HOOK_TYPE_CLIENT_LOGIN_FAILURE";
106 case HOOK_TYPE_CLIENT_END_PAINT:
107 return "HOOK_TYPE_CLIENT_END_PAINT";
108 case HOOK_TYPE_SERVER_POST_CONNECT:
109 return "HOOK_TYPE_SERVER_POST_CONNECT";
110 case HOOK_TYPE_SERVER_ACTIVATE:
111 return "HOOK_TYPE_SERVER_ACTIVATE";
112 case HOOK_TYPE_SERVER_CHANNELS_INIT:
113 return "HOOK_TYPE_SERVER_CHANNELS_INIT";
114 case HOOK_TYPE_SERVER_CHANNELS_FREE:
115 return "HOOK_TYPE_SERVER_CHANNELS_FREE";
116 case HOOK_TYPE_SERVER_SESSION_END:
117 return "HOOK_TYPE_SERVER_SESSION_END";
118 case HOOK_TYPE_CLIENT_LOAD_CHANNELS:
119 return "HOOK_TYPE_CLIENT_LOAD_CHANNELS";
120 case HOOK_TYPE_SERVER_SESSION_INITIALIZE:
121 return "HOOK_TYPE_SERVER_SESSION_INITIALIZE";
122 case HOOK_TYPE_SERVER_SESSION_STARTED:
123 return "HOOK_TYPE_SERVER_SESSION_STARTED";
124 case HOOK_LAST:
125 return "HOOK_LAST";
126 default:
127 return "HOOK_TYPE_UNKNOWN";
128 }
129}
130
131static BOOL pf_modules_proxy_ArrayList_ForEachFkt(void* data, size_t index, va_list ap)
132{
133 proxyPlugin* plugin = (proxyPlugin*)data;
134 BOOL ok = FALSE;
135
136 WINPR_UNUSED(index);
137
138 PF_HOOK_TYPE type = va_arg(ap, PF_HOOK_TYPE);
139 proxyData* pdata = va_arg(ap, proxyData*);
140 void* custom = va_arg(ap, void*);
141
142 WLog_VRB(TAG, "running hook %s.%s", plugin->name, pf_modules_get_hook_type_string(type));
143
144 switch (type)
145 {
146 case HOOK_TYPE_CLIENT_INIT_CONNECT:
147 ok = IFCALLRESULT(TRUE, plugin->ClientInitConnect, plugin, pdata, custom);
148 break;
149 case HOOK_TYPE_CLIENT_UNINIT_CONNECT:
150 ok = IFCALLRESULT(TRUE, plugin->ClientUninitConnect, plugin, pdata, custom);
151 break;
152 case HOOK_TYPE_CLIENT_PRE_CONNECT:
153 ok = IFCALLRESULT(TRUE, plugin->ClientPreConnect, plugin, pdata, custom);
154 break;
155
156 case HOOK_TYPE_CLIENT_POST_CONNECT:
157 ok = IFCALLRESULT(TRUE, plugin->ClientPostConnect, plugin, pdata, custom);
158 break;
159
160 case HOOK_TYPE_CLIENT_REDIRECT:
161 ok = IFCALLRESULT(TRUE, plugin->ClientRedirect, plugin, pdata, custom);
162 break;
163
164 case HOOK_TYPE_CLIENT_POST_DISCONNECT:
165 ok = IFCALLRESULT(TRUE, plugin->ClientPostDisconnect, plugin, pdata, custom);
166 break;
167
168 case HOOK_TYPE_CLIENT_VERIFY_X509:
169 ok = IFCALLRESULT(TRUE, plugin->ClientX509Certificate, plugin, pdata, custom);
170 break;
171
172 case HOOK_TYPE_CLIENT_LOGIN_FAILURE:
173 ok = IFCALLRESULT(TRUE, plugin->ClientLoginFailure, plugin, pdata, custom);
174 break;
175
176 case HOOK_TYPE_CLIENT_END_PAINT:
177 ok = IFCALLRESULT(TRUE, plugin->ClientEndPaint, plugin, pdata, custom);
178 break;
179
180 case HOOK_TYPE_CLIENT_LOAD_CHANNELS:
181 ok = IFCALLRESULT(TRUE, plugin->ClientLoadChannels, plugin, pdata, custom);
182 break;
183
184 case HOOK_TYPE_SERVER_POST_CONNECT:
185 ok = IFCALLRESULT(TRUE, plugin->ServerPostConnect, plugin, pdata, custom);
186 break;
187
188 case HOOK_TYPE_SERVER_ACTIVATE:
189 ok = IFCALLRESULT(TRUE, plugin->ServerPeerActivate, plugin, pdata, custom);
190 break;
191
192 case HOOK_TYPE_SERVER_CHANNELS_INIT:
193 ok = IFCALLRESULT(TRUE, plugin->ServerChannelsInit, plugin, pdata, custom);
194 break;
195
196 case HOOK_TYPE_SERVER_CHANNELS_FREE:
197 ok = IFCALLRESULT(TRUE, plugin->ServerChannelsFree, plugin, pdata, custom);
198 break;
199
200 case HOOK_TYPE_SERVER_SESSION_END:
201 ok = IFCALLRESULT(TRUE, plugin->ServerSessionEnd, plugin, pdata, custom);
202 break;
203
204 case HOOK_TYPE_SERVER_SESSION_INITIALIZE:
205 ok = IFCALLRESULT(TRUE, plugin->ServerSessionInitialize, plugin, pdata, custom);
206 break;
207
208 case HOOK_TYPE_SERVER_SESSION_STARTED:
209 ok = IFCALLRESULT(TRUE, plugin->ServerSessionStarted, plugin, pdata, custom);
210 break;
211
212 case HOOK_LAST:
213 default:
214 WLog_ERR(TAG, "invalid hook called");
215 }
216
217 if (!ok)
218 {
219 WLog_INFO(TAG, "plugin %s, hook %s failed!", plugin->name,
220 pf_modules_get_hook_type_string(type));
221 return FALSE;
222 }
223 return TRUE;
224}
225
226/*
227 * runs all hooks of type `type`.
228 *
229 * @type: hook type to run.
230 * @server: pointer of server's rdpContext struct of the current session.
231 */
232BOOL pf_modules_run_hook(proxyModule* module, PF_HOOK_TYPE type, proxyData* pdata, void* custom)
233{
234 WINPR_ASSERT(module);
235 WINPR_ASSERT(module->plugins);
236 return ArrayList_ForEach(module->plugins, pf_modules_proxy_ArrayList_ForEachFkt, type, pdata,
237 custom);
238}
239
240static BOOL pf_modules_ArrayList_ForEachFkt(void* data, size_t index, va_list ap)
241{
242 proxyPlugin* plugin = (proxyPlugin*)data;
243 BOOL result = FALSE;
244
245 WINPR_UNUSED(index);
246
247 PF_FILTER_TYPE type = va_arg(ap, PF_FILTER_TYPE);
248 proxyData* pdata = va_arg(ap, proxyData*);
249 void* param = va_arg(ap, void*);
250
251 WLog_VRB(TAG, "running filter: %s", plugin->name);
252
253 switch (type)
254 {
255 case FILTER_TYPE_KEYBOARD:
256 result = IFCALLRESULT(TRUE, plugin->KeyboardEvent, plugin, pdata, param);
257 break;
258
259 case FILTER_TYPE_UNICODE:
260 result = IFCALLRESULT(TRUE, plugin->UnicodeEvent, plugin, pdata, param);
261 break;
262
263 case FILTER_TYPE_MOUSE:
264 result = IFCALLRESULT(TRUE, plugin->MouseEvent, plugin, pdata, param);
265 break;
266
267 case FILTER_TYPE_MOUSE_EX:
268 result = IFCALLRESULT(TRUE, plugin->MouseExEvent, plugin, pdata, param);
269 break;
270
271 case FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_DATA:
272 result = IFCALLRESULT(TRUE, plugin->ClientChannelData, plugin, pdata, param);
273 break;
274
275 case FILTER_TYPE_SERVER_PASSTHROUGH_CHANNEL_DATA:
276 result = IFCALLRESULT(TRUE, plugin->ServerChannelData, plugin, pdata, param);
277 break;
278
279 case FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_CREATE:
280 result = IFCALLRESULT(TRUE, plugin->ChannelCreate, plugin, pdata, param);
281 break;
282
283 case FILTER_TYPE_CLIENT_PASSTHROUGH_DYN_CHANNEL_CREATE:
284 result = IFCALLRESULT(TRUE, plugin->DynamicChannelCreate, plugin, pdata, param);
285 break;
286
287 case FILTER_TYPE_SERVER_FETCH_TARGET_ADDR:
288 result = IFCALLRESULT(TRUE, plugin->ServerFetchTargetAddr, plugin, pdata, param);
289 break;
290
291 case FILTER_TYPE_SERVER_PEER_LOGON:
292 result = IFCALLRESULT(TRUE, plugin->ServerPeerLogon, plugin, pdata, param);
293 break;
294
295 case FILTER_TYPE_INTERCEPT_CHANNEL:
296 result = IFCALLRESULT(TRUE, plugin->DynChannelIntercept, plugin, pdata, param);
297 break;
298
299 case FILTER_TYPE_DYN_INTERCEPT_LIST:
300 result = IFCALLRESULT(TRUE, plugin->DynChannelToIntercept, plugin, pdata, param);
301 break;
302
303 case FILTER_TYPE_STATIC_INTERCEPT_LIST:
304 result = IFCALLRESULT(TRUE, plugin->StaticChannelToIntercept, plugin, pdata, param);
305 break;
306
307 case FILTER_LAST:
308 default:
309 WLog_ERR(TAG, "invalid filter called");
310 }
311
312 if (!result)
313 {
314 /* current filter return FALSE, no need to run other filters. */
315 WLog_DBG(TAG, "plugin %s, filter type [%s] returned FALSE", plugin->name,
316 pf_modules_get_filter_type_string(type));
317 }
318 return result;
319}
320
321/*
322 * runs all filters of type `type`.
323 *
324 * @type: filter type to run.
325 * @server: pointer of server's rdpContext struct of the current session.
326 */
327BOOL pf_modules_run_filter(proxyModule* module, PF_FILTER_TYPE type, proxyData* pdata, void* param)
328{
329 WINPR_ASSERT(module);
330 WINPR_ASSERT(module->plugins);
331
332 return ArrayList_ForEach(module->plugins, pf_modules_ArrayList_ForEachFkt, type, pdata, param);
333}
334
335/*
336 * stores per-session data needed by a plugin.
337 *
338 * @context: current session server's rdpContext instance.
339 * @info: pointer to per-session data.
340 */
341static BOOL pf_modules_set_plugin_data(WINPR_ATTR_UNUSED proxyPluginsManager* mgr,
342 const char* plugin_name, proxyData* pdata, void* data)
343{
344 union
345 {
346 const char* ccp;
347 char* cp;
348 } ccharconv;
349
350 WINPR_ASSERT(plugin_name);
351
352 ccharconv.ccp = plugin_name;
353 if (data == NULL) /* no need to store anything */
354 return FALSE;
355
356 if (!HashTable_Insert(pdata->modules_info, ccharconv.cp, data))
357 {
358 WLog_ERR(TAG, "[%s]: HashTable_Insert failed!");
359 return FALSE;
360 }
361
362 return TRUE;
363}
364
365/*
366 * returns per-session data needed a plugin.
367 *
368 * @context: current session server's rdpContext instance.
369 * if there's no data related to `plugin_name` in `context` (current session), a NULL will be
370 * returned.
371 */
372static void* pf_modules_get_plugin_data(WINPR_ATTR_UNUSED proxyPluginsManager* mgr,
373 const char* plugin_name, proxyData* pdata)
374{
375 union
376 {
377 const char* ccp;
378 char* cp;
379 } ccharconv;
380 WINPR_ASSERT(plugin_name);
381 WINPR_ASSERT(pdata);
382 ccharconv.ccp = plugin_name;
383
384 return HashTable_GetItemValue(pdata->modules_info, ccharconv.cp);
385}
386
387static void pf_modules_abort_connect(WINPR_ATTR_UNUSED proxyPluginsManager* mgr, proxyData* pdata)
388{
389 WINPR_ASSERT(pdata);
390 WLog_DBG(TAG, "is called!");
391 proxy_data_abort_connect(pdata);
392}
393
394static BOOL pf_modules_register_ArrayList_ForEachFkt(void* data, size_t index, va_list ap)
395{
396 proxyPlugin* plugin = (proxyPlugin*)data;
397 proxyPlugin* plugin_to_register = va_arg(ap, proxyPlugin*);
398
399 WINPR_UNUSED(index);
400
401 if (strcmp(plugin->name, plugin_to_register->name) == 0)
402 {
403 WLog_ERR(TAG, "can not register plugin '%s', it is already registered!", plugin->name);
404 return FALSE;
405 }
406 return TRUE;
407}
408
409static BOOL pf_modules_register_plugin(proxyPluginsManager* mgr,
410 const proxyPlugin* plugin_to_register)
411{
412 proxyPlugin internal = { 0 };
413 proxyModule* module = (proxyModule*)mgr;
414 WINPR_ASSERT(module);
415
416 if (!plugin_to_register)
417 return FALSE;
418
419 internal = *plugin_to_register;
420 internal.mgr = mgr;
421
422 /* make sure there's no other loaded plugin with the same name of `plugin_to_register`. */
423 if (!ArrayList_ForEach(module->plugins, pf_modules_register_ArrayList_ForEachFkt, &internal))
424 return FALSE;
425
426 if (!ArrayList_Append(module->plugins, &internal))
427 {
428 WLog_ERR(TAG, "failed adding plugin to list: %s", plugin_to_register->name);
429 return FALSE;
430 }
431
432 return TRUE;
433}
434
435static BOOL pf_modules_load_ArrayList_ForEachFkt(void* data, size_t index, va_list ap)
436{
437 proxyPlugin* plugin = (proxyPlugin*)data;
438 const char* plugin_name = va_arg(ap, const char*);
439 BOOL* res = va_arg(ap, BOOL*);
440
441 WINPR_UNUSED(index);
442 WINPR_UNUSED(ap);
443 WINPR_ASSERT(res);
444
445 if (strcmp(plugin->name, plugin_name) == 0)
446 *res = TRUE;
447 return TRUE;
448}
449
450BOOL pf_modules_is_plugin_loaded(proxyModule* module, const char* plugin_name)
451{
452 BOOL rc = FALSE;
453 WINPR_ASSERT(module);
454 if (ArrayList_Count(module->plugins) < 1)
455 return FALSE;
456 if (!ArrayList_ForEach(module->plugins, pf_modules_load_ArrayList_ForEachFkt, plugin_name, &rc))
457 return FALSE;
458 return rc;
459}
460
461static BOOL pf_modules_print_ArrayList_ForEachFkt(void* data, size_t index, va_list ap)
462{
463 proxyPlugin* plugin = (proxyPlugin*)data;
464
465 WINPR_UNUSED(index);
466 WINPR_UNUSED(ap);
467
468 WLog_INFO(TAG, "\tName: %s", plugin->name);
469 WLog_INFO(TAG, "\tDescription: %s", plugin->description);
470 return TRUE;
471}
472
473void pf_modules_list_loaded_plugins(proxyModule* module)
474{
475 size_t count = 0;
476
477 WINPR_ASSERT(module);
478 WINPR_ASSERT(module->plugins);
479
480 count = ArrayList_Count(module->plugins);
481
482 if (count > 0)
483 WLog_INFO(TAG, "Loaded plugins:");
484
485 ArrayList_ForEach(module->plugins, pf_modules_print_ArrayList_ForEachFkt);
486}
487
488static BOOL pf_modules_load_static_module(const char* module_name, proxyModule* module,
489 void* userdata)
490{
491 WINPR_ASSERT(module);
492
493 HANDLE handle = GetModuleHandleA(NULL);
494
495 if (handle == NULL)
496 {
497 WLog_ERR(TAG, "failed loading static library: %s", module_name);
498 return FALSE;
499 }
500
501 char name[256] = { 0 };
502 (void)_snprintf(name, sizeof(name), "%s_%s", module_name, MODULE_ENTRY_POINT);
503 for (size_t x = 0; x < strnlen(name, sizeof(name)); x++)
504 {
505 if (name[x] == '-')
506 name[x] = '_';
507 }
508
509 proxyModuleEntryPoint pEntryPoint = GetProcAddressAs(handle, name, proxyModuleEntryPoint);
510 if (!pEntryPoint)
511 {
512 WLog_ERR(TAG, "GetProcAddress failed for static %s (module %s)", name, module_name);
513 goto error;
514 }
515 if (!ArrayList_Append(module->handles, handle))
516 {
517 WLog_ERR(TAG, "ArrayList_Append failed!");
518 goto error;
519 }
520 return pf_modules_add(module, pEntryPoint, userdata);
521
522error:
523 FreeLibrary(handle);
524 return FALSE;
525}
526
527static BOOL pf_modules_load_module(const char* module_path, const char* module_name,
528 proxyModule* module, void* userdata)
529{
530 WINPR_ASSERT(module);
531
532 if (pf_modules_load_static_module(module_name, module, userdata))
533 return TRUE;
534
535 HANDLE handle = LoadLibraryX(module_path);
536
537 if (handle == NULL)
538 {
539 WLog_ERR(TAG, "failed loading external library: %s", module_path);
540 return FALSE;
541 }
542
543 proxyModuleEntryPoint pEntryPoint =
544 GetProcAddressAs(handle, MODULE_ENTRY_POINT, proxyModuleEntryPoint);
545 if (!pEntryPoint)
546 {
547 WLog_ERR(TAG, "GetProcAddress failed while loading %s", module_path);
548 goto error;
549 }
550 if (!ArrayList_Append(module->handles, handle))
551 {
552 WLog_ERR(TAG, "ArrayList_Append failed!");
553 goto error;
554 }
555 return pf_modules_add(module, pEntryPoint, userdata);
556
557error:
558 FreeLibrary(handle);
559 return FALSE;
560}
561
562static void free_handle(void* obj)
563{
564 HANDLE handle = (HANDLE)obj;
565 if (handle)
566 FreeLibrary(handle);
567}
568
569static void free_plugin(void* obj)
570{
571 proxyPlugin* plugin = (proxyPlugin*)obj;
572 WINPR_ASSERT(plugin);
573
574 if (!IFCALLRESULT(TRUE, plugin->PluginUnload, plugin))
575 WLog_WARN(TAG, "PluginUnload failed for plugin '%s'", plugin->name);
576
577 free(plugin);
578}
579
580static void* new_plugin(const void* obj)
581{
582 const proxyPlugin* src = obj;
583 proxyPlugin* proxy = calloc(1, sizeof(proxyPlugin));
584 if (!proxy)
585 return NULL;
586 *proxy = *src;
587 return proxy;
588}
589
590proxyModule* pf_modules_new(const char* root_dir, const char** modules, size_t count)
591{
592 wObject* obj = NULL;
593 char* path = NULL;
594 proxyModule* module = calloc(1, sizeof(proxyModule));
595 if (!module)
596 return NULL;
597
598 module->mgr.RegisterPlugin = pf_modules_register_plugin;
599 module->mgr.SetPluginData = pf_modules_set_plugin_data;
600 module->mgr.GetPluginData = pf_modules_get_plugin_data;
601 module->mgr.AbortConnect = pf_modules_abort_connect;
602 module->plugins = ArrayList_New(FALSE);
603
604 if (module->plugins == NULL)
605 {
606 WLog_ERR(TAG, "ArrayList_New failed!");
607 goto error;
608 }
609 obj = ArrayList_Object(module->plugins);
610 WINPR_ASSERT(obj);
611
612 obj->fnObjectFree = free_plugin;
613 obj->fnObjectNew = new_plugin;
614
615 module->handles = ArrayList_New(FALSE);
616 if (module->handles == NULL)
617 {
618
619 WLog_ERR(TAG, "ArrayList_New failed!");
620 goto error;
621 }
622 ArrayList_Object(module->handles)->fnObjectFree = free_handle;
623
624 if (count > 0)
625 {
626 WINPR_ASSERT(root_dir);
627 if (!winpr_PathFileExists(root_dir))
628 path = GetCombinedPath(FREERDP_INSTALL_PREFIX, root_dir);
629 else
630 path = _strdup(root_dir);
631
632 if (!winpr_PathFileExists(path))
633 {
634 if (!winpr_PathMakePath(path, NULL))
635 {
636 WLog_ERR(TAG, "error occurred while creating modules directory: %s", root_dir);
637 }
638 }
639
640 if (winpr_PathFileExists(path))
641 WLog_DBG(TAG, "modules root directory: %s", path);
642
643 for (size_t i = 0; i < count; i++)
644 {
645 char name[8192] = { 0 };
646 char* fullpath = NULL;
647 (void)_snprintf(name, sizeof(name), "proxy-%s-plugin%s", modules[i],
648 FREERDP_SHARED_LIBRARY_SUFFIX);
649 fullpath = GetCombinedPath(path, name);
650 pf_modules_load_module(fullpath, modules[i], module, NULL);
651 free(fullpath);
652 }
653 }
654
655 free(path);
656 return module;
657
658error:
659 free(path);
660 pf_modules_free(module);
661 return NULL;
662}
663
664void pf_modules_free(proxyModule* module)
665{
666 if (!module)
667 return;
668
669 ArrayList_Free(module->plugins);
670 ArrayList_Free(module->handles);
671 free(module);
672}
673
674BOOL pf_modules_add(proxyModule* module, proxyModuleEntryPoint ep, void* userdata)
675{
676 WINPR_ASSERT(module);
677 WINPR_ASSERT(ep);
678
679 return ep(&module->mgr, userdata);
680}
This struct contains function pointer to initialize/free objects.
Definition collections.h:57