22#include <freerdp/config.h> 
   25#include <winpr/assert.h> 
   26#include <winpr/path.h> 
   27#include <winpr/string.h> 
   28#include <winpr/file.h> 
   29#include <winpr/synch.h> 
   30#include <winpr/library.h> 
   31#include <winpr/collections.h> 
   33#include <freerdp/freerdp.h> 
   34#include <freerdp/addin.h> 
   35#include <freerdp/build-config.h> 
   36#include <freerdp/client/channels.h> 
   42#include <freerdp/channels/log.h> 
   43#define TAG CHANNELS_TAG("addin") 
   47static void* freerdp_channels_find_static_entry_in_table(
const STATIC_ENTRY_TABLE* table,
 
   48                                                         const char* identifier)
 
   53  while (pEntry->entry != NULL)
 
   55    static_entry_fn_t fkt = pEntry->entry;
 
   56    if (strcmp(pEntry->name, identifier) == 0)
 
   57      return WINPR_FUNC_PTR_CAST(fkt, 
void*);
 
   59    pEntry = &table->table.cse[index++];
 
   65void* freerdp_channels_client_find_static_entry(
const char* name, 
const char* identifier)
 
   70  while (pEntry->table.cse != NULL)
 
   72    if (strcmp(pEntry->name, name) == 0)
 
   74      return freerdp_channels_find_static_entry_in_table(pEntry, identifier);
 
   77    pEntry = &CLIENT_STATIC_ENTRY_TABLES[index++];
 
   85static FREERDP_ADDIN** freerdp_channels_list_client_static_addins(
 
   86    WINPR_ATTR_UNUSED LPCSTR pszName, WINPR_ATTR_UNUSED LPCSTR pszSubsystem,
 
   87    WINPR_ATTR_UNUSED LPCSTR pszType, WINPR_ATTR_UNUSED DWORD dwFlags)
 
   97    WLog_ERR(TAG, 
"calloc failed!");
 
  101  ppAddins[nAddins] = NULL;
 
  103  for (
size_t i = 0; CLIENT_STATIC_ADDIN_TABLE[i].name != NULL; i++)
 
  109      WLog_ERR(TAG, 
"calloc failed!");
 
  113    (void)sprintf_s(pAddin->cName, ARRAYSIZE(pAddin->cName), 
"%s", table->name);
 
  114    pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
 
  115    pAddin->dwFlags |= FREERDP_ADDIN_STATIC;
 
  116    pAddin->dwFlags |= FREERDP_ADDIN_NAME;
 
  117    ppAddins[nAddins++] = pAddin;
 
  118    subsystems = table->table;
 
  120    for (
size_t j = 0; subsystems[j].name != NULL; j++)
 
  126        WLog_ERR(TAG, 
"calloc failed!");
 
  130      (void)sprintf_s(pAddin->cName, ARRAYSIZE(pAddin->cName), 
"%s", table->name);
 
  131      (void)sprintf_s(pAddin->cSubsystem, ARRAYSIZE(pAddin->cSubsystem), 
"%s",
 
  133      pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
 
  134      pAddin->dwFlags |= FREERDP_ADDIN_STATIC;
 
  135      pAddin->dwFlags |= FREERDP_ADDIN_NAME;
 
  136      pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM;
 
  137      ppAddins[nAddins++] = pAddin;
 
  143  freerdp_channels_addin_list_free(ppAddins);
 
  147static HANDLE FindFirstFileUTF8(LPCSTR pszSearchPath, 
WIN32_FIND_DATAW* FindData)
 
  149  HANDLE hdl = INVALID_HANDLE_VALUE;
 
  152  WCHAR* wpath = ConvertUtf8ToWCharAlloc(pszSearchPath, NULL);
 
  156  hdl = FindFirstFileW(wpath, FindData);
 
  162static FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPCSTR pszName, LPCSTR pszSubsystem,
 
  164                                                            WINPR_ATTR_UNUSED DWORD dwFlags)
 
  169  LPSTR pszPattern = NULL;
 
  170  size_t cchPattern = 0;
 
  171  LPCSTR pszAddinPath = FREERDP_ADDIN_PATH;
 
  172  LPCSTR pszInstallPrefix = FREERDP_INSTALL_PREFIX;
 
  173  LPCSTR pszExtension = NULL;
 
  174  LPSTR pszSearchPath = NULL;
 
  175  size_t cchSearchPath = 0;
 
  176  size_t cchAddinPath = 0;
 
  177  size_t cchInstallPrefix = 0;
 
  180  cchAddinPath = strnlen(pszAddinPath, 
sizeof(FREERDP_ADDIN_PATH));
 
  181  cchInstallPrefix = strnlen(pszInstallPrefix, 
sizeof(FREERDP_INSTALL_PREFIX));
 
  182  pszExtension = PathGetSharedLibraryExtensionA(0);
 
  183  cchPattern = 128 + strnlen(pszExtension, MAX_PATH) + 2;
 
  184  pszPattern = (LPSTR)malloc(cchPattern + 1);
 
  188    WLog_ERR(TAG, 
"malloc failed!");
 
  192  if (pszName && pszSubsystem && pszType)
 
  194    (void)sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX 
"%s-client-%s-%s.%s",
 
  195                    pszName, pszSubsystem, pszType, pszExtension);
 
  197  else if (pszName && pszType)
 
  199    (void)sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX 
"%s-client-?-%s.%s",
 
  200                    pszName, pszType, pszExtension);
 
  204    (void)sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX 
"%s-client*.%s",
 
  205                    pszName, pszExtension);
 
  209    (void)sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX 
"?-client*.%s",
 
  213  cchPattern = strnlen(pszPattern, cchPattern);
 
  214  cchSearchPath = cchInstallPrefix + cchAddinPath + cchPattern + 3;
 
  215  pszSearchPath = (LPSTR)calloc(cchSearchPath + 1, 
sizeof(
char));
 
  219    WLog_ERR(TAG, 
"malloc failed!");
 
  224  CopyMemory(pszSearchPath, pszInstallPrefix, cchInstallPrefix);
 
  225  pszSearchPath[cchInstallPrefix] = 
'\0';
 
  226  const HRESULT hr1 = NativePathCchAppendA(pszSearchPath, cchSearchPath + 1, pszAddinPath);
 
  227  const HRESULT hr2 = NativePathCchAppendA(pszSearchPath, cchSearchPath + 1, pszPattern);
 
  230  if (FAILED(hr1) || FAILED(hr2))
 
  236  hFind = FindFirstFileUTF8(pszSearchPath, &FindData);
 
  245    WLog_ERR(TAG, 
"calloc failed!");
 
  249  if (hFind == INVALID_HANDLE_VALUE)
 
  254    char* cFileName = NULL;
 
  260      WLog_ERR(TAG, 
"calloc failed!");
 
  265        ConvertWCharNToUtf8Alloc(FindData.cFileName, ARRAYSIZE(FindData.cFileName), NULL);
 
  270    for (
size_t index = 0; cFileName[index]; index++)
 
  271      nDashes += (cFileName[index] == 
'-') ? 1 : 0;
 
  279      p[1] = strchr(p[0], 
'-');
 
  284      len = (size_t)(p[1] - p[0]);
 
  287        WLog_WARN(TAG, 
"Skipping file '%s', invalid format", cFileName);
 
  290      strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
 
  292      pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
 
  293      pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
 
  294      pAddin->dwFlags |= FREERDP_ADDIN_NAME;
 
  295      ppAddins[nAddins++] = pAddin;
 
  299    else if (nDashes == 2)
 
  305      p[1] = strchr(p[0], 
'-');
 
  309      p[2] = strchr(p[1], 
'-');
 
  313      p[3] = strchr(p[2], 
'.');
 
  318      len = (size_t)(p[1] - p[0]);
 
  321        WLog_WARN(TAG, 
"Skipping file '%s', invalid format", cFileName);
 
  324      strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
 
  326      len = (size_t)(p[3] - p[2]);
 
  329        WLog_WARN(TAG, 
"Skipping file '%s', invalid format", cFileName);
 
  332      strncpy(pAddin->cSubsystem, p[2], MIN(ARRAYSIZE(pAddin->cSubsystem), len - 1));
 
  334      pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
 
  335      pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
 
  336      pAddin->dwFlags |= FREERDP_ADDIN_NAME;
 
  337      pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM;
 
  338      ppAddins[nAddins++] = pAddin;
 
  342    else if (nDashes == 3)
 
  348      p[1] = strchr(p[0], 
'-');
 
  352      p[2] = strchr(p[1], 
'-');
 
  356      p[3] = strchr(p[2], 
'-');
 
  360      p[4] = strchr(p[3], 
'.');
 
  365      len = (size_t)(p[1] - p[0]);
 
  368        WLog_WARN(TAG, 
"Skipping file '%s', invalid format", cFileName);
 
  371      strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1));
 
  373      len = (size_t)(p[3] - p[2]);
 
  376        WLog_WARN(TAG, 
"Skipping file '%s', invalid format", cFileName);
 
  379      strncpy(pAddin->cSubsystem, p[2], MIN(ARRAYSIZE(pAddin->cSubsystem), len - 1));
 
  381      len = (size_t)(p[4] - p[3]);
 
  384        WLog_WARN(TAG, 
"Skipping file '%s', invalid format", cFileName);
 
  387      strncpy(pAddin->cType, p[3], MIN(ARRAYSIZE(pAddin->cType), len - 1));
 
  389      pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
 
  390      pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC;
 
  391      pAddin->dwFlags |= FREERDP_ADDIN_NAME;
 
  392      pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM;
 
  393      pAddin->dwFlags |= FREERDP_ADDIN_TYPE;
 
  394      ppAddins[nAddins++] = pAddin;
 
  404  } 
while (FindNextFileW(hFind, &FindData));
 
  407  ppAddins[nAddins] = NULL;
 
  411  freerdp_channels_addin_list_free(ppAddins);
 
  415FREERDP_ADDIN** freerdp_channels_list_addins(LPCSTR pszName, LPCSTR pszSubsystem, LPCSTR pszType,
 
  418  if (dwFlags & FREERDP_ADDIN_STATIC)
 
  419    return freerdp_channels_list_client_static_addins(pszName, pszSubsystem, pszType, dwFlags);
 
  420  else if (dwFlags & FREERDP_ADDIN_DYNAMIC)
 
  421    return freerdp_channels_list_dynamic_addins(pszName, pszSubsystem, pszType, dwFlags);
 
  426void freerdp_channels_addin_list_free(
FREERDP_ADDIN** ppAddins)
 
  431  for (
size_t index = 0; ppAddins[index] != NULL; index++)
 
  432    free(ppAddins[index]);
 
  434  free((
void*)ppAddins);
 
  437extern const STATIC_ENTRY CLIENT_VirtualChannelEntryEx_TABLE[];
 
  439static BOOL freerdp_channels_is_virtual_channel_entry_ex(LPCSTR pszName)
 
  441  for (
size_t i = 0; CLIENT_VirtualChannelEntryEx_TABLE[i].name != NULL; i++)
 
  443    const STATIC_ENTRY* entry = &CLIENT_VirtualChannelEntryEx_TABLE[i];
 
  445    if (!strncmp(entry->name, pszName, MAX_PATH))
 
  452PVIRTUALCHANNELENTRY freerdp_channels_load_static_addin_entry(LPCSTR pszName, LPCSTR pszSubsystem,
 
  453                                                              LPCSTR pszType, DWORD dwFlags)
 
  456  const char* type = NULL;
 
  461  if (dwFlags & FREERDP_ADDIN_CHANNEL_DYNAMIC)
 
  462    type = 
"DVCPluginEntry";
 
  463  else if (dwFlags & FREERDP_ADDIN_CHANNEL_DEVICE)
 
  464    type = 
"DeviceServiceEntry";
 
  465  else if (dwFlags & FREERDP_ADDIN_CHANNEL_STATIC)
 
  467    if (dwFlags & FREERDP_ADDIN_CHANNEL_ENTRYEX)
 
  468      type = 
"VirtualChannelEntryEx";
 
  470      type = 
"VirtualChannelEntry";
 
  473  for (; table->name != NULL; table++)
 
  475    if (strncmp(table->name, pszName, MAX_PATH) == 0)
 
  477      if (type && (strncmp(table->type, type, MAX_PATH) != 0))
 
  480      if (pszSubsystem != NULL)
 
  484        for (; subsystems->name != NULL; subsystems++)
 
  487          if ((strnlen(pszSubsystem, 1) ==
 
  489              (strncmp(subsystems->name, pszSubsystem, MAX_PATH) == 0))
 
  491            static_subsystem_entry_fn_t fkt = subsystems->entry;
 
  495              if (strncmp(subsystems->type, pszType, MAX_PATH) == 0)
 
  496                return WINPR_FUNC_PTR_CAST(fkt, PVIRTUALCHANNELENTRY);
 
  499              return WINPR_FUNC_PTR_CAST(fkt, PVIRTUALCHANNELENTRY);
 
  505        if (dwFlags & FREERDP_ADDIN_CHANNEL_ENTRYEX)
 
  507          if (!freerdp_channels_is_virtual_channel_entry_ex(pszName))
 
  511        return table->entry.csevc;
 
  521  wMessageQueue* queue;
 
  527  MsgHandler msg_handler;
 
  530static DWORD WINAPI channel_client_thread_proc(LPVOID userdata)
 
  532  UINT error = CHANNEL_RC_OK;
 
  534  wMessage message = { 0 };
 
  535  msg_proc_internals* internals = userdata;
 
  537  WINPR_ASSERT(internals);
 
  541    if (!MessageQueue_Wait(internals->queue))
 
  543      WLog_ERR(TAG, 
"MessageQueue_Wait failed!");
 
  544      error = ERROR_INTERNAL_ERROR;
 
  547    if (!MessageQueue_Peek(internals->queue, &message, TRUE))
 
  549      WLog_ERR(TAG, 
"MessageQueue_Peek failed!");
 
  550      error = ERROR_INTERNAL_ERROR;
 
  554    if (message.id == WMQ_QUIT)
 
  559      data = (
wStream*)message.wParam;
 
  561      if ((error = internals->msg_handler(internals->userdata, data)))
 
  563        WLog_ERR(TAG, 
"msg_handler failed with error %" PRIu32 
"!", error);
 
  568  if (error && internals->ctx)
 
  571    (void)_snprintf(msg, 127,
 
  572                    "%s_virtual_channel_client_thread reported an" 
  574                    internals->channel_name);
 
  575    setChannelError(internals->ctx, error, msg);
 
  581static void free_msg(
void* obj)
 
  583  wMessage* msg = (wMessage*)obj;
 
  585  if (msg && (msg->id == 0))
 
  588    Stream_Free(s, TRUE);
 
  592static void channel_client_handler_free(msg_proc_internals* internals)
 
  597  if (internals->thread)
 
  598    (void)CloseHandle(internals->thread);
 
  599  MessageQueue_Free(internals->queue);
 
  600  Stream_Free(internals->data_in, TRUE);
 
  601  free(internals->channel_name);
 
  606void* channel_client_create_handler(rdpContext* ctx, LPVOID userdata, MsgHandler msg_handler,
 
  607                                    const char* channel_name)
 
  609  msg_proc_internals* internals = calloc(1, 
sizeof(msg_proc_internals));
 
  612    WLog_ERR(TAG, 
"calloc failed!");
 
  615  internals->msg_handler = msg_handler;
 
  616  internals->userdata = userdata;
 
  619    internals->channel_name = _strdup(channel_name);
 
  620    if (!internals->channel_name)
 
  624  WINPR_ASSERT(ctx->settings);
 
  625  internals->ctx = ctx;
 
  627       THREADING_FLAGS_DISABLE_THREADS) == 0)
 
  630    obj.fnObjectFree = free_msg;
 
  631    internals->queue = MessageQueue_New(&obj);
 
  632    if (!internals->queue)
 
  634      WLog_ERR(TAG, 
"MessageQueue_New failed!");
 
  638    if (!(internals->thread =
 
  639              CreateThread(NULL, 0, channel_client_thread_proc, (
void*)internals, 0, NULL)))
 
  641      WLog_ERR(TAG, 
"CreateThread failed!");
 
  648  channel_client_handler_free(internals);
 
  652UINT channel_client_post_message(
void* MsgsHandle, LPVOID pData, UINT32 dataLength,
 
  653                                 UINT32 totalLength, UINT32 dataFlags)
 
  655  msg_proc_internals* internals = MsgsHandle;
 
  661    return CHANNEL_RC_OK;
 
  664  if ((dataFlags & CHANNEL_FLAG_SUSPEND) || (dataFlags & CHANNEL_FLAG_RESUME))
 
  666    return CHANNEL_RC_OK;
 
  669  if (dataFlags & CHANNEL_FLAG_FIRST)
 
  671    if (internals->data_in)
 
  673      if (!Stream_EnsureCapacity(internals->data_in, totalLength))
 
  674        return CHANNEL_RC_NO_MEMORY;
 
  677      internals->data_in = Stream_New(NULL, totalLength);
 
  680  if (!(data_in = internals->data_in))
 
  682    WLog_ERR(TAG, 
"Stream_New failed!");
 
  683    return CHANNEL_RC_NO_MEMORY;
 
  686  if (!Stream_EnsureRemainingCapacity(data_in, dataLength))
 
  688    Stream_Free(internals->data_in, TRUE);
 
  689    internals->data_in = NULL;
 
  690    return CHANNEL_RC_NO_MEMORY;
 
  693  Stream_Write(data_in, pData, dataLength);
 
  695  if (dataFlags & CHANNEL_FLAG_LAST)
 
  697    if (Stream_Capacity(data_in) != Stream_GetPosition(data_in))
 
  700      (void)_snprintf(msg, 127, 
"%s_plugin_process_received: read error",
 
  701                      internals->channel_name);
 
  703      return ERROR_INTERNAL_ERROR;
 
  706    internals->data_in = NULL;
 
  707    Stream_SealLength(data_in);
 
  708    Stream_SetPosition(data_in, 0);
 
  711         THREADING_FLAGS_DISABLE_THREADS) != 0)
 
  713      UINT error = CHANNEL_RC_OK;
 
  714      if ((error = internals->msg_handler(internals->userdata, data_in)))
 
  717                 "msg_handler failed with error" 
  720        return ERROR_INTERNAL_ERROR;
 
  723    else if (!MessageQueue_Post(internals->queue, NULL, 0, (
void*)data_in, NULL))
 
  725      WLog_ERR(TAG, 
"MessageQueue_Post failed!");
 
  726      return ERROR_INTERNAL_ERROR;
 
  729  return CHANNEL_RC_OK;
 
  732UINT channel_client_quit_handler(
void* MsgsHandle)
 
  734  msg_proc_internals* internals = MsgsHandle;
 
  739    return CHANNEL_RC_OK;
 
  742  WINPR_ASSERT(internals->ctx);
 
  743  WINPR_ASSERT(internals->ctx->settings);
 
  746       THREADING_FLAGS_DISABLE_THREADS) == 0)
 
  748    if (internals->queue && internals->thread)
 
  750      if (MessageQueue_PostQuit(internals->queue, 0) &&
 
  751          (WaitForSingleObject(internals->thread, INFINITE) == WAIT_FAILED))
 
  754        WLog_ERR(TAG, 
"WaitForSingleObject failed with error %" PRIu32 
"", rc);
 
  760  channel_client_handler_free(internals);
 
  761  return CHANNEL_RC_OK;
 
FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
 
This struct contains function pointer to initialize/free objects.