21#include <winpr/wlog.h> 
   22#include <winpr/print.h> 
   23#include <winpr/smartcard.h> 
   25#include <freerdp/utils/rdpdr_utils.h> 
   26#include <freerdp/channels/scard.h> 
   27#include <freerdp/channels/rdpdr.h> 
   29#include <freerdp/log.h> 
   31LONG scard_log_status_error(
const char* tag, 
const char* what, LONG status)
 
   33  wLog* log = WLog_Get(tag);
 
   34  return scard_log_status_error_wlog(log, what, status);
 
   37LONG scard_log_status_error_wlog(wLog* log, 
const char* what, LONG status)
 
   39  if (status != SCARD_S_SUCCESS)
 
   41    DWORD level = WLOG_ERROR;
 
   47      case SCARD_E_NO_READERS_AVAILABLE:
 
   53    WLog_Print(log, level, 
"%s failed with error %s [%" PRId32 
"]", what,
 
   54               SCardGetErrorString(status), status);
 
   59const char* scard_get_ioctl_string(UINT32 ioControlCode, BOOL funcName)
 
   61  switch (ioControlCode)
 
   63    case SCARD_IOCTL_ESTABLISHCONTEXT:
 
   64      return funcName ? 
"SCardEstablishContext" : 
"SCARD_IOCTL_ESTABLISHCONTEXT";
 
   66    case SCARD_IOCTL_RELEASECONTEXT:
 
   67      return funcName ? 
"SCardReleaseContext" : 
"SCARD_IOCTL_RELEASECONTEXT";
 
   69    case SCARD_IOCTL_ISVALIDCONTEXT:
 
   70      return funcName ? 
"SCardIsValidContext" : 
"SCARD_IOCTL_ISVALIDCONTEXT";
 
   72    case SCARD_IOCTL_LISTREADERGROUPSA:
 
   73      return funcName ? 
"SCardListReaderGroupsA" : 
"SCARD_IOCTL_LISTREADERGROUPSA";
 
   75    case SCARD_IOCTL_LISTREADERGROUPSW:
 
   76      return funcName ? 
"SCardListReaderGroupsW" : 
"SCARD_IOCTL_LISTREADERGROUPSW";
 
   78    case SCARD_IOCTL_LISTREADERSA:
 
   79      return funcName ? 
"SCardListReadersA" : 
"SCARD_IOCTL_LISTREADERSA";
 
   81    case SCARD_IOCTL_LISTREADERSW:
 
   82      return funcName ? 
"SCardListReadersW" : 
"SCARD_IOCTL_LISTREADERSW";
 
   84    case SCARD_IOCTL_INTRODUCEREADERGROUPA:
 
   85      return funcName ? 
"SCardIntroduceReaderGroupA" : 
"SCARD_IOCTL_INTRODUCEREADERGROUPA";
 
   87    case SCARD_IOCTL_INTRODUCEREADERGROUPW:
 
   88      return funcName ? 
"SCardIntroduceReaderGroupW" : 
"SCARD_IOCTL_INTRODUCEREADERGROUPW";
 
   90    case SCARD_IOCTL_FORGETREADERGROUPA:
 
   91      return funcName ? 
"SCardForgetReaderGroupA" : 
"SCARD_IOCTL_FORGETREADERGROUPA";
 
   93    case SCARD_IOCTL_FORGETREADERGROUPW:
 
   94      return funcName ? 
"SCardForgetReaderGroupW" : 
"SCARD_IOCTL_FORGETREADERGROUPW";
 
   96    case SCARD_IOCTL_INTRODUCEREADERA:
 
   97      return funcName ? 
"SCardIntroduceReaderA" : 
"SCARD_IOCTL_INTRODUCEREADERA";
 
   99    case SCARD_IOCTL_INTRODUCEREADERW:
 
  100      return funcName ? 
"SCardIntroduceReaderW" : 
"SCARD_IOCTL_INTRODUCEREADERW";
 
  102    case SCARD_IOCTL_FORGETREADERA:
 
  103      return funcName ? 
"SCardForgetReaderA" : 
"SCARD_IOCTL_FORGETREADERA";
 
  105    case SCARD_IOCTL_FORGETREADERW:
 
  106      return funcName ? 
"SCardForgetReaderW" : 
"SCARD_IOCTL_FORGETREADERW";
 
  108    case SCARD_IOCTL_ADDREADERTOGROUPA:
 
  109      return funcName ? 
"SCardAddReaderToGroupA" : 
"SCARD_IOCTL_ADDREADERTOGROUPA";
 
  111    case SCARD_IOCTL_ADDREADERTOGROUPW:
 
  112      return funcName ? 
"SCardAddReaderToGroupW" : 
"SCARD_IOCTL_ADDREADERTOGROUPW";
 
  114    case SCARD_IOCTL_REMOVEREADERFROMGROUPA:
 
  115      return funcName ? 
"SCardRemoveReaderFromGroupA" : 
"SCARD_IOCTL_REMOVEREADERFROMGROUPA";
 
  117    case SCARD_IOCTL_REMOVEREADERFROMGROUPW:
 
  118      return funcName ? 
"SCardRemoveReaderFromGroupW" : 
"SCARD_IOCTL_REMOVEREADERFROMGROUPW";
 
  120    case SCARD_IOCTL_LOCATECARDSA:
 
  121      return funcName ? 
"SCardLocateCardsA" : 
"SCARD_IOCTL_LOCATECARDSA";
 
  123    case SCARD_IOCTL_LOCATECARDSW:
 
  124      return funcName ? 
"SCardLocateCardsW" : 
"SCARD_IOCTL_LOCATECARDSW";
 
  126    case SCARD_IOCTL_GETSTATUSCHANGEA:
 
  127      return funcName ? 
"SCardGetStatusChangeA" : 
"SCARD_IOCTL_GETSTATUSCHANGEA";
 
  129    case SCARD_IOCTL_GETSTATUSCHANGEW:
 
  130      return funcName ? 
"SCardGetStatusChangeW" : 
"SCARD_IOCTL_GETSTATUSCHANGEW";
 
  132    case SCARD_IOCTL_CANCEL:
 
  133      return funcName ? 
"SCardCancel" : 
"SCARD_IOCTL_CANCEL";
 
  135    case SCARD_IOCTL_CONNECTA:
 
  136      return funcName ? 
"SCardConnectA" : 
"SCARD_IOCTL_CONNECTA";
 
  138    case SCARD_IOCTL_CONNECTW:
 
  139      return funcName ? 
"SCardConnectW" : 
"SCARD_IOCTL_CONNECTW";
 
  141    case SCARD_IOCTL_RECONNECT:
 
  142      return funcName ? 
"SCardReconnect" : 
"SCARD_IOCTL_RECONNECT";
 
  144    case SCARD_IOCTL_DISCONNECT:
 
  145      return funcName ? 
"SCardDisconnect" : 
"SCARD_IOCTL_DISCONNECT";
 
  147    case SCARD_IOCTL_BEGINTRANSACTION:
 
  148      return funcName ? 
"SCardBeginTransaction" : 
"SCARD_IOCTL_BEGINTRANSACTION";
 
  150    case SCARD_IOCTL_ENDTRANSACTION:
 
  151      return funcName ? 
"SCardEndTransaction" : 
"SCARD_IOCTL_ENDTRANSACTION";
 
  153    case SCARD_IOCTL_STATE:
 
  154      return funcName ? 
"SCardState" : 
"SCARD_IOCTL_STATE";
 
  156    case SCARD_IOCTL_STATUSA:
 
  157      return funcName ? 
"SCardStatusA" : 
"SCARD_IOCTL_STATUSA";
 
  159    case SCARD_IOCTL_STATUSW:
 
  160      return funcName ? 
"SCardStatusW" : 
"SCARD_IOCTL_STATUSW";
 
  162    case SCARD_IOCTL_TRANSMIT:
 
  163      return funcName ? 
"SCardTransmit" : 
"SCARD_IOCTL_TRANSMIT";
 
  165    case SCARD_IOCTL_CONTROL:
 
  166      return funcName ? 
"SCardControl" : 
"SCARD_IOCTL_CONTROL";
 
  168    case SCARD_IOCTL_GETATTRIB:
 
  169      return funcName ? 
"SCardGetAttrib" : 
"SCARD_IOCTL_GETATTRIB";
 
  171    case SCARD_IOCTL_SETATTRIB:
 
  172      return funcName ? 
"SCardSetAttrib" : 
"SCARD_IOCTL_SETATTRIB";
 
  174    case SCARD_IOCTL_ACCESSSTARTEDEVENT:
 
  175      return funcName ? 
"SCardAccessStartedEvent" : 
"SCARD_IOCTL_ACCESSSTARTEDEVENT";
 
  177    case SCARD_IOCTL_LOCATECARDSBYATRA:
 
  178      return funcName ? 
"SCardLocateCardsByATRA" : 
"SCARD_IOCTL_LOCATECARDSBYATRA";
 
  180    case SCARD_IOCTL_LOCATECARDSBYATRW:
 
  181      return funcName ? 
"SCardLocateCardsByATRB" : 
"SCARD_IOCTL_LOCATECARDSBYATRW";
 
  183    case SCARD_IOCTL_READCACHEA:
 
  184      return funcName ? 
"SCardReadCacheA" : 
"SCARD_IOCTL_READCACHEA";
 
  186    case SCARD_IOCTL_READCACHEW:
 
  187      return funcName ? 
"SCardReadCacheW" : 
"SCARD_IOCTL_READCACHEW";
 
  189    case SCARD_IOCTL_WRITECACHEA:
 
  190      return funcName ? 
"SCardWriteCacheA" : 
"SCARD_IOCTL_WRITECACHEA";
 
  192    case SCARD_IOCTL_WRITECACHEW:
 
  193      return funcName ? 
"SCardWriteCacheW" : 
"SCARD_IOCTL_WRITECACHEW";
 
  195    case SCARD_IOCTL_GETTRANSMITCOUNT:
 
  196      return funcName ? 
"SCardGetTransmitCount" : 
"SCARD_IOCTL_GETTRANSMITCOUNT";
 
  198    case SCARD_IOCTL_RELEASETARTEDEVENT:
 
  199      return funcName ? 
"SCardReleaseStartedEvent" : 
"SCARD_IOCTL_RELEASETARTEDEVENT";
 
  201    case SCARD_IOCTL_GETREADERICON:
 
  202      return funcName ? 
"SCardGetReaderIcon" : 
"SCARD_IOCTL_GETREADERICON";
 
  204    case SCARD_IOCTL_GETDEVICETYPEID:
 
  205      return funcName ? 
"SCardGetDeviceTypeId" : 
"SCARD_IOCTL_GETDEVICETYPEID";
 
  208      return funcName ? 
"SCardUnknown" : 
"SCARD_IOCTL_UNKNOWN";
 
  212const char* rdpdr_component_string(UINT16 component)
 
  217      return "RDPDR_CTYP_PRN";
 
  218    case RDPDR_CTYP_CORE:
 
  219      return "RDPDR_CTYP_CORE";
 
  225const char* rdpdr_packetid_string(UINT16 packetid)
 
  229    case PAKID_CORE_SERVER_ANNOUNCE:
 
  230      return "PAKID_CORE_SERVER_ANNOUNCE";
 
  231    case PAKID_CORE_CLIENTID_CONFIRM:
 
  232      return "PAKID_CORE_CLIENTID_CONFIRM";
 
  233    case PAKID_CORE_CLIENT_NAME:
 
  234      return "PAKID_CORE_CLIENT_NAME";
 
  235    case PAKID_CORE_DEVICELIST_ANNOUNCE:
 
  236      return "PAKID_CORE_DEVICELIST_ANNOUNCE";
 
  237    case PAKID_CORE_DEVICE_REPLY:
 
  238      return "PAKID_CORE_DEVICE_REPLY";
 
  239    case PAKID_CORE_DEVICE_IOREQUEST:
 
  240      return "PAKID_CORE_DEVICE_IOREQUEST";
 
  241    case PAKID_CORE_DEVICE_IOCOMPLETION:
 
  242      return "PAKID_CORE_DEVICE_IOCOMPLETION";
 
  243    case PAKID_CORE_SERVER_CAPABILITY:
 
  244      return "PAKID_CORE_SERVER_CAPABILITY";
 
  245    case PAKID_CORE_CLIENT_CAPABILITY:
 
  246      return "PAKID_CORE_CLIENT_CAPABILITY";
 
  247    case PAKID_CORE_DEVICELIST_REMOVE:
 
  248      return "PAKID_CORE_DEVICELIST_REMOVE";
 
  249    case PAKID_CORE_USER_LOGGEDON:
 
  250      return "PAKID_CORE_USER_LOGGEDON";
 
  251    case PAKID_PRN_CACHE_DATA:
 
  252      return "PAKID_PRN_CACHE_DATA";
 
  253    case PAKID_PRN_USING_XPS:
 
  254      return "PAKID_PRN_USING_XPS";
 
  260BOOL rdpdr_write_iocompletion_header(
wStream* out, UINT32 DeviceId, UINT32 CompletionId,
 
  264  Stream_SetPosition(out, 0);
 
  265  if (!Stream_EnsureRemainingCapacity(out, 16))
 
  267  Stream_Write_UINT16(out, RDPDR_CTYP_CORE);                
 
  268  Stream_Write_UINT16(out, PAKID_CORE_DEVICE_IOCOMPLETION); 
 
  269  Stream_Write_UINT32(out, DeviceId);                       
 
  270  Stream_Write_UINT32(out, CompletionId);                   
 
  271  Stream_Write_INT32(out, ioStatus);                        
 
  276static void rdpdr_dump_packet(wLog* log, DWORD lvl, 
wStream* s, 
const char* custom, BOOL send)
 
  278  if (!WLog_IsLevelActive(log, lvl))
 
  281  const size_t gpos = Stream_GetPosition(s);
 
  282  const size_t pos = send ? Stream_GetPosition(s) : Stream_Length(s);
 
  284  UINT16 component = 0;
 
  287  Stream_SetPosition(s, 0);
 
  290    Stream_Read_UINT16(s, component);
 
  292    Stream_Read_UINT16(s, packetid);
 
  296    case PAKID_CORE_SERVER_ANNOUNCE:
 
  297    case PAKID_CORE_CLIENTID_CONFIRM:
 
  299      UINT16 versionMajor = 0;
 
  300      UINT16 versionMinor = 0;
 
  304        Stream_Read_UINT16(s, versionMajor);
 
  306        Stream_Read_UINT16(s, versionMinor);
 
  308        Stream_Read_UINT32(s, clientID);
 
  310                 "%s [%s | %s] [version:%" PRIu16 
".%" PRIu16 
"][id:0x%08" PRIx32
 
  312                 custom, rdpdr_component_string(component), rdpdr_packetid_string(packetid),
 
  313                 versionMajor, versionMinor, clientID, pos);
 
  316    case PAKID_CORE_CLIENT_NAME:
 
  318      char name[256] = { 0 };
 
  319      UINT32 unicodeFlag = 0;
 
  321      UINT32 computerNameLen = 0;
 
  323        Stream_Read_UINT32(s, unicodeFlag);
 
  325        Stream_Read_UINT32(s, codePage);
 
  327        Stream_Read_UINT32(s, computerNameLen);
 
  328      if (pos >= 16 + computerNameLen)
 
  330        if (unicodeFlag == 0)
 
  331          Stream_Read(s, name, MIN(
sizeof(name), computerNameLen));
 
  333          (
void)ConvertWCharNToUtf8(Stream_ConstPointer(s),
 
  334                                    computerNameLen / 
sizeof(WCHAR), name, 
sizeof(name));
 
  337                 "%s [%s | %s] [ucs:%" PRIu32 
"|cp:%" PRIu32 
"][len:0x%08" PRIx32
 
  339                 custom, rdpdr_component_string(component), rdpdr_packetid_string(packetid),
 
  340                 unicodeFlag, codePage, computerNameLen, name, pos);
 
  344    case PAKID_CORE_DEVICE_IOREQUEST:
 
  346      UINT32 CompletionId = 0;
 
  349      UINT32 MajorFunction = 0;
 
  350      UINT32 MinorFunction = 0;
 
  353        Stream_Read_UINT32(s, deviceID);
 
  355        Stream_Read_UINT32(s, FileId);
 
  357        Stream_Read_UINT32(s, CompletionId);
 
  359        Stream_Read_UINT32(s, MajorFunction);
 
  361        Stream_Read_UINT32(s, MinorFunction);
 
  363                 "%s [%s | %s] [0x%08" PRIx32 
"] FileId=0x%08" PRIx32
 
  364                 ", CompletionId=0x%08" PRIx32 
", MajorFunction=0x%08" PRIx32
 
  365                 ", MinorFunction=0x%08" PRIx32 
" -> %" PRIuz,
 
  366                 custom, rdpdr_component_string(component), rdpdr_packetid_string(packetid),
 
  367                 deviceID, FileId, CompletionId, MajorFunction, MinorFunction, pos);
 
  370    case PAKID_CORE_DEVICE_IOCOMPLETION:
 
  372      UINT32 completionID = 0;
 
  376        Stream_Read_UINT32(s, deviceID);
 
  378        Stream_Read_UINT32(s, completionID);
 
  380        Stream_Read_UINT32(s, ioStatus);
 
  383                 "%s [%s | %s] [0x%08" PRIx32 
"] completionID=0x%08" PRIx32
 
  384                 ", ioStatus=0x%08" PRIx32 
" -> %" PRIuz,
 
  385                 custom, rdpdr_component_string(component), rdpdr_packetid_string(packetid),
 
  386                 deviceID, completionID, ioStatus, pos);
 
  389    case PAKID_CORE_DEVICE_REPLY:
 
  395        Stream_Read_UINT32(s, deviceID);
 
  397        Stream_Read_UINT32(s, status);
 
  399                 "%s [%s | %s] [id:0x%08" PRIx32 
",status=0x%08" PRIx32 
"] -> %" PRIuz,
 
  400                 custom, rdpdr_component_string(component), rdpdr_packetid_string(packetid),
 
  401                 deviceID, status, pos);
 
  404    case PAKID_CORE_CLIENT_CAPABILITY:
 
  405    case PAKID_CORE_SERVER_CAPABILITY:
 
  407      UINT16 numCapabilities = 0;
 
  409        Stream_Read_UINT16(s, numCapabilities);
 
  411        Stream_Seek_UINT16(s); 
 
  412      WLog_Print(log, lvl, 
"%s [%s | %s] [caps:%" PRIu16 
"] -> %" PRIuz, custom,
 
  413                 rdpdr_component_string(component), rdpdr_packetid_string(packetid),
 
  414                 numCapabilities, pos);
 
  415      for (UINT16 x = 0; x < numCapabilities; x++)
 
  418        const UINT error = rdpdr_read_capset_header(log, s, &header);
 
  419        if (error == CHANNEL_RC_OK)
 
  420          Stream_Seek(s, header.CapabilityLength);
 
  424    case PAKID_CORE_DEVICELIST_ANNOUNCE:
 
  430        Stream_Read_UINT32(s, count);
 
  432      WLog_Print(log, lvl, 
"%s [%s | %s] [%" PRIu32 
"] -> %" PRIuz, custom,
 
  433                 rdpdr_component_string(component), rdpdr_packetid_string(packetid), count,
 
  436      for (UINT32 x = 0; x < count; x++)
 
  443          Stream_Read_UINT32(s, device.DeviceType);       
 
  444          Stream_Read_UINT32(s, device.DeviceId);         
 
  445          Stream_Read(s, device.PreferredDosName, 8);     
 
  446          Stream_Read_UINT32(s, device.DeviceDataLength); 
 
  447          device.DeviceData = Stream_Pointer(s);
 
  449        offset += device.DeviceDataLength;
 
  452                   "%s [announce][%" PRIu32 
"] %s [0x%08" PRIx32
 
  453                   "] '%s' [DeviceDataLength=%" PRIu32 
"]",
 
  455                   device.PreferredDosName, device.DeviceDataLength);
 
  459    case PAKID_CORE_DEVICELIST_REMOVE:
 
  465        Stream_Read_UINT32(s, count);
 
  467      WLog_Print(log, lvl, 
"%s [%s | %s] [%" PRIu32 
"] -> %" PRIuz, custom,
 
  468                 rdpdr_component_string(component), rdpdr_packetid_string(packetid), count,
 
  471      for (UINT32 x = 0; x < count; x++)
 
  477          Stream_Read_UINT32(s, 
id);
 
  479        WLog_Print(log, lvl, 
"%s [remove][%" PRIu32 
"] id=%" PRIu32, custom, x, 
id);
 
  483    case PAKID_CORE_USER_LOGGEDON:
 
  484      WLog_Print(log, lvl, 
"%s [%s | %s] -> %" PRIuz, custom,
 
  485                 rdpdr_component_string(component), rdpdr_packetid_string(packetid), pos);
 
  489      WLog_Print(log, lvl, 
"%s [%s | %s] -> %" PRIuz, custom,
 
  490                 rdpdr_component_string(component), rdpdr_packetid_string(packetid), pos);
 
  496  Stream_SetPosition(s, gpos);
 
  499void rdpdr_dump_received_packet(wLog* log, DWORD lvl, 
wStream* s, 
const char* custom)
 
  501  rdpdr_dump_packet(log, lvl, s, custom, FALSE);
 
  504void rdpdr_dump_send_packet(wLog* log, DWORD lvl, 
wStream* s, 
const char* custom)
 
  506  rdpdr_dump_packet(log, lvl, s, custom, TRUE);
 
  509const char* rdpdr_irp_string(UINT32 major)
 
  514      return "IRP_MJ_CREATE";
 
  516      return "IRP_MJ_CLOSE";
 
  518      return "IRP_MJ_READ";
 
  520      return "IRP_MJ_WRITE";
 
  521    case IRP_MJ_DEVICE_CONTROL:
 
  522      return "IRP_MJ_DEVICE_CONTROL";
 
  523    case IRP_MJ_QUERY_VOLUME_INFORMATION:
 
  524      return "IRP_MJ_QUERY_VOLUME_INFORMATION";
 
  525    case IRP_MJ_SET_VOLUME_INFORMATION:
 
  526      return "IRP_MJ_SET_VOLUME_INFORMATION";
 
  527    case IRP_MJ_QUERY_INFORMATION:
 
  528      return "IRP_MJ_QUERY_INFORMATION";
 
  529    case IRP_MJ_SET_INFORMATION:
 
  530      return "IRP_MJ_SET_INFORMATION";
 
  531    case IRP_MJ_DIRECTORY_CONTROL:
 
  532      return "IRP_MJ_DIRECTORY_CONTROL";
 
  533    case IRP_MJ_LOCK_CONTROL:
 
  534      return "IRP_MJ_LOCK_CONTROL";
 
  536      return "IRP_UNKNOWN";
 
  540const char* rdpdr_cap_type_string(UINT16 capability)
 
  544    case CAP_GENERAL_TYPE:
 
  545      return "CAP_GENERAL_TYPE";
 
  546    case CAP_PRINTER_TYPE:
 
  547      return "CAP_PRINTER_TYPE";
 
  549      return "CAP_PORT_TYPE";
 
  551      return "CAP_DRIVE_TYPE";
 
  552    case CAP_SMARTCARD_TYPE:
 
  553      return "CAP_SMARTCARD_TYPE";
 
  555      return "CAP_UNKNOWN";
 
  561  WINPR_ASSERT(header);
 
  562  if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 8))
 
  563    return ERROR_INVALID_DATA;
 
  565  Stream_Read_UINT16(s, header->CapabilityType);   
 
  566  Stream_Read_UINT16(s, header->CapabilityLength); 
 
  567  Stream_Read_UINT32(s, header->Version);          
 
  569  WLog_Print(log, WLOG_TRACE,
 
  570             "capability %s [0x%04" PRIx16 
"] got version %" PRIu32 
", length %" PRIu16,
 
  571             rdpdr_cap_type_string(header->CapabilityType), header->CapabilityType,
 
  572             header->Version, header->CapabilityLength);
 
  573  if (header->CapabilityLength < 8)
 
  575    WLog_Print(log, WLOG_ERROR, 
"capability %s got short length %" PRIu32,
 
  576               rdpdr_cap_type_string(header->CapabilityType), header->CapabilityLength);
 
  577    return ERROR_INVALID_DATA;
 
  579  header->CapabilityLength -= 8;
 
  580  if (!Stream_CheckAndLogRequiredLengthWLog(log, s, header->CapabilityLength))
 
  581    return ERROR_INVALID_DATA;
 
  582  return CHANNEL_RC_OK;
 
  587  WINPR_ASSERT(header);
 
  588  WINPR_ASSERT(header->CapabilityLength >= 8);
 
  590  if (!Stream_EnsureRemainingCapacity(s, header->CapabilityLength))
 
  592    WLog_Print(log, WLOG_ERROR, 
"not enough data in stream!");
 
  593    return ERROR_INVALID_DATA;
 
  596  WLog_Print(log, WLOG_TRACE, 
"writing capability %s version %" PRIu32 
", length %" PRIu16,
 
  597             rdpdr_cap_type_string(header->CapabilityType), header->Version,
 
  598             header->CapabilityLength);
 
  599  Stream_Write_UINT16(s, header->CapabilityType);   
 
  600  Stream_Write_UINT16(s, header->CapabilityLength); 
 
  601  Stream_Write_UINT32(s, header->Version);          
 
  602  return CHANNEL_RC_OK;
 
FREERDP_API const char * freerdp_rdpdr_dtyp_string(UINT32 type)
Returns a string representation of RDPDR_DTYP_*.