FreeRDP
libfreerdp/common/addin.c
1 
20 #include <freerdp/config.h>
21 
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 
26 #include <winpr/crt.h>
27 #include <winpr/path.h>
28 #include <winpr/string.h>
29 #include <winpr/library.h>
30 
31 #include <freerdp/addin.h>
32 #include <freerdp/build-config.h>
33 
34 #include <freerdp/log.h>
35 #define TAG FREERDP_TAG("addin")
36 
37 static INLINE BOOL is_path_required(LPCSTR path, size_t len)
38 {
39  if (!path || (len <= 1))
40  return FALSE;
41 
42  if (strcmp(path, ".") == 0)
43  return FALSE;
44 
45  return TRUE;
46 }
47 
48 LPSTR freerdp_get_library_install_path(void)
49 {
50  LPSTR pszPath = NULL;
51  size_t cchPath = 0;
52  size_t cchLibraryPath = 0;
53  size_t cchInstallPrefix = 0;
54  BOOL needLibPath = 0;
55  BOOL needInstallPath = 0;
56  LPCSTR pszLibraryPath = FREERDP_LIBRARY_PATH;
57  LPCSTR pszInstallPrefix = FREERDP_INSTALL_PREFIX;
58  cchLibraryPath = strlen(pszLibraryPath) + 1;
59  cchInstallPrefix = strlen(pszInstallPrefix) + 1;
60  cchPath = cchInstallPrefix + cchLibraryPath;
61  needInstallPath = is_path_required(pszInstallPrefix, cchInstallPrefix);
62  needLibPath = is_path_required(pszLibraryPath, cchLibraryPath);
63 
64  if (!needInstallPath && !needLibPath)
65  return NULL;
66 
67  pszPath = (LPSTR)malloc(cchPath + 1);
68 
69  if (!pszPath)
70  return NULL;
71 
72  if (needInstallPath)
73  {
74  CopyMemory(pszPath, pszInstallPrefix, cchInstallPrefix);
75  pszPath[cchInstallPrefix] = '\0';
76  }
77 
78  if (needLibPath)
79  {
80  if (FAILED(NativePathCchAppendA(pszPath, cchPath + 1, pszLibraryPath)))
81  {
82  free(pszPath);
83  return NULL;
84  }
85  }
86 
87  return pszPath;
88 }
89 
90 LPSTR freerdp_get_dynamic_addin_install_path(void)
91 {
92 #if defined(WITH_ADD_PLUGIN_TO_RPATH)
93  return NULL;
94 #else
95  LPSTR pszPath = NULL;
96  size_t cchPath = 0;
97  size_t cchAddinPath = 0;
98  size_t cchInstallPrefix = 0;
99  BOOL needLibPath = 0;
100  BOOL needInstallPath = 0;
101  LPCSTR pszAddinPath = FREERDP_ADDIN_PATH;
102  LPCSTR pszInstallPrefix = FREERDP_INSTALL_PREFIX;
103  cchAddinPath = strlen(pszAddinPath) + 1;
104  cchInstallPrefix = strlen(pszInstallPrefix) + 1;
105  cchPath = cchInstallPrefix + cchAddinPath;
106  needInstallPath = is_path_required(pszInstallPrefix, cchInstallPrefix);
107  needLibPath = is_path_required(pszAddinPath, cchAddinPath);
108 
109  WLog_DBG(TAG,
110  "freerdp_get_dynamic_addin_install_path <- pszInstallPrefix: %s, pszAddinPath: %s",
111  pszInstallPrefix, pszAddinPath);
112 
113  if (!needInstallPath && !needLibPath)
114  return NULL;
115 
116  pszPath = (LPSTR)calloc(cchPath + 1, sizeof(CHAR));
117 
118  if (!pszPath)
119  return NULL;
120 
121  if (needInstallPath)
122  {
123  CopyMemory(pszPath, pszInstallPrefix, cchInstallPrefix);
124  pszPath[cchInstallPrefix] = '\0';
125  }
126 
127  if (needLibPath)
128  {
129  if (FAILED(NativePathCchAppendA(pszPath, cchPath + 1, pszAddinPath)))
130  {
131  free(pszPath);
132  return NULL;
133  }
134  }
135 
136  WLog_DBG(TAG, "freerdp_get_dynamic_addin_install_path -> pszPath: %s", pszPath);
137 
138  return pszPath;
139 #endif
140 }
141 
142 PVIRTUALCHANNELENTRY freerdp_load_dynamic_addin(LPCSTR pszFileName, LPCSTR pszPath,
143  LPCSTR pszEntryName)
144 {
145  LPSTR pszAddinInstallPath = freerdp_get_dynamic_addin_install_path();
146  PVIRTUALCHANNELENTRY entry = NULL;
147  BOOL bHasExt = TRUE;
148  PCSTR pszExt = NULL;
149  size_t cchExt = 0;
150  HINSTANCE library = NULL;
151  size_t cchFileName = 0;
152  size_t cchFilePath = 0;
153  LPSTR pszAddinFile = NULL;
154  LPSTR pszFilePath = NULL;
155  LPSTR pszRelativeFilePath = NULL;
156  size_t cchAddinFile = 0;
157  size_t cchAddinInstallPath = 0;
158 
159  if (!pszFileName || !pszEntryName)
160  goto fail;
161 
162  WLog_DBG(TAG, "freerdp_load_dynamic_addin <- pszFileName: %s, pszPath: %s, pszEntryName: %s",
163  pszFileName, pszPath, pszEntryName);
164 
165  cchFileName = strlen(pszFileName);
166 
167  /* Get file name with prefix and extension */
168  if (FAILED(PathCchFindExtensionA(pszFileName, cchFileName + 1, &pszExt)))
169  {
170  pszExt = PathGetSharedLibraryExtensionA(PATH_SHARED_LIB_EXT_WITH_DOT);
171  cchExt = strlen(pszExt);
172  bHasExt = FALSE;
173  }
174 
175  if (bHasExt)
176  {
177  pszAddinFile = _strdup(pszFileName);
178 
179  if (!pszAddinFile)
180  goto fail;
181  }
182  else
183  {
184  cchAddinFile = cchFileName + cchExt + 2 + sizeof(FREERDP_SHARED_LIBRARY_PREFIX);
185  pszAddinFile = (LPSTR)malloc(cchAddinFile + 1);
186 
187  if (!pszAddinFile)
188  goto fail;
189 
190  (void)sprintf_s(pszAddinFile, cchAddinFile, FREERDP_SHARED_LIBRARY_PREFIX "%s%s",
191  pszFileName, pszExt);
192  }
193 
194  cchAddinFile = strlen(pszAddinFile);
195 
196  /* If a path is provided prefix the library name with it. */
197  if (pszPath)
198  {
199  size_t relPathLen = strlen(pszPath) + cchAddinFile + 1;
200  pszRelativeFilePath = calloc(relPathLen, sizeof(CHAR));
201 
202  if (!pszRelativeFilePath)
203  goto fail;
204 
205  (void)sprintf_s(pszRelativeFilePath, relPathLen, "%s", pszPath);
206  const HRESULT hr = NativePathCchAppendA(pszRelativeFilePath, relPathLen, pszAddinFile);
207  if (FAILED(hr))
208  goto fail;
209  }
210  else
211  pszRelativeFilePath = _strdup(pszAddinFile);
212 
213  if (!pszRelativeFilePath)
214  goto fail;
215 
216  /* If a system prefix path is provided try these locations too. */
217  if (pszAddinInstallPath)
218  {
219  cchAddinInstallPath = strlen(pszAddinInstallPath);
220  cchFilePath = cchAddinInstallPath + cchFileName + 32;
221  pszFilePath = (LPSTR)malloc(cchFilePath + 1);
222 
223  if (!pszFilePath)
224  goto fail;
225 
226  CopyMemory(pszFilePath, pszAddinInstallPath, cchAddinInstallPath);
227  pszFilePath[cchAddinInstallPath] = '\0';
228  const HRESULT hr = NativePathCchAppendA(pszFilePath, cchFilePath + 1, pszRelativeFilePath);
229  if (FAILED(hr))
230  goto fail;
231  }
232  else
233  pszFilePath = _strdup(pszRelativeFilePath);
234 
235  library = LoadLibraryX(pszFilePath);
236 
237  if (!library)
238  goto fail;
239 
240  entry = GetProcAddressAs(library, pszEntryName, PVIRTUALCHANNELENTRY);
241 fail:
242  free(pszRelativeFilePath);
243  free(pszAddinFile);
244  free(pszFilePath);
245  free(pszAddinInstallPath);
246 
247  if (!entry && library)
248  FreeLibrary(library);
249 
250  return entry;
251 }
252 
253 PVIRTUALCHANNELENTRY freerdp_load_dynamic_channel_addin_entry(LPCSTR pszName, LPCSTR pszSubsystem,
254  LPCSTR pszType, DWORD dwFlags)
255 {
256  PVIRTUALCHANNELENTRY entry = NULL;
257  LPSTR pszFileName = NULL;
258  const size_t cchBaseFileName = sizeof(FREERDP_SHARED_LIBRARY_PREFIX) + 32;
259  size_t nameLen = 0;
260  size_t subsystemLen = 0;
261  size_t typeLen = 0;
262  size_t cchFileName = 0;
263 
264  if (pszName)
265  nameLen = strnlen(pszName, MAX_PATH);
266  if (pszSubsystem)
267  subsystemLen = strnlen(pszSubsystem, MAX_PATH);
268  if (pszType)
269  typeLen = strnlen(pszType, MAX_PATH);
270 
271  if (pszName && pszSubsystem && pszType)
272  {
273  cchFileName = cchBaseFileName + nameLen + subsystemLen + typeLen;
274  pszFileName = (LPSTR)malloc(cchFileName);
275 
276  if (!pszFileName)
277  return NULL;
278 
279  (void)sprintf_s(pszFileName, cchFileName, "%s-client-%s-%s", pszName, pszSubsystem,
280  pszType);
281  }
282  else if (pszName && pszSubsystem)
283  {
284  cchFileName = cchBaseFileName + nameLen + subsystemLen;
285  pszFileName = (LPSTR)malloc(cchFileName);
286 
287  if (!pszFileName)
288  return NULL;
289 
290  (void)sprintf_s(pszFileName, cchFileName, "%s-client-%s", pszName, pszSubsystem);
291  }
292  else if (pszName)
293  {
294  cchFileName = cchBaseFileName + nameLen;
295  pszFileName = (LPSTR)malloc(cchFileName);
296 
297  if (!pszFileName)
298  return NULL;
299 
300  (void)sprintf_s(pszFileName, cchFileName, "%s-client", pszName);
301  }
302  else
303  {
304  return NULL;
305  }
306 
307  {
308  LPCSTR pszExtension = PathGetSharedLibraryExtensionA(0);
309  const char pszPrefix[] = FREERDP_SHARED_LIBRARY_PREFIX;
310  int rc = 0;
311 
312  cchFileName += strnlen(pszPrefix, ARRAYSIZE(pszPrefix));
313  if (pszExtension)
314  cchFileName += strnlen(pszExtension, MAX_PATH) + 1;
315  LPSTR tmp = calloc(cchFileName, sizeof(CHAR));
316  if (tmp)
317  rc = sprintf_s(tmp, cchFileName, "%s%s.%s", pszPrefix, pszFileName, pszExtension);
318 
319  free(pszFileName);
320  pszFileName = tmp;
321  if (!pszFileName || (rc < 0))
322  {
323  free(pszFileName);
324  return NULL;
325  }
326  }
327 
328  if (pszSubsystem)
329  {
330  LPSTR pszEntryName = NULL;
331  size_t cchEntryName = 0;
332  /* subsystem add-in */
333  cchEntryName = 64 + nameLen;
334  pszEntryName = (LPSTR)malloc(cchEntryName + 1);
335 
336  if (!pszEntryName)
337  {
338  free(pszFileName);
339  return NULL;
340  }
341 
342  (void)sprintf_s(pszEntryName, cchEntryName + 1, "freerdp_%s_client_subsystem_entry",
343  pszName);
344  entry = freerdp_load_dynamic_addin(pszFileName, NULL, pszEntryName);
345  free(pszEntryName);
346  free(pszFileName);
347  return entry;
348  }
349 
350  /* channel add-in */
351 
352  if (dwFlags & FREERDP_ADDIN_CHANNEL_STATIC)
353  {
354  if (dwFlags & FREERDP_ADDIN_CHANNEL_ENTRYEX)
355  entry = freerdp_load_dynamic_addin(pszFileName, NULL, "VirtualChannelEntryEx");
356  else
357  entry = freerdp_load_dynamic_addin(pszFileName, NULL, "VirtualChannelEntry");
358  }
359  else if (dwFlags & FREERDP_ADDIN_CHANNEL_DYNAMIC)
360  entry = freerdp_load_dynamic_addin(pszFileName, NULL, "DVCPluginEntry");
361  else if (dwFlags & FREERDP_ADDIN_CHANNEL_DEVICE)
362  entry = freerdp_load_dynamic_addin(pszFileName, NULL, "DeviceServiceEntry");
363  else
364  entry = freerdp_load_dynamic_addin(pszFileName, NULL, pszType);
365 
366  free(pszFileName);
367  return entry;
368 }
369 
370 static FREERDP_LOAD_CHANNEL_ADDIN_ENTRY_FN freerdp_load_static_channel_addin_entry = NULL;
371 
372 int freerdp_register_addin_provider(FREERDP_LOAD_CHANNEL_ADDIN_ENTRY_FN provider, DWORD dwFlags)
373 {
374  freerdp_load_static_channel_addin_entry = provider;
375  return 0;
376 }
377 
378 FREERDP_LOAD_CHANNEL_ADDIN_ENTRY_FN freerdp_get_current_addin_provider(void)
379 {
380  return freerdp_load_static_channel_addin_entry;
381 }
382 
383 PVIRTUALCHANNELENTRY freerdp_load_channel_addin_entry(LPCSTR pszName, LPCSTR pszSubsystem,
384  LPCSTR pszType, DWORD dwFlags)
385 {
386  PVIRTUALCHANNELENTRY entry = NULL;
387 
388  if (freerdp_load_static_channel_addin_entry)
389  entry = freerdp_load_static_channel_addin_entry(pszName, pszSubsystem, pszType, dwFlags);
390 
391  if (!entry)
392  entry = freerdp_load_dynamic_channel_addin_entry(pszName, pszSubsystem, pszType, dwFlags);
393 
394  if (!entry)
395  WLog_WARN(TAG, "Failed to load channel %s [%s]", pszName, pszSubsystem);
396 
397  return entry;
398 }