diff options
Diffstat (limited to 'libfreerdp/common/addin.c')
-rw-r--r-- | libfreerdp/common/addin.c | 397 |
1 files changed, 397 insertions, 0 deletions
diff --git a/libfreerdp/common/addin.c b/libfreerdp/common/addin.c new file mode 100644 index 0000000..9c5cd1f --- /dev/null +++ b/libfreerdp/common/addin.c @@ -0,0 +1,397 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Addin Loader + * + * 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 <freerdp/config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <winpr/crt.h> +#include <winpr/path.h> +#include <winpr/string.h> +#include <winpr/library.h> + +#include <freerdp/addin.h> +#include <freerdp/build-config.h> + +#include <freerdp/log.h> +#define TAG FREERDP_TAG("addin") + +static INLINE BOOL is_path_required(LPCSTR path, size_t len) +{ + if (!path || (len <= 1)) + return FALSE; + + if (strcmp(path, ".") == 0) + return FALSE; + + return TRUE; +} + +LPSTR freerdp_get_library_install_path(void) +{ + LPSTR pszPath = NULL; + size_t cchPath = 0; + size_t cchLibraryPath = 0; + size_t cchInstallPrefix = 0; + BOOL needLibPath = 0; + BOOL needInstallPath = 0; + LPCSTR pszLibraryPath = FREERDP_LIBRARY_PATH; + LPCSTR pszInstallPrefix = FREERDP_INSTALL_PREFIX; + cchLibraryPath = strlen(pszLibraryPath) + 1; + cchInstallPrefix = strlen(pszInstallPrefix) + 1; + cchPath = cchInstallPrefix + cchLibraryPath; + needInstallPath = is_path_required(pszInstallPrefix, cchInstallPrefix); + needLibPath = is_path_required(pszLibraryPath, cchLibraryPath); + + if (!needInstallPath && !needLibPath) + return NULL; + + pszPath = (LPSTR)malloc(cchPath + 1); + + if (!pszPath) + return NULL; + + if (needInstallPath) + { + CopyMemory(pszPath, pszInstallPrefix, cchInstallPrefix); + pszPath[cchInstallPrefix] = '\0'; + } + + if (needLibPath) + { + if (FAILED(NativePathCchAppendA(pszPath, cchPath + 1, pszLibraryPath))) + { + free(pszPath); + return NULL; + } + } + + return pszPath; +} + +LPSTR freerdp_get_dynamic_addin_install_path(void) +{ +#if defined(WITH_ADD_PLUGIN_TO_RPATH) + return NULL; +#else + LPSTR pszPath = NULL; + size_t cchPath = 0; + size_t cchAddinPath = 0; + size_t cchInstallPrefix = 0; + BOOL needLibPath = 0; + BOOL needInstallPath = 0; + LPCSTR pszAddinPath = FREERDP_ADDIN_PATH; + LPCSTR pszInstallPrefix = FREERDP_INSTALL_PREFIX; + cchAddinPath = strlen(pszAddinPath) + 1; + cchInstallPrefix = strlen(pszInstallPrefix) + 1; + cchPath = cchInstallPrefix + cchAddinPath; + needInstallPath = is_path_required(pszInstallPrefix, cchInstallPrefix); + needLibPath = is_path_required(pszAddinPath, cchAddinPath); + + WLog_DBG(TAG, + "freerdp_get_dynamic_addin_install_path <- pszInstallPrefix: %s, pszAddinPath: %s", + pszInstallPrefix, pszAddinPath); + + if (!needInstallPath && !needLibPath) + return NULL; + + pszPath = (LPSTR)calloc(cchPath + 1, sizeof(CHAR)); + + if (!pszPath) + return NULL; + + if (needInstallPath) + { + CopyMemory(pszPath, pszInstallPrefix, cchInstallPrefix); + pszPath[cchInstallPrefix] = '\0'; + } + + if (needLibPath) + { + if (FAILED(NativePathCchAppendA(pszPath, cchPath + 1, pszAddinPath))) + { + free(pszPath); + return NULL; + } + } + + WLog_DBG(TAG, "freerdp_get_dynamic_addin_install_path -> pszPath: %s", pszPath); + + return pszPath; +#endif +} + +PVIRTUALCHANNELENTRY freerdp_load_dynamic_addin(LPCSTR pszFileName, LPCSTR pszPath, + LPCSTR pszEntryName) +{ + LPSTR pszAddinInstallPath = freerdp_get_dynamic_addin_install_path(); + PVIRTUALCHANNELENTRY entry = NULL; + BOOL bHasExt = TRUE; + PCSTR pszExt = NULL; + size_t cchExt = 0; + HINSTANCE library = NULL; + size_t cchFileName = 0; + size_t cchFilePath = 0; + LPSTR pszAddinFile = NULL; + LPSTR pszFilePath = NULL; + LPSTR pszRelativeFilePath = NULL; + size_t cchAddinFile = 0; + size_t cchAddinInstallPath = 0; + + if (!pszFileName || !pszEntryName) + goto fail; + + WLog_DBG(TAG, "freerdp_load_dynamic_addin <- pszFileName: %s, pszPath: %s, pszEntryName: %s", + pszFileName, pszPath, pszEntryName); + + cchFileName = strlen(pszFileName); + + /* Get file name with prefix and extension */ + if (FAILED(PathCchFindExtensionA(pszFileName, cchFileName + 1, &pszExt))) + { + pszExt = PathGetSharedLibraryExtensionA(PATH_SHARED_LIB_EXT_WITH_DOT); + cchExt = strlen(pszExt); + bHasExt = FALSE; + } + + if (bHasExt) + { + pszAddinFile = _strdup(pszFileName); + + if (!pszAddinFile) + goto fail; + } + else + { + cchAddinFile = cchFileName + cchExt + 2 + sizeof(FREERDP_SHARED_LIBRARY_PREFIX); + pszAddinFile = (LPSTR)malloc(cchAddinFile + 1); + + if (!pszAddinFile) + goto fail; + + sprintf_s(pszAddinFile, cchAddinFile, FREERDP_SHARED_LIBRARY_PREFIX "%s%s", pszFileName, + pszExt); + } + + cchAddinFile = strlen(pszAddinFile); + + /* If a path is provided prefix the library name with it. */ + if (pszPath) + { + size_t relPathLen = strlen(pszPath) + cchAddinFile + 1; + pszRelativeFilePath = calloc(relPathLen, sizeof(CHAR)); + + if (!pszRelativeFilePath) + goto fail; + + sprintf_s(pszRelativeFilePath, relPathLen, "%s", pszPath); + const HRESULT hr = NativePathCchAppendA(pszRelativeFilePath, relPathLen, pszAddinFile); + if (FAILED(hr)) + goto fail; + } + else + pszRelativeFilePath = _strdup(pszAddinFile); + + if (!pszRelativeFilePath) + goto fail; + + /* If a system prefix path is provided try these locations too. */ + if (pszAddinInstallPath) + { + cchAddinInstallPath = strlen(pszAddinInstallPath); + cchFilePath = cchAddinInstallPath + cchFileName + 32; + pszFilePath = (LPSTR)malloc(cchFilePath + 1); + + if (!pszFilePath) + goto fail; + + CopyMemory(pszFilePath, pszAddinInstallPath, cchAddinInstallPath); + pszFilePath[cchAddinInstallPath] = '\0'; + const HRESULT hr = + NativePathCchAppendA((LPSTR)pszFilePath, cchFilePath + 1, pszRelativeFilePath); + if (FAILED(hr)) + goto fail; + } + else + pszFilePath = _strdup(pszRelativeFilePath); + + library = LoadLibraryX(pszFilePath); + + if (!library) + goto fail; + + entry = (PVIRTUALCHANNELENTRY)GetProcAddress(library, pszEntryName); +fail: + free(pszRelativeFilePath); + free(pszAddinFile); + free(pszFilePath); + free(pszAddinInstallPath); + + if (!entry && library) + FreeLibrary(library); + + return entry; +} + +PVIRTUALCHANNELENTRY freerdp_load_dynamic_channel_addin_entry(LPCSTR pszName, LPCSTR pszSubsystem, + LPCSTR pszType, DWORD dwFlags) +{ + PVIRTUALCHANNELENTRY entry = NULL; + LPSTR pszFileName = NULL; + const size_t cchBaseFileName = sizeof(FREERDP_SHARED_LIBRARY_PREFIX) + 32; + size_t nameLen = 0; + size_t subsystemLen = 0; + size_t typeLen = 0; + size_t cchFileName = 0; + + if (pszName) + nameLen = strnlen(pszName, MAX_PATH); + if (pszSubsystem) + subsystemLen = strnlen(pszSubsystem, MAX_PATH); + if (pszType) + typeLen = strnlen(pszType, MAX_PATH); + + if (pszName && pszSubsystem && pszType) + { + cchFileName = cchBaseFileName + nameLen + subsystemLen + typeLen; + pszFileName = (LPSTR)malloc(cchFileName); + + if (!pszFileName) + return NULL; + + sprintf_s(pszFileName, cchFileName, "%s-client-%s-%s", pszName, pszSubsystem, pszType); + } + else if (pszName && pszSubsystem) + { + cchFileName = cchBaseFileName + nameLen + subsystemLen; + pszFileName = (LPSTR)malloc(cchFileName); + + if (!pszFileName) + return NULL; + + sprintf_s(pszFileName, cchFileName, "%s-client-%s", pszName, pszSubsystem); + } + else if (pszName) + { + cchFileName = cchBaseFileName + nameLen; + pszFileName = (LPSTR)malloc(cchFileName); + + if (!pszFileName) + return NULL; + + sprintf_s(pszFileName, cchFileName, "%s-client", pszName); + } + else + { + return NULL; + } + + { + LPCSTR pszExtension = PathGetSharedLibraryExtensionA(0); + const char pszPrefix[] = FREERDP_SHARED_LIBRARY_PREFIX; + int rc = 0; + + cchFileName += strnlen(pszPrefix, ARRAYSIZE(pszPrefix)); + if (pszExtension) + cchFileName += strnlen(pszExtension, MAX_PATH) + 1; + LPSTR tmp = calloc(cchFileName, sizeof(CHAR)); + if (tmp) + rc = sprintf_s(tmp, cchFileName, "%s%s.%s", pszPrefix, pszFileName, pszExtension); + + free(pszFileName); + pszFileName = tmp; + if (!pszFileName || (rc < 0)) + { + free(pszFileName); + return NULL; + } + } + + if (pszSubsystem) + { + LPSTR pszEntryName = NULL; + size_t cchEntryName = 0; + /* subsystem add-in */ + cchEntryName = 64 + nameLen; + pszEntryName = (LPSTR)malloc(cchEntryName + 1); + + if (!pszEntryName) + { + free(pszFileName); + return NULL; + } + + sprintf_s(pszEntryName, cchEntryName + 1, "freerdp_%s_client_subsystem_entry", pszName); + entry = freerdp_load_dynamic_addin(pszFileName, NULL, pszEntryName); + free(pszEntryName); + free(pszFileName); + return entry; + } + + /* channel add-in */ + + if (dwFlags & FREERDP_ADDIN_CHANNEL_STATIC) + { + if (dwFlags & FREERDP_ADDIN_CHANNEL_ENTRYEX) + entry = freerdp_load_dynamic_addin(pszFileName, NULL, "VirtualChannelEntryEx"); + else + entry = freerdp_load_dynamic_addin(pszFileName, NULL, "VirtualChannelEntry"); + } + else if (dwFlags & FREERDP_ADDIN_CHANNEL_DYNAMIC) + entry = freerdp_load_dynamic_addin(pszFileName, NULL, "DVCPluginEntry"); + else if (dwFlags & FREERDP_ADDIN_CHANNEL_DEVICE) + entry = freerdp_load_dynamic_addin(pszFileName, NULL, "DeviceServiceEntry"); + else + entry = freerdp_load_dynamic_addin(pszFileName, NULL, pszType); + + free(pszFileName); + return entry; +} + +static FREERDP_LOAD_CHANNEL_ADDIN_ENTRY_FN freerdp_load_static_channel_addin_entry = NULL; + +int freerdp_register_addin_provider(FREERDP_LOAD_CHANNEL_ADDIN_ENTRY_FN provider, DWORD dwFlags) +{ + freerdp_load_static_channel_addin_entry = provider; + return 0; +} + +FREERDP_LOAD_CHANNEL_ADDIN_ENTRY_FN freerdp_get_current_addin_provider(void) +{ + return freerdp_load_static_channel_addin_entry; +} + +PVIRTUALCHANNELENTRY freerdp_load_channel_addin_entry(LPCSTR pszName, LPCSTR pszSubsystem, + LPCSTR pszType, DWORD dwFlags) +{ + PVIRTUALCHANNELENTRY entry = NULL; + + if (freerdp_load_static_channel_addin_entry) + entry = freerdp_load_static_channel_addin_entry(pszName, pszSubsystem, pszType, dwFlags); + + if (!entry) + entry = freerdp_load_dynamic_channel_addin_entry(pszName, pszSubsystem, pszType, dwFlags); + + if (!entry) + WLog_WARN(TAG, "Failed to load channel %s [%s]", pszName, pszSubsystem); + + return entry; +} |