1 /**
2 * FreeRDP: A Remote Desktop Protocol Implementation
3 * Addin Loader
4 *
5 * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include <winpr/crt.h>
29 #include <winpr/path.h>
30 #include <winpr/string.h>
31 #include <winpr/library.h>
32
33 #include <freerdp/addin.h>
34 #include <freerdp/build-config.h>
35
36 #include <freerdp/log.h>
37 #define TAG FREERDP_TAG("addin")
38
is_path_required(LPCSTR path,size_t len)39 static INLINE BOOL is_path_required(LPCSTR path, size_t len)
40 {
41 if (!path || (len <= 1))
42 return FALSE;
43
44 if (strcmp(path, ".") == 0)
45 return FALSE;
46
47 return TRUE;
48 }
49
freerdp_get_library_install_path(void)50 LPSTR freerdp_get_library_install_path(void)
51 {
52 LPSTR pszPath;
53 size_t cchPath;
54 size_t cchLibraryPath;
55 size_t cchInstallPrefix;
56 BOOL needLibPath, needInstallPath;
57 LPCSTR pszLibraryPath = FREERDP_LIBRARY_PATH;
58 LPCSTR pszInstallPrefix = FREERDP_INSTALL_PREFIX;
59 cchLibraryPath = strlen(pszLibraryPath) + 1;
60 cchInstallPrefix = strlen(pszInstallPrefix) + 1;
61 cchPath = cchInstallPrefix + cchLibraryPath;
62 needInstallPath = is_path_required(pszInstallPrefix, cchInstallPrefix);
63 needLibPath = is_path_required(pszLibraryPath, cchLibraryPath);
64
65 if (!needInstallPath && !needLibPath)
66 return NULL;
67
68 pszPath = (LPSTR)malloc(cchPath + 1);
69
70 if (!pszPath)
71 return NULL;
72
73 if (needInstallPath)
74 {
75 CopyMemory(pszPath, pszInstallPrefix, cchInstallPrefix);
76 pszPath[cchInstallPrefix] = '\0';
77 }
78
79 if (needLibPath)
80 {
81 if (FAILED(NativePathCchAppendA(pszPath, cchPath + 1, pszLibraryPath)))
82 {
83 free(pszPath);
84 return NULL;
85 }
86 }
87
88 return pszPath;
89 }
90
freerdp_get_dynamic_addin_install_path(void)91 LPSTR freerdp_get_dynamic_addin_install_path(void)
92 {
93 LPSTR pszPath;
94 size_t cchPath;
95 size_t cchAddinPath;
96 size_t cchInstallPrefix;
97 BOOL needLibPath, needInstallPath;
98 LPCSTR pszAddinPath = FREERDP_ADDIN_PATH;
99 LPCSTR pszInstallPrefix = FREERDP_INSTALL_PREFIX;
100 cchAddinPath = strlen(pszAddinPath) + 1;
101 cchInstallPrefix = strlen(pszInstallPrefix) + 1;
102 cchPath = cchInstallPrefix + cchAddinPath;
103 needInstallPath = is_path_required(pszInstallPrefix, cchInstallPrefix);
104 needLibPath = is_path_required(pszAddinPath, cchAddinPath);
105
106 if (!needInstallPath && !needLibPath)
107 return NULL;
108
109 pszPath = (LPSTR)calloc(cchPath + 1, sizeof(CHAR));
110
111 if (!pszPath)
112 return NULL;
113
114 if (needInstallPath)
115 {
116 CopyMemory(pszPath, pszInstallPrefix, cchInstallPrefix);
117 pszPath[cchInstallPrefix] = '\0';
118 }
119
120 if (needLibPath)
121 {
122 if (FAILED(NativePathCchAppendA(pszPath, cchPath + 1, pszAddinPath)))
123 {
124 free(pszPath);
125 return NULL;
126 }
127 }
128
129 return pszPath;
130 }
131
freerdp_load_dynamic_addin(LPCSTR pszFileName,LPCSTR pszPath,LPCSTR pszEntryName)132 PVIRTUALCHANNELENTRY freerdp_load_dynamic_addin(LPCSTR pszFileName, LPCSTR pszPath,
133 LPCSTR pszEntryName)
134 {
135 LPSTR pszAddinInstallPath = freerdp_get_dynamic_addin_install_path();
136 PVIRTUALCHANNELENTRY entry = NULL;
137 BOOL bHasExt = TRUE;
138 PCSTR pszExt;
139 size_t cchExt = 0;
140 HINSTANCE library = NULL;
141 size_t cchFileName;
142 size_t cchFilePath;
143 LPSTR pszAddinFile = NULL;
144 LPSTR pszFilePath = NULL;
145 LPSTR pszRelativeFilePath = NULL;
146 size_t cchAddinFile;
147 size_t cchAddinInstallPath;
148
149 if (!pszFileName || !pszEntryName)
150 goto fail;
151
152 cchFileName = strlen(pszFileName);
153
154 /* Get file name with prefix and extension */
155 if (FAILED(PathCchFindExtensionA(pszFileName, cchFileName + 1, &pszExt)))
156 {
157 pszExt = PathGetSharedLibraryExtensionA(PATH_SHARED_LIB_EXT_WITH_DOT);
158 cchExt = strlen(pszExt);
159 bHasExt = FALSE;
160 }
161
162 if (bHasExt)
163 {
164 pszAddinFile = _strdup(pszFileName);
165
166 if (!pszAddinFile)
167 goto fail;
168 }
169 else
170 {
171 cchAddinFile = cchFileName + cchExt + 2 + sizeof(FREERDP_SHARED_LIBRARY_PREFIX);
172 pszAddinFile = (LPSTR)malloc(cchAddinFile + 1);
173
174 if (!pszAddinFile)
175 goto fail;
176
177 sprintf_s(pszAddinFile, cchAddinFile, FREERDP_SHARED_LIBRARY_PREFIX "%s%s", pszFileName,
178 pszExt);
179 }
180
181 cchAddinFile = strlen(pszAddinFile);
182
183 /* If a path is provided prefix the library name with it. */
184 if (pszPath)
185 {
186 size_t relPathLen = strlen(pszPath) + cchAddinFile + 1;
187 pszRelativeFilePath = calloc(relPathLen, sizeof(CHAR));
188
189 if (!pszRelativeFilePath)
190 goto fail;
191
192 sprintf_s(pszRelativeFilePath, relPathLen, "%s", pszPath);
193 NativePathCchAppendA(pszRelativeFilePath, relPathLen, pszAddinFile);
194 }
195 else
196 pszRelativeFilePath = _strdup(pszAddinFile);
197
198 if (!pszRelativeFilePath)
199 goto fail;
200
201 /* If a system prefix path is provided try these locations too. */
202 if (pszAddinInstallPath)
203 {
204 cchAddinInstallPath = strlen(pszAddinInstallPath);
205 cchFilePath = cchAddinInstallPath + cchFileName + 32;
206 pszFilePath = (LPSTR)malloc(cchFilePath + 1);
207
208 if (!pszFilePath)
209 goto fail;
210
211 CopyMemory(pszFilePath, pszAddinInstallPath, cchAddinInstallPath);
212 pszFilePath[cchAddinInstallPath] = '\0';
213 NativePathCchAppendA((LPSTR)pszFilePath, cchFilePath + 1, pszRelativeFilePath);
214 }
215 else
216 pszFilePath = _strdup(pszRelativeFilePath);
217
218 library = LoadLibraryA(pszFilePath);
219
220 if (!library)
221 goto fail;
222
223 entry = (PVIRTUALCHANNELENTRY)GetProcAddress(library, pszEntryName);
224 fail:
225 free(pszRelativeFilePath);
226 free(pszAddinFile);
227 free(pszFilePath);
228 free(pszAddinInstallPath);
229
230 if (!entry && library)
231 FreeLibrary(library);
232
233 return entry;
234 }
235
freerdp_load_dynamic_channel_addin_entry(LPCSTR pszName,LPCSTR pszSubsystem,LPCSTR pszType,DWORD dwFlags)236 PVIRTUALCHANNELENTRY freerdp_load_dynamic_channel_addin_entry(LPCSTR pszName, LPCSTR pszSubsystem,
237 LPCSTR pszType, DWORD dwFlags)
238 {
239 PVIRTUALCHANNELENTRY entry;
240 LPSTR pszFileName;
241 const size_t cchBaseFileName = sizeof(FREERDP_SHARED_LIBRARY_PREFIX) + 32;
242 LPCSTR pszExtension;
243 LPCSTR pszPrefix = FREERDP_SHARED_LIBRARY_PREFIX;
244 size_t nameLen = 0;
245 size_t subsystemLen = 0;
246 size_t typeLen = 0;
247 size_t extensionLen = 0;
248 pszExtension = PathGetSharedLibraryExtensionA(0);
249 if (pszName)
250 nameLen = strnlen(pszName, MAX_PATH);
251 if (pszSubsystem)
252 subsystemLen = strnlen(pszSubsystem, MAX_PATH);
253 if (pszType)
254 typeLen = strnlen(pszType, MAX_PATH);
255 if (pszExtension)
256 extensionLen = strnlen(pszExtension, MAX_PATH);
257
258 if (pszName && pszSubsystem && pszType)
259 {
260 const size_t cchFileName =
261 cchBaseFileName + nameLen + subsystemLen + typeLen + extensionLen;
262 pszFileName = (LPSTR)malloc(cchFileName);
263
264 if (!pszFileName)
265 return NULL;
266
267 sprintf_s(pszFileName, cchFileName, "%s%s-client-%s-%s.%s", pszPrefix, pszName,
268 pszSubsystem, pszType, pszExtension);
269 }
270 else if (pszName && pszSubsystem)
271 {
272 const size_t cchFileName = cchBaseFileName + nameLen + subsystemLen + extensionLen;
273 pszFileName = (LPSTR)malloc(cchFileName);
274
275 if (!pszFileName)
276 return NULL;
277
278 sprintf_s(pszFileName, cchFileName, "%s%s-client-%s.%s", pszPrefix, pszName, pszSubsystem,
279 pszExtension);
280 }
281 else if (pszName)
282 {
283 const size_t cchFileName = cchBaseFileName + nameLen + extensionLen;
284 pszFileName = (LPSTR)malloc(cchFileName);
285
286 if (!pszFileName)
287 return NULL;
288
289 sprintf_s(pszFileName, cchFileName, "%s%s-client.%s", pszPrefix, pszName, pszExtension);
290 }
291 else
292 {
293 return NULL;
294 }
295
296 if (pszSubsystem)
297 {
298 LPSTR pszEntryName;
299 size_t cchEntryName;
300 /* subsystem add-in */
301 cchEntryName = 64 + nameLen;
302 pszEntryName = (LPSTR)malloc(cchEntryName + 1);
303
304 if (!pszEntryName)
305 {
306 free(pszFileName);
307 return NULL;
308 }
309
310 sprintf_s(pszEntryName, cchEntryName + 1, "freerdp_%s_client_subsystem_entry", pszName);
311 entry = freerdp_load_dynamic_addin(pszFileName, NULL, pszEntryName);
312 free(pszEntryName);
313 free(pszFileName);
314 return entry;
315 }
316
317 /* channel add-in */
318
319 if (dwFlags & FREERDP_ADDIN_CHANNEL_STATIC)
320 {
321 if (dwFlags & FREERDP_ADDIN_CHANNEL_ENTRYEX)
322 entry = freerdp_load_dynamic_addin(pszFileName, NULL, "VirtualChannelEntryEx");
323 else
324 entry = freerdp_load_dynamic_addin(pszFileName, NULL, "VirtualChannelEntry");
325 }
326 else if (dwFlags & FREERDP_ADDIN_CHANNEL_DYNAMIC)
327 entry = freerdp_load_dynamic_addin(pszFileName, NULL, "DVCPluginEntry");
328 else if (dwFlags & FREERDP_ADDIN_CHANNEL_DEVICE)
329 entry = freerdp_load_dynamic_addin(pszFileName, NULL, "DeviceServiceEntry");
330 else
331 entry = freerdp_load_dynamic_addin(pszFileName, NULL, pszType);
332
333 free(pszFileName);
334 return entry;
335 }
336
337 static FREERDP_LOAD_CHANNEL_ADDIN_ENTRY_FN freerdp_load_static_channel_addin_entry = NULL;
338
freerdp_register_addin_provider(FREERDP_LOAD_CHANNEL_ADDIN_ENTRY_FN provider,DWORD dwFlags)339 int freerdp_register_addin_provider(FREERDP_LOAD_CHANNEL_ADDIN_ENTRY_FN provider, DWORD dwFlags)
340 {
341 freerdp_load_static_channel_addin_entry = provider;
342 return 0;
343 }
344
freerdp_load_channel_addin_entry(LPCSTR pszName,LPCSTR pszSubsystem,LPCSTR pszType,DWORD dwFlags)345 PVIRTUALCHANNELENTRY freerdp_load_channel_addin_entry(LPCSTR pszName, LPCSTR pszSubsystem,
346 LPCSTR pszType, DWORD dwFlags)
347 {
348 PVIRTUALCHANNELENTRY entry = NULL;
349
350 if (freerdp_load_static_channel_addin_entry)
351 entry = freerdp_load_static_channel_addin_entry(pszName, pszSubsystem, pszType, dwFlags);
352
353 if (!entry)
354 entry = freerdp_load_dynamic_channel_addin_entry(pszName, pszSubsystem, pszType, dwFlags);
355
356 if (!entry)
357 WLog_WARN(TAG, "Failed to load channel %s [%s]", pszName, pszSubsystem);
358
359 return entry;
360 }
361