Coverage Report

Created: 2026-08-31 06:25

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