server: proxy: rewrite modules API
authorKobi Mizrachi <kmizrachi18@gmail.com>
Tue, 31 Dec 2019 09:16:12 +0000 (11:16 +0200)
committerakallabeth <akallabeth@users.noreply.github.com>
Tue, 14 Jan 2020 06:58:47 +0000 (07:58 +0100)
* Add support for C++ plugins.
* Seperate between a "module" and a "plugin": a module is a shared
library that exposes a function called "proxy_module_entry_point". This
function gets a parameter to `proxyPluginsManager*`, which can be used to
register plugins.
* Refine the API of the modules infrastructure:
 * use proxyData* instead of rdpContext* when firing a hook or calling a filter.
 * use clearer names.
* Instead of having to specify a path for each module to be loaded in
the configuration, the proxy now loads modules from specificed
directory, using the CMAKE variable PROXY_PLUGINDIR.
* Add an option to specify required plugins: plugins that the proxy
wouldn't start without having them fully loaded.

17 files changed:
CMakeLists.txt
include/freerdp/build-config.h.in
server/proxy/config.ini
server/proxy/freerdp_proxy.c
server/proxy/modules/demo/CMakeLists.txt
server/proxy/modules/demo/demo.c [deleted file]
server/proxy/modules/demo/demo.cpp [new file with mode: 0644]
server/proxy/modules/modules_api.h
server/proxy/pf_channels.c
server/proxy/pf_client.c
server/proxy/pf_config.c
server/proxy/pf_context.c
server/proxy/pf_context.h
server/proxy/pf_input.c
server/proxy/pf_modules.c
server/proxy/pf_modules.h
server/proxy/pf_server.c

index 6c20267..91f4906 100644 (file)
@@ -982,6 +982,14 @@ set(FREERDP_ADDIN_PATH "${FREERDP_PLUGIN_PATH}")
 # Path to put extensions
 set(FREERDP_EXTENSION_PATH "${CMAKE_INSTALL_FULL_LIBDIR}/freerdp${FREERDP_VERSION_MAJOR}/extensions")
 
+# Proxy plugins path
+if(NOT DEFINED PROXY_PLUGINDIR)
+       message("using default plugins location")
+       set(FREERDP_PROXY_PLUGINDIR "${CMAKE_BINARY_DIR}/server/proxy/plugins")
+else()
+       set(FREERDP_PROXY_PLUGINDIR "${PROXY_PLUGINDIR}")
+endif()
+
 # Include directories
 include_directories(${CMAKE_CURRENT_BINARY_DIR})
 include_directories(${CMAKE_CURRENT_BINARY_DIR}/include)
index 261b8de..362429a 100644 (file)
@@ -17,4 +17,6 @@
 #define FREERDP_VENDOR_STRING "${VENDOR}"
 #define FREERDP_PRODUCT_STRING "${PRODUCT}"
 
+#define FREERDP_PROXY_PLUGINDIR "${FREERDP_PROXY_PLUGINDIR}"
+
 #endif /* FREERDP_BUILD_CONFIG_H */
index 2bcc5a1..2fe0501 100644 (file)
@@ -40,5 +40,5 @@ RemoteApp = TRUE
 TextOnly = FALSE
 MaxTextLength = 10 # 0 for no limit.
 
-[Modules]
-Demo = "modules/demo/libdemo.so"
+[Plugins]
+Required = "demo"
index df60aa7..a90b81f 100644 (file)
@@ -24,6 +24,7 @@
 #include "pf_log.h"
 #include "pf_modules.h"
 
+#include <freerdp/build-config.h>
 #include <winpr/collections.h>
 
 #define TAG PROXY_TAG("server")
@@ -40,8 +41,13 @@ int main(int argc, char* argv[])
        if (argc > 1)
                cfg = argv[1];
 
-       if (!pf_modules_init())
+       if (!pf_modules_init(FREERDP_PROXY_PLUGINDIR))
+       {
+               WLog_ERR(TAG, "failed to initialize proxy plugins!");
                goto fail;
+       }
+
+       pf_modules_list_loaded_plugins();
 
        if (!pf_server_config_load(cfg, config))
                goto fail;
index 849461e..ceadab4 100644 (file)
@@ -1,4 +1,29 @@
+#
+# FreeRDP: A Remote Desktop Protocol Implementation
+# FreeRDP Proxy Server Demo C++ Module
+#
+# Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
 
-add_library(demo SHARED
-       demo.c
+set(PLUGIN_NAME "proxy-demo-plugin")
+
+add_library(${PLUGIN_NAME} MODULE
+       demo.cpp
 )
+
+set_target_properties(${PLUGIN_NAME} PROPERTIES PREFIX "")
+set_target_properties(${PLUGIN_NAME} PROPERTIES NO_SONAME 1)
+set_target_properties(${PLUGIN_NAME} PROPERTIES
+LIBRARY_OUTPUT_DIRECTORY "${FREERDP_PROXY_PLUGINDIR}")
diff --git a/server/proxy/modules/demo/demo.c b/server/proxy/modules/demo/demo.c
deleted file mode 100644 (file)
index 0c70030..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/**
- * FreeRDP: A Remote Desktop Protocol Implementation
- * FreeRDP Proxy Server
- *
- * Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-
-#include "modules_api.h"
-
-static BOOL demo_filter_keyboard_event(moduleOperations* module, rdpContext* context, void* param)
-{
-       proxyKeyboardEventInfo* event_data = (proxyKeyboardEventInfo*)param;
-       WINPR_UNUSED(event_data);
-
-       return TRUE;
-}
-
-static BOOL demo_filter_mouse_event(moduleOperations* module, rdpContext* context, void* param)
-{
-       proxyMouseEventInfo* event_data = (proxyMouseEventInfo*)param;
-
-       if (event_data->x % 100 == 0)
-       {
-               printf("filter_demo: mouse x is currently %" PRIu16 "\n", event_data->x);
-       }
-
-       return TRUE;
-}
-
-BOOL module_init(moduleOperations* module)
-{
-       module->KeyboardEvent = demo_filter_keyboard_event;
-       module->MouseEvent = demo_filter_mouse_event;
-
-       return TRUE;
-}
-
-BOOL module_exit(moduleOperations* module)
-{
-       printf("bye bye\n");
-
-       return TRUE;
-}
diff --git a/server/proxy/modules/demo/demo.cpp b/server/proxy/modules/demo/demo.cpp
new file mode 100644 (file)
index 0000000..f105e29
--- /dev/null
@@ -0,0 +1,71 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * FreeRDP Proxy Server Demo C++ Module
+ *
+ * Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <iostream>
+
+#include "modules_api.h"
+
+#define TAG MODULE_TAG("demo")
+
+static constexpr char plugin_name[] = "demo";
+static constexpr char plugin_desc[] = "this is a test plugin";
+
+static proxyPluginsManager* g_plugins_manager = NULL;
+
+static BOOL demo_filter_keyboard_event(proxyData* pdata, void* param)
+{
+       auto event_data = static_cast<proxyKeyboardEventInfo*>(param);
+       if (event_data == NULL)
+               return FALSE;
+
+       if (event_data->rdp_scan_code == RDP_SCANCODE_KEY_B)
+       {
+               /* user typed 'B', that means bye :) */
+               std::cout << "C++ demo plugin: aborting connection" << std::endl;
+               g_plugins_manager->AbortConnect(pdata);
+       }
+
+       return TRUE;
+}
+
+static BOOL demo_plugin_unload()
+{
+       std::cout << "C++ demo plugin: unloading..." << std::endl;
+       return TRUE;
+}
+
+static proxyPlugin demo_plugin = {
+       plugin_name,                /* name */
+       plugin_desc,                /* description */
+       NULL,                       /* ClientPreConnect */
+       NULL,                       /* ClientLoginFailure */
+       NULL,                       /* ServerPostConnect */
+       NULL,                       /* ServerChannelsInit */
+       NULL,                       /* ServerChannelsFree */
+       demo_filter_keyboard_event, /* KeyboardEvent */
+       NULL,                       /* MouseEvent */
+       demo_plugin_unload          /* PluginUnload */
+};
+
+BOOL proxy_module_entry_point(proxyPluginsManager* plugins_manager)
+{
+       g_plugins_manager = plugins_manager;
+
+       return plugins_manager->RegisterPlugin(&demo_plugin);
+}
index 3759562..7330169 100644 (file)
 #include <freerdp/freerdp.h>
 #include <winpr/winpr.h>
 
-#define PROXY_API FREERDP_API
+#include "../pf_context.h"
 
-typedef struct module_operations moduleOperations;
+#define MODULE_TAG(module) "proxy.modules." module
 
-/* used for filtering */
-typedef BOOL (*proxyFilterFn)(moduleOperations*, rdpContext*, void*);
-
-/* used for hooks */
-typedef BOOL (*proxyHookFn)(moduleOperations*, rdpContext*);
-
-/*
- * used for per-session info.
- *
- * each module is allowed to store data per session.
- * it is useful, for example, when a module wants to create a server channel in runtime,
- * or to save information about a session (for example, the count of mouse clicks in the last
- * minute), and then do something with this information (maybe abort the connection).
- */
-typedef BOOL (*moduleSetSessionData)(moduleOperations*, rdpContext*, void*);
-typedef void* (*moduleGetSessionData)(moduleOperations*, rdpContext*);
+/* hook callback. should return TRUE on success or FALSE on error. */
+typedef BOOL (*proxyHookFn)(proxyData*);
 
 /*
- * used for connection management. when a module wants to forcibly close a connection, it should
- * call this method.
+ * Filter callback:
+ *     It MUST return TRUE if the related event should be proxied,
+ *     or FALSE if it should be ignored.
  */
-typedef void (*moduleAbortConnect)(moduleOperations*, rdpContext*);
-
-typedef struct connection_info connectionInfo;
-typedef struct proxy_keyboard_event_info proxyKeyboardEventInfo;
-typedef struct proxy_mouse_event_info proxyMouseEventInfo;
+typedef BOOL (*proxyFilterFn)(proxyData*, void*);
 
-/* represents a set of operations that a module can do */
-struct module_operations
+/* describes a plugin: name, description and callbacks to execute. */
+typedef struct proxy_plugin
 {
-       /* per-session API. a module must not change these function pointers. */
-       moduleSetSessionData SetSessionData;
-       moduleGetSessionData GetSessionData;
-       moduleAbortConnect AbortConnect;
+       const char* name;        /* unique module name */
+       const char* description; /* module description */
 
-       /* proxy hooks. a module can set these function pointers to register hooks. */
+       /* proxy hooks. a module can set these function pointers to register hooks */
        proxyHookFn ClientPreConnect;
+       proxyHookFn ClientLoginFailure;
+       proxyHookFn ServerPostConnect;
        proxyHookFn ServerChannelsInit;
        proxyHookFn ServerChannelsFree;
 
-       /* proxy filters a module can set these function pointers to register filters. */
+       /* proxy filters. a module can set these function pointers to register filters */
        proxyFilterFn KeyboardEvent;
        proxyFilterFn MouseEvent;
-};
+
+       BOOL (*PluginUnload)();
+} proxyPlugin;
+
+/*
+ * Main API for use by external modules.
+ * Supports:
+ *  - Registering a plugin.
+ *  - Setting/getting plugin's per-session specific data.
+ *  - Aborting a session.
+ */
+typedef struct proxy_plugins_manager
+{
+       /* used for registering a fresh new proxy plugin. */
+       BOOL (*RegisterPlugin)(proxyPlugin* plugin);
+
+       /* used for setting plugin's per-session info. */
+       BOOL (*SetPluginData)(const char*, proxyData*, void*);
+
+       /* used for getting plugin's per-session info. */
+       void* (*GetPluginData)(const char*, proxyData*);
+
+       /* used for aborting a session. */
+       void (*AbortConnect)(proxyData*);
+} proxyPluginsManager;
 
 /* filter events parameters */
 #define WINPR_PACK_PUSH
 #include <winpr/pack.h>
-struct proxy_keyboard_event_info
+typedef struct proxy_keyboard_event_info
 {
        UINT16 flags;
        UINT16 rdp_scan_code;
-};
+} proxyKeyboardEventInfo;
 
-struct proxy_mouse_event_info
+typedef struct proxy_mouse_event_info
 {
        UINT16 flags;
        UINT16 x;
        UINT16 y;
-};
+} proxyMouseEventInfo;
 #define WINPR_PACK_POP
 #include <winpr/pack.h>
 
-/*
- * these two functions must be implemented by any proxy module.
- * module_init: used for module initialization, hooks and filters registration.
- */
-PROXY_API BOOL module_init(moduleOperations* module);
-PROXY_API BOOL module_exit(moduleOperations* module);
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+       FREERDP_API BOOL proxy_module_entry_point(proxyPluginsManager* plugins_manager);
+
+#ifdef __cplusplus
+};
+#endif
 
 #endif /* FREERDP_SERVER_PROXY_MODULES_API_H */
index 073380b..08fd965 100644 (file)
@@ -230,7 +230,7 @@ BOOL pf_server_channels_init(pServerContext* ps)
                        return FALSE;
        }
 
-       return pf_modules_run_hook(HOOK_TYPE_SERVER_CHANNELS_INIT, context);
+       return pf_modules_run_hook(HOOK_TYPE_SERVER_CHANNELS_INIT, ps->pdata);
 }
 
 void pf_server_channels_free(pServerContext* ps)
@@ -265,5 +265,5 @@ void pf_server_channels_free(pServerContext* ps)
                ps->rail = NULL;
        }
 
-       pf_modules_run_hook(HOOK_TYPE_SERVER_CHANNELS_FREE, (rdpContext*)ps);
+       pf_modules_run_hook(HOOK_TYPE_SERVER_CHANNELS_FREE, ps->pdata);
 }
index 8c9f873..c834c9d 100644 (file)
@@ -343,6 +343,7 @@ static BOOL pf_client_connect(freerdp* instance)
                        goto out;
 
                LOG_ERR(TAG, pc, "failed to connect with NLA. retrying to connect without NLA");
+               pf_modules_run_hook(HOOK_TYPE_CLIENT_LOGIN_FAILURE, pc->pdata);
 
                if (!pf_client_connect_without_nla(pc))
                {
@@ -366,7 +367,6 @@ static DWORD WINAPI pf_client_thread_proc(LPVOID arg)
 {
        freerdp* instance = (freerdp*)arg;
        pClientContext* pc = (pClientContext*)instance->context;
-       pServerContext* ps = pc->pdata->ps;
        proxyData* pdata = pc->pdata;
        DWORD nCount;
        DWORD status;
@@ -381,7 +381,7 @@ static DWORD WINAPI pf_client_thread_proc(LPVOID arg)
         */
        handles[64] = pdata->abort_event;
 
-       if (!pf_modules_run_hook(HOOK_TYPE_CLIENT_PRE_CONNECT, (rdpContext*)ps))
+       if (!pf_modules_run_hook(HOOK_TYPE_CLIENT_PRE_CONNECT, pdata))
        {
                proxy_data_abort_connect(pdata);
                return FALSE;
@@ -399,7 +399,7 @@ static DWORD WINAPI pf_client_thread_proc(LPVOID arg)
 
                if (nCount == 0)
                {
-                       LOG_ERR(TAG, ps, "freerdp_get_event_handles failed!");
+                       LOG_ERR(TAG, pc, "freerdp_get_event_handles failed!");
                        break;
                }
 
index b411275..b208e82 100644 (file)
@@ -99,7 +99,7 @@ const char* pf_config_get_str(wIniFile* ini, const char* section, const char* ke
 
        if (!value)
        {
-               WLog_ERR(TAG, "[%s]: key '%s.%s' not found.", __FUNCTION__, key, section);
+               WLog_ERR(TAG, "[%s]: key '%s.%s' not found.", __FUNCTION__, section, key);
                return NULL;
        }
 
@@ -188,31 +188,27 @@ static BOOL pf_config_load_clipboard(wIniFile* ini, proxyConfig* config)
 
 static BOOL pf_config_load_modules(wIniFile* ini, proxyConfig* config)
 {
-       int index;
-       int modules_count = 0;
-       char** module_names = NULL;
+       const char* required_modules;
+       char* tmp;
 
-       module_names = IniFile_GetSectionKeyNames(ini, "Modules", &modules_count);
-
-       for (index = 0; index < modules_count; index++)
-       {
-               char* module_name = module_names[index];
-               const char* path = pf_config_get_str(ini, "Modules", module_name);
+       /* make sure that all required modules are loaded */
+       required_modules = IniFile_GetKeyValueString(ini, "Plugins", "Required");
+       if (!required_modules)
+               return TRUE;
 
-               if (!path)
-                       continue;
+       tmp = strtok((char*)required_modules, ",");
 
-               if (!pf_modules_register_new(path, module_name))
+       while (tmp != NULL)
+       {
+               if (!pf_modules_is_plugin_loaded(tmp))
                {
-                       WLog_ERR(TAG, "pf_config_load_modules(): failed to register %s (%s)", module_name,
-                                path);
-                       continue;
+                       WLog_ERR(TAG, "Required plugin '%s' is not loaded. stopping.", tmp);
+                       return FALSE;
                }
 
-               WLog_INFO(TAG, "module '%s' is loaded!", module_name);
+               tmp = strtok(NULL, ",");
        }
 
-       free(module_names);
        return TRUE;
 }
 
index 76e1a02..ac002b3 100644 (file)
 static BOOL client_to_proxy_context_new(freerdp_peer* client, pServerContext* context)
 {
        context->dynvcReady = NULL;
-       context->modules_info = NULL;
-
-       context->modules_info = HashTable_New(TRUE);
-       if (!context->modules_info)
-               return FALSE;
 
        context->vcm = WTSOpenServerA((LPSTR)client->context);
 
@@ -46,7 +41,6 @@ static BOOL client_to_proxy_context_new(freerdp_peer* client, pServerContext* co
        return TRUE;
 
 error:
-       HashTable_Free(context->modules_info);
        WTSCloseServer((HANDLE)context->vcm);
        context->vcm = NULL;
 
@@ -74,8 +68,6 @@ static void client_to_proxy_context_free(freerdp_peer* client, pServerContext* c
                CloseHandle(context->dynvcReady);
                context->dynvcReady = NULL;
        }
-
-       HashTable_Free(context->modules_info);
 }
 
 BOOL pf_context_init_server_context(freerdp_peer* client)
@@ -198,6 +190,17 @@ proxyData* proxy_data_new(void)
                return NULL;
        }
 
+       if (!(pdata->modules_info = HashTable_New(FALSE)))
+       {
+               proxy_data_free(pdata);
+               return NULL;
+       }
+
+       /* modules_info maps between plugin name to custom data */
+       pdata->modules_info->hash = HashTable_StringHash;
+       pdata->modules_info->keyCompare = HashTable_StringCompare;
+       pdata->modules_info->keyClone = HashTable_StringClone;
+       pdata->modules_info->keyFree = HashTable_StringFree;
        return pdata;
 }
 
@@ -238,6 +241,9 @@ void proxy_data_free(proxyData* pdata)
        if (pdata->session_id)
                free(pdata->session_id);
 
+       if (pdata->modules_info)
+               HashTable_Free(pdata->modules_info);
+
        free(pdata);
 }
 
index 201e406..597c84c 100644 (file)
@@ -56,9 +56,6 @@ struct p_server_context
        DispServerContext* disp;
        CliprdrServerContext* cliprdr;
        RdpsndServerContext* rdpsnd;
-
-       /* used to external modules to store per-session info */
-       wHashTable* modules_info;
 };
 typedef struct p_server_context pServerContext;
 
@@ -110,6 +107,9 @@ struct proxy_data
        HANDLE gfx_server_ready;
 
        char* session_id;
+
+       /* used to external modules to store per-session info */
+       wHashTable* modules_info;
 };
 
 BOOL pf_context_copy_settings(rdpSettings* dst, const rdpSettings* src);
index 156548d..2a9baaa 100644 (file)
@@ -43,7 +43,7 @@ static BOOL pf_server_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code)
        event.flags = flags;
        event.rdp_scan_code = code;
 
-       if (pf_modules_run_filter(FILTER_TYPE_KEYBOARD, input->context, &event))
+       if (pf_modules_run_filter(FILTER_TYPE_KEYBOARD, pc->pdata, &event))
                return freerdp_input_send_keyboard_event(pc->context.input, flags, code);
 
        return TRUE;
@@ -75,7 +75,7 @@ static BOOL pf_server_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT1
        event.x = x;
        event.y = y;
 
-       if (pf_modules_run_filter(FILTER_TYPE_MOUSE, input->context, &event))
+       if (pf_modules_run_filter(FILTER_TYPE_MOUSE, pc->pdata, &event))
                return freerdp_input_send_mouse_event(pc->context.input, flags, x, y);
 
        return TRUE;
index f4e8278..fa528bc 100644 (file)
 
 #include <assert.h>
 
+#include <winpr/file.h>
 #include <winpr/wlog.h>
 #include <winpr/library.h>
 #include <freerdp/api.h>
+#include <freerdp/build-config.h>
 
 #include "pf_log.h"
 #include "pf_modules.h"
 
 #define TAG PROXY_TAG("modules")
 
-#define MODULE_INIT_METHOD "module_init"
-#define MODULE_EXIT_METHOD "module_exit"
+#define MODULE_ENTRY_POINT "proxy_module_entry_point"
 
-static modules_list* proxy_modules = NULL;
+static wArrayList* plugins_list = NULL; /* list of all loaded plugins */
+static wArrayList* handles_list = NULL; /* list of module handles to free at shutdown */
 
-/* module init/exit methods */
-typedef BOOL (*moduleInitFn)(moduleOperations* ops);
-typedef BOOL (*moduleExitFn)(moduleOperations* ops);
+typedef BOOL (*moduleEntryPoint)(proxyPluginsManager* plugins_manager);
 
 static const char* FILTER_TYPE_STRINGS[] = {
        "KEYBOARD_EVENT",
@@ -45,14 +45,13 @@ static const char* FILTER_TYPE_STRINGS[] = {
 };
 
 static const char* HOOK_TYPE_STRINGS[] = {
-       "CLIENT_PRE_CONNECT",
-       "SERVER_CHANNELS_INIT",
-       "SERVER_CHANNELS_FREE",
+       "CLIENT_PRE_CONNECT",   "CLIENT_LOGIN_FAILURE", "SERVER_POST_CONNECT",
+       "SERVER_CHANNELS_INIT", "SERVER_CHANNELS_FREE",
 };
 
 static const char* pf_modules_get_filter_type_string(PF_FILTER_TYPE result)
 {
-       if (result >= FILTER_TYPE_KEYBOARD && result <= FILTER_TYPE_MOUSE)
+       if (result >= FILTER_TYPE_KEYBOARD && result < FILTER_LAST)
                return FILTER_TYPE_STRINGS[result];
        else
                return "FILTER_UNKNOWN";
@@ -60,65 +59,57 @@ static const char* pf_modules_get_filter_type_string(PF_FILTER_TYPE result)
 
 static const char* pf_modules_get_hook_type_string(PF_HOOK_TYPE result)
 {
-       if (result >= HOOK_TYPE_CLIENT_PRE_CONNECT && result <= HOOK_TYPE_SERVER_CHANNELS_FREE)
+       if (result >= HOOK_TYPE_CLIENT_PRE_CONNECT && result < HOOK_LAST)
                return HOOK_TYPE_STRINGS[result];
        else
                return "HOOK_UNKNOWN";
 }
 
-BOOL pf_modules_init(void)
-{
-       proxy_modules = ArrayList_New(FALSE);
-
-       if (proxy_modules == NULL)
-       {
-               WLog_ERR(TAG, "pf_modules_init(): ArrayList_New failed!");
-               return FALSE;
-       }
-
-       return TRUE;
-}
-
 /*
  * runs all hooks of type `type`.
  *
  * @type: hook type to run.
  * @server: pointer of server's rdpContext struct of the current session.
  */
-BOOL pf_modules_run_hook(PF_HOOK_TYPE type, rdpContext* context)
+BOOL pf_modules_run_hook(PF_HOOK_TYPE type, proxyData* pdata)
 {
-
-       proxyModule* module;
-       moduleOperations* ops;
        BOOL ok = TRUE;
-       const size_t count = (size_t)ArrayList_Count(proxy_modules);
        size_t index;
+       proxyPlugin* plugin;
 
-       for (index = 0; index < count; index++)
+       ArrayList_ForEach(plugins_list, proxyPlugin*, index, plugin)
        {
-               module = (proxyModule*)ArrayList_GetItem(proxy_modules, index);
-               ops = module->ops;
-               WLog_VRB(TAG, "[%s]: Running module %s, hook %s", __FUNCTION__, module->name,
-                        pf_modules_get_hook_type_string(type));
+               WLog_VRB(TAG, "running hook %s.%s", plugin->name, pf_modules_get_hook_type_string(type));
 
                switch (type)
                {
                        case HOOK_TYPE_CLIENT_PRE_CONNECT:
-                               IFCALLRET(ops->ClientPreConnect, ok, ops, context);
+                               IFCALLRET(plugin->ClientPreConnect, ok, pdata);
+                               break;
+
+                       case HOOK_TYPE_CLIENT_LOGIN_FAILURE:
+                               IFCALLRET(plugin->ClientLoginFailure, ok, pdata);
+                               break;
+
+                       case HOOK_TYPE_SERVER_POST_CONNECT:
+                               IFCALLRET(plugin->ServerPostConnect, ok, pdata);
                                break;
 
                        case HOOK_TYPE_SERVER_CHANNELS_INIT:
-                               IFCALLRET(ops->ServerChannelsInit, ok, ops, context);
+                               IFCALLRET(plugin->ServerChannelsInit, ok, pdata);
                                break;
 
                        case HOOK_TYPE_SERVER_CHANNELS_FREE:
-                               IFCALLRET(ops->ServerChannelsFree, ok, ops, context);
+                               IFCALLRET(plugin->ServerChannelsFree, ok, pdata);
                                break;
+
+                       default:
+                               WLog_ERR(TAG, "invalid hook called");
                }
 
                if (!ok)
                {
-                       WLog_INFO(TAG, "Module %s, hook %s failed!", module->name,
+                       WLog_INFO(TAG, "plugin %s, hook %s failed!", plugin->name,
                                  pf_modules_get_hook_type_string(type));
                        return FALSE;
                }
@@ -133,35 +124,34 @@ BOOL pf_modules_run_hook(PF_HOOK_TYPE type, rdpContext* context)
  * @type: filter type to run.
  * @server: pointer of server's rdpContext struct of the current session.
  */
-BOOL pf_modules_run_filter(PF_FILTER_TYPE type, rdpContext* server, void* param)
+BOOL pf_modules_run_filter(PF_FILTER_TYPE type, proxyData* pdata, void* param)
 {
-       proxyModule* module;
-       moduleOperations* ops;
        BOOL result = TRUE;
-       const size_t count = (size_t)ArrayList_Count(proxy_modules);
        size_t index;
+       proxyPlugin* plugin;
 
-       for (index = 0; index < count; index++)
+       ArrayList_ForEach(plugins_list, proxyPlugin*, index, plugin)
        {
-               module = (proxyModule*)ArrayList_GetItem(proxy_modules, index);
-               ops = module->ops;
-               WLog_VRB(TAG, "[%s]: running filter: %s", __FUNCTION__, module->name);
+               WLog_VRB(TAG, "[%s]: running filter: %s", __FUNCTION__, plugin->name);
 
                switch (type)
                {
                        case FILTER_TYPE_KEYBOARD:
-                               IFCALLRET(ops->KeyboardEvent, result, ops, server, param);
+                               IFCALLRET(plugin->KeyboardEvent, result, pdata, param);
                                break;
 
                        case FILTER_TYPE_MOUSE:
-                               IFCALLRET(ops->MouseEvent, result, ops, server, param);
+                               IFCALLRET(plugin->MouseEvent, result, pdata, param);
                                break;
+
+                       default:
+                               WLog_ERR(TAG, "invalid filter called");
                }
 
                if (!result)
                {
                        /* current filter return FALSE, no need to run other filters. */
-                       WLog_INFO(TAG, "module %s, filter type [%s] returned FALSE", module->name,
+                       WLog_INFO(TAG, "plugin %s, filter type [%s] returned FALSE", plugin->name,
                                  pf_modules_get_filter_type_string(type));
                        return result;
                }
@@ -171,180 +161,238 @@ BOOL pf_modules_run_filter(PF_FILTER_TYPE type, rdpContext* server, void* param)
        return TRUE;
 }
 
-static void pf_modules_module_free(proxyModule* module)
+/*
+ * stores per-session data needed by a plugin.
+ *
+ * @context: current session server's rdpContext instance.
+ * @info: pointer to per-session data.
+ */
+static BOOL pf_modules_set_plugin_data(const char* plugin_name, proxyData* pdata, void* data)
 {
-       moduleExitFn exitFn;
-
-       assert(module);
-       assert(module->handle);
+       assert(plugin_name);
 
-       exitFn = (moduleExitFn)GetProcAddress(module->handle, MODULE_EXIT_METHOD);
+       if (data == NULL) /* no need to store anything */
+               return FALSE;
 
-       if (!exitFn)
+       if (HashTable_Add(pdata->modules_info, (void*)plugin_name, data) < 0)
        {
-               WLog_ERR(TAG, "[%s]: GetProcAddress module_exit for %s failed!", __FUNCTION__,
-                        module->name);
-       }
-       else
-       {
-               if (!exitFn(module->ops))
-               {
-                       WLog_ERR(TAG, "[%s]: module_exit failed for %s!", __FUNCTION__, module->name);
-               }
+               WLog_ERR(TAG, "[%s]: HashTable_Add failed!");
+               return FALSE;
        }
 
-       FreeLibrary(module->handle);
-       module->handle = NULL;
-
-       free(module->name);
-       free(module->ops);
-       free(module);
+       return TRUE;
 }
 
-void pf_modules_free(void)
+/*
+ * returns per-session data needed a plugin.
+ *
+ * @context: current session server's rdpContext instance.
+ * if there's no data related to `plugin_name` in `context` (current session), a NULL will be
+ * returned.
+ */
+static void* pf_modules_get_plugin_data(const char* plugin_name, proxyData* pdata)
 {
-       size_t index, count;
+       assert(plugin_name);
+       assert(pdata);
 
-       if (proxy_modules == NULL)
-               return;
-
-       count = (size_t)ArrayList_Count(proxy_modules);
-
-       for (index = 0; index < count; index++)
-       {
-               proxyModule* module = (proxyModule*)ArrayList_GetItem(proxy_modules, index);
-               WLog_INFO(TAG, "[%s]: freeing module: %s", __FUNCTION__, module->name);
-               pf_modules_module_free(module);
-       }
+       return HashTable_GetItemValue(pdata->modules_info, (void*)plugin_name);
+}
 
-       ArrayList_Free(proxy_modules);
+static void pf_modules_abort_connect(proxyData* pdata)
+{
+       assert(pdata);
+       WLog_DBG(TAG, "%s is called!", __FUNCTION__);
+       proxy_data_abort_connect(pdata);
 }
 
-/*
- * stores per-session data needed by module.
- *
- * @context: current session server's rdpContext instance.
- * @info: pointer to per-session data.
- */
-static BOOL pf_modules_set_session_data(moduleOperations* module, rdpContext* context, void* data)
+static BOOL pf_modules_register_plugin(proxyPlugin* plugin_to_register)
 {
-       pServerContext* ps;
+       size_t index;
+       proxyPlugin* plugin;
 
-       assert(module);
-       assert(context);
+       assert(plugins_list != NULL);
 
-       if (data == NULL) /* no need to store anything */
-               return FALSE;
+       /* make sure there's no other loaded plugin with the same name of `plugin_to_register`. */
+       ArrayList_ForEach(plugins_list, proxyPlugin*, index, plugin)
+       {
+               if (strcmp(plugin->name, plugin_to_register->name) == 0)
+               {
+                       WLog_ERR(TAG, "can not register plugin '%s', it is already registered!");
+                       return FALSE;
+               }
+       }
 
-       ps = (pServerContext*)context;
-       if (HashTable_Add(ps->modules_info, (void*)module, data) < 0)
+       if (ArrayList_Add(plugins_list, plugin_to_register) < 0)
        {
-               WLog_ERR(TAG, "[%s]: HashTable_Add failed!");
+               WLog_ERR(TAG, "[%s]: failed adding plugin to list: %s", __FUNCTION__, plugin->name);
                return FALSE;
        }
 
        return TRUE;
 }
 
-/*
- * returns per-session data needed by module.
- *
- * @context: current session server's rdpContext instance.
- * if there's no data related to `module` in `context` (current session), a NULL will be returned.
- */
-static void* pf_modules_get_session_data(moduleOperations* module, rdpContext* context)
+BOOL pf_modules_is_plugin_loaded(const char* plugin_name)
 {
-       pServerContext* ps;
+       size_t i;
+       proxyPlugin* plugin;
+
+       if (plugins_list == NULL)
+               return FALSE;
 
-       assert(module);
-       assert(context);
+       ArrayList_ForEach(plugins_list, proxyPlugin*, i, plugin)
+       {
+               if (strcmp(plugin->name, plugin_name) == 0)
+                       return TRUE;
+       }
 
-       ps = (pServerContext*)context;
-       return HashTable_GetItemValue(ps->modules_info, module);
+       return FALSE;
 }
 
-static void pf_modules_abort_connect(moduleOperations* module, rdpContext* context)
+void pf_modules_list_loaded_plugins(void)
 {
-       pServerContext* ps;
+       size_t count;
+       size_t i;
+       proxyPlugin* plugin;
 
-       assert(module);
-       assert(context);
+       if (plugins_list == NULL)
+               return;
 
-       WLog_INFO(TAG, "%s is called!", __FUNCTION__);
+       count = (size_t)ArrayList_Count(plugins_list);
 
-       ps = (pServerContext*)context;
-       proxy_data_abort_connect(ps->pdata);
+       if (count > 0)
+               WLog_INFO(TAG, "Loaded plugins:");
+
+       ArrayList_ForEach(plugins_list, proxyPlugin*, i, plugin)
+       {
+
+               WLog_INFO(TAG, "\tName: %s", plugin->name);
+               WLog_INFO(TAG, "\tDescription: %s", plugin->description);
+       }
 }
 
-BOOL pf_modules_register_new(const char* module_path, const char* module_name)
+static proxyPluginsManager plugins_manager = { pf_modules_register_plugin,
+                                                  pf_modules_set_plugin_data,
+                                                  pf_modules_get_plugin_data,
+                                                  pf_modules_abort_connect };
+
+static BOOL pf_modules_load_module(const char* module_path)
 {
-       moduleOperations* ops = NULL;
-       proxyModule* module = NULL;
        HMODULE handle = NULL;
-       moduleInitFn fn;
-
-       assert(proxy_modules != NULL);
+       moduleEntryPoint pEntryPoint;
        handle = LoadLibraryA(module_path);
 
        if (handle == NULL)
        {
-               WLog_ERR(TAG, "pf_modules_register_new(): failed loading external module: %s", module_path);
+               WLog_ERR(TAG, "[%s]: failed loading external library: %s", __FUNCTION__, module_path);
                return FALSE;
        }
 
-       if (!(fn = (moduleInitFn)GetProcAddress(handle, MODULE_INIT_METHOD)))
+       if (!(pEntryPoint = (moduleEntryPoint)GetProcAddress(handle, MODULE_ENTRY_POINT)))
        {
-               WLog_ERR(TAG, "pf_modules_register_new(): GetProcAddress failed while loading %s",
-                        module_path);
+               WLog_ERR(TAG, "[%s]: GetProcAddress failed while loading %s", __FUNCTION__, module_path);
                goto error;
        }
 
-       module = (proxyModule*)calloc(1, sizeof(proxyModule));
-
-       if (module == NULL)
+       if (!pEntryPoint(&plugins_manager))
        {
-               WLog_ERR(TAG, "pf_modules_register_new(): malloc failed");
+               WLog_ERR(TAG, "[%s]: module %s entry point failed!", __FUNCTION__, module_path);
                goto error;
        }
 
-       ops = calloc(1, sizeof(moduleOperations));
+       /* save module handle for freeing the module later */
+       if (ArrayList_Add(handles_list, handle) < 0)
+       {
+               WLog_ERR(TAG, "ArrayList_Add failed!");
+               return FALSE;
+       }
+
+       return TRUE;
 
-       if (ops == NULL)
+error:
+       FreeLibrary(handle);
+       return FALSE;
+}
+
+BOOL pf_modules_init(const char* modules_directory)
+{
+       WIN32_FIND_DATA ffd;
+       HANDLE hFind = INVALID_HANDLE_VALUE;
+
+       WLog_DBG(TAG, "Searching plugins in directory %s", modules_directory);
+
+       hFind = FindFirstFile(modules_directory, &ffd);
+
+       if (INVALID_HANDLE_VALUE == hFind)
        {
-               WLog_ERR(TAG, "pf_modules_register_new(): calloc moduleOperations failed");
-               goto error;
+               WLog_ERR(TAG, "FindFirstFile failed!");
+               return FALSE;
        }
 
-       ops->AbortConnect = pf_modules_abort_connect;
-       ops->SetSessionData = pf_modules_set_session_data;
-       ops->GetSessionData = pf_modules_get_session_data;
+       plugins_list = ArrayList_New(FALSE);
 
-       if (!fn(ops))
+       if (plugins_list == NULL)
        {
-               WLog_ERR(TAG, "pf_modules_register_new(): failed to initialize module %s", module_path);
-               goto error;
+               WLog_ERR(TAG, "[%s]: ArrayList_New failed!", __FUNCTION__);
+               return FALSE;
        }
 
-       module->name = _strdup(module_name);
-       if (!module->name)
+       handles_list = ArrayList_New(FALSE);
+       if (handles_list == NULL)
        {
-               WLog_ERR(TAG, "pf_modules_register_new(): _strdup failed while loading %s", module_path);
-               goto error;
+               ArrayList_Free(plugins_list);
+               plugins_list = NULL;
+
+               WLog_ERR(TAG, "[%s]: ArrayList_New failed!", __FUNCTION__);
+               return FALSE;
        }
 
-       module->handle = handle;
-       module->ops = ops;
-       module->enabled = TRUE;
+       do
+       {
+               if ((ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
+               {
+                       char* fullpath = GetCombinedPath(modules_directory, ffd.cFileName);
+                       char* dot = strrchr(ffd.cFileName, '.');
+
+                       if (dot && strcmp(dot, FREERDP_SHARED_LIBRARY_SUFFIX) == 0)
+                               pf_modules_load_module(fullpath);
+
+                       free(fullpath);
+               }
+       } while (FindNextFile(hFind, &ffd) != 0);
+
+       FindClose(hFind);
+       return TRUE;
+}
+
+void pf_modules_free(void)
+{
+       size_t index;
 
-       if (ArrayList_Add(proxy_modules, module) < 0)
+       if (plugins_list)
        {
-               WLog_ERR(TAG, "pf_modules_register_new(): failed adding module to list: %s", module_path);
-               goto error;
+               proxyPlugin* plugin;
+
+               ArrayList_ForEach(plugins_list, proxyPlugin*, index, plugin)
+               {
+                       if (!IFCALLRESULT(TRUE, plugin->PluginUnload))
+                               WLog_WARN(TAG, "PluginUnload failed for plugin '%s'", plugin->name);
+               }
+
+               ArrayList_Free(plugins_list);
+               plugins_list = NULL;
        }
 
-       return TRUE;
+       if (handles_list)
+       {
+               HANDLE handle;
 
-error:
-       pf_modules_module_free(module);
-       return FALSE;
-}
\ No newline at end of file
+               ArrayList_ForEach(handles_list, HANDLE, index, handle)
+               {
+                       if (handle)
+                               FreeLibrary(handle);
+               };
+
+               ArrayList_Free(handles_list);
+               handles_list = NULL;
+       }
+}
index e6b6911..b2f1fe0 100644 (file)
 
 #include "modules/modules_api.h"
 
-typedef wArrayList modules_list;
-typedef struct proxy_module proxyModule;
-
 typedef enum _PF_FILTER_TYPE PF_FILTER_TYPE;
 enum _PF_FILTER_TYPE
 {
        FILTER_TYPE_KEYBOARD,
-       FILTER_TYPE_MOUSE
+       FILTER_TYPE_MOUSE,
+
+       FILTER_LAST
 };
 
 typedef enum _PF_HOOK_TYPE PF_HOOK_TYPE;
 enum _PF_HOOK_TYPE
 {
        HOOK_TYPE_CLIENT_PRE_CONNECT,
+       HOOK_TYPE_CLIENT_LOGIN_FAILURE,
+
+       HOOK_TYPE_SERVER_POST_CONNECT,
        HOOK_TYPE_SERVER_CHANNELS_INIT,
        HOOK_TYPE_SERVER_CHANNELS_FREE,
-};
-
-struct proxy_module
-{
-       /* Handle to the loaded library. Used for freeing the library */
-       HMODULE handle;
 
-       char* name;
-       BOOL enabled;
-       moduleOperations* ops;
+       HOOK_LAST
 };
 
-BOOL pf_modules_init(void);
-BOOL pf_modules_register_new(const char* module_path, const char* module_name);
+BOOL pf_modules_init(const char* modules_directory_path);
+BOOL pf_modules_is_plugin_loaded(const char* plugin_name);
+void pf_modules_list_loaded_plugins(void);
 
-BOOL pf_modules_run_filter(PF_FILTER_TYPE type, rdpContext* server, void* param);
-BOOL pf_modules_run_hook(PF_HOOK_TYPE type, rdpContext* context);
+BOOL pf_modules_run_filter(PF_FILTER_TYPE type, proxyData* pdata, void* param);
+BOOL pf_modules_run_hook(PF_HOOK_TYPE type, proxyData* pdata);
 
 void pf_modules_free(void);
 
index 10e8c05..696eac9 100644 (file)
@@ -51,6 +51,7 @@
 #include "pf_disp.h"
 #include "pf_rail.h"
 #include "pf_channels.h"
+#include "pf_modules.h"
 
 #define TAG PROXY_TAG("server")
 
@@ -189,7 +190,7 @@ static BOOL pf_server_post_connect(freerdp_peer* peer)
 
        pf_server_register_input_callbacks(peer->input);
        pf_server_register_update_callbacks(peer->update);
-       return TRUE;
+       return pf_modules_run_hook(HOOK_TYPE_SERVER_POST_CONNECT, pdata);
 }
 
 static BOOL pf_server_activate(freerdp_peer* peer)