Update upstream source from tag 'upstream/2.11.5+dfsg1'
Update to upstream version '2.11.5+dfsg1'
with Debian dir 3a144157ec
This commit is contained in:
commit
6fe4ba798f
@ -104,7 +104,6 @@ ForEachMacros:
|
||||
...
|
||||
Language: ObjC
|
||||
PointerBindsToType: false
|
||||
ObjCSpaceAfterProperty: true
|
||||
SortIncludes: false
|
||||
ObjCBlockIndentWidth: 4
|
||||
ObjCSpaceAfterProperty: false
|
||||
|
||||
@ -85,7 +85,7 @@ if ($ENV{BUILD_NUMBER})
|
||||
endif()
|
||||
set(WITH_LIBRARY_VERSIONING "ON")
|
||||
|
||||
set(RAW_VERSION_STRING "2.11.2")
|
||||
set(RAW_VERSION_STRING "2.11.5")
|
||||
if(EXISTS "${CMAKE_SOURCE_DIR}/.source_tag")
|
||||
file(READ ${CMAKE_SOURCE_DIR}/.source_tag RAW_VERSION_STRING)
|
||||
elseif(USE_VERSION_FROM_GIT_TAG)
|
||||
|
||||
33
ChangeLog
33
ChangeLog
@ -1,3 +1,34 @@
|
||||
# 2024-01-19 Version 2.11.5
|
||||
|
||||
Noteworthy changes:
|
||||
* Fix integer overflow in progressive decoder
|
||||
* Update OpenSSL API usage for compatiblility with newer versions (#9747)
|
||||
* Prevent NULL dereference for single thread decoder (#9712)
|
||||
|
||||
For a complete and detailed change log since the last release run:
|
||||
git log 2.11.5...2.11.4
|
||||
|
||||
# 2023-12-14 Version 2.11.4
|
||||
|
||||
Notworthy changes:
|
||||
* fix a typo in unicode commit (#9652)
|
||||
|
||||
For a complete and detailed change log since the last release run:
|
||||
git log 2.11.4...2.11.3
|
||||
|
||||
# 2023-12-14 Version 2.11.3
|
||||
|
||||
Notworthy changes:
|
||||
* Disabled windows MEDIA FOUNDATION h264 decoder due to reported issues (#9469)
|
||||
* Fix issues with drive redirection (#9530,9554, #9586, #9617)
|
||||
* Use endian safe ICU string converter (#9631)
|
||||
* Improve AAC support (#9577)
|
||||
* Fix swiss german keyboard layout (#9560)
|
||||
* Enable rfx-mode:image (#9428)
|
||||
|
||||
For a complete and detailed change log since the last release run:
|
||||
git log 2.11.3...2.11.2
|
||||
|
||||
# 2023-09-20 Version 2.11.2
|
||||
|
||||
Notworthy changes:
|
||||
@ -5,7 +36,7 @@ Notworthy changes:
|
||||
* Backported #9360: backported certificate algorithm detection
|
||||
|
||||
For a complete and detailed change log since the last release run:
|
||||
git log 2.11.2..2.11.1
|
||||
git log 2.11.2...2.11.1
|
||||
|
||||
# 2023-09-04 Version 2.11.1
|
||||
|
||||
|
||||
@ -35,6 +35,8 @@
|
||||
#include "rdpdr_main.h"
|
||||
#include "rdpdr_capabilities.h"
|
||||
|
||||
#define RDPDR_CAPABILITY_HEADER_LENGTH 8
|
||||
|
||||
/* Output device redirection capability set header */
|
||||
static void rdpdr_write_capset_header(wStream* s, UINT16 capabilityType, UINT16 capabilityLength,
|
||||
UINT32 version)
|
||||
@ -48,39 +50,59 @@ static void rdpdr_write_capset_header(wStream* s, UINT16 capabilityType, UINT16
|
||||
static void rdpdr_write_general_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
{
|
||||
WINPR_UNUSED(rdpdr);
|
||||
rdpdr_write_capset_header(s, CAP_GENERAL_TYPE, 44, GENERAL_CAPABILITY_VERSION_02);
|
||||
Stream_Write_UINT32(s, 0); /* osType, ignored on receipt */
|
||||
Stream_Write_UINT32(s, 0); /* osVersion, unused and must be set to zero */
|
||||
Stream_Write_UINT16(s, 1); /* protocolMajorVersion, must be set to 1 */
|
||||
Stream_Write_UINT16(s, RDPDR_MINOR_RDP_VERSION_5_2); /* protocolMinorVersion */
|
||||
Stream_Write_UINT32(s, 0x0000FFFF); /* ioCode1 */
|
||||
Stream_Write_UINT32(s, 0); /* ioCode2, must be set to zero, reserved for future use */
|
||||
Stream_Write_UINT32(s, RDPDR_DEVICE_REMOVE_PDUS | RDPDR_CLIENT_DISPLAY_NAME_PDU |
|
||||
RDPDR_USER_LOGGEDON_PDU); /* extendedPDU */
|
||||
Stream_Write_UINT32(s, ENABLE_ASYNCIO); /* extraFlags1 */
|
||||
Stream_Write_UINT32(s, 0); /* extraFlags2, must be set to zero, reserved for future use */
|
||||
|
||||
const UINT32 ioCode1 = rdpdr->clientIOCode1 & rdpdr->serverIOCode1;
|
||||
const UINT32 ioCode2 = rdpdr->clientIOCode2 & rdpdr->serverIOCode2;
|
||||
|
||||
rdpdr_write_capset_header(s, CAP_GENERAL_TYPE, RDPDR_CAPABILITY_HEADER_LENGTH + 36,
|
||||
GENERAL_CAPABILITY_VERSION_02);
|
||||
Stream_Write_UINT32(s, rdpdr->clientOsType); /* osType, ignored on receipt */
|
||||
Stream_Write_UINT32(s, rdpdr->clientOsVersion); /* osVersion, unused and must be set to zero */
|
||||
Stream_Write_UINT16(s, rdpdr->clientVersionMajor); /* protocolMajorVersion, must be set to 1 */
|
||||
Stream_Write_UINT16(s, rdpdr->clientVersionMinor); /* protocolMinorVersion */
|
||||
Stream_Write_UINT32(s, ioCode1); /* ioCode1 */
|
||||
Stream_Write_UINT32(s, ioCode2); /* ioCode2, must be set to zero, reserved for future use */
|
||||
Stream_Write_UINT32(s, rdpdr->clientExtendedPDU); /* extendedPDU */
|
||||
Stream_Write_UINT32(s, rdpdr->clientExtraFlags1); /* extraFlags1 */
|
||||
Stream_Write_UINT32(
|
||||
s, 0); /* SpecialTypeDeviceCap, number of special devices to be redirected before logon */
|
||||
s,
|
||||
rdpdr->clientExtraFlags2); /* extraFlags2, must be set to zero, reserved for future use */
|
||||
Stream_Write_UINT32(
|
||||
s, rdpdr->clientSpecialTypeDeviceCap); /* SpecialTypeDeviceCap, number of special devices to
|
||||
be redirected before logon */
|
||||
}
|
||||
|
||||
/* Process device direction general capability set */
|
||||
static UINT rdpdr_process_general_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
static UINT rdpdr_process_general_capset(rdpdrPlugin* rdpdr, wStream* s, UINT16 capabilityLength)
|
||||
{
|
||||
UINT16 capabilityLength;
|
||||
WINPR_UNUSED(rdpdr);
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, 2))
|
||||
if (capabilityLength != 36)
|
||||
{
|
||||
WLog_Print(rdpdr->log, WLOG_ERROR,
|
||||
"CAP_GENERAL_TYPE::CapabilityLength expected 36, got %" PRIu32,
|
||||
capabilityLength);
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, capabilityLength))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT16(s, capabilityLength);
|
||||
|
||||
if (capabilityLength < 4)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, capabilityLength - 4U))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Seek(s, capabilityLength - 4U);
|
||||
Stream_Read_UINT32(s, rdpdr->serverOsType); /* osType, ignored on receipt */
|
||||
Stream_Read_UINT32(s, rdpdr->serverOsVersion); /* osVersion, unused and must be set to zero */
|
||||
Stream_Read_UINT16(s, rdpdr->serverVersionMajor); /* protocolMajorVersion, must be set to 1 */
|
||||
Stream_Read_UINT16(s, rdpdr->serverVersionMinor); /* protocolMinorVersion */
|
||||
Stream_Read_UINT32(s, rdpdr->serverIOCode1); /* ioCode1 */
|
||||
Stream_Read_UINT32(
|
||||
s, rdpdr->serverIOCode2); /* ioCode2, must be set to zero, reserved for future use */
|
||||
Stream_Read_UINT32(s, rdpdr->serverExtendedPDU); /* extendedPDU */
|
||||
Stream_Read_UINT32(s, rdpdr->serverExtraFlags1); /* extraFlags1 */
|
||||
Stream_Read_UINT32(
|
||||
s,
|
||||
rdpdr->serverExtraFlags2); /* extraFlags2, must be set to zero, reserved for future use */
|
||||
Stream_Read_UINT32(
|
||||
s, rdpdr->serverSpecialTypeDeviceCap); /* SpecialTypeDeviceCap, number of special devices to
|
||||
be redirected before logon */
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
@ -92,23 +114,14 @@ static void rdpdr_write_printer_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
}
|
||||
|
||||
/* Process printer direction capability set */
|
||||
static UINT rdpdr_process_printer_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
static UINT rdpdr_process_printer_capset(rdpdrPlugin* rdpdr, wStream* s, UINT16 capabilityLength)
|
||||
{
|
||||
UINT16 capabilityLength;
|
||||
WINPR_UNUSED(rdpdr);
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, 2))
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, capabilityLength))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT16(s, capabilityLength);
|
||||
|
||||
if (capabilityLength < 4)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, capabilityLength - 4U))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Seek(s, capabilityLength - 4U);
|
||||
Stream_Seek(s, capabilityLength);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
@ -120,23 +133,14 @@ static void rdpdr_write_port_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
}
|
||||
|
||||
/* Process port redirection capability set */
|
||||
static UINT rdpdr_process_port_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
static UINT rdpdr_process_port_capset(rdpdrPlugin* rdpdr, wStream* s, UINT16 capabilityLength)
|
||||
{
|
||||
UINT16 capabilityLength;
|
||||
WINPR_UNUSED(rdpdr);
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, 2))
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, capabilityLength))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT16(s, capabilityLength);
|
||||
|
||||
if (capabilityLength < 4U)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, capabilityLength - 4U))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Seek(s, capabilityLength - 4U);
|
||||
Stream_Seek(s, capabilityLength);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
@ -148,23 +152,14 @@ static void rdpdr_write_drive_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
}
|
||||
|
||||
/* Process drive redirection capability set */
|
||||
static UINT rdpdr_process_drive_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
static UINT rdpdr_process_drive_capset(rdpdrPlugin* rdpdr, wStream* s, UINT16 capabilityLength)
|
||||
{
|
||||
UINT16 capabilityLength;
|
||||
WINPR_UNUSED(rdpdr);
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, 2))
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, capabilityLength))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT16(s, capabilityLength);
|
||||
|
||||
if (capabilityLength < 4)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, capabilityLength - 4U))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Seek(s, capabilityLength - 4U);
|
||||
Stream_Seek(s, capabilityLength);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
@ -176,23 +171,14 @@ static void rdpdr_write_smartcard_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
}
|
||||
|
||||
/* Process smartcard redirection capability set */
|
||||
static UINT rdpdr_process_smartcard_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
static UINT rdpdr_process_smartcard_capset(rdpdrPlugin* rdpdr, wStream* s, UINT16 capabilityLength)
|
||||
{
|
||||
UINT16 capabilityLength;
|
||||
WINPR_UNUSED(rdpdr);
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, 2))
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, capabilityLength))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT16(s, capabilityLength);
|
||||
|
||||
if (capabilityLength < 4)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, capabilityLength - 4U))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Seek(s, capabilityLength - 4U);
|
||||
Stream_Seek(s, capabilityLength);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
@ -201,7 +187,6 @@ UINT rdpdr_process_capability_request(rdpdrPlugin* rdpdr, wStream* s)
|
||||
UINT status = CHANNEL_RC_OK;
|
||||
UINT16 i;
|
||||
UINT16 numCapabilities;
|
||||
UINT16 capabilityType;
|
||||
|
||||
if (!rdpdr || !s)
|
||||
return CHANNEL_RC_NULL_DATA;
|
||||
@ -217,31 +202,44 @@ UINT rdpdr_process_capability_request(rdpdrPlugin* rdpdr, wStream* s)
|
||||
|
||||
for (i = 0; i < numCapabilities; i++)
|
||||
{
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, sizeof(UINT16)))
|
||||
UINT16 capabilityType;
|
||||
UINT16 capabilityLength;
|
||||
UINT32 version = 0;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s,
|
||||
2 * sizeof(UINT16) + sizeof(UINT32)))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT16(s, capabilityType);
|
||||
Stream_Read_UINT16(s, capabilityLength);
|
||||
Stream_Read_UINT32(s, version);
|
||||
|
||||
if (capabilityLength < 8)
|
||||
{
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
capabilityLength -= 8;
|
||||
|
||||
switch (capabilityType)
|
||||
{
|
||||
case CAP_GENERAL_TYPE:
|
||||
status = rdpdr_process_general_capset(rdpdr, s);
|
||||
status = rdpdr_process_general_capset(rdpdr, s, capabilityLength);
|
||||
break;
|
||||
|
||||
case CAP_PRINTER_TYPE:
|
||||
status = rdpdr_process_printer_capset(rdpdr, s);
|
||||
status = rdpdr_process_printer_capset(rdpdr, s, capabilityLength);
|
||||
break;
|
||||
|
||||
case CAP_PORT_TYPE:
|
||||
status = rdpdr_process_port_capset(rdpdr, s);
|
||||
status = rdpdr_process_port_capset(rdpdr, s, capabilityLength);
|
||||
break;
|
||||
|
||||
case CAP_DRIVE_TYPE:
|
||||
status = rdpdr_process_drive_capset(rdpdr, s);
|
||||
status = rdpdr_process_drive_capset(rdpdr, s, capabilityLength);
|
||||
break;
|
||||
|
||||
case CAP_SMARTCARD_TYPE:
|
||||
status = rdpdr_process_smartcard_capset(rdpdr, s);
|
||||
status = rdpdr_process_smartcard_capset(rdpdr, s, capabilityLength);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@ -79,6 +79,41 @@ struct _DEVICE_DRIVE_EXT
|
||||
BOOL automount;
|
||||
};
|
||||
|
||||
static const char* rdpdr_packetid_string(UINT16 packetid)
|
||||
{
|
||||
switch (packetid)
|
||||
{
|
||||
case PAKID_CORE_SERVER_ANNOUNCE:
|
||||
return "PAKID_CORE_SERVER_ANNOUNCE";
|
||||
case PAKID_CORE_CLIENTID_CONFIRM:
|
||||
return "PAKID_CORE_CLIENTID_CONFIRM";
|
||||
case PAKID_CORE_CLIENT_NAME:
|
||||
return "PAKID_CORE_CLIENT_NAME";
|
||||
case PAKID_CORE_DEVICELIST_ANNOUNCE:
|
||||
return "PAKID_CORE_DEVICELIST_ANNOUNCE";
|
||||
case PAKID_CORE_DEVICE_REPLY:
|
||||
return "PAKID_CORE_DEVICE_REPLY";
|
||||
case PAKID_CORE_DEVICE_IOREQUEST:
|
||||
return "PAKID_CORE_DEVICE_IOREQUEST";
|
||||
case PAKID_CORE_DEVICE_IOCOMPLETION:
|
||||
return "PAKID_CORE_DEVICE_IOCOMPLETION";
|
||||
case PAKID_CORE_SERVER_CAPABILITY:
|
||||
return "PAKID_CORE_SERVER_CAPABILITY";
|
||||
case PAKID_CORE_CLIENT_CAPABILITY:
|
||||
return "PAKID_CORE_CLIENT_CAPABILITY";
|
||||
case PAKID_CORE_DEVICELIST_REMOVE:
|
||||
return "PAKID_CORE_DEVICELIST_REMOVE";
|
||||
case PAKID_CORE_USER_LOGGEDON:
|
||||
return "PAKID_CORE_USER_LOGGEDON";
|
||||
case PAKID_PRN_CACHE_DATA:
|
||||
return "PAKID_PRN_CACHE_DATA";
|
||||
case PAKID_PRN_USING_XPS:
|
||||
return "PAKID_PRN_USING_XPS";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
static const char* rdpdr_state_str(enum RDPDR_CHANNEL_STATE state)
|
||||
{
|
||||
switch (state)
|
||||
@ -106,6 +141,63 @@ static const char* rdpdr_state_str(enum RDPDR_CHANNEL_STATE state)
|
||||
}
|
||||
}
|
||||
|
||||
static const char* rdpdr_device_type_string(UINT32 type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case RDPDR_DTYP_SERIAL:
|
||||
return "serial";
|
||||
case RDPDR_DTYP_PRINT:
|
||||
return "printer";
|
||||
case RDPDR_DTYP_FILESYSTEM:
|
||||
return "drive";
|
||||
case RDPDR_DTYP_SMARTCARD:
|
||||
return "smartcard";
|
||||
case RDPDR_DTYP_PARALLEL:
|
||||
return "parallel";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
static const char* support_str(BOOL val)
|
||||
{
|
||||
if (val)
|
||||
return "supported";
|
||||
return "not found";
|
||||
}
|
||||
|
||||
static const char* rdpdr_caps_pdu_str(UINT32 flag)
|
||||
{
|
||||
switch (flag)
|
||||
{
|
||||
case RDPDR_DEVICE_REMOVE_PDUS:
|
||||
return "RDPDR_USER_LOGGEDON_PDU";
|
||||
case RDPDR_CLIENT_DISPLAY_NAME_PDU:
|
||||
return "RDPDR_CLIENT_DISPLAY_NAME_PDU";
|
||||
case RDPDR_USER_LOGGEDON_PDU:
|
||||
return "RDPDR_USER_LOGGEDON_PDU";
|
||||
default:
|
||||
return "RDPDR_UNKNONW";
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL rdpdr_check_extended_pdu_flag(rdpdrPlugin* rdpdr, UINT32 flag)
|
||||
{
|
||||
WINPR_ASSERT(rdpdr);
|
||||
|
||||
const BOOL client = (rdpdr->clientExtendedPDU & flag) != 0;
|
||||
const BOOL server = (rdpdr->serverExtendedPDU & flag) != 0;
|
||||
|
||||
if (!client || !server)
|
||||
{
|
||||
WLog_Print(rdpdr->log, WLOG_WARN, "Checking ExtendedPDU::%s, client %s, server %s",
|
||||
rdpdr_caps_pdu_str(flag), support_str(client), support_str(server));
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL rdpdr_state_advance(rdpdrPlugin* rdpdr, enum RDPDR_CHANNEL_STATE next)
|
||||
{
|
||||
WINPR_ASSERT(rdpdr);
|
||||
@ -133,6 +225,16 @@ static UINT rdpdr_send_device_list_remove_request(rdpdrPlugin* rdpdr, UINT32 cou
|
||||
{
|
||||
UINT32 i;
|
||||
wStream* s;
|
||||
|
||||
WINPR_ASSERT(rdpdr);
|
||||
WINPR_ASSERT(ids || (count == 0));
|
||||
|
||||
if (count == 0)
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
if (!rdpdr_check_extended_pdu_flag(rdpdr, RDPDR_DEVICE_REMOVE_PDUS))
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
s = Stream_New(NULL, count * sizeof(UINT32) + 8);
|
||||
|
||||
if (!s)
|
||||
@ -1115,10 +1217,19 @@ static UINT rdpdr_process_server_announce_request(rdpdrPlugin* rdpdr, wStream* s
|
||||
if (Stream_GetRemainingLength(s) < 8)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT16(s, rdpdr->versionMajor);
|
||||
Stream_Read_UINT16(s, rdpdr->versionMinor);
|
||||
Stream_Read_UINT16(s, rdpdr->serverVersionMajor);
|
||||
Stream_Read_UINT16(s, rdpdr->serverVersionMinor);
|
||||
Stream_Read_UINT32(s, rdpdr->clientID);
|
||||
rdpdr->sequenceId++;
|
||||
|
||||
rdpdr->clientVersionMajor = MIN(RDPDR_VERSION_MAJOR, rdpdr->serverVersionMajor);
|
||||
rdpdr->clientVersionMinor = MIN(RDPDR_VERSION_MINOR_RDP10X, rdpdr->serverVersionMinor);
|
||||
WLog_Print(rdpdr->log, WLOG_DEBUG,
|
||||
"[rdpdr] server announces version %" PRIu32 ".%" PRIu32 ", client uses %" PRIu32
|
||||
".%" PRIu32,
|
||||
rdpdr->serverVersionMajor, rdpdr->serverVersionMinor, rdpdr->clientVersionMajor,
|
||||
rdpdr->clientVersionMinor);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
@ -1145,8 +1256,8 @@ static UINT rdpdr_send_client_announce_reply(rdpdrPlugin* rdpdr)
|
||||
|
||||
Stream_Write_UINT16(s, RDPDR_CTYP_CORE); /* Component (2 bytes) */
|
||||
Stream_Write_UINT16(s, PAKID_CORE_CLIENTID_CONFIRM); /* PacketId (2 bytes) */
|
||||
Stream_Write_UINT16(s, rdpdr->versionMajor);
|
||||
Stream_Write_UINT16(s, rdpdr->versionMinor);
|
||||
Stream_Write_UINT16(s, rdpdr->clientVersionMajor);
|
||||
Stream_Write_UINT16(s, rdpdr->clientVersionMinor);
|
||||
Stream_Write_UINT32(s, (UINT32)rdpdr->clientID);
|
||||
return rdpdr_send(rdpdr, s);
|
||||
}
|
||||
@ -1209,10 +1320,10 @@ static UINT rdpdr_process_server_clientid_confirm(rdpdrPlugin* rdpdr, wStream* s
|
||||
Stream_Read_UINT16(s, versionMinor);
|
||||
Stream_Read_UINT32(s, clientID);
|
||||
|
||||
if (versionMajor != rdpdr->versionMajor || versionMinor != rdpdr->versionMinor)
|
||||
if (versionMajor != rdpdr->clientVersionMajor || versionMinor != rdpdr->clientVersionMinor)
|
||||
{
|
||||
rdpdr->versionMajor = versionMajor;
|
||||
rdpdr->versionMinor = versionMinor;
|
||||
rdpdr->clientVersionMajor = versionMajor;
|
||||
rdpdr->clientVersionMinor = versionMinor;
|
||||
}
|
||||
|
||||
if (clientID != rdpdr->clientID)
|
||||
@ -1241,15 +1352,7 @@ static UINT rdpdr_send_device_list_announce_request(rdpdrPlugin* rdpdr, BOOL use
|
||||
ULONG_PTR* pKeys = NULL;
|
||||
|
||||
if (userLoggedOn)
|
||||
{
|
||||
WINPR_ASSERT(rdpdr->state >= RDPDR_CHANNEL_STATE_READY);
|
||||
rdpdr_state_advance(rdpdr, RDPDR_CHANNEL_STATE_USER_LOGGEDON);
|
||||
}
|
||||
else
|
||||
{
|
||||
WINPR_ASSERT(rdpdr->state >= RDPDR_CHANNEL_STATE_CLIENTID_CONFIRM);
|
||||
rdpdr_state_advance(rdpdr, RDPDR_CHANNEL_STATE_READY);
|
||||
}
|
||||
rdpdr->userLoggedOn = TRUE;
|
||||
|
||||
s = Stream_New(NULL, 256);
|
||||
|
||||
@ -1278,8 +1381,8 @@ static UINT rdpdr_send_device_list_announce_request(rdpdrPlugin* rdpdr, BOOL use
|
||||
* 3. other devices are sent only after user_loggedon
|
||||
*/
|
||||
|
||||
if ((rdpdr->versionMinor == 0x0005) || (device->type == RDPDR_DTYP_SMARTCARD) ||
|
||||
userLoggedOn)
|
||||
if ((rdpdr->clientVersionMinor == RDPDR_VERSION_MINOR_RDP51) ||
|
||||
(device->type == RDPDR_DTYP_SMARTCARD) || userLoggedOn)
|
||||
{
|
||||
data_len = (device->data == NULL ? 0 : Stream_GetPosition(device->data));
|
||||
|
||||
@ -1444,6 +1547,8 @@ static UINT rdpdr_process_init(rdpdrPlugin* rdpdr)
|
||||
DEVICE* device;
|
||||
ULONG_PTR* pKeys = NULL;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
rdpdr->userLoggedOn = FALSE; /* reset possible received state */
|
||||
pKeys = NULL;
|
||||
keyCount = ListDictionary_GetKeys(rdpdr->devman->devices, &pKeys);
|
||||
|
||||
@ -1464,18 +1569,50 @@ static UINT rdpdr_process_init(rdpdrPlugin* rdpdr)
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static BOOL rdpdr_state_check(rdpdrPlugin* rdpdr, UINT16 packetid,
|
||||
enum RDPDR_CHANNEL_STATE expected, enum RDPDR_CHANNEL_STATE next)
|
||||
static BOOL state_match(enum RDPDR_CHANNEL_STATE state, size_t count, va_list ap)
|
||||
{
|
||||
for (size_t x = 0; x < count; x++)
|
||||
{
|
||||
enum RDPDR_CHANNEL_STATE cur = va_arg(ap, enum RDPDR_CHANNEL_STATE);
|
||||
if (state == cur)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static const char* state_str(size_t count, va_list ap, char* buffer, size_t size)
|
||||
{
|
||||
for (size_t x = 0; x < count; x++)
|
||||
{
|
||||
enum RDPDR_CHANNEL_STATE cur = va_arg(ap, enum RDPDR_CHANNEL_STATE);
|
||||
const char* curstr = rdpdr_state_str(cur);
|
||||
winpr_str_append(curstr, buffer, size, "|");
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static BOOL rdpdr_state_check(rdpdrPlugin* rdpdr, UINT16 packetid, enum RDPDR_CHANNEL_STATE next,
|
||||
size_t count, ...)
|
||||
{
|
||||
va_list ap;
|
||||
WINPR_ASSERT(rdpdr);
|
||||
|
||||
const char* strstate = rdpdr_state_str(rdpdr->state);
|
||||
if (rdpdr->state != expected)
|
||||
va_start(ap, count);
|
||||
BOOL rc = state_match(rdpdr->state, count, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (!rc)
|
||||
{
|
||||
const char* strstate = rdpdr_state_str(rdpdr->state);
|
||||
char buffer[256] = { 0 };
|
||||
|
||||
va_start(ap, count);
|
||||
state_str(count, ap, buffer, sizeof(buffer));
|
||||
va_end(ap);
|
||||
|
||||
WLog_Print(rdpdr->log, WLOG_ERROR,
|
||||
"channel [RDPDR] received %" PRIu16
|
||||
", expected state %s but have state %s, aborting.",
|
||||
packetid, rdpdr_state_str(expected), strstate);
|
||||
"channel [RDPDR] received %s, expected states [%s] but have state %s, aborting.",
|
||||
rdpdr_packetid_string(packetid), buffer, strstate);
|
||||
|
||||
rdpdr_state_advance(rdpdr, RDPDR_CHANNEL_STATE_INITIAL);
|
||||
return FALSE;
|
||||
@ -1495,23 +1632,30 @@ static BOOL rdpdr_check_channel_state(rdpdrPlugin* rdpdr, UINT16 packetid)
|
||||
* then reinitialize the channel after login successful
|
||||
*/
|
||||
rdpdr_state_advance(rdpdr, RDPDR_CHANNEL_STATE_INITIAL);
|
||||
return rdpdr_state_check(rdpdr, packetid, RDPDR_CHANNEL_STATE_INITIAL,
|
||||
RDPDR_CHANNEL_STATE_ANNOUNCE);
|
||||
return rdpdr_state_check(rdpdr, packetid, RDPDR_CHANNEL_STATE_ANNOUNCE, 1,
|
||||
RDPDR_CHANNEL_STATE_INITIAL);
|
||||
case PAKID_CORE_SERVER_CAPABILITY:
|
||||
return rdpdr_state_check(rdpdr, packetid, RDPDR_CHANNEL_STATE_NAME_REQUEST,
|
||||
RDPDR_CHANNEL_STATE_SERVER_CAPS);
|
||||
return rdpdr_state_check(rdpdr, packetid, RDPDR_CHANNEL_STATE_SERVER_CAPS, 6,
|
||||
RDPDR_CHANNEL_STATE_NAME_REQUEST,
|
||||
RDPDR_CHANNEL_STATE_SERVER_CAPS, RDPDR_CHANNEL_STATE_READY,
|
||||
RDPDR_CHANNEL_STATE_CLIENT_CAPS, PAKID_CORE_CLIENTID_CONFIRM,
|
||||
PAKID_CORE_USER_LOGGEDON);
|
||||
case PAKID_CORE_CLIENTID_CONFIRM:
|
||||
return rdpdr_state_check(rdpdr, packetid, RDPDR_CHANNEL_STATE_CLIENT_CAPS,
|
||||
RDPDR_CHANNEL_STATE_CLIENTID_CONFIRM);
|
||||
case PAKID_CORE_USER_LOGGEDON:
|
||||
return rdpdr_state_check(rdpdr, packetid, RDPDR_CHANNEL_STATE_READY,
|
||||
return rdpdr_state_check(rdpdr, packetid, RDPDR_CHANNEL_STATE_CLIENTID_CONFIRM, 3,
|
||||
RDPDR_CHANNEL_STATE_CLIENT_CAPS, RDPDR_CHANNEL_STATE_READY,
|
||||
RDPDR_CHANNEL_STATE_USER_LOGGEDON);
|
||||
case PAKID_CORE_USER_LOGGEDON:
|
||||
if (!rdpdr_check_extended_pdu_flag(rdpdr, RDPDR_USER_LOGGEDON_PDU))
|
||||
return FALSE;
|
||||
|
||||
return rdpdr_state_check(
|
||||
rdpdr, packetid, RDPDR_CHANNEL_STATE_USER_LOGGEDON, 4,
|
||||
RDPDR_CHANNEL_STATE_NAME_REQUEST, RDPDR_CHANNEL_STATE_CLIENT_CAPS,
|
||||
RDPDR_CHANNEL_STATE_CLIENTID_CONFIRM, RDPDR_CHANNEL_STATE_READY);
|
||||
default:
|
||||
{
|
||||
enum RDPDR_CHANNEL_STATE state = RDPDR_CHANNEL_STATE_READY;
|
||||
if (rdpdr->state == RDPDR_CHANNEL_STATE_USER_LOGGEDON)
|
||||
state = RDPDR_CHANNEL_STATE_USER_LOGGEDON;
|
||||
return rdpdr_state_check(rdpdr, packetid, state, state);
|
||||
return rdpdr_state_check(rdpdr, packetid, state, 1, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1592,7 +1736,10 @@ static UINT rdpdr_process_receive(rdpdrPlugin* rdpdr, wStream* s)
|
||||
"rdpdr_send_device_list_announce_request failed with error %" PRIu32 "",
|
||||
error);
|
||||
}
|
||||
|
||||
else if (!rdpdr_state_advance(rdpdr, RDPDR_CHANNEL_STATE_READY))
|
||||
{
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
case PAKID_CORE_USER_LOGGEDON:
|
||||
@ -1603,6 +1750,10 @@ static UINT rdpdr_process_receive(rdpdrPlugin* rdpdr, wStream* s)
|
||||
"rdpdr_send_device_list_announce_request failed with error %" PRIu32 "",
|
||||
error);
|
||||
}
|
||||
else if (!rdpdr_state_advance(rdpdr, RDPDR_CHANNEL_STATE_READY))
|
||||
{
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@ -2035,6 +2186,10 @@ BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints, PVOID p
|
||||
UINT rc;
|
||||
rdpdrPlugin* rdpdr;
|
||||
CHANNEL_ENTRY_POINTS_FREERDP_EX* pEntryPointsEx;
|
||||
|
||||
WINPR_ASSERT(pEntryPoints);
|
||||
WINPR_ASSERT(pInitHandle);
|
||||
|
||||
rdpdr = (rdpdrPlugin*)calloc(1, sizeof(rdpdrPlugin));
|
||||
|
||||
if (!rdpdr)
|
||||
@ -2043,7 +2198,18 @@ BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints, PVOID p
|
||||
return FALSE;
|
||||
}
|
||||
rdpdr->log = WLog_Get(TAG);
|
||||
WINPR_ASSERT(rdpdr->log);
|
||||
|
||||
rdpdr->clientExtendedPDU =
|
||||
RDPDR_DEVICE_REMOVE_PDUS | RDPDR_CLIENT_DISPLAY_NAME_PDU | RDPDR_USER_LOGGEDON_PDU;
|
||||
rdpdr->clientIOCode1 =
|
||||
RDPDR_IRP_MJ_CREATE | RDPDR_IRP_MJ_CLEANUP | RDPDR_IRP_MJ_CLOSE | RDPDR_IRP_MJ_READ |
|
||||
RDPDR_IRP_MJ_WRITE | RDPDR_IRP_MJ_FLUSH_BUFFERS | RDPDR_IRP_MJ_SHUTDOWN |
|
||||
RDPDR_IRP_MJ_DEVICE_CONTROL | RDPDR_IRP_MJ_QUERY_VOLUME_INFORMATION |
|
||||
RDPDR_IRP_MJ_SET_VOLUME_INFORMATION | RDPDR_IRP_MJ_QUERY_INFORMATION |
|
||||
RDPDR_IRP_MJ_SET_INFORMATION | RDPDR_IRP_MJ_DIRECTORY_CONTROL | RDPDR_IRP_MJ_LOCK_CONTROL |
|
||||
RDPDR_IRP_MJ_QUERY_SECURITY | RDPDR_IRP_MJ_SET_SECURITY;
|
||||
|
||||
rdpdr->clientExtraFlags1 = ENABLE_ASYNCIO;
|
||||
|
||||
rdpdr->channelDef.options =
|
||||
CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP | CHANNEL_OPTION_COMPRESS_RDP;
|
||||
|
||||
@ -77,12 +77,33 @@ struct rdpdr_plugin
|
||||
|
||||
DEVMAN* devman;
|
||||
|
||||
UINT16 versionMajor;
|
||||
UINT16 versionMinor;
|
||||
UINT16 clientID;
|
||||
UINT32 serverOsType;
|
||||
UINT32 serverOsVersion;
|
||||
UINT16 serverVersionMajor;
|
||||
UINT16 serverVersionMinor;
|
||||
UINT32 serverExtendedPDU;
|
||||
UINT32 serverIOCode1;
|
||||
UINT32 serverIOCode2;
|
||||
UINT32 serverExtraFlags1;
|
||||
UINT32 serverExtraFlags2;
|
||||
UINT32 serverSpecialTypeDeviceCap;
|
||||
|
||||
UINT32 clientOsType;
|
||||
UINT32 clientOsVersion;
|
||||
UINT16 clientVersionMajor;
|
||||
UINT16 clientVersionMinor;
|
||||
UINT32 clientExtendedPDU;
|
||||
UINT32 clientIOCode1;
|
||||
UINT32 clientIOCode2;
|
||||
UINT32 clientExtraFlags1;
|
||||
UINT32 clientExtraFlags2;
|
||||
UINT32 clientSpecialTypeDeviceCap;
|
||||
|
||||
UINT32 clientID;
|
||||
char computerName[256];
|
||||
|
||||
UINT32 sequenceId;
|
||||
BOOL userLoggedOn;
|
||||
|
||||
/* hotplug support */
|
||||
HANDLE hotplugThread;
|
||||
|
||||
@ -57,13 +57,6 @@ struct _RDPDR_HEADER
|
||||
};
|
||||
typedef struct _RDPDR_HEADER RDPDR_HEADER;
|
||||
|
||||
#define RDPDR_VERSION_MAJOR 0x0001
|
||||
|
||||
#define RDPDR_VERSION_MINOR_RDP50 0x0002
|
||||
#define RDPDR_VERSION_MINOR_RDP51 0x0005
|
||||
#define RDPDR_VERSION_MINOR_RDP52 0x000A
|
||||
#define RDPDR_VERSION_MINOR_RDP6X 0x000C
|
||||
|
||||
#define RDPDR_CAPABILITY_HEADER_LENGTH 8
|
||||
|
||||
struct _RDPDR_CAPABILITY_HEADER
|
||||
|
||||
@ -23,11 +23,15 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/assert.h>
|
||||
#include <winpr/stream.h>
|
||||
#include <winpr/cmdline.h>
|
||||
|
||||
@ -51,8 +55,39 @@ struct rdpsnd_pulse_plugin
|
||||
pa_stream* stream;
|
||||
UINT32 latency;
|
||||
UINT32 volume;
|
||||
time_t reconnect_delay_seconds;
|
||||
time_t reconnect_time;
|
||||
};
|
||||
|
||||
static BOOL rdpsnd_check_pulse(rdpsndPulsePlugin* pulse, BOOL haveStream)
|
||||
{
|
||||
BOOL rc = TRUE;
|
||||
WINPR_ASSERT(pulse);
|
||||
|
||||
if (!pulse->context)
|
||||
{
|
||||
WLog_WARN(TAG, "pulse->context=%p", pulse->context);
|
||||
rc = FALSE;
|
||||
}
|
||||
|
||||
if (haveStream)
|
||||
{
|
||||
if (!pulse->stream)
|
||||
{
|
||||
WLog_WARN(TAG, "pulse->stream=%p", pulse->stream);
|
||||
rc = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pulse->mainloop)
|
||||
{
|
||||
WLog_WARN(TAG, "pulse->mainloop=%p", pulse->mainloop);
|
||||
rc = FALSE;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format);
|
||||
|
||||
static void rdpsnd_pulse_get_sink_info(pa_context* c, const pa_sink_info* i, int eol,
|
||||
@ -65,7 +100,8 @@ static void rdpsnd_pulse_get_sink_info(pa_context* c, const pa_sink_info* i, int
|
||||
;
|
||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)userdata;
|
||||
|
||||
if (!pulse || !c || !i)
|
||||
WINPR_ASSERT(c);
|
||||
if (!rdpsnd_check_pulse(pulse, FALSE) || !i)
|
||||
return;
|
||||
|
||||
for (x = 0; x < i->volume.channels; x++)
|
||||
@ -97,6 +133,10 @@ static void rdpsnd_pulse_context_state_callback(pa_context* context, void* userd
|
||||
{
|
||||
pa_context_state_t state;
|
||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)userdata;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(pulse);
|
||||
|
||||
state = pa_context_get_state(context);
|
||||
|
||||
switch (state)
|
||||
@ -106,6 +146,14 @@ static void rdpsnd_pulse_context_state_callback(pa_context* context, void* userd
|
||||
break;
|
||||
|
||||
case PA_CONTEXT_FAILED:
|
||||
// Destroy context now, create new one for next connection attempt
|
||||
pa_context_unref(pulse->context);
|
||||
pulse->context = NULL;
|
||||
if (pulse->reconnect_delay_seconds >= 0)
|
||||
pulse->reconnect_time = time(NULL) + pulse->reconnect_delay_seconds;
|
||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||
break;
|
||||
|
||||
case PA_CONTEXT_TERMINATED:
|
||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||
break;
|
||||
@ -117,21 +165,17 @@ static void rdpsnd_pulse_context_state_callback(pa_context* context, void* userd
|
||||
|
||||
static BOOL rdpsnd_pulse_connect(rdpsndDevicePlugin* device)
|
||||
{
|
||||
BOOL rc;
|
||||
pa_operation* o;
|
||||
pa_context_state_t state;
|
||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
|
||||
|
||||
if (!pulse->context)
|
||||
if (!rdpsnd_check_pulse(pulse, FALSE))
|
||||
return FALSE;
|
||||
|
||||
if (pa_context_connect(pulse->context, NULL, 0, NULL))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||
|
||||
if (pa_threaded_mainloop_start(pulse->mainloop) < 0)
|
||||
if (pa_context_connect(pulse->context, NULL, 0, NULL) < 0)
|
||||
{
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
return FALSE;
|
||||
@ -157,27 +201,35 @@ static BOOL rdpsnd_pulse_connect(rdpsndDevicePlugin* device)
|
||||
if (o)
|
||||
pa_operation_unref(o);
|
||||
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
|
||||
if (state == PA_CONTEXT_READY)
|
||||
{
|
||||
return TRUE;
|
||||
rc = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pa_context_disconnect(pulse->context);
|
||||
return FALSE;
|
||||
rc = FALSE;
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void rdpsnd_pulse_stream_success_callback(pa_stream* stream, int success, void* userdata)
|
||||
{
|
||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)userdata;
|
||||
|
||||
if (!rdpsnd_check_pulse(pulse, TRUE))
|
||||
return;
|
||||
|
||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||
}
|
||||
|
||||
static void rdpsnd_pulse_wait_for_operation(rdpsndPulsePlugin* pulse, pa_operation* operation)
|
||||
{
|
||||
if (!rdpsnd_check_pulse(pulse, TRUE))
|
||||
return;
|
||||
|
||||
if (!operation)
|
||||
return;
|
||||
|
||||
@ -193,6 +245,11 @@ static void rdpsnd_pulse_stream_state_callback(pa_stream* stream, void* userdata
|
||||
{
|
||||
pa_stream_state_t state;
|
||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)userdata;
|
||||
|
||||
WINPR_ASSERT(stream);
|
||||
if (!rdpsnd_check_pulse(pulse, TRUE))
|
||||
return;
|
||||
|
||||
state = pa_stream_get_state(stream);
|
||||
|
||||
switch (state)
|
||||
@ -203,6 +260,8 @@ static void rdpsnd_pulse_stream_state_callback(pa_stream* stream, void* userdata
|
||||
|
||||
case PA_STREAM_FAILED:
|
||||
case PA_STREAM_TERMINATED:
|
||||
// Stream object is about to be destroyed, clean up our pointer
|
||||
pulse->stream = NULL;
|
||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||
break;
|
||||
|
||||
@ -214,6 +273,11 @@ static void rdpsnd_pulse_stream_state_callback(pa_stream* stream, void* userdata
|
||||
static void rdpsnd_pulse_stream_request_callback(pa_stream* stream, size_t length, void* userdata)
|
||||
{
|
||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)userdata;
|
||||
|
||||
WINPR_ASSERT(stream);
|
||||
if (!rdpsnd_check_pulse(pulse, TRUE))
|
||||
return;
|
||||
|
||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||
}
|
||||
|
||||
@ -221,15 +285,20 @@ static void rdpsnd_pulse_close(rdpsndDevicePlugin* device)
|
||||
{
|
||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
|
||||
|
||||
if (!pulse->context || !pulse->stream)
|
||||
WINPR_ASSERT(pulse);
|
||||
|
||||
if (!rdpsnd_check_pulse(pulse, FALSE))
|
||||
return;
|
||||
|
||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||
rdpsnd_pulse_wait_for_operation(
|
||||
pulse, pa_stream_drain(pulse->stream, rdpsnd_pulse_stream_success_callback, pulse));
|
||||
pa_stream_disconnect(pulse->stream);
|
||||
pa_stream_unref(pulse->stream);
|
||||
pulse->stream = NULL;
|
||||
if (pulse->stream)
|
||||
{
|
||||
rdpsnd_pulse_wait_for_operation(
|
||||
pulse, pa_stream_drain(pulse->stream, rdpsnd_pulse_stream_success_callback, pulse));
|
||||
pa_stream_disconnect(pulse->stream);
|
||||
pa_stream_unref(pulse->stream);
|
||||
pulse->stream = NULL;
|
||||
}
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
}
|
||||
|
||||
@ -237,7 +306,9 @@ static BOOL rdpsnd_pulse_set_format_spec(rdpsndPulsePlugin* pulse, const AUDIO_F
|
||||
{
|
||||
pa_sample_spec sample_spec = { 0 };
|
||||
|
||||
if (!pulse->context)
|
||||
WINPR_ASSERT(format);
|
||||
|
||||
if (!rdpsnd_check_pulse(pulse, FALSE))
|
||||
return FALSE;
|
||||
|
||||
if (!rdpsnd_pulse_format_supported(&pulse->device, format))
|
||||
@ -281,30 +352,52 @@ static BOOL rdpsnd_pulse_set_format_spec(rdpsndPulsePlugin* pulse, const AUDIO_F
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_pulse_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format,
|
||||
UINT32 latency)
|
||||
static BOOL rdpsnd_pulse_context_connect(rdpsndDevicePlugin* device)
|
||||
{
|
||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
|
||||
|
||||
pulse->context = pa_context_new(pa_threaded_mainloop_get_api(pulse->mainloop), "freerdp");
|
||||
|
||||
if (!pulse->context)
|
||||
return FALSE;
|
||||
|
||||
pa_context_set_state_callback(pulse->context, rdpsnd_pulse_context_state_callback, pulse);
|
||||
|
||||
if (!rdpsnd_pulse_connect((rdpsndDevicePlugin*)pulse))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_pulse_open_stream(rdpsndDevicePlugin* device)
|
||||
{
|
||||
pa_stream_state_t state;
|
||||
pa_stream_flags_t flags;
|
||||
pa_buffer_attr buffer_attr = { 0 };
|
||||
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX];
|
||||
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX] = { 0 };
|
||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
|
||||
|
||||
if (!pulse->context || pulse->stream)
|
||||
return TRUE;
|
||||
|
||||
if (!rdpsnd_pulse_set_format_spec(pulse, format))
|
||||
return FALSE;
|
||||
|
||||
pulse->latency = latency;
|
||||
|
||||
if (pa_sample_spec_valid(&pulse->sample_spec) == 0)
|
||||
{
|
||||
pa_sample_spec_snprint(ss, sizeof(ss), &pulse->sample_spec);
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||
if (!pulse->context)
|
||||
{
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
if (pulse->reconnect_delay_seconds >= 0 && time(NULL) - pulse->reconnect_time >= 0)
|
||||
rdpsnd_pulse_context_connect(device);
|
||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||
}
|
||||
|
||||
if (!rdpsnd_check_pulse(pulse, FALSE))
|
||||
{
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
pulse->stream = pa_stream_new(pulse->context, "freerdp", &pulse->sample_spec, NULL);
|
||||
|
||||
if (!pulse->stream)
|
||||
@ -320,19 +413,22 @@ static BOOL rdpsnd_pulse_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* fo
|
||||
|
||||
if (pulse->latency > 0)
|
||||
{
|
||||
buffer_attr.maxlength = pa_usec_to_bytes(pulse->latency * 2 * 1000, &pulse->sample_spec);
|
||||
buffer_attr.maxlength = UINT32_MAX;
|
||||
buffer_attr.tlength = pa_usec_to_bytes(pulse->latency * 1000, &pulse->sample_spec);
|
||||
buffer_attr.prebuf = (UINT32)-1;
|
||||
buffer_attr.minreq = (UINT32)-1;
|
||||
buffer_attr.fragsize = (UINT32)-1;
|
||||
buffer_attr.prebuf = UINT32_MAX;
|
||||
buffer_attr.minreq = UINT32_MAX;
|
||||
buffer_attr.fragsize = UINT32_MAX;
|
||||
flags |= PA_STREAM_ADJUST_LATENCY;
|
||||
}
|
||||
|
||||
if (pa_stream_connect_playback(pulse->stream, pulse->device_name,
|
||||
pulse->latency > 0 ? &buffer_attr : NULL, flags, NULL, NULL) < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "error connecting playback stream");
|
||||
pa_stream_unref(pulse->stream);
|
||||
pulse->stream = NULL;
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
for (;;)
|
||||
@ -359,6 +455,24 @@ static BOOL rdpsnd_pulse_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* fo
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_pulse_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format,
|
||||
UINT32 latency)
|
||||
{
|
||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
|
||||
|
||||
WINPR_ASSERT(format);
|
||||
|
||||
if (!rdpsnd_check_pulse(pulse, FALSE))
|
||||
return TRUE;
|
||||
|
||||
if (!rdpsnd_pulse_set_format_spec(pulse, format))
|
||||
return FALSE;
|
||||
|
||||
pulse->latency = latency;
|
||||
|
||||
return rdpsnd_pulse_open_stream(device);
|
||||
}
|
||||
|
||||
static void rdpsnd_pulse_free(rdpsndDevicePlugin* device)
|
||||
{
|
||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
|
||||
@ -369,9 +483,7 @@ static void rdpsnd_pulse_free(rdpsndDevicePlugin* device)
|
||||
rdpsnd_pulse_close(device);
|
||||
|
||||
if (pulse->mainloop)
|
||||
{
|
||||
pa_threaded_mainloop_stop(pulse->mainloop);
|
||||
}
|
||||
|
||||
if (pulse->context)
|
||||
{
|
||||
@ -394,6 +506,7 @@ static BOOL rdpsnd_pulse_default_format(rdpsndDevicePlugin* device, const AUDIO_
|
||||
AUDIO_FORMAT* defaultFormat)
|
||||
{
|
||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
|
||||
|
||||
if (!pulse || !defaultFormat)
|
||||
return FALSE;
|
||||
|
||||
@ -415,6 +528,9 @@ static BOOL rdpsnd_pulse_default_format(rdpsndDevicePlugin* device, const AUDIO_
|
||||
|
||||
BOOL rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format)
|
||||
{
|
||||
WINPR_ASSERT(device);
|
||||
WINPR_ASSERT(format);
|
||||
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_PCM:
|
||||
@ -439,39 +555,51 @@ static UINT32 rdpsnd_pulse_get_volume(rdpsndDevicePlugin* device)
|
||||
pa_operation* o;
|
||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
|
||||
|
||||
if (!pulse)
|
||||
return 0;
|
||||
|
||||
if (!pulse->context || !pulse->mainloop)
|
||||
if (!rdpsnd_check_pulse(pulse, FALSE))
|
||||
return 0;
|
||||
|
||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||
o = pa_context_get_sink_info_by_index(pulse->context, 0, rdpsnd_pulse_get_sink_info, pulse);
|
||||
pa_operation_unref(o);
|
||||
if (o)
|
||||
pa_operation_unref(o);
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
return pulse->volume;
|
||||
}
|
||||
|
||||
static void rdpsnd_set_volume_success_cb(pa_context* c, int success, void* userdata)
|
||||
{
|
||||
rdpsndPulsePlugin* pulse = userdata;
|
||||
|
||||
if (!rdpsnd_check_pulse(pulse, TRUE))
|
||||
return;
|
||||
WINPR_ASSERT(c);
|
||||
|
||||
WLog_INFO(TAG, "%s: %d", __FUNCTION__, success);
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_pulse_set_volume(rdpsndDevicePlugin* device, UINT32 value)
|
||||
{
|
||||
pa_cvolume cv;
|
||||
pa_cvolume cv = { 0 };
|
||||
pa_volume_t left;
|
||||
pa_volume_t right;
|
||||
pa_operation* operation;
|
||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
|
||||
|
||||
if (!pulse->context || !pulse->stream)
|
||||
if (!rdpsnd_check_pulse(pulse, TRUE))
|
||||
{
|
||||
WLog_WARN(TAG, "%s called before pulse backend was initialized");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
left = (pa_volume_t)(value & 0xFFFF);
|
||||
right = (pa_volume_t)((value >> 16) & 0xFFFF);
|
||||
pa_cvolume_init(&cv);
|
||||
cv.channels = 2;
|
||||
cv.values[0] = PA_VOLUME_MUTED + (left * (PA_VOLUME_NORM - PA_VOLUME_MUTED)) / 0xFFFF;
|
||||
cv.values[1] = PA_VOLUME_MUTED + (right * (PA_VOLUME_NORM - PA_VOLUME_MUTED)) / 0xFFFF;
|
||||
cv.values[0] = PA_VOLUME_MUTED + (left * (PA_VOLUME_NORM - PA_VOLUME_MUTED)) / PA_VOLUME_NORM;
|
||||
cv.values[1] = PA_VOLUME_MUTED + (right * (PA_VOLUME_NORM - PA_VOLUME_MUTED)) / PA_VOLUME_NORM;
|
||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||
operation = pa_context_set_sink_input_volume(pulse->context, pa_stream_get_index(pulse->stream),
|
||||
&cv, NULL, NULL);
|
||||
&cv, rdpsnd_set_volume_success_cb, pulse);
|
||||
|
||||
if (operation)
|
||||
pa_operation_unref(operation);
|
||||
@ -483,28 +611,38 @@ static BOOL rdpsnd_pulse_set_volume(rdpsndDevicePlugin* device, UINT32 value)
|
||||
static UINT rdpsnd_pulse_play(rdpsndDevicePlugin* device, const BYTE* data, size_t size)
|
||||
{
|
||||
size_t length;
|
||||
void* pa_data;
|
||||
int status;
|
||||
pa_usec_t latency;
|
||||
int negative;
|
||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
|
||||
|
||||
if (!pulse->stream || !data)
|
||||
if (!data)
|
||||
return 0;
|
||||
|
||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||
|
||||
if (!rdpsnd_check_pulse(pulse, TRUE))
|
||||
{
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
// Discard this playback request and just attempt to reconnect the stream
|
||||
WLog_DBG(TAG, "reconnecting playback stream");
|
||||
rdpsnd_pulse_open_stream(device);
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (size > 0)
|
||||
{
|
||||
while ((length = pa_stream_writable_size(pulse->stream)) == 0)
|
||||
pa_threaded_mainloop_wait(pulse->mainloop);
|
||||
length = size;
|
||||
|
||||
if (length == (size_t)-1)
|
||||
status = pa_stream_begin_write(pulse->stream, &pa_data, &length);
|
||||
|
||||
if (status < 0)
|
||||
break;
|
||||
|
||||
if (length > size)
|
||||
length = size;
|
||||
memcpy(pa_data, data, length);
|
||||
|
||||
status = pa_stream_write(pulse->stream, data, length, NULL, 0LL, PA_SEEK_RELATIVE);
|
||||
status = pa_stream_write(pulse->stream, pa_data, length, NULL, 0LL, PA_SEEK_RELATIVE);
|
||||
|
||||
if (status < 0)
|
||||
{
|
||||
@ -522,22 +660,24 @@ static UINT rdpsnd_pulse_play(rdpsndDevicePlugin* device, const BYTE* data, size
|
||||
return latency / 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rdpsnd_pulse_parse_addin_args(rdpsndDevicePlugin* device, ADDIN_ARGV* args)
|
||||
{
|
||||
int status;
|
||||
DWORD flags;
|
||||
COMMAND_LINE_ARGUMENT_A* arg;
|
||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
|
||||
COMMAND_LINE_ARGUMENT_A rdpsnd_pulse_args[] = { { "dev", COMMAND_LINE_VALUE_REQUIRED,
|
||||
"<device>", NULL, NULL, -1, NULL, "device" },
|
||||
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } };
|
||||
COMMAND_LINE_ARGUMENT_A rdpsnd_pulse_args[] = {
|
||||
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "device" },
|
||||
{ "reconnect_delay_seconds", COMMAND_LINE_VALUE_REQUIRED, "<reconnect_delay_seconds>", NULL,
|
||||
NULL, -1, NULL, "reconnect_delay_seconds" },
|
||||
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
|
||||
};
|
||||
flags =
|
||||
COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
||||
|
||||
WINPR_ASSERT(pulse);
|
||||
WINPR_ASSERT(args);
|
||||
|
||||
status = CommandLineParseArgumentsA(args->argc, args->argv, rdpsnd_pulse_args, flags, pulse,
|
||||
NULL, NULL);
|
||||
|
||||
@ -558,6 +698,15 @@ static UINT rdpsnd_pulse_parse_addin_args(rdpsndDevicePlugin* device, ADDIN_ARGV
|
||||
if (!pulse->device_name)
|
||||
return ERROR_OUTOFMEMORY;
|
||||
}
|
||||
CommandLineSwitchCase(arg, "reconnect_delay_seconds")
|
||||
{
|
||||
unsigned long val = strtoul(arg->Value, NULL, 0);
|
||||
|
||||
if ((errno != 0) || (val > INT32_MAX))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
pulse->reconnect_delay_seconds = val;
|
||||
}
|
||||
CommandLineSwitchEnd(arg)
|
||||
} while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
||||
|
||||
@ -570,16 +719,14 @@ static UINT rdpsnd_pulse_parse_addin_args(rdpsndDevicePlugin* device, ADDIN_ARGV
|
||||
#define freerdp_rdpsnd_client_subsystem_entry FREERDP_API freerdp_rdpsnd_client_subsystem_entry
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS pEntryPoints)
|
||||
{
|
||||
ADDIN_ARGV* args;
|
||||
rdpsndPulsePlugin* pulse;
|
||||
UINT ret;
|
||||
|
||||
WINPR_ASSERT(pEntryPoints);
|
||||
|
||||
pulse = (rdpsndPulsePlugin*)calloc(1, sizeof(rdpsndPulsePlugin));
|
||||
|
||||
if (!pulse)
|
||||
@ -605,6 +752,8 @@ UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS p
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
pulse->reconnect_delay_seconds = 5;
|
||||
pulse->reconnect_time = time(NULL);
|
||||
|
||||
ret = CHANNEL_RC_NO_MEMORY;
|
||||
pulse->mainloop = pa_threaded_mainloop_new();
|
||||
@ -612,15 +761,17 @@ UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS p
|
||||
if (!pulse->mainloop)
|
||||
goto error;
|
||||
|
||||
pulse->context = pa_context_new(pa_threaded_mainloop_get_api(pulse->mainloop), "freerdp");
|
||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||
|
||||
if (!pulse->context)
|
||||
goto error;
|
||||
if (pa_threaded_mainloop_start(pulse->mainloop) < 0)
|
||||
{
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
pa_context_set_state_callback(pulse->context, rdpsnd_pulse_context_state_callback, pulse);
|
||||
ret = ERROR_INVALID_OPERATION;
|
||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||
|
||||
if (!rdpsnd_pulse_connect((rdpsndDevicePlugin*)pulse))
|
||||
if (!rdpsnd_pulse_context_connect((rdpsndDevicePlugin*)pulse))
|
||||
goto error;
|
||||
|
||||
pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin*)pulse);
|
||||
|
||||
@ -126,6 +126,10 @@ struct rdpsnd_plugin
|
||||
HANDLE thread;
|
||||
wMessageQueue* queue;
|
||||
BOOL initialized;
|
||||
|
||||
UINT16 wVersion;
|
||||
UINT32 volume;
|
||||
BOOL applyVolume;
|
||||
};
|
||||
|
||||
static const char* rdpsnd_is_dyn_str(BOOL dynamic)
|
||||
@ -268,9 +272,9 @@ static UINT rdpsnd_send_client_audio_formats(rdpsndPlugin* rdpsnd)
|
||||
static UINT rdpsnd_recv_server_audio_formats_pdu(rdpsndPlugin* rdpsnd, wStream* s)
|
||||
{
|
||||
UINT16 index;
|
||||
UINT16 wVersion;
|
||||
UINT16 wNumberOfFormats;
|
||||
UINT ret = ERROR_BAD_LENGTH;
|
||||
|
||||
audio_formats_free(rdpsnd->ServerFormats, rdpsnd->NumberOfServerFormats);
|
||||
rdpsnd->NumberOfServerFormats = 0;
|
||||
rdpsnd->ServerFormats = NULL;
|
||||
@ -285,7 +289,7 @@ static UINT rdpsnd_recv_server_audio_formats_pdu(rdpsndPlugin* rdpsnd, wStream*
|
||||
Stream_Seek_UINT16(s); /* wDGramPort */
|
||||
Stream_Read_UINT16(s, wNumberOfFormats);
|
||||
Stream_Read_UINT8(s, rdpsnd->cBlockNo); /* cLastBlockConfirmed */
|
||||
Stream_Read_UINT16(s, wVersion); /* wVersion */
|
||||
Stream_Read_UINT16(s, rdpsnd->wVersion); /* wVersion */
|
||||
Stream_Seek_UINT8(s); /* bPad */
|
||||
rdpsnd->NumberOfServerFormats = wNumberOfFormats;
|
||||
|
||||
@ -312,7 +316,7 @@ static UINT rdpsnd_recv_server_audio_formats_pdu(rdpsndPlugin* rdpsnd, wStream*
|
||||
|
||||
if (ret == CHANNEL_RC_OK)
|
||||
{
|
||||
if (wVersion >= CHANNEL_VERSION_WIN_7)
|
||||
if (rdpsnd->wVersion >= CHANNEL_VERSION_WIN_7)
|
||||
ret = rdpsnd_send_quality_mode_pdu(rdpsnd);
|
||||
}
|
||||
|
||||
@ -373,6 +377,20 @@ static UINT rdpsnd_recv_training_pdu(rdpsndPlugin* rdpsnd, wStream* s)
|
||||
return rdpsnd_send_training_confirm_pdu(rdpsnd, wTimeStamp, wPackSize);
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_apply_volume(rdpsndPlugin* rdpsnd)
|
||||
{
|
||||
WINPR_ASSERT(rdpsnd);
|
||||
|
||||
if (rdpsnd->isOpen && rdpsnd->applyVolume && rdpsnd->device)
|
||||
{
|
||||
BOOL rc = IFCALLRESULT(TRUE, rdpsnd->device->SetVolume, rdpsnd->device, rdpsnd->volume);
|
||||
if (!rc)
|
||||
return FALSE;
|
||||
rdpsnd->applyVolume = FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_ensure_device_is_open(rdpsndPlugin* rdpsnd, UINT32 wFormatNo,
|
||||
const AUDIO_FORMAT* format)
|
||||
{
|
||||
@ -421,7 +439,7 @@ static BOOL rdpsnd_ensure_device_is_open(rdpsndPlugin* rdpsnd, UINT32 wFormatNo,
|
||||
rdpsnd->totalPlaySize = 0;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
return rdpsnd_apply_volume(rdpsnd);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -710,7 +728,7 @@ static void rdpsnd_recv_close_pdu(rdpsndPlugin* rdpsnd)
|
||||
*/
|
||||
static UINT rdpsnd_recv_volume_pdu(rdpsndPlugin* rdpsnd, wStream* s)
|
||||
{
|
||||
BOOL rc = FALSE;
|
||||
BOOL rc = TRUE;
|
||||
UINT32 dwVolume;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 4)
|
||||
@ -719,8 +737,10 @@ static UINT rdpsnd_recv_volume_pdu(rdpsndPlugin* rdpsnd, wStream* s)
|
||||
Stream_Read_UINT32(s, dwVolume);
|
||||
WLog_Print(rdpsnd->log, WLOG_DEBUG, "%s Volume: 0x%08" PRIX32 "",
|
||||
rdpsnd_is_dyn_str(rdpsnd->dynamic), dwVolume);
|
||||
if (rdpsnd->device)
|
||||
rc = IFCALLRESULT(FALSE, rdpsnd->device->SetVolume, rdpsnd->device, dwVolume);
|
||||
|
||||
rdpsnd->volume = dwVolume;
|
||||
rdpsnd->applyVolume = TRUE;
|
||||
rc = rdpsnd_apply_volume(rdpsnd);
|
||||
|
||||
if (!rc)
|
||||
{
|
||||
|
||||
@ -2728,7 +2728,10 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings,
|
||||
if (strcmp(arg->Value, "video") == 0)
|
||||
settings->RemoteFxCodecMode = 0x00;
|
||||
else if (strcmp(arg->Value, "image") == 0)
|
||||
{
|
||||
settings->RemoteFxImageCodec = TRUE;
|
||||
settings->RemoteFxCodecMode = 0x02;
|
||||
}
|
||||
}
|
||||
CommandLineSwitchCase(arg, "frame-ack")
|
||||
{
|
||||
|
||||
@ -47,12 +47,12 @@ if(NOT WIN32)
|
||||
CMAKE_DEPENDENT_OPTION(WITH_SANITIZE_ADDRESS "Compile with gcc/clang address sanitizer." OFF
|
||||
"NOT WITH_VALGRIND_MEMCHECK; NOT WITH_SANITIZE_MEMORY; NOT WITH_SANITIZE_THREAD" OFF)
|
||||
CMAKE_DEPENDENT_OPTION(WITH_SANITIZE_MEMORY "Compile with gcc/clang memory sanitizer." OFF
|
||||
"NOT WITH_VALGRIND_MEMCHECK; NOT WITH_SANITIZE_ADDRESS; NOT WITH_SANITIZE_THREAD" OFF)
|
||||
"NOT WITH_VALGRIND_MEMCHECK; NOT WITH_SANITIZE_ADDRESS; NOT WITH_SANITIZE_THREAD" OFF)
|
||||
CMAKE_DEPENDENT_OPTION(WITH_SANITIZE_THREAD "Compile with gcc/clang thread sanitizer." OFF
|
||||
"NOT WITH_VALGRIND_MEMCHECK; NOT WITH_SANITIZE_ADDRESS; NOT WITH_SANITIZE_MEMORY" OFF)
|
||||
else()
|
||||
if(NOT UWP)
|
||||
option(WITH_MEDIA_FOUNDATION "Enable H264 media foundation decoder." ON)
|
||||
option(WITH_MEDIA_FOUNDATION "Enable H264 media foundation decoder." OFF)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@ -98,7 +98,7 @@ option(WITH_SERVER_INTERFACE "Build servers as a library with an interface" ON)
|
||||
option(WITH_DEBUG_ALL "Print all debug messages." OFF)
|
||||
|
||||
if(WITH_DEBUG_ALL)
|
||||
message(WARNING "WITH_DEBUG_ALL=ON, the build will be slow and might leak sensitive information, do not use with release builds!")
|
||||
message(WARNING "WITH_DEBUG_ALL=ON, the build will be slow and might leak sensitive information, do not use with release builds!")
|
||||
set(DEFAULT_DEBUG_OPTION "ON")
|
||||
else()
|
||||
set(DEFAULT_DEBUG_OPTION "OFF")
|
||||
@ -106,7 +106,7 @@ endif()
|
||||
|
||||
option(WITH_DEBUG_CERTIFICATE "Print certificate related debug messages." ${DEFAULT_DEBUG_OPTION})
|
||||
if(WITH_DEBUG_CERTIFICATE)
|
||||
message(WARNING "WITH_DEBUG_CERTIFICATE=ON, the build might leak sensitive information, do not use with release builds!")
|
||||
message(WARNING "WITH_DEBUG_CERTIFICATE=ON, the build might leak sensitive information, do not use with release builds!")
|
||||
endif()
|
||||
option(WITH_DEBUG_CAPABILITIES "Print capability negotiation debug messages." ${DEFAULT_DEBUG_OPTION})
|
||||
option(WITH_DEBUG_CHANNELS "Print channel manager debug messages." ${DEFAULT_DEBUG_OPTION})
|
||||
@ -116,23 +116,23 @@ option(WITH_DEBUG_DVC "Print dynamic virtual channel debug messages." ${DEFAULT_
|
||||
CMAKE_DEPENDENT_OPTION(WITH_DEBUG_TSMF "Print TSMF virtual channel debug messages." ${DEFAULT_DEBUG_OPTION} "CHANNEL_TSMF" OFF)
|
||||
option(WITH_DEBUG_KBD "Print keyboard related debug messages." OFF)
|
||||
if(WITH_DEBUG_KBD)
|
||||
message(WARNING "WITH_DEBUG_KBD=ON, the build might leak sensitive information, do not use with release builds!")
|
||||
message(WARNING "WITH_DEBUG_KBD=ON, the build might leak sensitive information, do not use with release builds!")
|
||||
endif()
|
||||
option(WITH_DEBUG_LICENSE "Print license debug messages." OFF)
|
||||
if(WITH_DEBUG_LICENSE)
|
||||
message(WARNING "WITH_DEBUG_LICENSE=ON, the build might leak sensitive information, do not use with release builds!")
|
||||
message(WARNING "WITH_DEBUG_LICENSE=ON, the build might leak sensitive information, do not use with release builds!")
|
||||
endif()
|
||||
option(WITH_DEBUG_NEGO "Print negotiation related debug messages." OFF)
|
||||
if(WITH_DEBUG_NEGO)
|
||||
message(WARNING "WITH_DEBUG_NEGO=ON, the build might leak sensitive information, do not use with release builds!")
|
||||
message(WARNING "WITH_DEBUG_NEGO=ON, the build might leak sensitive information, do not use with release builds!")
|
||||
endif()
|
||||
option(WITH_DEBUG_NLA "Print authentication related debug messages." OFF)
|
||||
if(WITH_DEBUG_NLA)
|
||||
message(WARNING "WITH_DEBUG_NLA=ON, the build might leak sensitive information, do not use with release builds!")
|
||||
message(WARNING "WITH_DEBUG_NLA=ON, the build might leak sensitive information, do not use with release builds!")
|
||||
endif()
|
||||
option(WITH_DEBUG_NTLM "Print NTLM debug messages" OFF)
|
||||
if(WITH_DEBUG_NTLM)
|
||||
message(WARNING "WITH_DEBUG_NTLM=ON, the build might leak sensitive information, do not use with release builds!")
|
||||
message(WARNING "WITH_DEBUG_NTLM=ON, the build might leak sensitive information, do not use with release builds!")
|
||||
endif()
|
||||
option(WITH_DEBUG_TSG "Print Terminal Server Gateway debug messages" ${DEFAULT_DEBUG_OPTION})
|
||||
option(WITH_DEBUG_RAIL "Print RemoteApp debug messages" ${DEFAULT_DEBUG_OPTION})
|
||||
@ -163,13 +163,13 @@ option(WITH_GSSAPI "Compile support for kerberos authentication. (EXPERIMENTAL)"
|
||||
|
||||
option(WITH_DSP_EXPERIMENTAL "Enable experimental sound encoder/decoder formats" OFF)
|
||||
if (WITH_FFMPEG)
|
||||
option(WITH_DSP_FFMPEG "Use FFMPEG for audio encoding/decoding" OFF)
|
||||
option(WITH_VAAPI "Use FFMPEG VAAPI (EXPERIMENTAL)" OFF)
|
||||
option(WITH_DSP_FFMPEG "Use FFMPEG for audio encoding/decoding" OFF)
|
||||
option(WITH_VAAPI "Use FFMPEG VAAPI (EXPERIMENTAL)" OFF)
|
||||
endif(WITH_FFMPEG)
|
||||
|
||||
option(USE_VERSION_FROM_GIT_TAG "Extract FreeRDP version from git tag." OFF)
|
||||
|
||||
option(WITH_CAIRO "Use CAIRO image library for screen resizing" OFF)
|
||||
option(WITH_CAIRO "Use CAIRO image library for screen resizing" OFF)
|
||||
option(WITH_SWSCALE "Use SWScale image library for screen resizing" OFF)
|
||||
|
||||
option(DEFINE_NO_DEPRECATED "Compile without legacy functions and symbols" OFF)
|
||||
|
||||
@ -41,6 +41,14 @@
|
||||
#define RDPDR_DEVICE_IO_CONTROL_REQ_HDR_LENGTH 32
|
||||
#define RDPDR_DEVICE_IO_CONTROL_RSP_HDR_LENGTH 4
|
||||
|
||||
#define RDPDR_VERSION_MAJOR 0x0001
|
||||
|
||||
#define RDPDR_VERSION_MINOR_RDP50 0x0002
|
||||
#define RDPDR_VERSION_MINOR_RDP51 0x0005
|
||||
#define RDPDR_VERSION_MINOR_RDP52 0x000A
|
||||
#define RDPDR_VERSION_MINOR_RDP6X 0x000C
|
||||
#define RDPDR_VERSION_MINOR_RDP10X 0x000D
|
||||
|
||||
/* RDPDR_HEADER.Component */
|
||||
enum RDPDR_CTYP
|
||||
{
|
||||
|
||||
@ -418,9 +418,29 @@ static BOOL ffmpeg_resample_frame(AVAudioResampleContext* context, AVFrame* in,
|
||||
static BOOL ffmpeg_encode_frame(AVCodecContext* context, AVFrame* in, AVPacket* packet,
|
||||
wStream* out)
|
||||
{
|
||||
int ret;
|
||||
if (in->format == AV_SAMPLE_FMT_FLTP)
|
||||
{
|
||||
uint8_t** pp = in->extended_data;
|
||||
for (int y = 0; y < in->channels; y++)
|
||||
{
|
||||
float* data = pp[y];
|
||||
for (int x = 0; x < in->nb_samples; x++)
|
||||
{
|
||||
const float val1 = data[x];
|
||||
if (isnan(val1))
|
||||
data[x] = 0.0f;
|
||||
else if (isinf(val1))
|
||||
{
|
||||
if (val1 < 0.0f)
|
||||
data[x] = -1.0f;
|
||||
else
|
||||
data[x] = 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* send the packet with the compressed data to the encoder */
|
||||
ret = avcodec_send_frame(context, in);
|
||||
int ret = avcodec_send_frame(context, in);
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
|
||||
@ -1496,7 +1496,13 @@ BOOL freerdp_bitmap_planar_context_reset(BITMAP_PLANAR_CONTEXT* context, UINT32
|
||||
context->bgr = FALSE;
|
||||
context->maxWidth = PLANAR_ALIGN(width, 4);
|
||||
context->maxHeight = PLANAR_ALIGN(height, 4);
|
||||
context->maxPlaneSize = context->maxWidth * context->maxHeight;
|
||||
const UINT64 tmp = (UINT64)context->maxWidth * context->maxHeight;
|
||||
if (tmp > UINT32_MAX)
|
||||
return FALSE;
|
||||
context->maxPlaneSize = tmp;
|
||||
|
||||
if (context->maxWidth > UINT32_MAX / 4)
|
||||
return FALSE;
|
||||
context->nTempStep = context->maxWidth * 4;
|
||||
free(context->planesBuffer);
|
||||
free(context->pTempData);
|
||||
|
||||
@ -1796,13 +1796,13 @@ static INLINE int progressive_process_tiles(PROGRESSIVE_CONTEXT* progressive, wS
|
||||
for (index = 0; index < region->numTiles; index++)
|
||||
{
|
||||
RFX_PROGRESSIVE_TILE* tile = region->tiles[index];
|
||||
params[index].progressive = progressive;
|
||||
params[index].region = region;
|
||||
params[index].context = context;
|
||||
params[index].tile = tile;
|
||||
|
||||
if (progressive->rfx_context->priv->UseThreads)
|
||||
{
|
||||
params[index].progressive = progressive;
|
||||
params[index].region = region;
|
||||
params[index].context = context;
|
||||
params[index].tile = tile;
|
||||
if (!(work_objects[index] = CreateThreadpoolWork(
|
||||
progressive_process_tiles_tile_work_callback, (void*)¶ms[index],
|
||||
&progressive->rfx_context->priv->ThreadPoolEnv)))
|
||||
@ -1817,7 +1817,10 @@ static INLINE int progressive_process_tiles(PROGRESSIVE_CONTEXT* progressive, wS
|
||||
}
|
||||
else
|
||||
{
|
||||
progressive_process_tiles_tile_work_callback(0, ¶ms[index], 0);
|
||||
PROGRESSIVE_TILE_PROCESS_WORK_PARAM param = {
|
||||
.progressive = progressive, .region = region, .context = context, .tile = tile
|
||||
};
|
||||
progressive_process_tiles_tile_work_callback(0, ¶m, 0);
|
||||
}
|
||||
|
||||
if (status < 0)
|
||||
|
||||
@ -950,11 +950,12 @@ WINPR_MD_TYPE crypto_cert_get_signature_alg(X509* xcert)
|
||||
{
|
||||
WINPR_ASSERT(xcert);
|
||||
|
||||
EVP_PKEY* evp = X509_get0_pubkey(xcert);
|
||||
EVP_PKEY* evp = X509_get_pubkey(xcert);
|
||||
WINPR_ASSERT(evp);
|
||||
|
||||
int hash_nid = 0;
|
||||
const int res = EVP_PKEY_get_default_digest_nid(evp, &hash_nid);
|
||||
EVP_PKEY_free(evp);
|
||||
if (res <= 0)
|
||||
return WINPR_MD_NONE;
|
||||
|
||||
|
||||
@ -806,7 +806,7 @@ static const XKB_LAYOUT xkbLayouts[] = {
|
||||
{ "sk", KBD_SLOVAK, sk_variants }, /* Slovakia */
|
||||
{ "es", KBD_SPANISH, es_variants }, /* Spain */
|
||||
{ "se", KBD_SWEDISH, se_variants }, /* Sweden */
|
||||
{ "ch", KBD_SWISS_FRENCH, ch_variants }, /* Switzerland */
|
||||
{ "ch", KBD_SWISS_GERMAN, ch_variants }, /* Switzerland */
|
||||
{ "sy", KBD_SYRIAC, sy_variants }, /* Syria */
|
||||
{ "tj", 0, tj_variants }, /* Tajikistan */
|
||||
{ "lk", 0, lk_variants }, /* Sri Lanka */
|
||||
|
||||
@ -57,7 +57,7 @@ if (NOT WIN32)
|
||||
endif()
|
||||
|
||||
# Soname versioning
|
||||
set(RAW_VERSION_STRING "2.11.2")
|
||||
set(RAW_VERSION_STRING "2.11.5")
|
||||
if(EXISTS "${CMAKE_SOURCE_DIR}/.source_tag")
|
||||
file(READ ${CMAKE_SOURCE_DIR}/.source_tag RAW_VERSION_STRING)
|
||||
elseif(USE_VERSION_FROM_GIT_TAG)
|
||||
|
||||
@ -29,7 +29,7 @@ if (NOT WITH_ICU)
|
||||
endif(NOT WITH_ICU)
|
||||
|
||||
if (WITH_ICU)
|
||||
find_package(ICU REQUIRED i18n uc io)
|
||||
find_package(ICU REQUIRED i18n uc io data)
|
||||
winpr_include_directory_add(${ICU_INCLUDE_DIRS})
|
||||
winpr_library_add_private(${ICU_LIBRARIES})
|
||||
endif (WITH_ICU)
|
||||
|
||||
@ -40,6 +40,8 @@
|
||||
#include "../log.h"
|
||||
#define TAG WINPR_TAG("unicode")
|
||||
|
||||
#define UCNV_CONVERT 1
|
||||
|
||||
/**
|
||||
* Notes on cross-platform Unicode portability:
|
||||
*
|
||||
@ -201,6 +203,26 @@ int MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int
|
||||
targetCapacity = cchWideChar;
|
||||
error = U_ZERO_ERROR;
|
||||
|
||||
#if defined(UCNV_CONVERT)
|
||||
if (cchWideChar == 0)
|
||||
{
|
||||
targetLength =
|
||||
ucnv_convert("UTF-16LE", "UTF-8", NULL, 0, lpMultiByteStr, cbMultiByte, &error);
|
||||
if (targetLength > 0)
|
||||
targetLength /= sizeof(WCHAR);
|
||||
cchWideChar = targetLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
targetLength =
|
||||
ucnv_convert("UTF-16LE", "UTF-8", targetStart, targetCapacity * sizeof(WCHAR),
|
||||
lpMultiByteStr, cbMultiByte, &error);
|
||||
if (targetLength > 0)
|
||||
targetLength /= sizeof(WCHAR);
|
||||
cchWideChar = U_SUCCESS(error) ? targetLength : 0;
|
||||
}
|
||||
|
||||
#else
|
||||
if (cchWideChar == 0)
|
||||
{
|
||||
u_strFromUTF8(NULL, 0, &targetLength, lpMultiByteStr, cbMultiByte, &error);
|
||||
@ -212,6 +234,7 @@ int MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int
|
||||
&error);
|
||||
cchWideChar = U_SUCCESS(error) ? targetLength : 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
|
||||
@ -327,6 +350,21 @@ int WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int
|
||||
targetCapacity = cbMultiByte;
|
||||
error = U_ZERO_ERROR;
|
||||
|
||||
#if defined(UCNV_CONVERT)
|
||||
if (cbMultiByte == 0)
|
||||
{
|
||||
targetLength = ucnv_convert("UTF-8", "UTF-16LE", NULL, 0, lpWideCharStr,
|
||||
cchWideChar * sizeof(WCHAR), &error);
|
||||
cbMultiByte = targetLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
targetLength = ucnv_convert("UTF-8", "UTF-16LE", targetStart, targetCapacity,
|
||||
lpWideCharStr, cchWideChar * sizeof(WCHAR), &error);
|
||||
cbMultiByte = U_SUCCESS(error) ? targetLength : 0;
|
||||
}
|
||||
|
||||
#else
|
||||
if (cbMultiByte == 0)
|
||||
{
|
||||
u_strToUTF8(NULL, 0, &targetLength, lpWideCharStr, cchWideChar, &error);
|
||||
@ -338,6 +376,7 @@ int WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int
|
||||
&error);
|
||||
cbMultiByte = U_SUCCESS(error) ? targetLength : 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
|
||||
|
||||
@ -66,7 +66,11 @@ static WINPR_RC4_CTX* winpr_RC4_New_Internal(const BYTE* key, size_t keylen, BOO
|
||||
if (!evp)
|
||||
return NULL;
|
||||
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
|
||||
EVP_CIPHER_CTX_reset((EVP_CIPHER_CTX*)ctx);
|
||||
#else
|
||||
EVP_CIPHER_CTX_init((EVP_CIPHER_CTX*)ctx);
|
||||
#endif
|
||||
if (EVP_EncryptInit_ex((EVP_CIPHER_CTX*)ctx, evp, NULL, NULL, NULL) != 1)
|
||||
{
|
||||
EVP_CIPHER_CTX_free ((EVP_CIPHER_CTX*)ctx);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user