summaryrefslogtreecommitdiffstats
path: root/channels/client
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--channels/client/CMakeLists.txt125
-rw-r--r--channels/client/addin.c761
-rw-r--r--channels/client/addin.h28
-rw-r--r--channels/client/generic_dynvc.c212
-rw-r--r--channels/client/tables.c.in33
-rw-r--r--channels/client/tables.h54
6 files changed, 1213 insertions, 0 deletions
diff --git a/channels/client/CMakeLists.txt b/channels/client/CMakeLists.txt
new file mode 100644
index 0000000..923683a
--- /dev/null
+++ b/channels/client/CMakeLists.txt
@@ -0,0 +1,125 @@
+# FreeRDP: A Remote Desktop Protocol Implementation
+# FreeRDP cmake build script
+#
+# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@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.
+
+set(MODULE_NAME "freerdp-channels-client")
+set(MODULE_PREFIX "FREERDP_CHANNELS_CLIENT")
+
+set(${MODULE_PREFIX}_SRCS
+ ${CMAKE_CURRENT_BINARY_DIR}/tables.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tables.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/addin.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/addin.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/generic_dynvc.c)
+
+if(CHANNEL_STATIC_CLIENT_ENTRIES)
+ list(REMOVE_DUPLICATES CHANNEL_STATIC_CLIENT_ENTRIES)
+endif()
+
+set(CLIENT_STATIC_TYPEDEFS "#if __GNUC__\n")
+set(CLIENT_STATIC_TYPEDEFS "${CLIENT_STATIC_TYPEDEFS}#pragma GCC diagnostic push\n")
+set(CLIENT_STATIC_TYPEDEFS "${CLIENT_STATIC_TYPEDEFS}#pragma GCC diagnostic ignored \"-Wstrict-prototypes\"\n")
+set(CLIENT_STATIC_TYPEDEFS "${CLIENT_STATIC_TYPEDEFS}#endif\n")
+set(CLIENT_STATIC_TYPEDEFS "${CLIENT_STATIC_TYPEDEFS}typedef UINT (*static_entry_fkt)();\n")
+set(CLIENT_STATIC_TYPEDEFS "${CLIENT_STATIC_TYPEDEFS}typedef UINT (*static_addin_fkt)();\n")
+set(CLIENT_STATIC_TYPEDEFS "${CLIENT_STATIC_TYPEDEFS}#if __GNUC__\n")
+ set(CLIENT_STATIC_TYPEDEFS "${CLIENT_STATIC_TYPEDEFS}#pragma GCC diagnostic pop\n")
+ set(CLIENT_STATIC_TYPEDEFS "${CLIENT_STATIC_TYPEDEFS}#endif\n")
+
+foreach(STATIC_ENTRY ${CHANNEL_STATIC_CLIENT_ENTRIES})
+ foreach(STATIC_MODULE ${CHANNEL_STATIC_CLIENT_MODULES})
+ foreach(ENTRY ${${STATIC_MODULE}_CLIENT_ENTRY})
+ if(${ENTRY} STREQUAL ${STATIC_ENTRY})
+ set(STATIC_MODULE_NAME ${${STATIC_MODULE}_CLIENT_NAME})
+ set(STATIC_MODULE_CHANNEL ${${STATIC_MODULE}_CLIENT_CHANNEL})
+ set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${STATIC_MODULE_NAME})
+
+ set(ENTRY_POINT_NAME "${STATIC_MODULE_CHANNEL}_${ENTRY}")
+ if(${ENTRY} STREQUAL "VirtualChannelEntry")
+ set(ENTRY_POINT_IMPORT "extern BOOL VCAPITYPE ${ENTRY_POINT_NAME}(PCHANNEL_ENTRY_POINTS);")
+ elseif(${ENTRY} STREQUAL "VirtualChannelEntryEx")
+ set(ENTRY_POINT_IMPORT "extern BOOL VCAPITYPE ${ENTRY_POINT_NAME}(PCHANNEL_ENTRY_POINTS,PVOID);")
+ elseif(${ENTRY} MATCHES "DVCPluginEntry$")
+ set(ENTRY_POINT_IMPORT "extern UINT ${ENTRY_POINT_NAME}(IDRDYNVC_ENTRY_POINTS* pEntryPoints);")
+ elseif(${ENTRY} MATCHES "DeviceServiceEntry$")
+ set(ENTRY_POINT_IMPORT "extern UINT ${ENTRY_POINT_NAME}(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints);")
+ else()
+ set(ENTRY_POINT_IMPORT "extern UINT ${ENTRY_POINT_NAME}(void);")
+ endif()
+ set(${STATIC_ENTRY}_IMPORTS "${${STATIC_ENTRY}_IMPORTS}\n${ENTRY_POINT_IMPORT}")
+ set(${STATIC_ENTRY}_TABLE "${${STATIC_ENTRY}_TABLE}\n\t{ \"${STATIC_MODULE_CHANNEL}\", (static_entry_fkt)${ENTRY_POINT_NAME} },")
+ endif()
+ endforeach()
+ endforeach()
+endforeach()
+
+set(CLIENT_STATIC_ENTRY_TABLES_LIST "${CLIENT_STATIC_ENTRY_TABLES_LIST}\nextern const STATIC_ENTRY_TABLE CLIENT_STATIC_ENTRY_TABLES[];\nconst STATIC_ENTRY_TABLE CLIENT_STATIC_ENTRY_TABLES[] =\n{")
+
+foreach(STATIC_ENTRY ${CHANNEL_STATIC_CLIENT_ENTRIES})
+ set(CLIENT_STATIC_ENTRY_IMPORTS "${CLIENT_STATIC_ENTRY_IMPORTS}\n${${STATIC_ENTRY}_IMPORTS}")
+ set(CLIENT_STATIC_ENTRY_TABLES "${CLIENT_STATIC_ENTRY_TABLES}\nextern const STATIC_ENTRY CLIENT_${STATIC_ENTRY}_TABLE[];\nconst STATIC_ENTRY CLIENT_${STATIC_ENTRY}_TABLE[] =\n{")
+ set(CLIENT_STATIC_ENTRY_TABLES "${CLIENT_STATIC_ENTRY_TABLES}\n${${STATIC_ENTRY}_TABLE}")
+ set(CLIENT_STATIC_ENTRY_TABLES "${CLIENT_STATIC_ENTRY_TABLES}\n\t{ NULL, NULL }\n};")
+ set(CLIENT_STATIC_ENTRY_TABLES_LIST "${CLIENT_STATIC_ENTRY_TABLES_LIST}\n\t{ \"${STATIC_ENTRY}\", CLIENT_${STATIC_ENTRY}_TABLE },")
+endforeach()
+
+set(CLIENT_STATIC_ENTRY_TABLES_LIST "${CLIENT_STATIC_ENTRY_TABLES_LIST}\n\t{ NULL, NULL }\n};")
+
+set(CLIENT_STATIC_ADDIN_TABLE "extern const STATIC_ADDIN_TABLE CLIENT_STATIC_ADDIN_TABLE[];\nconst STATIC_ADDIN_TABLE CLIENT_STATIC_ADDIN_TABLE[] =\n{")
+foreach(STATIC_MODULE ${CHANNEL_STATIC_CLIENT_MODULES})
+ set(STATIC_MODULE_NAME ${${STATIC_MODULE}_CLIENT_NAME})
+ set(STATIC_MODULE_CHANNEL ${${STATIC_MODULE}_CLIENT_CHANNEL})
+ string(TOUPPER "CLIENT_${STATIC_MODULE_CHANNEL}_SUBSYSTEM_TABLE" SUBSYSTEM_TABLE_NAME)
+ set(SUBSYSTEM_TABLE "extern const STATIC_SUBSYSTEM_ENTRY ${SUBSYSTEM_TABLE_NAME}[];\nconst STATIC_SUBSYSTEM_ENTRY ${SUBSYSTEM_TABLE_NAME}[] =\n{")
+ get_target_property(CHANNEL_SUBSYSTEMS ${STATIC_MODULE_NAME} SUBSYSTEMS)
+ if(CHANNEL_SUBSYSTEMS MATCHES "NOTFOUND")
+ set(CHANNEL_SUBSYSTEMS "")
+ endif()
+ foreach(STATIC_SUBSYSTEM ${CHANNEL_SUBSYSTEMS})
+ if(${STATIC_SUBSYSTEM} MATCHES "^([^-]*)-(.*)")
+ string(REGEX REPLACE "^([^-]*)-(.*)" "\\1" STATIC_SUBSYSTEM_NAME ${STATIC_SUBSYSTEM})
+ string(REGEX REPLACE "^([^-]*)-(.*)" "\\2" STATIC_SUBSYSTEM_TYPE ${STATIC_SUBSYSTEM})
+ else()
+ set(STATIC_SUBSYSTEM_NAME "${STATIC_SUBSYSTEM}")
+ set(STATIC_SUBSYSTEM_TYPE "")
+ endif()
+ string(LENGTH "${STATIC_SUBSYSTEM_TYPE}" _type_length)
+ set(SUBSYSTEM_MODULE_NAME "${STATIC_MODULE_NAME}-${STATIC_SUBSYSTEM}")
+ set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${SUBSYSTEM_MODULE_NAME})
+ if(_type_length GREATER 0)
+ set(STATIC_SUBSYSTEM_ENTRY "${STATIC_SUBSYSTEM_NAME}_freerdp_${STATIC_MODULE_CHANNEL}_client_${STATIC_SUBSYSTEM_TYPE}_subsystem_entry")
+ else()
+ set(STATIC_SUBSYSTEM_ENTRY "${STATIC_SUBSYSTEM_NAME}_freerdp_${STATIC_MODULE_CHANNEL}_client_subsystem_entry")
+ endif()
+ set(SUBSYSTEM_TABLE "${SUBSYSTEM_TABLE}\n\t{ \"${STATIC_SUBSYSTEM_NAME}\", \"${STATIC_SUBSYSTEM_TYPE}\", ${STATIC_SUBSYSTEM_ENTRY} },")
+ set(SUBSYSTEM_IMPORT "extern UINT ${STATIC_SUBSYSTEM_ENTRY}(void*);")
+ set(CLIENT_STATIC_SUBSYSTEM_IMPORTS "${CLIENT_STATIC_SUBSYSTEM_IMPORTS}\n${SUBSYSTEM_IMPORT}")
+ endforeach()
+ set(SUBSYSTEM_TABLE "${SUBSYSTEM_TABLE}\n\t{ NULL, NULL, NULL }\n};")
+ set(CLIENT_STATIC_SUBSYSTEM_TABLES "${CLIENT_STATIC_SUBSYSTEM_TABLES}\n${SUBSYSTEM_TABLE}")
+ foreach(ENTRY ${${STATIC_MODULE}_CLIENT_ENTRY})
+ set (ENTRY_POINT_NAME ${STATIC_MODULE_CHANNEL}_${ENTRY})
+ set(CLIENT_STATIC_ADDIN_TABLE "${CLIENT_STATIC_ADDIN_TABLE}\n\t{ \"${STATIC_MODULE_CHANNEL}\", \"${ENTRY}\", (static_addin_fkt)${ENTRY_POINT_NAME}, ${SUBSYSTEM_TABLE_NAME} },")
+ endforeach()
+endforeach()
+set(CLIENT_STATIC_ADDIN_TABLE "${CLIENT_STATIC_ADDIN_TABLE}\n\t{ NULL, NULL, NULL, NULL }\n};")
+
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/tables.c.in ${CMAKE_CURRENT_BINARY_DIR}/tables.c)
+
+set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} freerdp winpr)
+
+set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS} PARENT_SCOPE)
+set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} PARENT_SCOPE)
diff --git a/channels/client/addin.c b/channels/client/addin.c
new file mode 100644
index 0000000..6d87f6c
--- /dev/null
+++ b/channels/client/addin.c
@@ -0,0 +1,761 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Channel Addins
+ *
+ * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ * Copyright 2015 Thincast Technologies GmbH
+ * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.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 <freerdp/config.h>
+
+#include <winpr/crt.h>
+#include <winpr/assert.h>
+#include <winpr/path.h>
+#include <winpr/string.h>
+#include <winpr/file.h>
+#include <winpr/synch.h>
+#include <winpr/library.h>
+#include <winpr/collections.h>
+
+#include <freerdp/freerdp.h>
+#include <freerdp/addin.h>
+#include <freerdp/build-config.h>
+#include <freerdp/client/channels.h>
+
+#include "tables.h"
+
+#include "addin.h"
+
+#include <freerdp/channels/log.h>
+#define TAG CHANNELS_TAG("addin")
+
+extern const STATIC_ENTRY_TABLE CLIENT_STATIC_ENTRY_TABLES[];
+
+static void* freerdp_channels_find_static_entry_in_table(const STATIC_ENTRY_TABLE* table,
+ const char* identifier)
+{
+ size_t index = 0;
+ const STATIC_ENTRY* pEntry = (const STATIC_ENTRY*)&table->table[index++];
+
+ while (pEntry->entry != NULL)
+ {
+ if (strcmp(pEntry->name, identifier) == 0)
+ {
+ return (void*)pEntry->entry;
+ }
+
+ pEntry = (const STATIC_ENTRY*)&table->table[index++];
+ }
+
+ return NULL;
+}
+
+void* freerdp_channels_client_find_static_entry(const char* name, const char* identifier)
+{
+ size_t index = 0;
+ const STATIC_ENTRY_TABLE* pEntry = &CLIENT_STATIC_ENTRY_TABLES[index++];
+
+ while (pEntry->table != NULL)
+ {
+ if (strcmp(pEntry->name, name) == 0)
+ {
+ return freerdp_channels_find_static_entry_in_table(pEntry, identifier);
+ }
+
+ pEntry = &CLIENT_STATIC_ENTRY_TABLES[index++];
+ }
+
+ return NULL;
+}
+
+extern const STATIC_ADDIN_TABLE CLIENT_STATIC_ADDIN_TABLE[];
+
+static FREERDP_ADDIN** freerdp_channels_list_client_static_addins(LPCSTR pszName,
+ LPCSTR pszSubsystem,
+ LPCSTR pszType, DWORD dwFlags)
+{
+ DWORD nAddins = 0;
+ FREERDP_ADDIN** ppAddins = NULL;
+ const STATIC_SUBSYSTEM_ENTRY* subsystems = NULL;
+ nAddins = 0;
+ ppAddins = (FREERDP_ADDIN**)calloc(128, sizeof(FREERDP_ADDIN*));
+
+ if (!ppAddins)
+ {
+ WLog_ERR(TAG, "calloc failed!");
+ return NULL;
+ }
+
+ ppAddins[nAddins] = NULL;
+
+ for (size_t i = 0; CLIENT_STATIC_ADDIN_TABLE[i].name != NULL; i++)
+ {
+ FREERDP_ADDIN* pAddin = (FREERDP_ADDIN*)calloc(1, sizeof(FREERDP_ADDIN));
+
+ if (!pAddin)
+ {
+ WLog_ERR(TAG, "calloc failed!");
+ goto error_out;
+ }
+
+ sprintf_s(pAddin->cName, ARRAYSIZE(pAddin->cName), "%s", CLIENT_STATIC_ADDIN_TABLE[i].name);
+ pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
+ pAddin->dwFlags |= FREERDP_ADDIN_STATIC;
+ pAddin->dwFlags |= FREERDP_ADDIN_NAME;
+ ppAddins[nAddins++] = pAddin;
+ subsystems = (const STATIC_SUBSYSTEM_ENTRY*)CLIENT_STATIC_ADDIN_TABLE[i].table;
+
+ for (size_t j = 0; subsystems[j].name != NULL; j++)
+ {
+ pAddin = (FREERDP_ADDIN*)calloc(1, sizeof(FREERDP_ADDIN));
+
+ if (!pAddin)
+ {
+ WLog_ERR(TAG, "calloc failed!");
+ goto error_out;
+ }
+
+ sprintf_s(pAddin->cName, ARRAYSIZE(pAddin->cName), "%s",
+ CLIENT_STATIC_ADDIN_TABLE[i].name);
+ sprintf_s(pAddin->cSubsystem, ARRAYSIZE(pAddin->cSubsystem), "%s", subsystems[j].name);
+ pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
+ pAddin->dwFlags |= FREERDP_ADDIN_STATIC;
+ pAddin->dwFlags |= FREERDP_ADDIN_NAME;
+ pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM;
+ ppAddins[nAddins++] = pAddin;
+ }
+ }
+
+ return ppAddins;
+error_out:
+ freerdp_channels_addin_list_free(ppAddins);
+ return NULL;
+}
+
+static HANDLE FindFirstFileUTF8(LPCSTR pszSearchPath, WIN32_FIND_DATAW* FindData)
+{
+ HANDLE hdl = INVALID_HANDLE_VALUE;
+ if (!pszSearchPath)
+ return hdl;
+ WCHAR* wpath = ConvertUtf8ToWCharAlloc(pszSearchPath, NULL);
+ if (!wpath)
+ return hdl;
+
+ hdl = FindFirstFileW(wpath, FindData);
+ free(wpath);
+
+ return hdl;
+}
+
+static FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPCSTR pszName, LPCSTR pszSubsystem,
+ LPCSTR pszType, DWORD dwFlags)
+{
+ int nDashes = 0;
+ HANDLE hFind = NULL;
+ DWORD nAddins = 0;
+ LPSTR pszPattern = NULL;
+ size_t cchPattern = 0;
+ LPCSTR pszAddinPath = FREERDP_ADDIN_PATH;
+ LPCSTR pszInstallPrefix = FREERDP_INSTALL_PREFIX;
+ LPCSTR pszExtension = NULL;
+ LPSTR pszSearchPath = NULL;
+ size_t cchSearchPath = 0;
+ size_t cchAddinPath = 0;
+ size_t cchInstallPrefix = 0;
+ FREERDP_ADDIN** ppAddins = NULL;
+ WIN32_FIND_DATAW FindData = { 0 };
+ cchAddinPath = strnlen(pszAddinPath, sizeof(FREERDP_ADDIN_PATH));
+ cchInstallPrefix = strnlen(pszInstallPrefix, sizeof(FREERDP_INSTALL_PREFIX));
+ pszExtension = PathGetSharedLibraryExtensionA(0);
+ cchPattern = 128 + strnlen(pszExtension, MAX_PATH) + 2;
+ pszPattern = (LPSTR)malloc(cchPattern + 1);
+
+ if (!pszPattern)
+ {
+ WLog_ERR(TAG, "malloc failed!");
+ return NULL;
+ }
+
+ if (pszName && pszSubsystem && pszType)
+ {
+ sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX "%s-client-%s-%s.%s",
+ pszName, pszSubsystem, pszType, pszExtension);
+ }
+ else if (pszName && pszType)
+ {
+ sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX "%s-client-?-%s.%s",
+ pszName, pszType, pszExtension);
+ }
+ else if (pszName)
+ {
+ sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX "%s-client*.%s", pszName,
+ pszExtension);
+ }
+ else
+ {
+ sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX "?-client*.%s",
+ pszExtension);
+ }
+
+ cchPattern = strnlen(pszPattern, cchPattern);
+ cchSearchPath = cchInstallPrefix + cchAddinPath + cchPattern + 3;
+ pszSearchPath = (LPSTR)calloc(cchSearchPath + 1, sizeof(char));
+
+ if (!pszSearchPath)
+ {
+ WLog_ERR(TAG, "malloc failed!");
+ free(pszPattern);
+ return NULL;
+ }
+
+ CopyMemory(pszSearchPath, pszInstallPrefix, cchInstallPrefix);
+ pszSearchPath[cchInstallPrefix] = '\0';
+ const HRESULT hr1 = NativePathCchAppendA(pszSearchPath, cchSearchPath + 1, pszAddinPath);
+ const HRESULT hr2 = NativePathCchAppendA(pszSearchPath, cchSearchPath + 1, pszPattern);
+ free(pszPattern);
+
+ if (FAILED(hr1) || FAILED(hr2))
+ {
+ free(pszSearchPath);
+ return NULL;
+ }
+
+ hFind = FindFirstFileUTF8(pszSearchPath, &FindData);
+
+ free(pszSearchPath);
+ nAddins = 0;
+ ppAddins = (FREERDP_ADDIN**)calloc(128, sizeof(FREERDP_ADDIN*));
+
+ if (!ppAddins)
+ {
+ FindClose(hFind);
+ WLog_ERR(TAG, "calloc failed!");
+ return NULL;
+ }
+
+ if (hFind == INVALID_HANDLE_VALUE)
+ return ppAddins;
+
+ do
+ {
+ char* cFileName = NULL;
+ BOOL used = FALSE;
+ FREERDP_ADDIN* pAddin = (FREERDP_ADDIN*)calloc(1, sizeof(FREERDP_ADDIN));
+
+ if (!pAddin)
+ {
+ WLog_ERR(TAG, "calloc failed!");
+ goto error_out;
+ }
+
+ cFileName =
+ ConvertWCharNToUtf8Alloc(FindData.cFileName, ARRAYSIZE(FindData.cFileName), NULL);
+ if (!cFileName)
+ goto skip;
+
+ nDashes = 0;
+ for (size_t index = 0; cFileName[index]; index++)
+ nDashes += (cFileName[index] == '-') ? 1 : 0;
+
+ if (nDashes == 1)
+ {
+ size_t len = 0;
+ char* p[2] = { 0 };
+ /* <name>-client.<extension> */
+ p[0] = cFileName;
+ p[1] = strchr(p[0], '-');
+ if (!p[1])
+ goto skip;
+ p[1] += 1;
+
+ len = (size_t)(p[1] - p[0]);
+ if (len < 1)
+ {
+ WLog_WARN(TAG, "Skipping file '%s', invalid format", cFileName);
+ goto skip;
+ }
+ strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
+
+ pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
+ pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
+ pAddin->dwFlags |= FREERDP_ADDIN_NAME;
+ ppAddins[nAddins++] = pAddin;
+
+ used = TRUE;
+ }
+ else if (nDashes == 2)
+ {
+ size_t len = 0;
+ char* p[4] = { 0 };
+ /* <name>-client-<subsystem>.<extension> */
+ p[0] = cFileName;
+ p[1] = strchr(p[0], '-');
+ if (!p[1])
+ goto skip;
+ p[1] += 1;
+ p[2] = strchr(p[1], '-');
+ if (!p[2])
+ goto skip;
+ p[2] += 1;
+ p[3] = strchr(p[2], '.');
+ if (!p[3])
+ goto skip;
+ p[3] += 1;
+
+ len = (size_t)(p[1] - p[0]);
+ if (len < 1)
+ {
+ WLog_WARN(TAG, "Skipping file '%s', invalid format", cFileName);
+ goto skip;
+ }
+ strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
+
+ len = (size_t)(p[3] - p[2]);
+ if (len < 1)
+ {
+ WLog_WARN(TAG, "Skipping file '%s', invalid format", cFileName);
+ goto skip;
+ }
+ strncpy(pAddin->cSubsystem, p[2], MIN(ARRAYSIZE(pAddin->cSubsystem), len - 1));
+
+ pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
+ pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
+ pAddin->dwFlags |= FREERDP_ADDIN_NAME;
+ pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM;
+ ppAddins[nAddins++] = pAddin;
+
+ used = TRUE;
+ }
+ else if (nDashes == 3)
+ {
+ size_t len = 0;
+ char* p[5] = { 0 };
+ /* <name>-client-<subsystem>-<type>.<extension> */
+ p[0] = cFileName;
+ p[1] = strchr(p[0], '-');
+ if (!p[1])
+ goto skip;
+ p[1] += 1;
+ p[2] = strchr(p[1], '-');
+ if (!p[2])
+ goto skip;
+ p[2] += 1;
+ p[3] = strchr(p[2], '-');
+ if (!p[3])
+ goto skip;
+ p[3] += 1;
+ p[4] = strchr(p[3], '.');
+ if (!p[4])
+ goto skip;
+ p[4] += 1;
+
+ len = (size_t)(p[1] - p[0]);
+ if (len < 1)
+ {
+ WLog_WARN(TAG, "Skipping file '%s', invalid format", cFileName);
+ goto skip;
+ }
+ strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
+
+ len = (size_t)(p[3] - p[2]);
+ if (len < 1)
+ {
+ WLog_WARN(TAG, "Skipping file '%s', invalid format", cFileName);
+ goto skip;
+ }
+ strncpy(pAddin->cSubsystem, p[2], MIN(ARRAYSIZE(pAddin->cSubsystem), len - 1));
+
+ len = (size_t)(p[4] - p[3]);
+ if (len < 1)
+ {
+ WLog_WARN(TAG, "Skipping file '%s', invalid format", cFileName);
+ goto skip;
+ }
+ strncpy(pAddin->cType, p[3], MIN(ARRAYSIZE(pAddin->cType), len - 1));
+
+ pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
+ pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
+ pAddin->dwFlags |= FREERDP_ADDIN_NAME;
+ pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM;
+ pAddin->dwFlags |= FREERDP_ADDIN_TYPE;
+ ppAddins[nAddins++] = pAddin;
+
+ used = TRUE;
+ }
+
+ skip:
+ free(cFileName);
+ if (!used)
+ free(pAddin);
+
+ } while (FindNextFileW(hFind, &FindData));
+
+ FindClose(hFind);
+ ppAddins[nAddins] = NULL;
+ return ppAddins;
+error_out:
+ FindClose(hFind);
+ freerdp_channels_addin_list_free(ppAddins);
+ return NULL;
+}
+
+FREERDP_ADDIN** freerdp_channels_list_addins(LPCSTR pszName, LPCSTR pszSubsystem, LPCSTR pszType,
+ DWORD dwFlags)
+{
+ if (dwFlags & FREERDP_ADDIN_STATIC)
+ return freerdp_channels_list_client_static_addins(pszName, pszSubsystem, pszType, dwFlags);
+ else if (dwFlags & FREERDP_ADDIN_DYNAMIC)
+ return freerdp_channels_list_dynamic_addins(pszName, pszSubsystem, pszType, dwFlags);
+
+ return NULL;
+}
+
+void freerdp_channels_addin_list_free(FREERDP_ADDIN** ppAddins)
+{
+ if (!ppAddins)
+ return;
+
+ for (size_t index = 0; ppAddins[index] != NULL; index++)
+ free(ppAddins[index]);
+
+ free(ppAddins);
+}
+
+extern const STATIC_ENTRY CLIENT_VirtualChannelEntryEx_TABLE[];
+
+static BOOL freerdp_channels_is_virtual_channel_entry_ex(LPCSTR pszName)
+{
+ for (size_t i = 0; CLIENT_VirtualChannelEntryEx_TABLE[i].name != NULL; i++)
+ {
+ const STATIC_ENTRY* entry = &CLIENT_VirtualChannelEntryEx_TABLE[i];
+
+ if (!strncmp(entry->name, pszName, MAX_PATH))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+PVIRTUALCHANNELENTRY freerdp_channels_load_static_addin_entry(LPCSTR pszName, LPCSTR pszSubsystem,
+ LPCSTR pszType, DWORD dwFlags)
+{
+ const STATIC_ADDIN_TABLE* table = CLIENT_STATIC_ADDIN_TABLE;
+ const char* type = NULL;
+
+ if (!pszName)
+ return NULL;
+
+ if (dwFlags & FREERDP_ADDIN_CHANNEL_DYNAMIC)
+ type = "DVCPluginEntry";
+ else if (dwFlags & FREERDP_ADDIN_CHANNEL_DEVICE)
+ type = "DeviceServiceEntry";
+ else if (dwFlags & FREERDP_ADDIN_CHANNEL_STATIC)
+ {
+ if (dwFlags & FREERDP_ADDIN_CHANNEL_ENTRYEX)
+ type = "VirtualChannelEntryEx";
+ else
+ type = "VirtualChannelEntry";
+ }
+
+ for (; table->name != NULL; table++)
+ {
+ if (strncmp(table->name, pszName, MAX_PATH) == 0)
+ {
+ if (type && strncmp(table->type, type, MAX_PATH))
+ continue;
+
+ if (pszSubsystem != NULL)
+ {
+ const STATIC_SUBSYSTEM_ENTRY* subsystems = table->table;
+
+ for (; subsystems->name != NULL; subsystems++)
+ {
+ /* If the pszSubsystem is an empty string use the default backend. */
+ if ((strnlen(pszSubsystem, 1) ==
+ 0) || /* we only want to know if strnlen is > 0 */
+ (strncmp(subsystems->name, pszSubsystem, MAX_PATH) == 0))
+ {
+ if (pszType)
+ {
+ if (strncmp(subsystems->type, pszType, MAX_PATH) == 0)
+ return (PVIRTUALCHANNELENTRY)subsystems->entry;
+ }
+ else
+ {
+ return (PVIRTUALCHANNELENTRY)subsystems->entry;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (dwFlags & FREERDP_ADDIN_CHANNEL_ENTRYEX)
+ {
+ if (!freerdp_channels_is_virtual_channel_entry_ex(pszName))
+ return NULL;
+ }
+
+ return (PVIRTUALCHANNELENTRY)table->entry;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+typedef struct
+{
+ wMessageQueue* queue;
+ wStream* data_in;
+ HANDLE thread;
+ char* channel_name;
+ rdpContext* ctx;
+ LPVOID userdata;
+ MsgHandler msg_handler;
+} msg_proc_internals;
+
+static DWORD WINAPI channel_client_thread_proc(LPVOID userdata)
+{
+ UINT error = CHANNEL_RC_OK;
+ wStream* data = NULL;
+ wMessage message = { 0 };
+ msg_proc_internals* internals = userdata;
+
+ WINPR_ASSERT(internals);
+
+ while (1)
+ {
+ if (!MessageQueue_Wait(internals->queue))
+ {
+ WLog_ERR(TAG, "MessageQueue_Wait failed!");
+ error = ERROR_INTERNAL_ERROR;
+ break;
+ }
+ if (!MessageQueue_Peek(internals->queue, &message, TRUE))
+ {
+ WLog_ERR(TAG, "MessageQueue_Peek failed!");
+ error = ERROR_INTERNAL_ERROR;
+ break;
+ }
+
+ if (message.id == WMQ_QUIT)
+ break;
+
+ if (message.id == 0)
+ {
+ data = (wStream*)message.wParam;
+
+ if ((error = internals->msg_handler(internals->userdata, data)))
+ {
+ WLog_ERR(TAG, "msg_handler failed with error %" PRIu32 "!", error);
+ break;
+ }
+ }
+ }
+ if (error && internals->ctx)
+ {
+ char msg[128];
+ _snprintf(msg, 127,
+ "%s_virtual_channel_client_thread reported an"
+ " error",
+ internals->channel_name);
+ setChannelError(internals->ctx, error, msg);
+ }
+ ExitThread(error);
+ return error;
+}
+
+static void free_msg(void* obj)
+{
+ wMessage* msg = (wMessage*)obj;
+
+ if (msg && (msg->id == 0))
+ {
+ wStream* s = (wStream*)msg->wParam;
+ Stream_Free(s, TRUE);
+ }
+}
+
+static void channel_client_handler_free(msg_proc_internals* internals)
+{
+ if (!internals)
+ return;
+
+ if (internals->thread)
+ CloseHandle(internals->thread);
+ MessageQueue_Free(internals->queue);
+ Stream_Free(internals->data_in, TRUE);
+ free(internals->channel_name);
+ free(internals);
+}
+
+/* Create message queue and thread or not, depending on settings */
+void* channel_client_create_handler(rdpContext* ctx, LPVOID userdata, MsgHandler msg_handler,
+ const char* channel_name)
+{
+ msg_proc_internals* internals = calloc(1, sizeof(msg_proc_internals));
+ if (!internals)
+ {
+ WLog_ERR(TAG, "calloc failed!");
+ return NULL;
+ }
+ internals->msg_handler = msg_handler;
+ internals->userdata = userdata;
+ if (channel_name)
+ {
+ internals->channel_name = _strdup(channel_name);
+ if (!internals->channel_name)
+ goto fail;
+ }
+ WINPR_ASSERT(ctx);
+ WINPR_ASSERT(ctx->settings);
+ internals->ctx = ctx;
+ if ((freerdp_settings_get_uint32(ctx->settings, FreeRDP_ThreadingFlags) &
+ THREADING_FLAGS_DISABLE_THREADS) == 0)
+ {
+ wObject obj = { 0 };
+ obj.fnObjectFree = free_msg;
+ internals->queue = MessageQueue_New(&obj);
+ if (!internals->queue)
+ {
+ WLog_ERR(TAG, "MessageQueue_New failed!");
+ goto fail;
+ }
+
+ if (!(internals->thread =
+ CreateThread(NULL, 0, channel_client_thread_proc, (void*)internals, 0, NULL)))
+ {
+ WLog_ERR(TAG, "CreateThread failed!");
+ goto fail;
+ }
+ }
+ return internals;
+
+fail:
+ channel_client_handler_free(internals);
+ return NULL;
+}
+/* post a message in the queue or directly call the processing handler */
+UINT channel_client_post_message(void* MsgsHandle, LPVOID pData, UINT32 dataLength,
+ UINT32 totalLength, UINT32 dataFlags)
+{
+ msg_proc_internals* internals = MsgsHandle;
+ wStream* data_in = NULL;
+
+ if (!internals)
+ {
+ /* TODO: return some error here */
+ return CHANNEL_RC_OK;
+ }
+
+ if ((dataFlags & CHANNEL_FLAG_SUSPEND) || (dataFlags & CHANNEL_FLAG_RESUME))
+ {
+ return CHANNEL_RC_OK;
+ }
+
+ if (dataFlags & CHANNEL_FLAG_FIRST)
+ {
+ if (internals->data_in)
+ {
+ if (!Stream_EnsureCapacity(internals->data_in, totalLength))
+ return CHANNEL_RC_NO_MEMORY;
+ }
+ else
+ internals->data_in = Stream_New(NULL, totalLength);
+ }
+
+ if (!(data_in = internals->data_in))
+ {
+ WLog_ERR(TAG, "Stream_New failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ if (!Stream_EnsureRemainingCapacity(data_in, dataLength))
+ {
+ Stream_Free(internals->data_in, TRUE);
+ internals->data_in = NULL;
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ Stream_Write(data_in, pData, dataLength);
+
+ if (dataFlags & CHANNEL_FLAG_LAST)
+ {
+ if (Stream_Capacity(data_in) != Stream_GetPosition(data_in))
+ {
+ char msg[128];
+ _snprintf(msg, 127, "%s_plugin_process_received: read error", internals->channel_name);
+ WLog_ERR(TAG, msg);
+ return ERROR_INTERNAL_ERROR;
+ }
+
+ internals->data_in = NULL;
+ Stream_SealLength(data_in);
+ Stream_SetPosition(data_in, 0);
+
+ if ((freerdp_settings_get_uint32(internals->ctx->settings, FreeRDP_ThreadingFlags) &
+ THREADING_FLAGS_DISABLE_THREADS) != 0)
+ {
+ UINT error = CHANNEL_RC_OK;
+ if ((error = internals->msg_handler(internals->userdata, data_in)))
+ {
+ WLog_ERR(TAG,
+ "msg_handler failed with error"
+ " %" PRIu32 "!",
+ error);
+ return ERROR_INTERNAL_ERROR;
+ }
+ }
+ else if (!MessageQueue_Post(internals->queue, NULL, 0, (void*)data_in, NULL))
+ {
+ WLog_ERR(TAG, "MessageQueue_Post failed!");
+ return ERROR_INTERNAL_ERROR;
+ }
+ }
+ return CHANNEL_RC_OK;
+}
+/* Tear down queue and thread */
+UINT channel_client_quit_handler(void* MsgsHandle)
+{
+ msg_proc_internals* internals = MsgsHandle;
+ UINT rc = 0;
+ if (!internals)
+ {
+ /* TODO: return some error here */
+ return CHANNEL_RC_OK;
+ }
+
+ WINPR_ASSERT(internals->ctx);
+ WINPR_ASSERT(internals->ctx->settings);
+
+ if ((freerdp_settings_get_uint32(internals->ctx->settings, FreeRDP_ThreadingFlags) &
+ THREADING_FLAGS_DISABLE_THREADS) == 0)
+ {
+ if (internals->queue && internals->thread)
+ {
+ if (MessageQueue_PostQuit(internals->queue, 0) &&
+ (WaitForSingleObject(internals->thread, INFINITE) == WAIT_FAILED))
+ {
+ rc = GetLastError();
+ WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", rc);
+ return rc;
+ }
+ }
+ }
+
+ channel_client_handler_free(internals);
+ return CHANNEL_RC_OK;
+}
diff --git a/channels/client/addin.h b/channels/client/addin.h
new file mode 100644
index 0000000..1b794e7
--- /dev/null
+++ b/channels/client/addin.h
@@ -0,0 +1,28 @@
+/*
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Channel Addins
+ *
+ * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@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.
+ */
+
+typedef UINT (*MsgHandler)(LPVOID userdata, wStream* data);
+
+FREERDP_API void* channel_client_create_handler(rdpContext* ctx, LPVOID userdata,
+ MsgHandler handler, const char* channel_name);
+
+UINT channel_client_post_message(void* MsgsHandle, LPVOID pData, UINT32 dataLength,
+ UINT32 totalLength, UINT32 dataFlags);
+
+UINT channel_client_quit_handler(void* MsgsHandle);
diff --git a/channels/client/generic_dynvc.c b/channels/client/generic_dynvc.c
new file mode 100644
index 0000000..263b5ce
--- /dev/null
+++ b/channels/client/generic_dynvc.c
@@ -0,0 +1,212 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Dynamic channel
+ *
+ * Copyright 2022 David Fort <contact@hardening-consulting.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 <freerdp/config.h>
+#include <freerdp/log.h>
+#include <freerdp/client/channels.h>
+
+#define TAG FREERDP_TAG("genericdynvc")
+
+static UINT generic_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
+ IWTSVirtualChannel* pChannel, BYTE* Data,
+ BOOL* pbAccept,
+ IWTSVirtualChannelCallback** ppCallback)
+{
+ GENERIC_CHANNEL_CALLBACK* callback = NULL;
+ GENERIC_DYNVC_PLUGIN* plugin = NULL;
+ GENERIC_LISTENER_CALLBACK* listener_callback = (GENERIC_LISTENER_CALLBACK*)pListenerCallback;
+
+ if (!listener_callback || !listener_callback->plugin)
+ return ERROR_INTERNAL_ERROR;
+
+ plugin = (GENERIC_DYNVC_PLUGIN*)listener_callback->plugin;
+ WLog_Print(plugin->log, WLOG_TRACE, "...");
+
+ callback = (GENERIC_CHANNEL_CALLBACK*)calloc(1, plugin->channelCallbackSize);
+ if (!callback)
+ {
+ WLog_Print(plugin->log, WLOG_ERROR, "calloc failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ /* implant configured channel callbacks */
+ callback->iface = *plugin->channel_callbacks;
+
+ callback->plugin = listener_callback->plugin;
+ callback->channel_mgr = listener_callback->channel_mgr;
+ callback->channel = pChannel;
+
+ listener_callback->channel_callback = callback;
+ *ppCallback = (IWTSVirtualChannelCallback*)callback;
+ return CHANNEL_RC_OK;
+}
+
+static UINT generic_dynvc_plugin_initialize(IWTSPlugin* pPlugin,
+ IWTSVirtualChannelManager* pChannelMgr)
+{
+ UINT rc = 0;
+ GENERIC_LISTENER_CALLBACK* listener_callback = NULL;
+ GENERIC_DYNVC_PLUGIN* plugin = (GENERIC_DYNVC_PLUGIN*)pPlugin;
+
+ if (!plugin)
+ return CHANNEL_RC_BAD_CHANNEL_HANDLE;
+
+ if (!pChannelMgr)
+ return ERROR_INVALID_PARAMETER;
+
+ if (plugin->initialized)
+ {
+ WLog_ERR(TAG, "[%s] channel initialized twice, aborting", plugin->dynvc_name);
+ return ERROR_INVALID_DATA;
+ }
+
+ WLog_Print(plugin->log, WLOG_TRACE, "...");
+ listener_callback = (GENERIC_LISTENER_CALLBACK*)calloc(1, sizeof(GENERIC_LISTENER_CALLBACK));
+ if (!listener_callback)
+ {
+ WLog_Print(plugin->log, WLOG_ERROR, "calloc failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ plugin->listener_callback = listener_callback;
+ listener_callback->iface.OnNewChannelConnection = generic_on_new_channel_connection;
+ listener_callback->plugin = pPlugin;
+ listener_callback->channel_mgr = pChannelMgr;
+ rc = pChannelMgr->CreateListener(pChannelMgr, plugin->dynvc_name, 0, &listener_callback->iface,
+ &plugin->listener);
+
+ plugin->listener->pInterface = plugin->iface.pInterface;
+ plugin->initialized = (rc == CHANNEL_RC_OK);
+ return rc;
+}
+
+static UINT generic_plugin_terminated(IWTSPlugin* pPlugin)
+{
+ GENERIC_DYNVC_PLUGIN* plugin = (GENERIC_DYNVC_PLUGIN*)pPlugin;
+ UINT error = CHANNEL_RC_OK;
+
+ if (!plugin)
+ return CHANNEL_RC_BAD_CHANNEL_HANDLE;
+
+ WLog_Print(plugin->log, WLOG_TRACE, "...");
+
+ /* some channels (namely rdpei), look at initialized to see if they should continue to run */
+ plugin->initialized = FALSE;
+
+ if (plugin->terminatePluginFn)
+ plugin->terminatePluginFn(plugin);
+
+ if (plugin->listener_callback)
+ {
+ IWTSVirtualChannelManager* mgr = plugin->listener_callback->channel_mgr;
+ if (mgr)
+ IFCALL(mgr->DestroyListener, mgr, plugin->listener);
+ }
+
+ free(plugin->listener_callback);
+ free(plugin->dynvc_name);
+ free(plugin);
+ return error;
+}
+
+static UINT generic_dynvc_plugin_attached(IWTSPlugin* pPlugin)
+{
+ GENERIC_DYNVC_PLUGIN* pluginn = (GENERIC_DYNVC_PLUGIN*)pPlugin;
+ UINT error = CHANNEL_RC_OK;
+
+ if (!pluginn)
+ return CHANNEL_RC_BAD_CHANNEL_HANDLE;
+
+ pluginn->attached = TRUE;
+ return error;
+}
+
+static UINT generic_dynvc_plugin_detached(IWTSPlugin* pPlugin)
+{
+ GENERIC_DYNVC_PLUGIN* plugin = (GENERIC_DYNVC_PLUGIN*)pPlugin;
+ UINT error = CHANNEL_RC_OK;
+
+ if (!plugin)
+ return CHANNEL_RC_BAD_CHANNEL_HANDLE;
+
+ plugin->attached = FALSE;
+ return error;
+}
+
+UINT freerdp_generic_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints, const char* logTag,
+ const char* name, size_t pluginSize, size_t channelCallbackSize,
+ const IWTSVirtualChannelCallback* channel_callbacks,
+ DYNVC_PLUGIN_INIT_FN initPluginFn,
+ DYNVC_PLUGIN_TERMINATE_FN terminatePluginFn)
+{
+ GENERIC_DYNVC_PLUGIN* plugin = NULL;
+ UINT error = CHANNEL_RC_INITIALIZATION_ERROR;
+
+ WINPR_ASSERT(pEntryPoints);
+ WINPR_ASSERT(pEntryPoints->GetPlugin);
+ WINPR_ASSERT(logTag);
+ WINPR_ASSERT(name);
+ WINPR_ASSERT(pluginSize >= sizeof(*plugin));
+ WINPR_ASSERT(channelCallbackSize >= sizeof(GENERIC_CHANNEL_CALLBACK));
+
+ plugin = (GENERIC_DYNVC_PLUGIN*)pEntryPoints->GetPlugin(pEntryPoints, name);
+ if (plugin != NULL)
+ return CHANNEL_RC_ALREADY_INITIALIZED;
+
+ plugin = (GENERIC_DYNVC_PLUGIN*)calloc(1, pluginSize);
+ if (!plugin)
+ {
+ WLog_ERR(TAG, "calloc failed!");
+ return CHANNEL_RC_NO_MEMORY;
+ }
+
+ plugin->log = WLog_Get(logTag);
+ plugin->attached = TRUE;
+ plugin->channel_callbacks = channel_callbacks;
+ plugin->channelCallbackSize = channelCallbackSize;
+ plugin->iface.Initialize = generic_dynvc_plugin_initialize;
+ plugin->iface.Connected = NULL;
+ plugin->iface.Disconnected = NULL;
+ plugin->iface.Terminated = generic_plugin_terminated;
+ plugin->iface.Attached = generic_dynvc_plugin_attached;
+ plugin->iface.Detached = generic_dynvc_plugin_detached;
+ plugin->terminatePluginFn = terminatePluginFn;
+
+ if (initPluginFn)
+ {
+ rdpSettings* settings = pEntryPoints->GetRdpSettings(pEntryPoints);
+ rdpContext* context = pEntryPoints->GetRdpContext(pEntryPoints);
+
+ error = initPluginFn(plugin, context, settings);
+ if (error != CHANNEL_RC_OK)
+ goto error;
+ }
+
+ plugin->dynvc_name = _strdup(name);
+ if (!plugin->dynvc_name)
+ goto error;
+
+ error = pEntryPoints->RegisterPlugin(pEntryPoints, name, &plugin->iface);
+ if (error == CHANNEL_RC_OK)
+ return error;
+
+error:
+ generic_plugin_terminated(&plugin->iface);
+ return error;
+}
diff --git a/channels/client/tables.c.in b/channels/client/tables.c.in
new file mode 100644
index 0000000..a22621b
--- /dev/null
+++ b/channels/client/tables.c.in
@@ -0,0 +1,33 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Static Entry Point Tables
+ *
+ * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ * Copyright 2015 Thincast Technologies GmbH
+ * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.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 <freerdp/dvc.h>
+#include <freerdp/channels/rdpdr.h>
+#include "tables.h"
+
+${CLIENT_STATIC_TYPEDEFS}
+${CLIENT_STATIC_ENTRY_IMPORTS}
+${CLIENT_STATIC_ENTRY_TABLES}
+${CLIENT_STATIC_ENTRY_TABLES_LIST}
+${CLIENT_STATIC_SUBSYSTEM_IMPORTS}
+${CLIENT_STATIC_SUBSYSTEM_TABLES}
+${CLIENT_STATIC_ADDIN_TABLE}
+
diff --git a/channels/client/tables.h b/channels/client/tables.h
new file mode 100644
index 0000000..e67beb5
--- /dev/null
+++ b/channels/client/tables.h
@@ -0,0 +1,54 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Static Entry Point Tables
+ *
+ * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@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 <winpr/platform.h>
+#include <freerdp/svc.h>
+
+/* The 'entry' function pointers have variable arguments. */
+WINPR_PRAGMA_DIAG_PUSH
+WINPR_PRAGMA_DIAG_IGNORED_STRICT_PROTOTYPES
+
+typedef struct
+{
+ const char* name;
+ UINT (*entry)();
+} STATIC_ENTRY;
+
+typedef struct
+{
+ const char* name;
+ const STATIC_ENTRY* table;
+} STATIC_ENTRY_TABLE;
+
+typedef struct
+{
+ const char* name;
+ const char* type;
+ UINT (*entry)();
+} STATIC_SUBSYSTEM_ENTRY;
+
+typedef struct
+{
+ const char* name;
+ const char* type;
+ UINT (*entry)();
+ const STATIC_SUBSYSTEM_ENTRY* table;
+} STATIC_ADDIN_TABLE;
+
+WINPR_PRAGMA_DIAG_POP