/** * FreeRDP: A Remote Desktop Protocol Implementation * Addin Loader * * Copyright 2012 Marc-Andre Moreau * * 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 #include #include #include #include #include #include #include #include #include #include #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; }