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