summaryrefslogtreecommitdiffstats
path: root/libfreerdp/core/capabilities.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 01:24:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 01:24:41 +0000
commita9bcc81f821d7c66f623779fa5147e728eb3c388 (patch)
tree98676963bcdd537ae5908a067a8eb110b93486a6 /libfreerdp/core/capabilities.c
parentInitial commit. (diff)
downloadfreerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.tar.xz
freerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.zip
Adding upstream version 3.3.0+dfsg1.upstream/3.3.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'libfreerdp/core/capabilities.c')
-rw-r--r--libfreerdp/core/capabilities.c4667
1 files changed, 4667 insertions, 0 deletions
diff --git a/libfreerdp/core/capabilities.c b/libfreerdp/core/capabilities.c
new file mode 100644
index 0000000..427e434
--- /dev/null
+++ b/libfreerdp/core/capabilities.c
@@ -0,0 +1,4667 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * RDP Capability Sets
+ *
+ * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/wtsapi.h>
+#include <freerdp/config.h>
+
+#include "settings.h"
+#include "capabilities.h"
+#include "fastpath.h"
+
+#include <winpr/crt.h>
+#include <winpr/rpc.h>
+
+#include <freerdp/log.h>
+
+#define TAG FREERDP_TAG("core.capabilities")
+
+static const char* const CAPSET_TYPE_STRINGS[] = { "Unknown",
+ "General",
+ "Bitmap",
+ "Order",
+ "Bitmap Cache",
+ "Control",
+ "Unknown",
+ "Window Activation",
+ "Pointer",
+ "Share",
+ "Color Cache",
+ "Unknown",
+ "Sound",
+ "Input",
+ "Font",
+ "Brush",
+ "Glyph Cache",
+ "Offscreen Bitmap Cache",
+ "Bitmap Cache Host Support",
+ "Bitmap Cache v2",
+ "Virtual Channel",
+ "DrawNineGrid Cache",
+ "Draw GDI+ Cache",
+ "Remote Programs",
+ "Window List",
+ "Desktop Composition",
+ "Multifragment Update",
+ "Large Pointer",
+ "Surface Commands",
+ "Bitmap Codecs",
+ "Frame Acknowledge" };
+
+static const char* get_capability_name(UINT16 type)
+{
+ if (type > CAPSET_TYPE_FRAME_ACKNOWLEDGE)
+ return "<unknown>";
+
+ return CAPSET_TYPE_STRINGS[type];
+}
+
+#ifdef WITH_DEBUG_CAPABILITIES
+static BOOL rdp_print_capability_sets(wStream* s, size_t start, BOOL receiving);
+#endif
+
+/* CODEC_GUID_REMOTEFX: 0x76772F12BD724463AFB3B73C9C6F7886 */
+
+static const GUID CODEC_GUID_REMOTEFX = {
+ 0x76772F12, 0xBD72, 0x4463, { 0xAF, 0xB3, 0xB7, 0x3C, 0x9C, 0x6F, 0x78, 0x86 }
+};
+
+/* CODEC_GUID_NSCODEC 0xCA8D1BB9000F154F589FAE2D1A87E2D6 */
+
+static const GUID CODEC_GUID_NSCODEC = {
+ 0xCA8D1BB9, 0x000F, 0x154F, { 0x58, 0x9F, 0xAE, 0x2D, 0x1A, 0x87, 0xE2, 0xD6 }
+};
+
+/* CODEC_GUID_IGNORE 0x9C4351A6353542AE910CCDFCE5760B58 */
+
+static const GUID CODEC_GUID_IGNORE = {
+ 0x9C4351A6, 0x3535, 0x42AE, { 0x91, 0x0C, 0xCD, 0xFC, 0xE5, 0x76, 0x0B, 0x58 }
+};
+
+/* CODEC_GUID_IMAGE_REMOTEFX 0x2744CCD49D8A4E74803C0ECBEEA19C54 */
+
+static const GUID CODEC_GUID_IMAGE_REMOTEFX = {
+ 0x2744CCD4, 0x9D8A, 0x4E74, { 0x80, 0x3C, 0x0E, 0xCB, 0xEE, 0xA1, 0x9C, 0x54 }
+};
+
+#if defined(WITH_JPEG)
+/* CODEC_GUID_JPEG 0x430C9EED1BAF4CE6869ACB8B37B66237 */
+
+static const GUID CODEC_GUID_JPEG = {
+ 0x430C9EED, 0x1BAF, 0x4CE6, { 0x86, 0x9A, 0xCB, 0x8B, 0x37, 0xB6, 0x62, 0x37 }
+};
+#endif
+
+static BOOL rdp_read_capability_set_header(wStream* s, UINT16* length, UINT16* type)
+{
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(length);
+ WINPR_ASSERT(type);
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return FALSE;
+ Stream_Read_UINT16(s, *type); /* capabilitySetType */
+ Stream_Read_UINT16(s, *length); /* lengthCapability */
+ if (*length < 4)
+ return FALSE;
+ return TRUE;
+}
+
+static void rdp_write_capability_set_header(wStream* s, UINT16 length, UINT16 type)
+{
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(Stream_GetRemainingCapacity(s) >= 4);
+ Stream_Write_UINT16(s, type); /* capabilitySetType */
+ Stream_Write_UINT16(s, length); /* lengthCapability */
+}
+
+static size_t rdp_capability_set_start(wStream* s)
+{
+ size_t header = Stream_GetPosition(s);
+ if (!Stream_CheckAndLogRequiredCapacity(TAG, (s), CAPSET_HEADER_LENGTH))
+ return SIZE_MAX;
+ Stream_Zero(s, CAPSET_HEADER_LENGTH);
+ return header;
+}
+
+static BOOL rdp_capability_set_finish(wStream* s, size_t header, UINT16 type)
+{
+ const size_t footer = Stream_GetPosition(s);
+ if (header > footer)
+ return FALSE;
+ if (header > UINT16_MAX)
+ return FALSE;
+ const size_t length = footer - header;
+ if ((Stream_Capacity(s) < header + 4ULL) || (length > UINT16_MAX))
+ return FALSE;
+ Stream_SetPosition(s, header);
+ rdp_write_capability_set_header(s, (UINT16)length, type);
+ Stream_SetPosition(s, footer);
+ return TRUE;
+}
+
+static BOOL rdp_apply_general_capability_set(rdpSettings* settings, const rdpSettings* src)
+{
+ WINPR_ASSERT(settings);
+ WINPR_ASSERT(src);
+
+ if (settings->ServerMode)
+ {
+ settings->OsMajorType = src->OsMajorType;
+ settings->OsMinorType = src->OsMinorType;
+ }
+
+ settings->NoBitmapCompressionHeader = src->NoBitmapCompressionHeader;
+ settings->LongCredentialsSupported = src->LongCredentialsSupported;
+ settings->AutoReconnectionEnabled = src->AutoReconnectionEnabled;
+ if (!src->FastPathOutput)
+ settings->FastPathOutput = FALSE;
+
+ if (!src->SaltedChecksum)
+ settings->SaltedChecksum = FALSE;
+
+ if (!settings->ServerMode)
+ {
+ /*
+ * Note: refreshRectSupport and suppressOutputSupport are
+ * server-only flags indicating to the client weather the
+ * respective PDUs are supported. See MS-RDPBCGR 2.2.7.1.1
+ */
+ if (!src->RefreshRect)
+ settings->RefreshRect = FALSE;
+
+ if (!src->SuppressOutput)
+ settings->SuppressOutput = FALSE;
+ }
+ return TRUE;
+}
+
+/*
+ * Read general capability set.
+ * msdn{cc240549}
+ */
+
+static BOOL rdp_read_general_capability_set(wStream* s, rdpSettings* settings)
+{
+ UINT16 extraFlags = 0;
+ BYTE refreshRectSupport = 0;
+ BYTE suppressOutputSupport = 0;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 20))
+ return FALSE;
+
+ WINPR_ASSERT(settings);
+ Stream_Read_UINT16(s, settings->OsMajorType); /* osMajorType (2 bytes) */
+ Stream_Read_UINT16(s, settings->OsMinorType); /* osMinorType (2 bytes) */
+
+ Stream_Read_UINT16(s, settings->CapsProtocolVersion); /* protocolVersion (2 bytes) */
+ Stream_Seek_UINT16(s); /* pad2OctetsA (2 bytes) */
+ Stream_Read_UINT16(
+ s, settings->CapsGeneralCompressionTypes); /* generalCompressionTypes (2 bytes) */
+ Stream_Read_UINT16(s, extraFlags); /* extraFlags (2 bytes) */
+ Stream_Read_UINT16(s, settings->CapsUpdateCapabilityFlag); /* updateCapabilityFlag (2 bytes) */
+ Stream_Read_UINT16(s, settings->CapsRemoteUnshareFlag); /* remoteUnshareFlag (2 bytes) */
+ Stream_Read_UINT16(
+ s, settings->CapsGeneralCompressionLevel); /* generalCompressionLevel (2 bytes) */
+ Stream_Read_UINT8(s, refreshRectSupport); /* refreshRectSupport (1 byte) */
+ Stream_Read_UINT8(s, suppressOutputSupport); /* suppressOutputSupport (1 byte) */
+ settings->NoBitmapCompressionHeader = (extraFlags & NO_BITMAP_COMPRESSION_HDR) ? TRUE : FALSE;
+ settings->LongCredentialsSupported = (extraFlags & LONG_CREDENTIALS_SUPPORTED) ? TRUE : FALSE;
+
+ settings->AutoReconnectionEnabled = (extraFlags & AUTORECONNECT_SUPPORTED) ? TRUE : FALSE;
+ settings->FastPathOutput = (extraFlags & FASTPATH_OUTPUT_SUPPORTED) ? TRUE : FALSE;
+ settings->SaltedChecksum = (extraFlags & ENC_SALTED_CHECKSUM) ? TRUE : FALSE;
+ settings->RefreshRect = refreshRectSupport;
+ settings->SuppressOutput = suppressOutputSupport;
+
+ return TRUE;
+}
+
+/*
+ * Write general capability set.
+ * msdn{cc240549}
+ */
+
+static BOOL rdp_write_general_capability_set(wStream* s, const rdpSettings* settings)
+{
+ if (!Stream_EnsureRemainingCapacity(s, 64))
+ return FALSE;
+
+ const size_t header = rdp_capability_set_start(s);
+ UINT16 extraFlags = 0;
+
+ WINPR_ASSERT(settings);
+ if (settings->LongCredentialsSupported)
+ extraFlags |= LONG_CREDENTIALS_SUPPORTED;
+
+ if (settings->NoBitmapCompressionHeader)
+ extraFlags |= NO_BITMAP_COMPRESSION_HDR;
+
+ if (settings->AutoReconnectionEnabled)
+ extraFlags |= AUTORECONNECT_SUPPORTED;
+
+ if (settings->FastPathOutput)
+ extraFlags |= FASTPATH_OUTPUT_SUPPORTED;
+
+ if (settings->SaltedChecksum)
+ extraFlags |= ENC_SALTED_CHECKSUM;
+
+ if ((settings->OsMajorType > UINT16_MAX) || (settings->OsMinorType > UINT16_MAX))
+ {
+ WLog_ERR(TAG,
+ "OsMajorType=%08" PRIx32 ", OsMinorType=%08" PRIx32
+ " they need to be smaller %04" PRIx16,
+ settings->OsMajorType, settings->OsMinorType, UINT16_MAX);
+ return FALSE;
+ }
+ Stream_Write_UINT16(s, (UINT16)settings->OsMajorType); /* osMajorType (2 bytes) */
+ Stream_Write_UINT16(s, (UINT16)settings->OsMinorType); /* osMinorType (2 bytes) */
+ Stream_Write_UINT16(s, settings->CapsProtocolVersion); /* protocolVersion (2 bytes) */
+ Stream_Write_UINT16(s, 0); /* pad2OctetsA (2 bytes) */
+ Stream_Write_UINT16(
+ s, settings->CapsGeneralCompressionTypes); /* generalCompressionTypes (2 bytes) */
+ Stream_Write_UINT16(s, extraFlags); /* extraFlags (2 bytes) */
+ Stream_Write_UINT16(s, settings->CapsUpdateCapabilityFlag); /* updateCapabilityFlag (2 bytes) */
+ Stream_Write_UINT16(s, settings->CapsRemoteUnshareFlag); /* remoteUnshareFlag (2 bytes) */
+ Stream_Write_UINT16(
+ s, settings->CapsGeneralCompressionLevel); /* generalCompressionLevel (2 bytes) */
+ Stream_Write_UINT8(s, settings->RefreshRect ? 1 : 0); /* refreshRectSupport (1 byte) */
+ Stream_Write_UINT8(s, settings->SuppressOutput ? 1 : 0); /* suppressOutputSupport (1 byte) */
+ return rdp_capability_set_finish(s, header, CAPSET_TYPE_GENERAL);
+}
+
+#ifdef WITH_DEBUG_CAPABILITIES
+static BOOL rdp_print_general_capability_set(wStream* s)
+{
+ UINT16 osMajorType = 0;
+ UINT16 osMinorType = 0;
+ UINT16 protocolVersion = 0;
+ UINT16 pad2OctetsA = 0;
+ UINT16 generalCompressionTypes = 0;
+ UINT16 extraFlags = 0;
+ UINT16 updateCapabilityFlag = 0;
+ UINT16 remoteUnshareFlag = 0;
+ UINT16 generalCompressionLevel = 0;
+ BYTE refreshRectSupport = 0;
+ BYTE suppressOutputSupport = 0;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 20))
+ return FALSE;
+
+ WLog_VRB(TAG, "GeneralCapabilitySet (length %" PRIuz "):", Stream_GetRemainingLength(s));
+ Stream_Read_UINT16(s, osMajorType); /* osMajorType (2 bytes) */
+ Stream_Read_UINT16(s, osMinorType); /* osMinorType (2 bytes) */
+ Stream_Read_UINT16(s, protocolVersion); /* protocolVersion (2 bytes) */
+ Stream_Read_UINT16(s, pad2OctetsA); /* pad2OctetsA (2 bytes) */
+ Stream_Read_UINT16(s, generalCompressionTypes); /* generalCompressionTypes (2 bytes) */
+ Stream_Read_UINT16(s, extraFlags); /* extraFlags (2 bytes) */
+ Stream_Read_UINT16(s, updateCapabilityFlag); /* updateCapabilityFlag (2 bytes) */
+ Stream_Read_UINT16(s, remoteUnshareFlag); /* remoteUnshareFlag (2 bytes) */
+ Stream_Read_UINT16(s, generalCompressionLevel); /* generalCompressionLevel (2 bytes) */
+ Stream_Read_UINT8(s, refreshRectSupport); /* refreshRectSupport (1 byte) */
+ Stream_Read_UINT8(s, suppressOutputSupport); /* suppressOutputSupport (1 byte) */
+ WLog_VRB(TAG, "\tosMajorType: 0x%04" PRIX16 "", osMajorType);
+ WLog_VRB(TAG, "\tosMinorType: 0x%04" PRIX16 "", osMinorType);
+ WLog_VRB(TAG, "\tprotocolVersion: 0x%04" PRIX16 "", protocolVersion);
+ WLog_VRB(TAG, "\tpad2OctetsA: 0x%04" PRIX16 "", pad2OctetsA);
+ WLog_VRB(TAG, "\tgeneralCompressionTypes: 0x%04" PRIX16 "", generalCompressionTypes);
+ WLog_VRB(TAG, "\textraFlags: 0x%04" PRIX16 "", extraFlags);
+ WLog_VRB(TAG, "\tupdateCapabilityFlag: 0x%04" PRIX16 "", updateCapabilityFlag);
+ WLog_VRB(TAG, "\tremoteUnshareFlag: 0x%04" PRIX16 "", remoteUnshareFlag);
+ WLog_VRB(TAG, "\tgeneralCompressionLevel: 0x%04" PRIX16 "", generalCompressionLevel);
+ WLog_VRB(TAG, "\trefreshRectSupport: 0x%02" PRIX8 "", refreshRectSupport);
+ WLog_VRB(TAG, "\tsuppressOutputSupport: 0x%02" PRIX8 "", suppressOutputSupport);
+ return TRUE;
+}
+#endif
+static BOOL rdp_apply_bitmap_capability_set(rdpSettings* settings, const rdpSettings* src)
+{
+ WINPR_ASSERT(settings);
+ WINPR_ASSERT(src);
+
+ if (!settings->ServerMode)
+ freerdp_settings_set_uint32(settings, FreeRDP_ColorDepth,
+ freerdp_settings_get_uint32(src, FreeRDP_ColorDepth));
+
+ if (!src->DesktopResize)
+ settings->DesktopResize = FALSE;
+
+ if (!settings->ServerMode && settings->DesktopResize)
+ {
+ /* The server may request a different desktop size during Deactivation-Reactivation sequence
+ */
+ settings->DesktopWidth = src->DesktopWidth;
+ settings->DesktopHeight = src->DesktopHeight;
+ }
+
+ if (settings->DrawAllowSkipAlpha)
+ settings->DrawAllowSkipAlpha = src->DrawAllowSkipAlpha;
+
+ if (settings->DrawAllowDynamicColorFidelity)
+ settings->DrawAllowDynamicColorFidelity = src->DrawAllowDynamicColorFidelity;
+
+ if (settings->DrawAllowColorSubsampling)
+ settings->DrawAllowColorSubsampling = src->DrawAllowColorSubsampling;
+
+ return TRUE;
+}
+
+/*
+ * Read bitmap capability set.
+ * msdn{cc240554}
+ */
+
+static BOOL rdp_read_bitmap_capability_set(wStream* s, rdpSettings* settings)
+{
+ BYTE drawingFlags = 0;
+ UINT16 desktopWidth = 0;
+ UINT16 desktopHeight = 0;
+ UINT16 desktopResizeFlag = 0;
+ UINT16 preferredBitsPerPixel = 0;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 24))
+ return FALSE;
+
+ Stream_Read_UINT16(s, preferredBitsPerPixel); /* preferredBitsPerPixel (2 bytes) */
+ Stream_Seek_UINT16(s); /* receive1BitPerPixel (2 bytes) */
+ Stream_Seek_UINT16(s); /* receive4BitsPerPixel (2 bytes) */
+ Stream_Seek_UINT16(s); /* receive8BitsPerPixel (2 bytes) */
+ Stream_Read_UINT16(s, desktopWidth); /* desktopWidth (2 bytes) */
+ Stream_Read_UINT16(s, desktopHeight); /* desktopHeight (2 bytes) */
+ Stream_Seek_UINT16(s); /* pad2Octets (2 bytes) */
+ Stream_Read_UINT16(s, desktopResizeFlag); /* desktopResizeFlag (2 bytes) */
+ Stream_Seek_UINT16(s); /* bitmapCompressionFlag (2 bytes) */
+ Stream_Seek_UINT8(s); /* highColorFlags (1 byte) */
+ Stream_Read_UINT8(s, drawingFlags); /* drawingFlags (1 byte) */
+ Stream_Seek_UINT16(s); /* multipleRectangleSupport (2 bytes) */
+ Stream_Seek_UINT16(s); /* pad2OctetsB (2 bytes) */
+
+ if (!freerdp_settings_set_uint32(settings, FreeRDP_ColorDepth, preferredBitsPerPixel))
+ return FALSE;
+ settings->DesktopResize = desktopResizeFlag;
+ settings->DesktopWidth = desktopWidth;
+ settings->DesktopHeight = desktopHeight;
+ settings->DrawAllowSkipAlpha = (drawingFlags & DRAW_ALLOW_SKIP_ALPHA) ? TRUE : FALSE;
+ settings->DrawAllowDynamicColorFidelity =
+ (drawingFlags & DRAW_ALLOW_DYNAMIC_COLOR_FIDELITY) ? TRUE : FALSE;
+ settings->DrawAllowColorSubsampling =
+ (drawingFlags & DRAW_ALLOW_COLOR_SUBSAMPLING) ? TRUE : FALSE;
+
+ return TRUE;
+}
+
+/*
+ * Write bitmap capability set.
+ * msdn{cc240554}
+ */
+
+static BOOL rdp_write_bitmap_capability_set(wStream* s, const rdpSettings* settings)
+{
+ BYTE drawingFlags = 0;
+ UINT16 preferredBitsPerPixel = 0;
+
+ if (!Stream_EnsureRemainingCapacity(s, 64))
+ return FALSE;
+
+ const size_t header = rdp_capability_set_start(s);
+
+ WINPR_ASSERT(settings);
+ if (settings->DrawAllowSkipAlpha)
+ drawingFlags |= DRAW_ALLOW_SKIP_ALPHA;
+
+ if (settings->DrawAllowDynamicColorFidelity)
+ drawingFlags |= DRAW_ALLOW_DYNAMIC_COLOR_FIDELITY;
+
+ if (settings->DrawAllowColorSubsampling)
+ drawingFlags |= DRAW_ALLOW_COLOR_SUBSAMPLING; /* currently unimplemented */
+
+ /* While bitmap_decode.c now implements YCoCg, in turning it
+ * on we have found Microsoft is inconsistent on whether to invert R & B.
+ * And it's not only from one server to another; on Win7/2008R2, it appears
+ * to send the main content with a different inversion than the Windows
+ * button! So... don't advertise that we support YCoCg and the server
+ * will not send it. YCoCg is still needed for EGFX, but it at least
+ * appears consistent in its use.
+ */
+
+ if ((freerdp_settings_get_uint32(settings, FreeRDP_ColorDepth) > UINT16_MAX) ||
+ (settings->DesktopWidth > UINT16_MAX) || (settings->DesktopHeight > UINT16_MAX))
+ return FALSE;
+
+ if (settings->RdpVersion >= RDP_VERSION_5_PLUS)
+ preferredBitsPerPixel = (UINT16)freerdp_settings_get_uint32(settings, FreeRDP_ColorDepth);
+ else
+ preferredBitsPerPixel = 8;
+
+ Stream_Write_UINT16(s, preferredBitsPerPixel); /* preferredBitsPerPixel (2 bytes) */
+ Stream_Write_UINT16(s, 1); /* receive1BitPerPixel (2 bytes) */
+ Stream_Write_UINT16(s, 1); /* receive4BitsPerPixel (2 bytes) */
+ Stream_Write_UINT16(s, 1); /* receive8BitsPerPixel (2 bytes) */
+ Stream_Write_UINT16(s, (UINT16)settings->DesktopWidth); /* desktopWidth (2 bytes) */
+ Stream_Write_UINT16(s, (UINT16)settings->DesktopHeight); /* desktopHeight (2 bytes) */
+ Stream_Write_UINT16(s, 0); /* pad2Octets (2 bytes) */
+ Stream_Write_UINT16(s, (UINT16)settings->DesktopResize); /* desktopResizeFlag (2 bytes) */
+ Stream_Write_UINT16(s, 1); /* bitmapCompressionFlag (2 bytes) */
+ Stream_Write_UINT8(s, 0); /* highColorFlags (1 byte) */
+ Stream_Write_UINT8(s, drawingFlags); /* drawingFlags (1 byte) */
+ Stream_Write_UINT16(s, 1); /* multipleRectangleSupport (2 bytes) */
+ Stream_Write_UINT16(s, 0); /* pad2OctetsB (2 bytes) */
+ return rdp_capability_set_finish(s, header, CAPSET_TYPE_BITMAP);
+}
+
+#ifdef WITH_DEBUG_CAPABILITIES
+static BOOL rdp_print_bitmap_capability_set(wStream* s)
+{
+ UINT16 preferredBitsPerPixel = 0;
+ UINT16 receive1BitPerPixel = 0;
+ UINT16 receive4BitsPerPixel = 0;
+ UINT16 receive8BitsPerPixel = 0;
+ UINT16 desktopWidth = 0;
+ UINT16 desktopHeight = 0;
+ UINT16 pad2Octets = 0;
+ UINT16 desktopResizeFlag = 0;
+ UINT16 bitmapCompressionFlag = 0;
+ BYTE highColorFlags = 0;
+ BYTE drawingFlags = 0;
+ UINT16 multipleRectangleSupport = 0;
+ UINT16 pad2OctetsB = 0;
+ WLog_VRB(TAG, "BitmapCapabilitySet (length %" PRIuz "):", Stream_GetRemainingLength(s));
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 24))
+ return FALSE;
+
+ Stream_Read_UINT16(s, preferredBitsPerPixel); /* preferredBitsPerPixel (2 bytes) */
+ Stream_Read_UINT16(s, receive1BitPerPixel); /* receive1BitPerPixel (2 bytes) */
+ Stream_Read_UINT16(s, receive4BitsPerPixel); /* receive4BitsPerPixel (2 bytes) */
+ Stream_Read_UINT16(s, receive8BitsPerPixel); /* receive8BitsPerPixel (2 bytes) */
+ Stream_Read_UINT16(s, desktopWidth); /* desktopWidth (2 bytes) */
+ Stream_Read_UINT16(s, desktopHeight); /* desktopHeight (2 bytes) */
+ Stream_Read_UINT16(s, pad2Octets); /* pad2Octets (2 bytes) */
+ Stream_Read_UINT16(s, desktopResizeFlag); /* desktopResizeFlag (2 bytes) */
+ Stream_Read_UINT16(s, bitmapCompressionFlag); /* bitmapCompressionFlag (2 bytes) */
+ Stream_Read_UINT8(s, highColorFlags); /* highColorFlags (1 byte) */
+ Stream_Read_UINT8(s, drawingFlags); /* drawingFlags (1 byte) */
+ Stream_Read_UINT16(s, multipleRectangleSupport); /* multipleRectangleSupport (2 bytes) */
+ Stream_Read_UINT16(s, pad2OctetsB); /* pad2OctetsB (2 bytes) */
+ WLog_VRB(TAG, "\tpreferredBitsPerPixel: 0x%04" PRIX16 "", preferredBitsPerPixel);
+ WLog_VRB(TAG, "\treceive1BitPerPixel: 0x%04" PRIX16 "", receive1BitPerPixel);
+ WLog_VRB(TAG, "\treceive4BitsPerPixel: 0x%04" PRIX16 "", receive4BitsPerPixel);
+ WLog_VRB(TAG, "\treceive8BitsPerPixel: 0x%04" PRIX16 "", receive8BitsPerPixel);
+ WLog_VRB(TAG, "\tdesktopWidth: 0x%04" PRIX16 "", desktopWidth);
+ WLog_VRB(TAG, "\tdesktopHeight: 0x%04" PRIX16 "", desktopHeight);
+ WLog_VRB(TAG, "\tpad2Octets: 0x%04" PRIX16 "", pad2Octets);
+ WLog_VRB(TAG, "\tdesktopResizeFlag: 0x%04" PRIX16 "", desktopResizeFlag);
+ WLog_VRB(TAG, "\tbitmapCompressionFlag: 0x%04" PRIX16 "", bitmapCompressionFlag);
+ WLog_VRB(TAG, "\thighColorFlags: 0x%02" PRIX8 "", highColorFlags);
+ WLog_VRB(TAG, "\tdrawingFlags: 0x%02" PRIX8 "", drawingFlags);
+ WLog_VRB(TAG, "\tmultipleRectangleSupport: 0x%04" PRIX16 "", multipleRectangleSupport);
+ WLog_VRB(TAG, "\tpad2OctetsB: 0x%04" PRIX16 "", pad2OctetsB);
+ return TRUE;
+}
+#endif
+static BOOL rdp_apply_order_capability_set(rdpSettings* settings, const rdpSettings* src)
+{
+ WINPR_ASSERT(settings);
+ WINPR_ASSERT(src);
+
+ BOOL BitmapCacheV3Enabled = FALSE;
+ BOOL FrameMarkerCommandEnabled = FALSE;
+
+ for (size_t i = 0; i < 32; i++)
+ {
+ if (!src->OrderSupport[i])
+ settings->OrderSupport[i] = FALSE;
+ }
+
+ if (settings->OrderSupportFlags & ORDER_FLAGS_EXTRA_SUPPORT)
+ {
+ if (src->OrderSupportFlagsEx & CACHE_BITMAP_V3_SUPPORT)
+ BitmapCacheV3Enabled = TRUE;
+
+ if (src->OrderSupportFlagsEx & ALTSEC_FRAME_MARKER_SUPPORT)
+ FrameMarkerCommandEnabled = TRUE;
+ }
+
+ if (BitmapCacheV3Enabled)
+ {
+ settings->BitmapCacheV3Enabled = src->BitmapCacheV3Enabled;
+ settings->BitmapCacheVersion = src->BitmapCacheVersion;
+ }
+ else
+ settings->BitmapCacheV3Enabled = FALSE;
+
+ if (FrameMarkerCommandEnabled && !src->FrameMarkerCommandEnabled)
+ settings->FrameMarkerCommandEnabled = FALSE;
+
+ return TRUE;
+}
+
+/*
+ * Read order capability set.
+ * msdn{cc240556}
+ */
+
+static BOOL rdp_read_order_capability_set(wStream* s, rdpSettings* settings)
+{
+ char terminalDescriptor[17] = { 0 };
+ BYTE orderSupport[32] = { 0 };
+ BOOL BitmapCacheV3Enabled = FALSE;
+ BOOL FrameMarkerCommandEnabled = FALSE;
+
+ WINPR_ASSERT(settings);
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 84))
+ return FALSE;
+
+ Stream_Read(s, terminalDescriptor, 16); /* terminalDescriptor (16 bytes) */
+ Stream_Seek_UINT32(s); /* pad4OctetsA (4 bytes) */
+ Stream_Seek_UINT16(s); /* desktopSaveXGranularity (2 bytes) */
+ Stream_Seek_UINT16(s); /* desktopSaveYGranularity (2 bytes) */
+ Stream_Seek_UINT16(s); /* pad2OctetsA (2 bytes) */
+ Stream_Seek_UINT16(s); /* maximumOrderLevel (2 bytes) */
+ Stream_Seek_UINT16(s); /* numberFonts (2 bytes) */
+ Stream_Read_UINT16(s, settings->OrderSupportFlags); /* orderFlags (2 bytes) */
+ Stream_Read(s, orderSupport, 32); /* orderSupport (32 bytes) */
+ Stream_Seek_UINT16(s); /* textFlags (2 bytes) */
+ Stream_Read_UINT16(s, settings->OrderSupportFlagsEx); /* orderSupportExFlags (2 bytes) */
+ Stream_Seek_UINT32(s); /* pad4OctetsB (4 bytes) */
+ Stream_Seek_UINT32(s); /* desktopSaveSize (4 bytes) */
+ Stream_Seek_UINT16(s); /* pad2OctetsC (2 bytes) */
+ Stream_Seek_UINT16(s); /* pad2OctetsD (2 bytes) */
+ Stream_Read_UINT16(s, settings->TextANSICodePage); /* textANSICodePage (2 bytes) */
+ Stream_Seek_UINT16(s); /* pad2OctetsE (2 bytes) */
+
+ if (!freerdp_settings_set_string(settings, FreeRDP_TerminalDescriptor, terminalDescriptor))
+ return FALSE;
+
+ for (size_t i = 0; i < ARRAYSIZE(orderSupport); i++)
+ settings->OrderSupport[i] = orderSupport[i];
+
+ if (settings->OrderSupportFlags & ORDER_FLAGS_EXTRA_SUPPORT)
+ {
+ BitmapCacheV3Enabled =
+ (settings->OrderSupportFlagsEx & CACHE_BITMAP_V3_SUPPORT) ? TRUE : FALSE;
+ FrameMarkerCommandEnabled =
+ (settings->OrderSupportFlagsEx & ALTSEC_FRAME_MARKER_SUPPORT) ? TRUE : FALSE;
+ }
+
+ settings->BitmapCacheV3Enabled = BitmapCacheV3Enabled;
+ if (BitmapCacheV3Enabled)
+ settings->BitmapCacheVersion = 3;
+
+ settings->FrameMarkerCommandEnabled = FrameMarkerCommandEnabled;
+
+ return TRUE;
+}
+
+/*
+ * Write order capability set.
+ * msdn{cc240556}
+ */
+
+static BOOL rdp_write_order_capability_set(wStream* s, const rdpSettings* settings)
+{
+ char terminalDescriptor[16] = { 0 };
+
+ WINPR_ASSERT(settings);
+ if (!Stream_EnsureRemainingCapacity(s, 64))
+ return FALSE;
+
+ const size_t header = rdp_capability_set_start(s);
+
+ UINT16 orderSupportExFlags = settings->OrderSupportFlagsEx;
+ UINT16 orderFlags = settings->OrderSupportFlags;
+
+ if (settings->BitmapCacheV3Enabled)
+ {
+ orderSupportExFlags |= CACHE_BITMAP_V3_SUPPORT;
+ orderFlags |= ORDER_FLAGS_EXTRA_SUPPORT;
+ }
+
+ if (settings->FrameMarkerCommandEnabled)
+ {
+ orderSupportExFlags |= ALTSEC_FRAME_MARKER_SUPPORT;
+ orderFlags |= ORDER_FLAGS_EXTRA_SUPPORT;
+ }
+
+ const char* dsc = freerdp_settings_get_string(settings, FreeRDP_TerminalDescriptor);
+ if (dsc)
+ {
+ const size_t len = strnlen(dsc, ARRAYSIZE(terminalDescriptor));
+ strncpy(terminalDescriptor, dsc, len);
+ }
+ Stream_Write(s, terminalDescriptor,
+ sizeof(terminalDescriptor)); /* terminalDescriptor (16 bytes) */
+ Stream_Write_UINT32(s, 0); /* pad4OctetsA (4 bytes) */
+ Stream_Write_UINT16(s, 1); /* desktopSaveXGranularity (2 bytes) */
+ Stream_Write_UINT16(s, 20); /* desktopSaveYGranularity (2 bytes) */
+ Stream_Write_UINT16(s, 0); /* pad2OctetsA (2 bytes) */
+ Stream_Write_UINT16(s, 1); /* maximumOrderLevel (2 bytes) */
+ Stream_Write_UINT16(s, 0); /* numberFonts (2 bytes) */
+ Stream_Write_UINT16(s, orderFlags); /* orderFlags (2 bytes) */
+ Stream_Write(s, settings->OrderSupport, 32); /* orderSupport (32 bytes) */
+ Stream_Write_UINT16(s, 0); /* textFlags (2 bytes) */
+ Stream_Write_UINT16(s, orderSupportExFlags); /* orderSupportExFlags (2 bytes) */
+ Stream_Write_UINT32(s, 0); /* pad4OctetsB (4 bytes) */
+ Stream_Write_UINT32(s, 230400); /* desktopSaveSize (4 bytes) */
+ Stream_Write_UINT16(s, 0); /* pad2OctetsC (2 bytes) */
+ Stream_Write_UINT16(s, 0); /* pad2OctetsD (2 bytes) */
+ Stream_Write_UINT16(s, settings->TextANSICodePage); /* textANSICodePage (2 bytes) */
+ Stream_Write_UINT16(s, 0); /* pad2OctetsE (2 bytes) */
+ return rdp_capability_set_finish(s, header, CAPSET_TYPE_ORDER);
+}
+
+#ifdef WITH_DEBUG_CAPABILITIES
+static BOOL rdp_print_order_capability_set(wStream* s)
+{
+ BYTE terminalDescriptor[16];
+ UINT32 pad4OctetsA = 0;
+ UINT16 desktopSaveXGranularity = 0;
+ UINT16 desktopSaveYGranularity = 0;
+ UINT16 pad2OctetsA = 0;
+ UINT16 maximumOrderLevel = 0;
+ UINT16 numberFonts = 0;
+ UINT16 orderFlags = 0;
+ BYTE orderSupport[32];
+ UINT16 textFlags = 0;
+ UINT16 orderSupportExFlags = 0;
+ UINT32 pad4OctetsB = 0;
+ UINT32 desktopSaveSize = 0;
+ UINT16 pad2OctetsC = 0;
+ UINT16 pad2OctetsD = 0;
+ UINT16 textANSICodePage = 0;
+ UINT16 pad2OctetsE = 0;
+ WLog_VRB(TAG, "OrderCapabilitySet (length %" PRIuz "):", Stream_GetRemainingLength(s));
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 84))
+ return FALSE;
+
+ Stream_Read(s, terminalDescriptor, 16); /* terminalDescriptor (16 bytes) */
+ Stream_Read_UINT32(s, pad4OctetsA); /* pad4OctetsA (4 bytes) */
+ Stream_Read_UINT16(s, desktopSaveXGranularity); /* desktopSaveXGranularity (2 bytes) */
+ Stream_Read_UINT16(s, desktopSaveYGranularity); /* desktopSaveYGranularity (2 bytes) */
+ Stream_Read_UINT16(s, pad2OctetsA); /* pad2OctetsA (2 bytes) */
+ Stream_Read_UINT16(s, maximumOrderLevel); /* maximumOrderLevel (2 bytes) */
+ Stream_Read_UINT16(s, numberFonts); /* numberFonts (2 bytes) */
+ Stream_Read_UINT16(s, orderFlags); /* orderFlags (2 bytes) */
+ Stream_Read(s, orderSupport, 32); /* orderSupport (32 bytes) */
+ Stream_Read_UINT16(s, textFlags); /* textFlags (2 bytes) */
+ Stream_Read_UINT16(s, orderSupportExFlags); /* orderSupportExFlags (2 bytes) */
+ Stream_Read_UINT32(s, pad4OctetsB); /* pad4OctetsB (4 bytes) */
+ Stream_Read_UINT32(s, desktopSaveSize); /* desktopSaveSize (4 bytes) */
+ Stream_Read_UINT16(s, pad2OctetsC); /* pad2OctetsC (2 bytes) */
+ Stream_Read_UINT16(s, pad2OctetsD); /* pad2OctetsD (2 bytes) */
+ Stream_Read_UINT16(s, textANSICodePage); /* textANSICodePage (2 bytes) */
+ Stream_Read_UINT16(s, pad2OctetsE); /* pad2OctetsE (2 bytes) */
+ WLog_VRB(TAG, "\tpad4OctetsA: 0x%08" PRIX32 "", pad4OctetsA);
+ WLog_VRB(TAG, "\tdesktopSaveXGranularity: 0x%04" PRIX16 "", desktopSaveXGranularity);
+ WLog_VRB(TAG, "\tdesktopSaveYGranularity: 0x%04" PRIX16 "", desktopSaveYGranularity);
+ WLog_VRB(TAG, "\tpad2OctetsA: 0x%04" PRIX16 "", pad2OctetsA);
+ WLog_VRB(TAG, "\tmaximumOrderLevel: 0x%04" PRIX16 "", maximumOrderLevel);
+ WLog_VRB(TAG, "\tnumberFonts: 0x%04" PRIX16 "", numberFonts);
+ WLog_VRB(TAG, "\torderFlags: 0x%04" PRIX16 "", orderFlags);
+ WLog_VRB(TAG, "\torderSupport:");
+ WLog_VRB(TAG, "\t\tDSTBLT: %" PRIu8 "", orderSupport[NEG_DSTBLT_INDEX]);
+ WLog_VRB(TAG, "\t\tPATBLT: %" PRIu8 "", orderSupport[NEG_PATBLT_INDEX]);
+ WLog_VRB(TAG, "\t\tSCRBLT: %" PRIu8 "", orderSupport[NEG_SCRBLT_INDEX]);
+ WLog_VRB(TAG, "\t\tMEMBLT: %" PRIu8 "", orderSupport[NEG_MEMBLT_INDEX]);
+ WLog_VRB(TAG, "\t\tMEM3BLT: %" PRIu8 "", orderSupport[NEG_MEM3BLT_INDEX]);
+ WLog_VRB(TAG, "\t\tATEXTOUT: %" PRIu8 "", orderSupport[NEG_ATEXTOUT_INDEX]);
+ WLog_VRB(TAG, "\t\tAEXTTEXTOUT: %" PRIu8 "", orderSupport[NEG_AEXTTEXTOUT_INDEX]);
+ WLog_VRB(TAG, "\t\tDRAWNINEGRID: %" PRIu8 "", orderSupport[NEG_DRAWNINEGRID_INDEX]);
+ WLog_VRB(TAG, "\t\tLINETO: %" PRIu8 "", orderSupport[NEG_LINETO_INDEX]);
+ WLog_VRB(TAG, "\t\tMULTI_DRAWNINEGRID: %" PRIu8 "", orderSupport[NEG_MULTI_DRAWNINEGRID_INDEX]);
+ WLog_VRB(TAG, "\t\tOPAQUE_RECT: %" PRIu8 "", orderSupport[NEG_OPAQUE_RECT_INDEX]);
+ WLog_VRB(TAG, "\t\tSAVEBITMAP: %" PRIu8 "", orderSupport[NEG_SAVEBITMAP_INDEX]);
+ WLog_VRB(TAG, "\t\tWTEXTOUT: %" PRIu8 "", orderSupport[NEG_WTEXTOUT_INDEX]);
+ WLog_VRB(TAG, "\t\tMEMBLT_V2: %" PRIu8 "", orderSupport[NEG_MEMBLT_V2_INDEX]);
+ WLog_VRB(TAG, "\t\tMEM3BLT_V2: %" PRIu8 "", orderSupport[NEG_MEM3BLT_V2_INDEX]);
+ WLog_VRB(TAG, "\t\tMULTIDSTBLT: %" PRIu8 "", orderSupport[NEG_MULTIDSTBLT_INDEX]);
+ WLog_VRB(TAG, "\t\tMULTIPATBLT: %" PRIu8 "", orderSupport[NEG_MULTIPATBLT_INDEX]);
+ WLog_VRB(TAG, "\t\tMULTISCRBLT: %" PRIu8 "", orderSupport[NEG_MULTISCRBLT_INDEX]);
+ WLog_VRB(TAG, "\t\tMULTIOPAQUERECT: %" PRIu8 "", orderSupport[NEG_MULTIOPAQUERECT_INDEX]);
+ WLog_VRB(TAG, "\t\tFAST_INDEX: %" PRIu8 "", orderSupport[NEG_FAST_INDEX_INDEX]);
+ WLog_VRB(TAG, "\t\tPOLYGON_SC: %" PRIu8 "", orderSupport[NEG_POLYGON_SC_INDEX]);
+ WLog_VRB(TAG, "\t\tPOLYGON_CB: %" PRIu8 "", orderSupport[NEG_POLYGON_CB_INDEX]);
+ WLog_VRB(TAG, "\t\tPOLYLINE: %" PRIu8 "", orderSupport[NEG_POLYLINE_INDEX]);
+ WLog_VRB(TAG, "\t\tUNUSED23: %" PRIu8 "", orderSupport[NEG_UNUSED23_INDEX]);
+ WLog_VRB(TAG, "\t\tFAST_GLYPH: %" PRIu8 "", orderSupport[NEG_FAST_GLYPH_INDEX]);
+ WLog_VRB(TAG, "\t\tELLIPSE_SC: %" PRIu8 "", orderSupport[NEG_ELLIPSE_SC_INDEX]);
+ WLog_VRB(TAG, "\t\tELLIPSE_CB: %" PRIu8 "", orderSupport[NEG_ELLIPSE_CB_INDEX]);
+ WLog_VRB(TAG, "\t\tGLYPH_INDEX: %" PRIu8 "", orderSupport[NEG_GLYPH_INDEX_INDEX]);
+ WLog_VRB(TAG, "\t\tGLYPH_WEXTTEXTOUT: %" PRIu8 "", orderSupport[NEG_GLYPH_WEXTTEXTOUT_INDEX]);
+ WLog_VRB(TAG, "\t\tGLYPH_WLONGTEXTOUT: %" PRIu8 "", orderSupport[NEG_GLYPH_WLONGTEXTOUT_INDEX]);
+ WLog_VRB(TAG, "\t\tGLYPH_WLONGEXTTEXTOUT: %" PRIu8 "",
+ orderSupport[NEG_GLYPH_WLONGEXTTEXTOUT_INDEX]);
+ WLog_VRB(TAG, "\t\tUNUSED31: %" PRIu8 "", orderSupport[NEG_UNUSED31_INDEX]);
+ WLog_VRB(TAG, "\ttextFlags: 0x%04" PRIX16 "", textFlags);
+ WLog_VRB(TAG, "\torderSupportExFlags: 0x%04" PRIX16 "", orderSupportExFlags);
+ WLog_VRB(TAG, "\tpad4OctetsB: 0x%08" PRIX32 "", pad4OctetsB);
+ WLog_VRB(TAG, "\tdesktopSaveSize: 0x%08" PRIX32 "", desktopSaveSize);
+ WLog_VRB(TAG, "\tpad2OctetsC: 0x%04" PRIX16 "", pad2OctetsC);
+ WLog_VRB(TAG, "\tpad2OctetsD: 0x%04" PRIX16 "", pad2OctetsD);
+ WLog_VRB(TAG, "\ttextANSICodePage: 0x%04" PRIX16 "", textANSICodePage);
+ WLog_VRB(TAG, "\tpad2OctetsE: 0x%04" PRIX16 "", pad2OctetsE);
+ return TRUE;
+}
+#endif
+
+static BOOL rdp_apply_bitmap_cache_capability_set(rdpSettings* settings, const rdpSettings* src)
+{
+ WINPR_ASSERT(settings);
+ WINPR_ASSERT(src);
+ return TRUE;
+}
+
+/*
+ * Read bitmap cache capability set.
+ * msdn{cc240559}
+ */
+
+static BOOL rdp_read_bitmap_cache_capability_set(wStream* s, rdpSettings* settings)
+{
+ WINPR_UNUSED(settings);
+ WINPR_ASSERT(settings);
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 36))
+ return FALSE;
+
+ Stream_Seek_UINT32(s); /* pad1 (4 bytes) */
+ Stream_Seek_UINT32(s); /* pad2 (4 bytes) */
+ Stream_Seek_UINT32(s); /* pad3 (4 bytes) */
+ Stream_Seek_UINT32(s); /* pad4 (4 bytes) */
+ Stream_Seek_UINT32(s); /* pad5 (4 bytes) */
+ Stream_Seek_UINT32(s); /* pad6 (4 bytes) */
+ Stream_Seek_UINT16(s); /* Cache0Entries (2 bytes) */
+ Stream_Seek_UINT16(s); /* Cache0MaximumCellSize (2 bytes) */
+ Stream_Seek_UINT16(s); /* Cache1Entries (2 bytes) */
+ Stream_Seek_UINT16(s); /* Cache1MaximumCellSize (2 bytes) */
+ Stream_Seek_UINT16(s); /* Cache2Entries (2 bytes) */
+ Stream_Seek_UINT16(s); /* Cache2MaximumCellSize (2 bytes) */
+ return TRUE;
+}
+
+/*
+ * Write bitmap cache capability set.
+ * msdn{cc240559}
+ */
+
+static BOOL rdp_write_bitmap_cache_capability_set(wStream* s, const rdpSettings* settings)
+{
+ if (!Stream_EnsureRemainingCapacity(s, 64))
+ return FALSE;
+
+ const size_t header = rdp_capability_set_start(s);
+ const UINT32 bpp = (freerdp_settings_get_uint32(settings, FreeRDP_ColorDepth) + 7) / 8;
+ if (bpp > UINT16_MAX)
+ return FALSE;
+ Stream_Write_UINT32(s, 0); /* pad1 (4 bytes) */
+ Stream_Write_UINT32(s, 0); /* pad2 (4 bytes) */
+ Stream_Write_UINT32(s, 0); /* pad3 (4 bytes) */
+ Stream_Write_UINT32(s, 0); /* pad4 (4 bytes) */
+ Stream_Write_UINT32(s, 0); /* pad5 (4 bytes) */
+ Stream_Write_UINT32(s, 0); /* pad6 (4 bytes) */
+ UINT32 size = bpp * 256;
+ if (size > UINT16_MAX)
+ return FALSE;
+ Stream_Write_UINT16(s, 200); /* Cache0Entries (2 bytes) */
+ Stream_Write_UINT16(s, (UINT16)size); /* Cache0MaximumCellSize (2 bytes) */
+ size = bpp * 1024;
+ if (size > UINT16_MAX)
+ return FALSE;
+ Stream_Write_UINT16(s, 600); /* Cache1Entries (2 bytes) */
+ Stream_Write_UINT16(s, (UINT16)size); /* Cache1MaximumCellSize (2 bytes) */
+ size = bpp * 4096;
+ if (size > UINT16_MAX)
+ return FALSE;
+ Stream_Write_UINT16(s, 1000); /* Cache2Entries (2 bytes) */
+ Stream_Write_UINT16(s, (UINT16)size); /* Cache2MaximumCellSize (2 bytes) */
+ return rdp_capability_set_finish(s, header, CAPSET_TYPE_BITMAP_CACHE);
+}
+
+#ifdef WITH_DEBUG_CAPABILITIES
+static BOOL rdp_print_bitmap_cache_capability_set(wStream* s)
+{
+ UINT32 pad1 = 0;
+ UINT32 pad2 = 0;
+ UINT32 pad3 = 0;
+ UINT32 pad4 = 0;
+ UINT32 pad5 = 0;
+ UINT32 pad6 = 0;
+ UINT16 Cache0Entries = 0;
+ UINT16 Cache0MaximumCellSize = 0;
+ UINT16 Cache1Entries = 0;
+ UINT16 Cache1MaximumCellSize = 0;
+ UINT16 Cache2Entries = 0;
+ UINT16 Cache2MaximumCellSize = 0;
+ WLog_VRB(TAG, "BitmapCacheCapabilitySet (length %" PRIuz "):", Stream_GetRemainingLength(s));
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 36))
+ return FALSE;
+
+ Stream_Read_UINT32(s, pad1); /* pad1 (4 bytes) */
+ Stream_Read_UINT32(s, pad2); /* pad2 (4 bytes) */
+ Stream_Read_UINT32(s, pad3); /* pad3 (4 bytes) */
+ Stream_Read_UINT32(s, pad4); /* pad4 (4 bytes) */
+ Stream_Read_UINT32(s, pad5); /* pad5 (4 bytes) */
+ Stream_Read_UINT32(s, pad6); /* pad6 (4 bytes) */
+ Stream_Read_UINT16(s, Cache0Entries); /* Cache0Entries (2 bytes) */
+ Stream_Read_UINT16(s, Cache0MaximumCellSize); /* Cache0MaximumCellSize (2 bytes) */
+ Stream_Read_UINT16(s, Cache1Entries); /* Cache1Entries (2 bytes) */
+ Stream_Read_UINT16(s, Cache1MaximumCellSize); /* Cache1MaximumCellSize (2 bytes) */
+ Stream_Read_UINT16(s, Cache2Entries); /* Cache2Entries (2 bytes) */
+ Stream_Read_UINT16(s, Cache2MaximumCellSize); /* Cache2MaximumCellSize (2 bytes) */
+ WLog_VRB(TAG, "\tpad1: 0x%08" PRIX32 "", pad1);
+ WLog_VRB(TAG, "\tpad2: 0x%08" PRIX32 "", pad2);
+ WLog_VRB(TAG, "\tpad3: 0x%08" PRIX32 "", pad3);
+ WLog_VRB(TAG, "\tpad4: 0x%08" PRIX32 "", pad4);
+ WLog_VRB(TAG, "\tpad5: 0x%08" PRIX32 "", pad5);
+ WLog_VRB(TAG, "\tpad6: 0x%08" PRIX32 "", pad6);
+ WLog_VRB(TAG, "\tCache0Entries: 0x%04" PRIX16 "", Cache0Entries);
+ WLog_VRB(TAG, "\tCache0MaximumCellSize: 0x%04" PRIX16 "", Cache0MaximumCellSize);
+ WLog_VRB(TAG, "\tCache1Entries: 0x%04" PRIX16 "", Cache1Entries);
+ WLog_VRB(TAG, "\tCache1MaximumCellSize: 0x%04" PRIX16 "", Cache1MaximumCellSize);
+ WLog_VRB(TAG, "\tCache2Entries: 0x%04" PRIX16 "", Cache2Entries);
+ WLog_VRB(TAG, "\tCache2MaximumCellSize: 0x%04" PRIX16 "", Cache2MaximumCellSize);
+ return TRUE;
+}
+#endif
+
+static BOOL rdp_apply_control_capability_set(rdpSettings* settings, const rdpSettings* src)
+{
+ WINPR_ASSERT(settings);
+ WINPR_ASSERT(src);
+
+ return TRUE;
+}
+
+/*
+ * Read control capability set.
+ * msdn{cc240568}
+ */
+
+static BOOL rdp_read_control_capability_set(wStream* s, rdpSettings* settings)
+{
+ WINPR_UNUSED(settings);
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
+ return FALSE;
+
+ Stream_Seek_UINT16(s); /* controlFlags (2 bytes) */
+ Stream_Seek_UINT16(s); /* remoteDetachFlag (2 bytes) */
+ Stream_Seek_UINT16(s); /* controlInterest (2 bytes) */
+ Stream_Seek_UINT16(s); /* detachInterest (2 bytes) */
+ return TRUE;
+}
+
+/*
+ * Write control capability set.
+ * msdn{cc240568}
+ */
+
+static BOOL rdp_write_control_capability_set(wStream* s, const rdpSettings* settings)
+{
+ WINPR_UNUSED(settings);
+ if (!Stream_EnsureRemainingCapacity(s, 32))
+ return FALSE;
+
+ const size_t header = rdp_capability_set_start(s);
+ Stream_Write_UINT16(s, 0); /* controlFlags (2 bytes) */
+ Stream_Write_UINT16(s, 0); /* remoteDetachFlag (2 bytes) */
+ Stream_Write_UINT16(s, 2); /* controlInterest (2 bytes) */
+ Stream_Write_UINT16(s, 2); /* detachInterest (2 bytes) */
+ return rdp_capability_set_finish(s, header, CAPSET_TYPE_CONTROL);
+}
+
+#ifdef WITH_DEBUG_CAPABILITIES
+static BOOL rdp_print_control_capability_set(wStream* s)
+{
+ UINT16 controlFlags = 0;
+ UINT16 remoteDetachFlag = 0;
+ UINT16 controlInterest = 0;
+ UINT16 detachInterest = 0;
+ WLog_VRB(TAG, "ControlCapabilitySet (length %" PRIuz "):", Stream_GetRemainingLength(s));
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
+ return FALSE;
+
+ Stream_Read_UINT16(s, controlFlags); /* controlFlags (2 bytes) */
+ Stream_Read_UINT16(s, remoteDetachFlag); /* remoteDetachFlag (2 bytes) */
+ Stream_Read_UINT16(s, controlInterest); /* controlInterest (2 bytes) */
+ Stream_Read_UINT16(s, detachInterest); /* detachInterest (2 bytes) */
+ WLog_VRB(TAG, "\tcontrolFlags: 0x%04" PRIX16 "", controlFlags);
+ WLog_VRB(TAG, "\tremoteDetachFlag: 0x%04" PRIX16 "", remoteDetachFlag);
+ WLog_VRB(TAG, "\tcontrolInterest: 0x%04" PRIX16 "", controlInterest);
+ WLog_VRB(TAG, "\tdetachInterest: 0x%04" PRIX16 "", detachInterest);
+ return TRUE;
+}
+#endif
+
+static BOOL rdp_apply_window_activation_capability_set(rdpSettings* settings,
+ const rdpSettings* src)
+{
+ WINPR_ASSERT(settings);
+ WINPR_ASSERT(src);
+
+ return TRUE;
+}
+
+/*
+ * Read window activation capability set.
+ * msdn{cc240569}
+ */
+
+static BOOL rdp_read_window_activation_capability_set(wStream* s, rdpSettings* settings)
+{
+ WINPR_UNUSED(settings);
+ WINPR_ASSERT(settings);
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
+ return FALSE;
+
+ Stream_Seek_UINT16(s); /* helpKeyFlag (2 bytes) */
+ Stream_Seek_UINT16(s); /* helpKeyIndexFlag (2 bytes) */
+ Stream_Seek_UINT16(s); /* helpExtendedKeyFlag (2 bytes) */
+ Stream_Seek_UINT16(s); /* windowManagerKeyFlag (2 bytes) */
+ return TRUE;
+}
+
+/*
+ * Write window activation capability set.
+ * msdn{cc240569}
+ */
+
+static BOOL rdp_write_window_activation_capability_set(wStream* s, const rdpSettings* settings)
+{
+ WINPR_UNUSED(settings);
+ WINPR_ASSERT(settings);
+ if (!Stream_EnsureRemainingCapacity(s, 32))
+ return FALSE;
+
+ const size_t header = rdp_capability_set_start(s);
+ Stream_Write_UINT16(s, 0); /* helpKeyFlag (2 bytes) */
+ Stream_Write_UINT16(s, 0); /* helpKeyIndexFlag (2 bytes) */
+ Stream_Write_UINT16(s, 0); /* helpExtendedKeyFlag (2 bytes) */
+ Stream_Write_UINT16(s, 0); /* windowManagerKeyFlag (2 bytes) */
+ return rdp_capability_set_finish(s, header, CAPSET_TYPE_ACTIVATION);
+}
+
+#ifdef WITH_DEBUG_CAPABILITIES
+static BOOL rdp_print_window_activation_capability_set(wStream* s)
+{
+ UINT16 helpKeyFlag = 0;
+ UINT16 helpKeyIndexFlag = 0;
+ UINT16 helpExtendedKeyFlag = 0;
+ UINT16 windowManagerKeyFlag = 0;
+ WLog_VRB(TAG,
+ "WindowActivationCapabilitySet (length %" PRIuz "):", Stream_GetRemainingLength(s));
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
+ return FALSE;
+
+ Stream_Read_UINT16(s, helpKeyFlag); /* helpKeyFlag (2 bytes) */
+ Stream_Read_UINT16(s, helpKeyIndexFlag); /* helpKeyIndexFlag (2 bytes) */
+ Stream_Read_UINT16(s, helpExtendedKeyFlag); /* helpExtendedKeyFlag (2 bytes) */
+ Stream_Read_UINT16(s, windowManagerKeyFlag); /* windowManagerKeyFlag (2 bytes) */
+ WLog_VRB(TAG, "\thelpKeyFlag: 0x%04" PRIX16 "", helpKeyFlag);
+ WLog_VRB(TAG, "\thelpKeyIndexFlag: 0x%04" PRIX16 "", helpKeyIndexFlag);
+ WLog_VRB(TAG, "\thelpExtendedKeyFlag: 0x%04" PRIX16 "", helpExtendedKeyFlag);
+ WLog_VRB(TAG, "\twindowManagerKeyFlag: 0x%04" PRIX16 "", windowManagerKeyFlag);
+ return TRUE;
+}
+#endif
+
+static BOOL rdp_apply_pointer_capability_set(rdpSettings* settings, const rdpSettings* src)
+{
+ WINPR_ASSERT(settings);
+ WINPR_ASSERT(src);
+
+ const UINT32 pointerCacheSize = freerdp_settings_get_uint32(src, FreeRDP_PointerCacheSize);
+ const UINT32 colorPointerCacheSize =
+ freerdp_settings_get_uint32(src, FreeRDP_ColorPointerCacheSize);
+ const UINT32 dstPointerCacheSize =
+ freerdp_settings_get_uint32(settings, FreeRDP_PointerCacheSize);
+ const UINT32 dstColorPointerCacheSize =
+ freerdp_settings_get_uint32(settings, FreeRDP_ColorPointerCacheSize);
+
+ /* We want the minimum of our setting and the remote announced value. */
+ const UINT32 actualPointerCacheSize = MIN(pointerCacheSize, dstPointerCacheSize);
+ const UINT32 actualColorPointerCacheSize = MIN(colorPointerCacheSize, dstColorPointerCacheSize);
+
+ if (!freerdp_settings_set_uint32(settings, FreeRDP_PointerCacheSize, actualPointerCacheSize) ||
+ !freerdp_settings_set_uint32(settings, FreeRDP_ColorPointerCacheSize,
+ actualColorPointerCacheSize))
+ return FALSE;
+
+ return TRUE;
+}
+
+/*
+ * Read pointer capability set.
+ * msdn{cc240562}
+ */
+
+static BOOL rdp_read_pointer_capability_set(wStream* s, rdpSettings* settings)
+{
+ UINT16 colorPointerFlag = 0;
+ UINT16 colorPointerCacheSize = 0;
+ UINT16 pointerCacheSize = 0;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return FALSE;
+
+ Stream_Read_UINT16(s, colorPointerFlag); /* colorPointerFlag (2 bytes) */
+ Stream_Read_UINT16(s, colorPointerCacheSize); /* colorPointerCacheSize (2 bytes) */
+
+ if (colorPointerFlag == 0)
+ {
+ WLog_WARN(TAG, "[MS-RDPBCGR] 2.2.7.1.5 Pointer Capability Set "
+ "(TS_POINTER_CAPABILITYSET)::colorPointerFlag received is %" PRIu16
+ ". Vaue is ignored and always assumed to be TRUE");
+ }
+
+ /* pointerCacheSize is optional */
+ if (Stream_GetRemainingLength(s) >= 2)
+ Stream_Read_UINT16(s, pointerCacheSize); /* pointerCacheSize (2 bytes) */
+
+ WINPR_ASSERT(settings);
+ settings->PointerCacheSize = pointerCacheSize;
+ settings->ColorPointerCacheSize = colorPointerCacheSize;
+
+ return TRUE;
+}
+
+/*
+ * Write pointer capability set.
+ * msdn{cc240562}
+ */
+
+static BOOL rdp_write_pointer_capability_set(wStream* s, const rdpSettings* settings)
+{
+ if (!Stream_EnsureRemainingCapacity(s, 32))
+ return FALSE;
+
+ const size_t header = rdp_capability_set_start(s);
+ if (settings->PointerCacheSize > UINT16_MAX)
+ return FALSE;
+ if (settings->ColorPointerCacheSize > UINT16_MAX)
+ return FALSE;
+
+ WINPR_ASSERT(settings);
+ const UINT32 colorPointerFlag =
+ 1; /* [MS-RDPBCGR] 2.2.7.1.5 Pointer Capability Set (TS_POINTER_CAPABILITYSET)
+ * colorPointerFlag is ignored and always assumed to be TRUE */
+ Stream_Write_UINT16(s, colorPointerFlag); /* colorPointerFlag (2 bytes) */
+ Stream_Write_UINT16(
+ s, (UINT16)settings->ColorPointerCacheSize); /* colorPointerCacheSize (2 bytes) */
+ Stream_Write_UINT16(s, (UINT16)settings->PointerCacheSize); /* pointerCacheSize (2 bytes) */
+
+ return rdp_capability_set_finish(s, header, CAPSET_TYPE_POINTER);
+}
+
+#ifdef WITH_DEBUG_CAPABILITIES
+static BOOL rdp_print_pointer_capability_set(wStream* s)
+{
+ UINT16 colorPointerFlag = 0;
+ UINT16 colorPointerCacheSize = 0;
+ UINT16 pointerCacheSize = 0;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
+ return FALSE;
+
+ WLog_VRB(TAG, "PointerCapabilitySet (length %" PRIuz "):", Stream_GetRemainingLength(s));
+ Stream_Read_UINT16(s, colorPointerFlag); /* colorPointerFlag (2 bytes) */
+ Stream_Read_UINT16(s, colorPointerCacheSize); /* colorPointerCacheSize (2 bytes) */
+ Stream_Read_UINT16(s, pointerCacheSize); /* pointerCacheSize (2 bytes) */
+ WLog_VRB(TAG, "\tcolorPointerFlag: 0x%04" PRIX16 "", colorPointerFlag);
+ WLog_VRB(TAG, "\tcolorPointerCacheSize: 0x%04" PRIX16 "", colorPointerCacheSize);
+ WLog_VRB(TAG, "\tpointerCacheSize: 0x%04" PRIX16 "", pointerCacheSize);
+ return TRUE;
+}
+#endif
+
+static BOOL rdp_apply_share_capability_set(rdpSettings* settings, const rdpSettings* src)
+{
+ WINPR_ASSERT(settings);
+ WINPR_ASSERT(src);
+
+ return TRUE;
+}
+
+/*
+ * Read share capability set.
+ * msdn{cc240570}
+ */
+
+static BOOL rdp_read_share_capability_set(wStream* s, rdpSettings* settings)
+{
+ WINPR_UNUSED(settings);
+ WINPR_ASSERT(settings);
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return FALSE;
+
+ Stream_Seek_UINT16(s); /* nodeId (2 bytes) */
+ Stream_Seek_UINT16(s); /* pad2Octets (2 bytes) */
+ return TRUE;
+}
+
+/*
+ * Write share capability set.
+ * msdn{cc240570}
+ */
+
+static BOOL rdp_write_share_capability_set(wStream* s, const rdpSettings* settings)
+{
+ if (!Stream_EnsureRemainingCapacity(s, 32))
+ return FALSE;
+
+ const size_t header = rdp_capability_set_start(s);
+
+ WINPR_ASSERT(settings);
+ const UINT16 nodeId = (settings->ServerMode) ? 0x03EA : 0;
+ Stream_Write_UINT16(s, nodeId); /* nodeId (2 bytes) */
+ Stream_Write_UINT16(s, 0); /* pad2Octets (2 bytes) */
+ return rdp_capability_set_finish(s, header, CAPSET_TYPE_SHARE);
+}
+
+#ifdef WITH_DEBUG_CAPABILITIES
+static BOOL rdp_print_share_capability_set(wStream* s)
+{
+ UINT16 nodeId = 0;
+ UINT16 pad2Octets = 0;
+ WLog_VRB(TAG, "ShareCapabilitySet (length %" PRIuz "):", Stream_GetRemainingLength(s));
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return FALSE;
+
+ Stream_Read_UINT16(s, nodeId); /* nodeId (2 bytes) */
+ Stream_Read_UINT16(s, pad2Octets); /* pad2Octets (2 bytes) */
+ WLog_VRB(TAG, "\tnodeId: 0x%04" PRIX16 "", nodeId);
+ WLog_VRB(TAG, "\tpad2Octets: 0x%04" PRIX16 "", pad2Octets);
+ return TRUE;
+}
+#endif
+
+static BOOL rdp_apply_color_cache_capability_set(rdpSettings* settings, const rdpSettings* src)
+{
+ WINPR_ASSERT(settings);
+ WINPR_ASSERT(src);
+ return TRUE;
+}
+
+/*
+ * Read color cache capability set.
+ * msdn{cc241564}
+ */
+
+static BOOL rdp_read_color_cache_capability_set(wStream* s, rdpSettings* settings)
+{
+ WINPR_UNUSED(settings);
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return FALSE;
+
+ Stream_Seek_UINT16(s); /* colorTableCacheSize (2 bytes) */
+ Stream_Seek_UINT16(s); /* pad2Octets (2 bytes) */
+ return TRUE;
+}
+
+/*
+ * Write color cache capability set.
+ * msdn{cc241564}
+ */
+
+static BOOL rdp_write_color_cache_capability_set(wStream* s, const rdpSettings* settings)
+{
+ WINPR_UNUSED(settings);
+ if (!Stream_EnsureRemainingCapacity(s, 32))
+ return FALSE;
+
+ const size_t header = rdp_capability_set_start(s);
+ Stream_Write_UINT16(s, 6); /* colorTableCacheSize (2 bytes) */
+ Stream_Write_UINT16(s, 0); /* pad2Octets (2 bytes) */
+ return rdp_capability_set_finish(s, header, CAPSET_TYPE_COLOR_CACHE);
+}
+
+#ifdef WITH_DEBUG_CAPABILITIES
+static BOOL rdp_print_color_cache_capability_set(wStream* s)
+{
+ UINT16 colorTableCacheSize = 0;
+ UINT16 pad2Octets = 0;
+ WLog_VRB(TAG, "ColorCacheCapabilitySet (length %" PRIuz "):", Stream_GetRemainingLength(s));
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return FALSE;
+
+ Stream_Read_UINT16(s, colorTableCacheSize); /* colorTableCacheSize (2 bytes) */
+ Stream_Read_UINT16(s, pad2Octets); /* pad2Octets (2 bytes) */
+ WLog_VRB(TAG, "\tcolorTableCacheSize: 0x%04" PRIX16 "", colorTableCacheSize);
+ WLog_VRB(TAG, "\tpad2Octets: 0x%04" PRIX16 "", pad2Octets);
+ return TRUE;
+}
+#endif
+
+static BOOL rdp_apply_sound_capability_set(rdpSettings* settings, const rdpSettings* src)
+{
+ WINPR_ASSERT(settings);
+ WINPR_ASSERT(src);
+
+ settings->SoundBeepsEnabled = src->SoundBeepsEnabled;
+
+ return TRUE;
+}
+
+/*
+ * Read sound capability set.
+ * msdn{cc240552}
+ */
+
+static BOOL rdp_read_sound_capability_set(wStream* s, rdpSettings* settings)
+{
+ UINT16 soundFlags = 0;
+
+ WINPR_ASSERT(settings);
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return FALSE;
+
+ Stream_Read_UINT16(s, soundFlags); /* soundFlags (2 bytes) */
+ Stream_Seek_UINT16(s); /* pad2OctetsA (2 bytes) */
+ settings->SoundBeepsEnabled = (soundFlags & SOUND_BEEPS_FLAG) ? TRUE : FALSE;
+ return TRUE;
+}
+
+/*
+ * Write sound capability set.
+ * msdn{cc240552}
+ */
+
+static BOOL rdp_write_sound_capability_set(wStream* s, const rdpSettings* settings)
+{
+ WINPR_ASSERT(settings);
+ if (!Stream_EnsureRemainingCapacity(s, 32))
+ return FALSE;
+
+ const size_t header = rdp_capability_set_start(s);
+ const UINT16 soundFlags = (settings->SoundBeepsEnabled) ? SOUND_BEEPS_FLAG : 0;
+ Stream_Write_UINT16(s, soundFlags); /* soundFlags (2 bytes) */
+ Stream_Write_UINT16(s, 0); /* pad2OctetsA (2 bytes) */
+ return rdp_capability_set_finish(s, header, CAPSET_TYPE_SOUND);
+}
+
+#ifdef WITH_DEBUG_CAPABILITIES
+static BOOL rdp_print_sound_capability_set(wStream* s)
+{
+ UINT16 soundFlags = 0;
+ UINT16 pad2OctetsA = 0;
+ WLog_VRB(TAG, "SoundCapabilitySet (length %" PRIuz "):", Stream_GetRemainingLength(s));
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return FALSE;
+
+ Stream_Read_UINT16(s, soundFlags); /* soundFlags (2 bytes) */
+ Stream_Read_UINT16(s, pad2OctetsA); /* pad2OctetsA (2 bytes) */
+ WLog_VRB(TAG, "\tsoundFlags: 0x%04" PRIX16 "", soundFlags);
+ WLog_VRB(TAG, "\tpad2OctetsA: 0x%04" PRIX16 "", pad2OctetsA);
+ return TRUE;
+}
+#endif
+
+static BOOL rdp_apply_input_capability_set(rdpSettings* settings, const rdpSettings* src)
+{
+ WINPR_ASSERT(settings);
+ WINPR_ASSERT(src);
+
+ if (settings->ServerMode)
+ {
+ settings->KeyboardLayout = src->KeyboardLayout;
+ settings->KeyboardType = src->KeyboardType;
+ settings->KeyboardSubType = src->KeyboardSubType;
+ settings->KeyboardFunctionKey = src->KeyboardFunctionKey;
+ }
+
+ if (!freerdp_settings_set_string(settings, FreeRDP_ImeFileName, src->ImeFileName))
+ return FALSE;
+
+ if (!settings->ServerMode)
+ {
+ settings->FastPathInput = src->FastPathInput;
+
+ /* Note: These settings have split functionality:
+ * 1. If disabled in client pre_connect, it can disable announcing the feature
+ * 2. If enabled in client pre_connect, override it with the server announced support flag.
+ */
+ if (settings->HasHorizontalWheel)
+ settings->HasHorizontalWheel = src->HasHorizontalWheel;
+ const BOOL UnicodeInput = freerdp_settings_get_bool(settings, FreeRDP_UnicodeInput);
+ if (UnicodeInput)
+ {
+ const BOOL srcVal = freerdp_settings_get_bool(settings, FreeRDP_UnicodeInput);
+ if (!freerdp_settings_set_bool(settings, FreeRDP_UnicodeInput, srcVal))
+ return FALSE;
+ }
+ if (settings->HasExtendedMouseEvent)
+ settings->HasExtendedMouseEvent = src->HasExtendedMouseEvent;
+ if (settings->HasRelativeMouseEvent)
+ settings->HasRelativeMouseEvent = src->HasRelativeMouseEvent;
+ if (freerdp_settings_get_bool(settings, FreeRDP_HasQoeEvent))
+ settings->HasQoeEvent = freerdp_settings_get_bool(settings, FreeRDP_HasQoeEvent);
+ }
+ return TRUE;
+}
+
+/*
+ * Read input capability set.
+ * msdn{cc240563}
+ */
+
+static BOOL rdp_read_input_capability_set(wStream* s, rdpSettings* settings)
+{
+ UINT16 inputFlags = 0;
+
+ WINPR_ASSERT(settings);
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 84))
+ return FALSE;
+
+ Stream_Read_UINT16(s, inputFlags); /* inputFlags (2 bytes) */
+ Stream_Seek_UINT16(s); /* pad2OctetsA (2 bytes) */
+
+ Stream_Read_UINT32(s, settings->KeyboardLayout); /* keyboardLayout (4 bytes) */
+ Stream_Read_UINT32(s, settings->KeyboardType); /* keyboardType (4 bytes) */
+ Stream_Read_UINT32(s, settings->KeyboardSubType); /* keyboardSubType (4 bytes) */
+ Stream_Read_UINT32(s, settings->KeyboardFunctionKey); /* keyboardFunctionKeys (4 bytes) */
+
+ {
+ WCHAR wstr[32] = { 0 };
+ char str[65] = { 0 };
+
+ /* Older windows versions report invalid UTF16
+ * [MS-RDPBCGR] <29> Section 2.2.7.1.6: Microsoft RDP 4.0, 5.0, 5.1, and 5.2 servers do not
+ * explicitly fill the imeFileName field with zeros.
+ */
+ if (!Stream_Read_UTF16_String(s, wstr, ARRAYSIZE(wstr)))
+ return FALSE;
+
+ if (ConvertWCharNToUtf8(wstr, ARRAYSIZE(wstr), str, ARRAYSIZE(str)) < 0)
+ memset(str, 0, sizeof(str));
+
+ if (!freerdp_settings_set_string(settings, FreeRDP_ImeFileName, str))
+ return FALSE;
+ }
+
+ if (!freerdp_settings_set_bool(settings, FreeRDP_FastPathInput,
+ inputFlags &
+ (INPUT_FLAG_FASTPATH_INPUT | INPUT_FLAG_FASTPATH_INPUT2)))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_HasHorizontalWheel,
+ (inputFlags & TS_INPUT_FLAG_MOUSE_HWHEEL) ? TRUE : FALSE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_UnicodeInput,
+ (inputFlags & INPUT_FLAG_UNICODE) ? TRUE : FALSE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_HasRelativeMouseEvent,
+ (inputFlags & INPUT_FLAG_MOUSE_RELATIVE) ? TRUE : FALSE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_HasExtendedMouseEvent,
+ (inputFlags & INPUT_FLAG_MOUSEX) ? TRUE : FALSE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_HasQoeEvent,
+ (inputFlags & TS_INPUT_FLAG_QOE_TIMESTAMPS) ? TRUE : FALSE))
+ return FALSE;
+
+ return TRUE;
+}
+
+/*
+ * Write input capability set.
+ * msdn{cc240563}
+ */
+
+static BOOL rdp_write_input_capability_set(wStream* s, const rdpSettings* settings)
+{
+ WINPR_ASSERT(settings);
+ if (!Stream_EnsureRemainingCapacity(s, 128))
+ return FALSE;
+
+ const size_t header = rdp_capability_set_start(s);
+ UINT16 inputFlags = INPUT_FLAG_SCANCODES;
+
+ if (settings->FastPathInput)
+ {
+ inputFlags |= INPUT_FLAG_FASTPATH_INPUT;
+ inputFlags |= INPUT_FLAG_FASTPATH_INPUT2;
+ }
+
+ if (freerdp_settings_get_bool(settings, FreeRDP_HasRelativeMouseEvent))
+ inputFlags |= INPUT_FLAG_MOUSE_RELATIVE;
+
+ if (freerdp_settings_get_bool(settings, FreeRDP_HasHorizontalWheel))
+ inputFlags |= TS_INPUT_FLAG_MOUSE_HWHEEL;
+
+ if (freerdp_settings_get_bool(settings, FreeRDP_UnicodeInput))
+ inputFlags |= INPUT_FLAG_UNICODE;
+
+ if (freerdp_settings_get_bool(settings, FreeRDP_HasQoeEvent))
+ inputFlags |= TS_INPUT_FLAG_QOE_TIMESTAMPS;
+
+ if (settings->HasExtendedMouseEvent)
+ inputFlags |= INPUT_FLAG_MOUSEX;
+
+ Stream_Write_UINT16(s, inputFlags); /* inputFlags (2 bytes) */
+ Stream_Write_UINT16(s, 0); /* pad2OctetsA (2 bytes) */
+ Stream_Write_UINT32(s, settings->KeyboardLayout); /* keyboardLayout (4 bytes) */
+ Stream_Write_UINT32(s, settings->KeyboardType); /* keyboardType (4 bytes) */
+ Stream_Write_UINT32(s, settings->KeyboardSubType); /* keyboardSubType (4 bytes) */
+ Stream_Write_UINT32(s, settings->KeyboardFunctionKey); /* keyboardFunctionKeys (4 bytes) */
+ Stream_Zero(s, 64); /* imeFileName (64 bytes) */
+ return rdp_capability_set_finish(s, header, CAPSET_TYPE_INPUT);
+}
+
+#ifdef WITH_DEBUG_CAPABILITIES
+static BOOL rdp_print_input_capability_set(wStream* s)
+{
+ UINT16 inputFlags = 0;
+ UINT16 pad2OctetsA = 0;
+ UINT32 keyboardLayout = 0;
+ UINT32 keyboardType = 0;
+ UINT32 keyboardSubType = 0;
+ UINT32 keyboardFunctionKey = 0;
+ WLog_VRB(TAG, "InputCapabilitySet (length %" PRIuz ")", Stream_GetRemainingLength(s));
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 84))
+ return FALSE;
+
+ Stream_Read_UINT16(s, inputFlags); /* inputFlags (2 bytes) */
+ Stream_Read_UINT16(s, pad2OctetsA); /* pad2OctetsA (2 bytes) */
+ Stream_Read_UINT32(s, keyboardLayout); /* keyboardLayout (4 bytes) */
+ Stream_Read_UINT32(s, keyboardType); /* keyboardType (4 bytes) */
+ Stream_Read_UINT32(s, keyboardSubType); /* keyboardSubType (4 bytes) */
+ Stream_Read_UINT32(s, keyboardFunctionKey); /* keyboardFunctionKeys (4 bytes) */
+ Stream_Seek(s, 64); /* imeFileName (64 bytes) */
+ WLog_VRB(TAG, "\tinputFlags: 0x%04" PRIX16 "", inputFlags);
+ WLog_VRB(TAG, "\tpad2OctetsA: 0x%04" PRIX16 "", pad2OctetsA);
+ WLog_VRB(TAG, "\tkeyboardLayout: 0x%08" PRIX32 "", keyboardLayout);
+ WLog_VRB(TAG, "\tkeyboardType: 0x%08" PRIX32 "", keyboardType);
+ WLog_VRB(TAG, "\tkeyboardSubType: 0x%08" PRIX32 "", keyboardSubType);
+ WLog_VRB(TAG, "\tkeyboardFunctionKey: 0x%08" PRIX32 "", keyboardFunctionKey);
+ return TRUE;
+}
+#endif
+
+static BOOL rdp_apply_font_capability_set(rdpSettings* settings, const rdpSettings* src)
+{
+ WINPR_ASSERT(settings);
+ WINPR_ASSERT(src);
+ return TRUE;
+}
+
+/*
+ * Read font capability set.
+ * msdn{cc240571}
+ */
+
+static BOOL rdp_read_font_capability_set(wStream* s, rdpSettings* settings)
+{
+ WINPR_UNUSED(settings);
+ if (Stream_GetRemainingLength(s) >= 2)
+ Stream_Seek_UINT16(s); /* fontSupportFlags (2 bytes) */
+
+ if (Stream_GetRemainingLength(s) >= 2)
+ Stream_Seek_UINT16(s); /* pad2Octets (2 bytes) */
+
+ return TRUE;
+}
+
+/*
+ * Write font capability set.
+ * msdn{cc240571}
+ */
+
+static BOOL rdp_write_font_capability_set(wStream* s, const rdpSettings* settings)
+{
+ WINPR_UNUSED(settings);
+ if (!Stream_EnsureRemainingCapacity(s, 32))
+ return FALSE;
+
+ const size_t header = rdp_capability_set_start(s);
+ Stream_Write_UINT16(s, FONTSUPPORT_FONTLIST); /* fontSupportFlags (2 bytes) */
+ Stream_Write_UINT16(s, 0); /* pad2Octets (2 bytes) */
+ return rdp_capability_set_finish(s, header, CAPSET_TYPE_FONT);
+}
+
+#ifdef WITH_DEBUG_CAPABILITIES
+static BOOL rdp_print_font_capability_set(wStream* s)
+{
+ UINT16 fontSupportFlags = 0;
+ UINT16 pad2Octets = 0;
+ WLog_VRB(TAG, "FontCapabilitySet (length %" PRIuz "):", Stream_GetRemainingLength(s));
+
+ if (Stream_GetRemainingLength(s) >= 2)
+ Stream_Read_UINT16(s, fontSupportFlags); /* fontSupportFlags (2 bytes) */
+
+ if (Stream_GetRemainingLength(s) >= 2)
+ Stream_Read_UINT16(s, pad2Octets); /* pad2Octets (2 bytes) */
+
+ WLog_VRB(TAG, "\tfontSupportFlags: 0x%04" PRIX16 "", fontSupportFlags);
+ WLog_VRB(TAG, "\tpad2Octets: 0x%04" PRIX16 "", pad2Octets);
+ return TRUE;
+}
+#endif
+
+static BOOL rdp_apply_brush_capability_set(rdpSettings* settings, const rdpSettings* src)
+{
+ WINPR_ASSERT(settings);
+ WINPR_ASSERT(src);
+
+ // TODO: Minimum of what?
+ settings->BrushSupportLevel = src->BrushSupportLevel;
+ return TRUE;
+}
+
+/*
+ * Read brush capability set.
+ * msdn{cc240564}
+ */
+
+static BOOL rdp_read_brush_capability_set(wStream* s, rdpSettings* settings)
+{
+ WINPR_UNUSED(settings);
+ WINPR_ASSERT(settings);
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return FALSE;
+ Stream_Read_UINT32(s, settings->BrushSupportLevel); /* brushSupportLevel (4 bytes) */
+ return TRUE;
+}
+
+/*
+ * Write brush capability set.
+ * msdn{cc240564}
+ */
+
+static BOOL rdp_write_brush_capability_set(wStream* s, const rdpSettings* settings)
+{
+ WINPR_ASSERT(settings);
+ if (!Stream_EnsureRemainingCapacity(s, 32))
+ return FALSE;
+
+ const size_t header = rdp_capability_set_start(s);
+ Stream_Write_UINT32(s, settings->BrushSupportLevel); /* brushSupportLevel (4 bytes) */
+ return rdp_capability_set_finish(s, header, CAPSET_TYPE_BRUSH);
+}
+
+#ifdef WITH_DEBUG_CAPABILITIES
+static BOOL rdp_print_brush_capability_set(wStream* s)
+{
+ UINT32 brushSupportLevel = 0;
+ WLog_VRB(TAG, "BrushCapabilitySet (length %" PRIuz "):", Stream_GetRemainingLength(s));
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return FALSE;
+
+ Stream_Read_UINT32(s, brushSupportLevel); /* brushSupportLevel (4 bytes) */
+ WLog_VRB(TAG, "\tbrushSupportLevel: 0x%08" PRIX32 "", brushSupportLevel);
+ return TRUE;
+}
+#endif
+
+/*
+ * Read cache definition (glyph).
+ * msdn{cc240566}
+ */
+static void rdp_read_cache_definition(wStream* s, GLYPH_CACHE_DEFINITION* cache_definition)
+{
+ WINPR_ASSERT(cache_definition);
+ Stream_Read_UINT16(s, cache_definition->cacheEntries); /* cacheEntries (2 bytes) */
+ Stream_Read_UINT16(s,
+ cache_definition->cacheMaximumCellSize); /* cacheMaximumCellSize (2 bytes) */
+}
+
+/*
+ * Write cache definition (glyph).
+ * msdn{cc240566}
+ */
+static void rdp_write_cache_definition(wStream* s, GLYPH_CACHE_DEFINITION* cache_definition)
+{
+ WINPR_ASSERT(cache_definition);
+ Stream_Write_UINT16(s, cache_definition->cacheEntries); /* cacheEntries (2 bytes) */
+ Stream_Write_UINT16(
+ s, cache_definition->cacheMaximumCellSize); /* cacheMaximumCellSize (2 bytes) */
+}
+
+static BOOL rdp_apply_glyph_cache_capability_set(rdpSettings* settings, const rdpSettings* src)
+{
+ WINPR_ASSERT(settings);
+ WINPR_ASSERT(src);
+
+ WINPR_ASSERT(src->GlyphCache);
+ WINPR_ASSERT(settings->GlyphCache);
+ for (size_t x = 0; x < 10; x++)
+ settings->GlyphCache[x] = src->GlyphCache[x];
+
+ WINPR_ASSERT(src->FragCache);
+ WINPR_ASSERT(settings->FragCache);
+ settings->FragCache[0] = src->FragCache[0];
+ settings->GlyphSupportLevel = src->GlyphSupportLevel;
+
+ return TRUE;
+}
+
+/*
+ * Read glyph cache capability set.
+ * msdn{cc240565}
+ */
+
+static BOOL rdp_read_glyph_cache_capability_set(wStream* s, rdpSettings* settings)
+{
+ WINPR_ASSERT(settings);
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 48))
+ return FALSE;
+
+ /* glyphCache (40 bytes) */
+ for (size_t x = 0; x < 10; x++)
+ rdp_read_cache_definition(s, &(settings->GlyphCache[x])); /* glyphCache0 (4 bytes) */
+ rdp_read_cache_definition(s, settings->FragCache); /* fragCache (4 bytes) */
+ Stream_Read_UINT16(s, settings->GlyphSupportLevel); /* glyphSupportLevel (2 bytes) */
+ Stream_Seek_UINT16(s); /* pad2Octets (2 bytes) */
+ return TRUE;
+}
+
+/*
+ * Write glyph cache capability set.
+ * msdn{cc240565}
+ */
+
+static BOOL rdp_write_glyph_cache_capability_set(wStream* s, const rdpSettings* settings)
+{
+ WINPR_ASSERT(settings);
+ if (!Stream_EnsureRemainingCapacity(s, 64))
+ return FALSE;
+
+ const size_t header = rdp_capability_set_start(s);
+ if (settings->GlyphSupportLevel > UINT16_MAX)
+ return FALSE;
+ /* glyphCache (40 bytes) */
+ for (size_t x = 0; x < 10; x++)
+ rdp_write_cache_definition(s, &(settings->GlyphCache[x])); /* glyphCache0 (4 bytes) */
+ rdp_write_cache_definition(s, settings->FragCache); /* fragCache (4 bytes) */
+ Stream_Write_UINT16(s, (UINT16)settings->GlyphSupportLevel); /* glyphSupportLevel (2 bytes) */
+ Stream_Write_UINT16(s, 0); /* pad2Octets (2 bytes) */
+ return rdp_capability_set_finish(s, header, CAPSET_TYPE_GLYPH_CACHE);
+}
+
+#ifdef WITH_DEBUG_CAPABILITIES
+static BOOL rdp_print_glyph_cache_capability_set(wStream* s)
+{
+ GLYPH_CACHE_DEFINITION glyphCache[10] = { 0 };
+ GLYPH_CACHE_DEFINITION fragCache = { 0 };
+ UINT16 glyphSupportLevel = 0;
+ UINT16 pad2Octets = 0;
+ WLog_VRB(TAG, "GlyphCacheCapabilitySet (length %" PRIuz "):", Stream_GetRemainingLength(s));
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 48))
+ return FALSE;
+
+ /* glyphCache (40 bytes) */
+ rdp_read_cache_definition(s, &glyphCache[0]); /* glyphCache0 (4 bytes) */
+ rdp_read_cache_definition(s, &glyphCache[1]); /* glyphCache1 (4 bytes) */
+ rdp_read_cache_definition(s, &glyphCache[2]); /* glyphCache2 (4 bytes) */
+ rdp_read_cache_definition(s, &glyphCache[3]); /* glyphCache3 (4 bytes) */
+ rdp_read_cache_definition(s, &glyphCache[4]); /* glyphCache4 (4 bytes) */
+ rdp_read_cache_definition(s, &glyphCache[5]); /* glyphCache5 (4 bytes) */
+ rdp_read_cache_definition(s, &glyphCache[6]); /* glyphCache6 (4 bytes) */
+ rdp_read_cache_definition(s, &glyphCache[7]); /* glyphCache7 (4 bytes) */
+ rdp_read_cache_definition(s, &glyphCache[8]); /* glyphCache8 (4 bytes) */
+ rdp_read_cache_definition(s, &glyphCache[9]); /* glyphCache9 (4 bytes) */
+ rdp_read_cache_definition(s, &fragCache); /* fragCache (4 bytes) */
+ Stream_Read_UINT16(s, glyphSupportLevel); /* glyphSupportLevel (2 bytes) */
+ Stream_Read_UINT16(s, pad2Octets); /* pad2Octets (2 bytes) */
+ WLog_VRB(TAG, "\tglyphCache0: Entries: %" PRIu16 " MaximumCellSize: %" PRIu16 "",
+ glyphCache[0].cacheEntries, glyphCache[0].cacheMaximumCellSize);
+ WLog_VRB(TAG, "\tglyphCache1: Entries: %" PRIu16 " MaximumCellSize: %" PRIu16 "",
+ glyphCache[1].cacheEntries, glyphCache[1].cacheMaximumCellSize);
+ WLog_VRB(TAG, "\tglyphCache2: Entries: %" PRIu16 " MaximumCellSize: %" PRIu16 "",
+ glyphCache[2].cacheEntries, glyphCache[2].cacheMaximumCellSize);
+ WLog_VRB(TAG, "\tglyphCache3: Entries: %" PRIu16 " MaximumCellSize: %" PRIu16 "",
+ glyphCache[3].cacheEntries, glyphCache[3].cacheMaximumCellSize);
+ WLog_VRB(TAG, "\tglyphCache4: Entries: %" PRIu16 " MaximumCellSize: %" PRIu16 "",
+ glyphCache[4].cacheEntries, glyphCache[4].cacheMaximumCellSize);
+ WLog_VRB(TAG, "\tglyphCache5: Entries: %" PRIu16 " MaximumCellSize: %" PRIu16 "",
+ glyphCache[5].cacheEntries, glyphCache[5].cacheMaximumCellSize);
+ WLog_VRB(TAG, "\tglyphCache6: Entries: %" PRIu16 " MaximumCellSize: %" PRIu16 "",
+ glyphCache[6].cacheEntries, glyphCache[6].cacheMaximumCellSize);
+ WLog_VRB(TAG, "\tglyphCache7: Entries: %" PRIu16 " MaximumCellSize: %" PRIu16 "",
+ glyphCache[7].cacheEntries, glyphCache[7].cacheMaximumCellSize);
+ WLog_VRB(TAG, "\tglyphCache8: Entries: %" PRIu16 " MaximumCellSize: %" PRIu16 "",
+ glyphCache[8].cacheEntries, glyphCache[8].cacheMaximumCellSize);
+ WLog_VRB(TAG, "\tglyphCache9: Entries: %" PRIu16 " MaximumCellSize: %" PRIu16 "",
+ glyphCache[9].cacheEntries, glyphCache[9].cacheMaximumCellSize);
+ WLog_VRB(TAG, "\tfragCache: Entries: %" PRIu16 " MaximumCellSize: %" PRIu16 "",
+ fragCache.cacheEntries, fragCache.cacheMaximumCellSize);
+ WLog_VRB(TAG, "\tglyphSupportLevel: 0x%04" PRIX16 "", glyphSupportLevel);
+ WLog_VRB(TAG, "\tpad2Octets: 0x%04" PRIX16 "", pad2Octets);
+ return TRUE;
+}
+#endif
+
+static BOOL rdp_apply_offscreen_bitmap_cache_capability_set(rdpSettings* settings,
+ const rdpSettings* src)
+{
+ WINPR_ASSERT(settings);
+ WINPR_ASSERT(src);
+
+ settings->OffscreenCacheSize = src->OffscreenCacheSize;
+ settings->OffscreenCacheEntries = src->OffscreenCacheEntries;
+ settings->OffscreenSupportLevel = src->OffscreenSupportLevel;
+
+ return TRUE;
+}
+
+/*
+ * Read offscreen bitmap cache capability set.
+ * msdn{cc240550}
+ */
+
+static BOOL rdp_read_offscreen_bitmap_cache_capability_set(wStream* s, rdpSettings* settings)
+{
+ UINT32 offscreenSupportLevel = 0;
+
+ WINPR_ASSERT(settings);
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
+ return FALSE;
+
+ Stream_Read_UINT32(s, offscreenSupportLevel); /* offscreenSupportLevel (4 bytes) */
+ Stream_Read_UINT16(s, settings->OffscreenCacheSize); /* offscreenCacheSize (2 bytes) */
+ Stream_Read_UINT16(s, settings->OffscreenCacheEntries); /* offscreenCacheEntries (2 bytes) */
+
+ settings->OffscreenSupportLevel = offscreenSupportLevel & 0x01;
+
+ return TRUE;
+}
+
+/*
+ * Write offscreen bitmap cache capability set.
+ * msdn{cc240550}
+ */
+
+static BOOL rdp_write_offscreen_bitmap_cache_capability_set(wStream* s, const rdpSettings* settings)
+{
+ UINT32 offscreenSupportLevel = 0x00;
+
+ WINPR_ASSERT(settings);
+ if (!Stream_EnsureRemainingCapacity(s, 32))
+ return FALSE;
+
+ const size_t header = rdp_capability_set_start(s);
+ if (settings->OffscreenSupportLevel)
+ {
+ offscreenSupportLevel = 0x01;
+ Stream_Write_UINT32(s, offscreenSupportLevel); /* offscreenSupportLevel (4 bytes) */
+ Stream_Write_UINT16(s, settings->OffscreenCacheSize); /* offscreenCacheSize (2 bytes) */
+ Stream_Write_UINT16(s,
+ settings->OffscreenCacheEntries); /* offscreenCacheEntries (2 bytes) */
+ }
+ else
+ Stream_Zero(s, 8);
+
+ return rdp_capability_set_finish(s, header, CAPSET_TYPE_OFFSCREEN_CACHE);
+}
+
+#ifdef WITH_DEBUG_CAPABILITIES
+static BOOL rdp_print_offscreen_bitmap_cache_capability_set(wStream* s)
+{
+ UINT32 offscreenSupportLevel = 0;
+ UINT16 offscreenCacheSize = 0;
+ UINT16 offscreenCacheEntries = 0;
+ WLog_VRB(TAG, "OffscreenBitmapCacheCapabilitySet (length %" PRIuz "):",
+ Stream_GetRemainingLength(s));
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
+ return FALSE;
+
+ Stream_Read_UINT32(s, offscreenSupportLevel); /* offscreenSupportLevel (4 bytes) */
+ Stream_Read_UINT16(s, offscreenCacheSize); /* offscreenCacheSize (2 bytes) */
+ Stream_Read_UINT16(s, offscreenCacheEntries); /* offscreenCacheEntries (2 bytes) */
+ WLog_VRB(TAG, "\toffscreenSupportLevel: 0x%08" PRIX32 "", offscreenSupportLevel);
+ WLog_VRB(TAG, "\toffscreenCacheSize: 0x%04" PRIX16 "", offscreenCacheSize);
+ WLog_VRB(TAG, "\toffscreenCacheEntries: 0x%04" PRIX16 "", offscreenCacheEntries);
+ return TRUE;
+}
+#endif
+
+static BOOL rdp_apply_bitmap_cache_host_support_capability_set(rdpSettings* settings,
+ const rdpSettings* src)
+{
+ const BOOL val = (freerdp_settings_get_bool(src, FreeRDP_BitmapCachePersistEnabled) &&
+ freerdp_settings_get_bool(settings, FreeRDP_BitmapCachePersistEnabled));
+ return freerdp_settings_set_bool(settings, FreeRDP_BitmapCachePersistEnabled, val);
+}
+
+/*
+ * Read bitmap cache host support capability set.
+ * msdn{cc240557}
+ */
+
+static BOOL rdp_read_bitmap_cache_host_support_capability_set(wStream* s, rdpSettings* settings)
+{
+ BYTE cacheVersion = 0;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return FALSE;
+
+ Stream_Read_UINT8(s, cacheVersion); /* cacheVersion (1 byte) */
+ Stream_Seek_UINT8(s); /* pad1 (1 byte) */
+ Stream_Seek_UINT16(s); /* pad2 (2 bytes) */
+
+ return freerdp_settings_set_bool(settings, FreeRDP_BitmapCachePersistEnabled,
+ cacheVersion & BITMAP_CACHE_V2);
+}
+
+/*
+ * Write bitmap cache host support capability set.
+ * msdn{cc240557}
+ */
+
+static BOOL rdp_write_bitmap_cache_host_support_capability_set(wStream* s,
+ const rdpSettings* settings)
+{
+ UINT8 cacheVersion = 0;
+
+ if (freerdp_settings_get_bool(settings, FreeRDP_BitmapCacheEnabled))
+ cacheVersion |= BITMAP_CACHE_V2;
+
+ if (!Stream_EnsureRemainingCapacity(s, 32))
+ return FALSE;
+
+ const size_t header = rdp_capability_set_start(s);
+ Stream_Write_UINT8(s, cacheVersion); /* cacheVersion (1 byte) */
+ Stream_Write_UINT8(s, 0); /* pad1 (1 byte) */
+ Stream_Write_UINT16(s, 0); /* pad2 (2 bytes) */
+ return rdp_capability_set_finish(s, header, CAPSET_TYPE_BITMAP_CACHE_HOST_SUPPORT);
+}
+
+#ifdef WITH_DEBUG_CAPABILITIES
+static BOOL rdp_print_bitmap_cache_host_support_capability_set(wStream* s)
+{
+ BYTE cacheVersion = 0;
+ BYTE pad1 = 0;
+ UINT16 pad2 = 0;
+ WLog_VRB(TAG, "BitmapCacheHostSupportCapabilitySet (length %" PRIuz "):",
+ Stream_GetRemainingLength(s));
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return FALSE;
+
+ Stream_Read_UINT8(s, cacheVersion); /* cacheVersion (1 byte) */
+ Stream_Read_UINT8(s, pad1); /* pad1 (1 byte) */
+ Stream_Read_UINT16(s, pad2); /* pad2 (2 bytes) */
+ WLog_VRB(TAG, "\tcacheVersion: 0x%02" PRIX8 "", cacheVersion);
+ WLog_VRB(TAG, "\tpad1: 0x%02" PRIX8 "", pad1);
+ WLog_VRB(TAG, "\tpad2: 0x%04" PRIX16 "", pad2);
+ return TRUE;
+}
+#endif
+
+static BOOL rdp_read_bitmap_cache_cell_info(wStream* s, BITMAP_CACHE_V2_CELL_INFO* cellInfo)
+{
+ UINT32 info = 0;
+
+ WINPR_ASSERT(cellInfo);
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return FALSE;
+
+ /*
+ * numEntries is in the first 31 bits, while the last bit (k)
+ * is used to indicate a persistent bitmap cache.
+ */
+ Stream_Read_UINT32(s, info);
+ cellInfo->numEntries = (info & 0x7FFFFFFF);
+ cellInfo->persistent = (info & 0x80000000) ? 1 : 0;
+ return TRUE;
+}
+
+static void rdp_write_bitmap_cache_cell_info(wStream* s, BITMAP_CACHE_V2_CELL_INFO* cellInfo)
+{
+ UINT32 info = 0;
+ /*
+ * numEntries is in the first 31 bits, while the last bit (k)
+ * is used to indicate a persistent bitmap cache.
+ */
+ WINPR_ASSERT(cellInfo);
+ info = (cellInfo->numEntries | (cellInfo->persistent << 31));
+ Stream_Write_UINT32(s, info);
+}
+
+static BOOL rdp_apply_bitmap_cache_v2_capability_set(rdpSettings* settings, const rdpSettings* src)
+{
+ const FreeRDP_Settings_Keys_Bool keys[] = { FreeRDP_BitmapCacheEnabled,
+ FreeRDP_BitmapCachePersistEnabled };
+
+ for (size_t x = 0; x < ARRAYSIZE(keys); x++)
+ {
+ const FreeRDP_Settings_Keys_Bool id = keys[x];
+ const BOOL val = freerdp_settings_get_bool(src, id);
+ if (!freerdp_settings_set_bool(settings, id, val))
+ return FALSE;
+ }
+
+ {
+ const UINT32 BitmapCacheV2NumCells =
+ freerdp_settings_get_uint32(src, FreeRDP_BitmapCacheV2NumCells);
+ if (!freerdp_settings_set_uint32(settings, FreeRDP_BitmapCacheV2NumCells,
+ BitmapCacheV2NumCells))
+ return FALSE;
+
+ for (size_t x = 0; x < BitmapCacheV2NumCells; x++)
+ {
+ const BITMAP_CACHE_V2_CELL_INFO* cdata =
+ freerdp_settings_get_pointer_array(src, FreeRDP_BitmapCacheV2CellInfo, x);
+ if (!freerdp_settings_set_pointer_array(settings, FreeRDP_BitmapCacheV2CellInfo, x,
+ cdata))
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/*
+ * Read bitmap cache v2 capability set.
+ * msdn{cc240560}
+ */
+
+static BOOL rdp_read_bitmap_cache_v2_capability_set(wStream* s, rdpSettings* settings)
+{
+ UINT16 cacheFlags = 0;
+ WINPR_UNUSED(settings);
+ WINPR_ASSERT(settings);
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 36))
+ return FALSE;
+
+ Stream_Read_UINT16(s, cacheFlags); /* cacheFlags (2 bytes) */
+
+ if (!freerdp_settings_set_bool(settings, FreeRDP_BitmapCacheEnabled, TRUE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_BitmapCachePersistEnabled,
+ cacheFlags & PERSISTENT_KEYS_EXPECTED_FLAG))
+ return FALSE;
+
+ Stream_Seek_UINT8(s); /* pad2 (1 byte) */
+ Stream_Read_UINT8(s, settings->BitmapCacheV2NumCells); /* numCellCaches (1 byte) */
+ for (size_t x = 0; x < 5; x++)
+ {
+ BITMAP_CACHE_V2_CELL_INFO* info =
+ freerdp_settings_get_pointer_array_writable(settings, FreeRDP_BitmapCacheV2CellInfo, x);
+ if (!rdp_read_bitmap_cache_cell_info(s, info))
+ return FALSE;
+ }
+ Stream_Seek(s, 12); /* pad3 (12 bytes) */
+ return TRUE;
+}
+
+/*
+ * Write bitmap cache v2 capability set.
+ * msdn{cc240560}
+ */
+
+static BOOL rdp_write_bitmap_cache_v2_capability_set(wStream* s, const rdpSettings* settings)
+{
+ WINPR_ASSERT(settings);
+ if (!Stream_EnsureRemainingCapacity(s, 64))
+ return FALSE;
+
+ const size_t header = rdp_capability_set_start(s);
+ UINT16 cacheFlags = ALLOW_CACHE_WAITING_LIST_FLAG;
+
+ if (freerdp_settings_get_bool(settings, FreeRDP_BitmapCachePersistEnabled))
+ {
+ cacheFlags |= PERSISTENT_KEYS_EXPECTED_FLAG;
+ settings->BitmapCacheV2CellInfo[0].persistent = 1;
+ settings->BitmapCacheV2CellInfo[1].persistent = 1;
+ settings->BitmapCacheV2CellInfo[2].persistent = 1;
+ settings->BitmapCacheV2CellInfo[3].persistent = 1;
+ settings->BitmapCacheV2CellInfo[4].persistent = 1;
+ }
+
+ Stream_Write_UINT16(s, cacheFlags); /* cacheFlags (2 bytes) */
+ Stream_Write_UINT8(s, 0); /* pad2 (1 byte) */
+ Stream_Write_UINT8(s, settings->BitmapCacheV2NumCells); /* numCellCaches (1 byte) */
+ rdp_write_bitmap_cache_cell_info(
+ s, &settings->BitmapCacheV2CellInfo[0]); /* bitmapCache0CellInfo (4 bytes) */
+ rdp_write_bitmap_cache_cell_info(
+ s, &settings->BitmapCacheV2CellInfo[1]); /* bitmapCache1CellInfo (4 bytes) */
+ rdp_write_bitmap_cache_cell_info(
+ s, &settings->BitmapCacheV2CellInfo[2]); /* bitmapCache2CellInfo (4 bytes) */
+ rdp_write_bitmap_cache_cell_info(
+ s, &settings->BitmapCacheV2CellInfo[3]); /* bitmapCache3CellInfo (4 bytes) */
+ rdp_write_bitmap_cache_cell_info(
+ s, &settings->BitmapCacheV2CellInfo[4]); /* bitmapCache4CellInfo (4 bytes) */
+ Stream_Zero(s, 12); /* pad3 (12 bytes) */
+ return rdp_capability_set_finish(s, header, CAPSET_TYPE_BITMAP_CACHE_V2);
+}
+
+#ifdef WITH_DEBUG_CAPABILITIES
+static BOOL rdp_print_bitmap_cache_v2_capability_set(wStream* s)
+{
+ UINT16 cacheFlags = 0;
+ BYTE pad2 = 0;
+ BYTE numCellCaches = 0;
+ BITMAP_CACHE_V2_CELL_INFO bitmapCacheV2CellInfo[5];
+ WLog_VRB(TAG, "BitmapCacheV2CapabilitySet (length %" PRIuz "):", Stream_GetRemainingLength(s));
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 36))
+ return FALSE;
+
+ Stream_Read_UINT16(s, cacheFlags); /* cacheFlags (2 bytes) */
+ Stream_Read_UINT8(s, pad2); /* pad2 (1 byte) */
+ Stream_Read_UINT8(s, numCellCaches); /* numCellCaches (1 byte) */
+ rdp_read_bitmap_cache_cell_info(s,
+ &bitmapCacheV2CellInfo[0]); /* bitmapCache0CellInfo (4 bytes) */
+ rdp_read_bitmap_cache_cell_info(s,
+ &bitmapCacheV2CellInfo[1]); /* bitmapCache1CellInfo (4 bytes) */
+ rdp_read_bitmap_cache_cell_info(s,
+ &bitmapCacheV2CellInfo[2]); /* bitmapCache2CellInfo (4 bytes) */
+ rdp_read_bitmap_cache_cell_info(s,
+ &bitmapCacheV2CellInfo[3]); /* bitmapCache3CellInfo (4 bytes) */
+ rdp_read_bitmap_cache_cell_info(s,
+ &bitmapCacheV2CellInfo[4]); /* bitmapCache4CellInfo (4 bytes) */
+ Stream_Seek(s, 12); /* pad3 (12 bytes) */
+ WLog_VRB(TAG, "\tcacheFlags: 0x%04" PRIX16 "", cacheFlags);
+ WLog_VRB(TAG, "\tpad2: 0x%02" PRIX8 "", pad2);
+ WLog_VRB(TAG, "\tnumCellCaches: 0x%02" PRIX8 "", numCellCaches);
+ WLog_VRB(TAG, "\tbitmapCache0CellInfo: numEntries: %" PRIu32 " persistent: %" PRId32 "",
+ bitmapCacheV2CellInfo[0].numEntries, bitmapCacheV2CellInfo[0].persistent);
+ WLog_VRB(TAG, "\tbitmapCache1CellInfo: numEntries: %" PRIu32 " persistent: %" PRId32 "",
+ bitmapCacheV2CellInfo[1].numEntries, bitmapCacheV2CellInfo[1].persistent);
+ WLog_VRB(TAG, "\tbitmapCache2CellInfo: numEntries: %" PRIu32 " persistent: %" PRId32 "",
+ bitmapCacheV2CellInfo[2].numEntries, bitmapCacheV2CellInfo[2].persistent);
+ WLog_VRB(TAG, "\tbitmapCache3CellInfo: numEntries: %" PRIu32 " persistent: %" PRId32 "",
+ bitmapCacheV2CellInfo[3].numEntries, bitmapCacheV2CellInfo[3].persistent);
+ WLog_VRB(TAG, "\tbitmapCache4CellInfo: numEntries: %" PRIu32 " persistent: %" PRId32 "",
+ bitmapCacheV2CellInfo[4].numEntries, bitmapCacheV2CellInfo[4].persistent);
+ return TRUE;
+}
+#endif
+
+static BOOL rdp_apply_virtual_channel_capability_set(rdpSettings* settings, const rdpSettings* src)
+{
+ WINPR_ASSERT(settings);
+ WINPR_ASSERT(src);
+
+ /* MS servers and clients disregard in advertising what is relevant for their own side */
+ if (settings->ServerMode && (settings->VCFlags & VCCAPS_COMPR_SC) &&
+ (src->VCFlags & VCCAPS_COMPR_SC))
+ settings->VCFlags |= VCCAPS_COMPR_SC;
+ else
+ settings->VCFlags &= ~VCCAPS_COMPR_SC;
+
+ if (!settings->ServerMode && (settings->VCFlags & VCCAPS_COMPR_CS_8K) &&
+ (src->VCFlags & VCCAPS_COMPR_CS_8K))
+ settings->VCFlags |= VCCAPS_COMPR_CS_8K;
+ else
+ settings->VCFlags &= ~VCCAPS_COMPR_CS_8K;
+
+ /*
+ * When one peer does not write the VCChunkSize, the VCChunkSize must not be
+ * larger than CHANNEL_CHUNK_LENGTH (1600) bytes.
+ * Also prevent an invalid 0 size.
+ */
+ if (!settings->ServerMode)
+ {
+ if ((src->VCChunkSize > CHANNEL_CHUNK_MAX_LENGTH) || (src->VCChunkSize == 0))
+ settings->VCChunkSize = CHANNEL_CHUNK_LENGTH;
+ else
+ {
+ settings->VCChunkSize = src->VCChunkSize;
+ }
+ }
+
+ return TRUE;
+}
+
+/*
+ * Read virtual channel capability set.
+ * msdn{cc240551}
+ */
+
+static BOOL rdp_read_virtual_channel_capability_set(wStream* s, rdpSettings* settings)
+{
+ UINT32 flags = 0;
+ UINT32 VCChunkSize = 0;
+
+ WINPR_ASSERT(settings);
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return FALSE;
+
+ Stream_Read_UINT32(s, flags); /* flags (4 bytes) */
+
+ if (Stream_GetRemainingLength(s) >= 4)
+ Stream_Read_UINT32(s, VCChunkSize); /* VCChunkSize (4 bytes) */
+ else
+ VCChunkSize = UINT32_MAX; /* Use an invalid value to determine that value is not present */
+
+ settings->VCFlags = flags;
+ settings->VCChunkSize = VCChunkSize;
+
+ return TRUE;
+}
+
+/*
+ * Write virtual channel capability set.
+ * msdn{cc240551}
+ */
+
+static BOOL rdp_write_virtual_channel_capability_set(wStream* s, const rdpSettings* settings)
+{
+ WINPR_ASSERT(settings);
+ if (!Stream_EnsureRemainingCapacity(s, 32))
+ return FALSE;
+
+ const size_t header = rdp_capability_set_start(s);
+ Stream_Write_UINT32(s, settings->VCFlags); /* flags (4 bytes) */
+ Stream_Write_UINT32(s, settings->VCChunkSize); /* VCChunkSize (4 bytes) */
+ return rdp_capability_set_finish(s, header, CAPSET_TYPE_VIRTUAL_CHANNEL);
+}
+
+#ifdef WITH_DEBUG_CAPABILITIES
+static BOOL rdp_print_virtual_channel_capability_set(wStream* s)
+{
+ UINT32 flags = 0;
+ UINT32 VCChunkSize = 0;
+ WLog_VRB(TAG, "VirtualChannelCapabilitySet (length %" PRIuz "):", Stream_GetRemainingLength(s));
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return FALSE;
+
+ Stream_Read_UINT32(s, flags); /* flags (4 bytes) */
+
+ if (Stream_GetRemainingLength(s) >= 4)
+ Stream_Read_UINT32(s, VCChunkSize); /* VCChunkSize (4 bytes) */
+ else
+ VCChunkSize = 1600;
+
+ WLog_VRB(TAG, "\tflags: 0x%08" PRIX32 "", flags);
+ WLog_VRB(TAG, "\tVCChunkSize: 0x%08" PRIX32 "", VCChunkSize);
+ return TRUE;
+}
+#endif
+
+static BOOL rdp_apply_draw_nine_grid_cache_capability_set(rdpSettings* settings,
+ const rdpSettings* src)
+{
+ WINPR_ASSERT(settings);
+ WINPR_ASSERT(src);
+
+ settings->DrawNineGridCacheSize = src->DrawNineGridCacheSize;
+ settings->DrawNineGridCacheEntries = src->DrawNineGridCacheEntries;
+ settings->DrawNineGridEnabled = src->DrawNineGridEnabled;
+
+ return TRUE;
+}
+
+/*
+ * Read drawn nine grid cache capability set.
+ * msdn{cc241565}
+ */
+
+static BOOL rdp_read_draw_nine_grid_cache_capability_set(wStream* s, rdpSettings* settings)
+{
+ UINT32 drawNineGridSupportLevel = 0;
+
+ WINPR_ASSERT(settings);
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
+ return FALSE;
+
+ Stream_Read_UINT32(s, drawNineGridSupportLevel); /* drawNineGridSupportLevel (4 bytes) */
+ Stream_Read_UINT16(s, settings->DrawNineGridCacheSize); /* drawNineGridCacheSize (2 bytes) */
+ Stream_Read_UINT16(s,
+ settings->DrawNineGridCacheEntries); /* drawNineGridCacheEntries (2 bytes) */
+
+ settings->DrawNineGridEnabled =
+ (drawNineGridSupportLevel & (DRAW_NINEGRID_SUPPORTED | DRAW_NINEGRID_SUPPORTED_V2)) ? TRUE
+ : FALSE;
+
+ return TRUE;
+}
+
+/*
+ * Write drawn nine grid cache capability set.
+ * msdn{cc241565}
+ */
+
+static BOOL rdp_write_draw_nine_grid_cache_capability_set(wStream* s, const rdpSettings* settings)
+{
+ WINPR_ASSERT(settings);
+ if (!Stream_EnsureRemainingCapacity(s, 32))
+ return FALSE;
+
+ const size_t header = rdp_capability_set_start(s);
+ const UINT32 drawNineGridSupportLevel =
+ (settings->DrawNineGridEnabled) ? DRAW_NINEGRID_SUPPORTED_V2 : DRAW_NINEGRID_NO_SUPPORT;
+ Stream_Write_UINT32(s, drawNineGridSupportLevel); /* drawNineGridSupportLevel (4 bytes) */
+ Stream_Write_UINT16(s, settings->DrawNineGridCacheSize); /* drawNineGridCacheSize (2 bytes) */
+ Stream_Write_UINT16(
+ s, settings->DrawNineGridCacheEntries); /* drawNineGridCacheEntries (2 bytes) */
+ return rdp_capability_set_finish(s, header, CAPSET_TYPE_DRAW_NINE_GRID_CACHE);
+}
+
+static void rdp_write_gdiplus_cache_entries(wStream* s, UINT16 gce, UINT16 bce, UINT16 pce,
+ UINT16 ice, UINT16 ace)
+{
+ Stream_Write_UINT16(s, gce); /* gdipGraphicsCacheEntries (2 bytes) */
+ Stream_Write_UINT16(s, bce); /* gdipBrushCacheEntries (2 bytes) */
+ Stream_Write_UINT16(s, pce); /* gdipPenCacheEntries (2 bytes) */
+ Stream_Write_UINT16(s, ice); /* gdipImageCacheEntries (2 bytes) */
+ Stream_Write_UINT16(s, ace); /* gdipImageAttributesCacheEntries (2 bytes) */
+}
+
+static void rdp_write_gdiplus_cache_chunk_size(wStream* s, UINT16 gccs, UINT16 obccs, UINT16 opccs,
+ UINT16 oiaccs)
+{
+ Stream_Write_UINT16(s, gccs); /* gdipGraphicsCacheChunkSize (2 bytes) */
+ Stream_Write_UINT16(s, obccs); /* gdipObjectBrushCacheChunkSize (2 bytes) */
+ Stream_Write_UINT16(s, opccs); /* gdipObjectPenCacheChunkSize (2 bytes) */
+ Stream_Write_UINT16(s, oiaccs); /* gdipObjectImageAttributesCacheChunkSize (2 bytes) */
+}
+
+static void rdp_write_gdiplus_image_cache_properties(wStream* s, UINT16 oiccs, UINT16 oicts,
+ UINT16 oicms)
+{
+ Stream_Write_UINT16(s, oiccs); /* gdipObjectImageCacheChunkSize (2 bytes) */
+ Stream_Write_UINT16(s, oicts); /* gdipObjectImageCacheTotalSize (2 bytes) */
+ Stream_Write_UINT16(s, oicms); /* gdipObjectImageCacheMaxSize (2 bytes) */
+}
+
+#ifdef WITH_DEBUG_CAPABILITIES
+static BOOL rdp_print_draw_nine_grid_cache_capability_set(wStream* s)
+{
+ UINT32 drawNineGridSupportLevel = 0;
+ UINT16 DrawNineGridCacheSize = 0;
+ UINT16 DrawNineGridCacheEntries = 0;
+ WLog_VRB(TAG,
+ "DrawNineGridCacheCapabilitySet (length %" PRIuz "):", Stream_GetRemainingLength(s));
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
+ return FALSE;
+
+ Stream_Read_UINT32(s, drawNineGridSupportLevel); /* drawNineGridSupportLevel (4 bytes) */
+ Stream_Read_UINT16(s, DrawNineGridCacheSize); /* drawNineGridCacheSize (2 bytes) */
+ Stream_Read_UINT16(s, DrawNineGridCacheEntries); /* drawNineGridCacheEntries (2 bytes) */
+ return TRUE;
+}
+#endif
+
+static BOOL rdp_apply_draw_gdiplus_cache_capability_set(rdpSettings* settings,
+ const rdpSettings* src)
+{
+ WINPR_ASSERT(settings);
+ WINPR_ASSERT(src);
+
+ if (src->DrawGdiPlusEnabled)
+ settings->DrawGdiPlusEnabled = TRUE;
+
+ if (src->DrawGdiPlusCacheEnabled)
+ settings->DrawGdiPlusCacheEnabled = TRUE;
+
+ return TRUE;
+}
+
+/*
+ * Read GDI+ cache capability set.
+ * msdn{cc241566}
+ */
+
+static BOOL rdp_read_draw_gdiplus_cache_capability_set(wStream* s, rdpSettings* settings)
+{
+ UINT32 drawGDIPlusSupportLevel = 0;
+ UINT32 drawGdiplusCacheLevel = 0;
+
+ WINPR_ASSERT(settings);
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 36))
+ return FALSE;
+
+ Stream_Read_UINT32(s, drawGDIPlusSupportLevel); /* drawGDIPlusSupportLevel (4 bytes) */
+ Stream_Seek_UINT32(s); /* GdipVersion (4 bytes) */
+ Stream_Read_UINT32(s, drawGdiplusCacheLevel); /* drawGdiplusCacheLevel (4 bytes) */
+ Stream_Seek(s, 10); /* GdipCacheEntries (10 bytes) */
+ Stream_Seek(s, 8); /* GdipCacheChunkSize (8 bytes) */
+ Stream_Seek(s, 6); /* GdipImageCacheProperties (6 bytes) */
+
+ settings->DrawGdiPlusEnabled =
+ (drawGDIPlusSupportLevel & DRAW_GDIPLUS_SUPPORTED) ? TRUE : FALSE;
+ settings->DrawGdiPlusCacheEnabled =
+ (drawGdiplusCacheLevel & DRAW_GDIPLUS_CACHE_LEVEL_ONE) ? TRUE : FALSE;
+
+ return TRUE;
+}
+
+/*
+ * Write GDI+ cache capability set.
+ * msdn{cc241566}
+ */
+
+static BOOL rdp_write_draw_gdiplus_cache_capability_set(wStream* s, const rdpSettings* settings)
+{
+ WINPR_ASSERT(settings);
+ if (!Stream_EnsureRemainingCapacity(s, 64))
+ return FALSE;
+
+ const size_t header = rdp_capability_set_start(s);
+ const UINT32 drawGDIPlusSupportLevel =
+ (settings->DrawGdiPlusEnabled) ? DRAW_GDIPLUS_SUPPORTED : DRAW_GDIPLUS_DEFAULT;
+ const UINT32 drawGdiplusCacheLevel = (settings->DrawGdiPlusEnabled)
+ ? DRAW_GDIPLUS_CACHE_LEVEL_ONE
+ : DRAW_GDIPLUS_CACHE_LEVEL_DEFAULT;
+ Stream_Write_UINT32(s, drawGDIPlusSupportLevel); /* drawGDIPlusSupportLevel (4 bytes) */
+ Stream_Write_UINT32(s, 0); /* GdipVersion (4 bytes) */
+ Stream_Write_UINT32(s, drawGdiplusCacheLevel); /* drawGdiplusCacheLevel (4 bytes) */
+ rdp_write_gdiplus_cache_entries(s, 10, 5, 5, 10, 2); /* GdipCacheEntries (10 bytes) */
+ rdp_write_gdiplus_cache_chunk_size(s, 512, 2048, 1024, 64); /* GdipCacheChunkSize (8 bytes) */
+ rdp_write_gdiplus_image_cache_properties(s, 4096, 256,
+ 128); /* GdipImageCacheProperties (6 bytes) */
+ return rdp_capability_set_finish(s, header, CAPSET_TYPE_DRAW_GDI_PLUS);
+}
+
+#ifdef WITH_DEBUG_CAPABILITIES
+static BOOL rdp_print_draw_gdiplus_cache_capability_set(wStream* s)
+{
+ UINT32 drawGdiPlusSupportLevel = 0;
+ UINT32 GdipVersion = 0;
+ UINT32 drawGdiplusCacheLevel = 0;
+ WLog_VRB(TAG,
+ "DrawGdiPlusCacheCapabilitySet (length %" PRIuz "):", Stream_GetRemainingLength(s));
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 36))
+ return FALSE;
+
+ Stream_Read_UINT32(s, drawGdiPlusSupportLevel); /* drawGdiPlusSupportLevel (4 bytes) */
+ Stream_Read_UINT32(s, GdipVersion); /* GdipVersion (4 bytes) */
+ Stream_Read_UINT32(s, drawGdiplusCacheLevel); /* drawGdiPlusCacheLevel (4 bytes) */
+ Stream_Seek(s, 10); /* GdipCacheEntries (10 bytes) */
+ Stream_Seek(s, 8); /* GdipCacheChunkSize (8 bytes) */
+ Stream_Seek(s, 6); /* GdipImageCacheProperties (6 bytes) */
+ return TRUE;
+}
+#endif
+
+static BOOL rdp_apply_remote_programs_capability_set(rdpSettings* settings, const rdpSettings* src)
+{
+ WINPR_ASSERT(settings);
+ WINPR_ASSERT(src);
+
+ if (settings->RemoteApplicationMode)
+ settings->RemoteApplicationMode = src->RemoteApplicationMode;
+
+ /* 2.2.2.2.3 HandshakeEx PDU (TS_RAIL_ORDER_HANDSHAKE_EX)
+ * the handshake ex pdu is supported when both, client and server announce
+ * it OR if we are ready to begin enhanced remoteAPP mode. */
+ UINT32 supportLevel = src->RemoteApplicationSupportLevel;
+ if (settings->RemoteApplicationMode)
+ supportLevel |= RAIL_LEVEL_HANDSHAKE_EX_SUPPORTED;
+
+ settings->RemoteApplicationSupportLevel = supportLevel & settings->RemoteApplicationSupportMask;
+
+ return TRUE;
+}
+
+/*
+ * Read remote programs capability set.
+ * msdn{cc242518}
+ */
+
+static BOOL rdp_read_remote_programs_capability_set(wStream* s, rdpSettings* settings)
+{
+ UINT32 railSupportLevel = 0;
+
+ WINPR_ASSERT(settings);
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return FALSE;
+
+ Stream_Read_UINT32(s, railSupportLevel); /* railSupportLevel (4 bytes) */
+
+ settings->RemoteApplicationMode = (railSupportLevel & RAIL_LEVEL_SUPPORTED) ? TRUE : FALSE;
+ settings->RemoteApplicationSupportLevel = railSupportLevel;
+ return TRUE;
+}
+
+/*
+ * Write remote programs capability set.
+ * msdn{cc242518}
+ */
+
+static BOOL rdp_write_remote_programs_capability_set(wStream* s, const rdpSettings* settings)
+{
+ WINPR_ASSERT(settings);
+ if (!Stream_EnsureRemainingCapacity(s, 64))
+ return FALSE;
+
+ const size_t header = rdp_capability_set_start(s);
+ UINT32 railSupportLevel = RAIL_LEVEL_SUPPORTED;
+
+ if (settings->RemoteApplicationSupportLevel & RAIL_LEVEL_DOCKED_LANGBAR_SUPPORTED)
+ {
+ if (settings->RemoteAppLanguageBarSupported)
+ railSupportLevel |= RAIL_LEVEL_DOCKED_LANGBAR_SUPPORTED;
+ }
+
+ railSupportLevel |= RAIL_LEVEL_SHELL_INTEGRATION_SUPPORTED;
+ railSupportLevel |= RAIL_LEVEL_LANGUAGE_IME_SYNC_SUPPORTED;
+ railSupportLevel |= RAIL_LEVEL_SERVER_TO_CLIENT_IME_SYNC_SUPPORTED;
+ railSupportLevel |= RAIL_LEVEL_HIDE_MINIMIZED_APPS_SUPPORTED;
+ railSupportLevel |= RAIL_LEVEL_WINDOW_CLOAKING_SUPPORTED;
+ railSupportLevel |= RAIL_LEVEL_HANDSHAKE_EX_SUPPORTED;
+ /* Mask out everything the server does not support. */
+ railSupportLevel &= settings->RemoteApplicationSupportLevel;
+ Stream_Write_UINT32(s, railSupportLevel); /* railSupportLevel (4 bytes) */
+ return rdp_capability_set_finish(s, header, CAPSET_TYPE_RAIL);
+}
+
+#ifdef WITH_DEBUG_CAPABILITIES
+static BOOL rdp_print_remote_programs_capability_set(wStream* s)
+{
+ UINT32 railSupportLevel = 0;
+ WLog_VRB(TAG, "RemoteProgramsCapabilitySet (length %" PRIuz "):", Stream_GetRemainingLength(s));
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return FALSE;
+
+ Stream_Read_UINT32(s, railSupportLevel); /* railSupportLevel (4 bytes) */
+ WLog_VRB(TAG, "\trailSupportLevel: 0x%08" PRIX32 "", railSupportLevel);
+ return TRUE;
+}
+#endif
+
+static BOOL rdp_apply_window_list_capability_set(rdpSettings* settings, const rdpSettings* src)
+{
+ WINPR_ASSERT(settings);
+ WINPR_ASSERT(src);
+
+ settings->RemoteWndSupportLevel = src->RemoteWndSupportLevel;
+ settings->RemoteAppNumIconCaches = src->RemoteAppNumIconCaches;
+ settings->RemoteAppNumIconCacheEntries = src->RemoteAppNumIconCacheEntries;
+
+ return TRUE;
+}
+
+/*
+ * Read window list capability set.
+ * msdn{cc242564}
+ */
+
+static BOOL rdp_read_window_list_capability_set(wStream* s, rdpSettings* settings)
+{
+ WINPR_ASSERT(settings);
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 7))
+ return FALSE;
+
+ Stream_Read_UINT32(s, settings->RemoteWndSupportLevel); /* wndSupportLevel (4 bytes) */
+ Stream_Read_UINT8(s, settings->RemoteAppNumIconCaches); /* numIconCaches (1 byte) */
+ Stream_Read_UINT16(s,
+ settings->RemoteAppNumIconCacheEntries); /* numIconCacheEntries (2 bytes) */
+ return TRUE;
+}
+
+/*
+ * Write window list capability set.
+ * msdn{cc242564}
+ */
+
+static BOOL rdp_write_window_list_capability_set(wStream* s, const rdpSettings* settings)
+{
+ WINPR_ASSERT(settings);
+ if (!Stream_EnsureRemainingCapacity(s, 32))
+ return FALSE;
+
+ const size_t header = rdp_capability_set_start(s);
+ Stream_Write_UINT32(s, settings->RemoteWndSupportLevel); /* wndSupportLevel (4 bytes) */
+ Stream_Write_UINT8(s, settings->RemoteAppNumIconCaches); /* numIconCaches (1 byte) */
+ Stream_Write_UINT16(s,
+ settings->RemoteAppNumIconCacheEntries); /* numIconCacheEntries (2 bytes) */
+ return rdp_capability_set_finish(s, header, CAPSET_TYPE_WINDOW);
+}
+
+#ifdef WITH_DEBUG_CAPABILITIES
+static BOOL rdp_print_window_list_capability_set(wStream* s)
+{
+ UINT32 wndSupportLevel = 0;
+ BYTE numIconCaches = 0;
+ UINT16 numIconCacheEntries = 0;
+ WLog_VRB(TAG, "WindowListCapabilitySet (length %" PRIuz "):", Stream_GetRemainingLength(s));
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 7))
+ return FALSE;
+
+ Stream_Read_UINT32(s, wndSupportLevel); /* wndSupportLevel (4 bytes) */
+ Stream_Read_UINT8(s, numIconCaches); /* numIconCaches (1 byte) */
+ Stream_Read_UINT16(s, numIconCacheEntries); /* numIconCacheEntries (2 bytes) */
+ WLog_VRB(TAG, "\twndSupportLevel: 0x%08" PRIX32 "", wndSupportLevel);
+ WLog_VRB(TAG, "\tnumIconCaches: 0x%02" PRIX8 "", numIconCaches);
+ WLog_VRB(TAG, "\tnumIconCacheEntries: 0x%04" PRIX16 "", numIconCacheEntries);
+ return TRUE;
+}
+#endif
+
+static BOOL rdp_apply_desktop_composition_capability_set(rdpSettings* settings,
+ const rdpSettings* src)
+{
+ WINPR_ASSERT(settings);
+ WINPR_ASSERT(src);
+
+ settings->CompDeskSupportLevel = src->CompDeskSupportLevel;
+ return TRUE;
+}
+
+/*
+ * Read desktop composition capability set.
+ * msdn{cc240855}
+ */
+
+static BOOL rdp_read_desktop_composition_capability_set(wStream* s, rdpSettings* settings)
+{
+ WINPR_UNUSED(settings);
+ WINPR_ASSERT(settings);
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
+ return FALSE;
+
+ Stream_Read_UINT16(s, settings->CompDeskSupportLevel); /* compDeskSupportLevel (2 bytes) */
+ return TRUE;
+}
+
+/*
+ * Write desktop composition capability set.
+ * msdn{cc240855}
+ */
+
+static BOOL rdp_write_desktop_composition_capability_set(wStream* s, const rdpSettings* settings)
+{
+ WINPR_ASSERT(settings);
+
+ if (!Stream_EnsureRemainingCapacity(s, 32))
+ return FALSE;
+
+ const size_t header = rdp_capability_set_start(s);
+ const UINT16 compDeskSupportLevel =
+ (settings->AllowDesktopComposition) ? COMPDESK_SUPPORTED : COMPDESK_NOT_SUPPORTED;
+ Stream_Write_UINT16(s, compDeskSupportLevel); /* compDeskSupportLevel (2 bytes) */
+ return rdp_capability_set_finish(s, header, CAPSET_TYPE_COMP_DESK);
+}
+
+#ifdef WITH_DEBUG_CAPABILITIES
+static BOOL rdp_print_desktop_composition_capability_set(wStream* s)
+{
+ UINT16 compDeskSupportLevel = 0;
+ WLog_VRB(TAG,
+ "DesktopCompositionCapabilitySet (length %" PRIuz "):", Stream_GetRemainingLength(s));
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
+ return FALSE;
+
+ Stream_Read_UINT16(s, compDeskSupportLevel); /* compDeskSupportLevel (2 bytes) */
+ WLog_VRB(TAG, "\tcompDeskSupportLevel: 0x%04" PRIX16 "", compDeskSupportLevel);
+ return TRUE;
+}
+#endif
+
+static BOOL rdp_apply_multifragment_update_capability_set(rdpSettings* settings,
+ const rdpSettings* src)
+{
+ UINT32 multifragMaxRequestSize = 0;
+
+ WINPR_ASSERT(settings);
+ WINPR_ASSERT(src);
+
+ multifragMaxRequestSize = src->MultifragMaxRequestSize;
+
+ if (settings->ServerMode)
+ {
+ /*
+ * Special case: The client announces multifragment update support but sets the maximum
+ * request size to something smaller than maximum size for *one* fast-path PDU. In this case
+ * behave like no multifragment updates were supported and make sure no fragmentation
+ * happens by setting FASTPATH_FRAGMENT_SAFE_SIZE.
+ *
+ * This behaviour was observed with some windows ce rdp clients.
+ */
+ if (multifragMaxRequestSize < FASTPATH_MAX_PACKET_SIZE)
+ multifragMaxRequestSize = FASTPATH_FRAGMENT_SAFE_SIZE;
+
+ if (settings->RemoteFxCodec)
+ {
+ /*
+ * If we are using RemoteFX the client MUST use a value greater
+ * than or equal to the value we've previously sent in the server to
+ * client multi-fragment update capability set (MS-RDPRFX 1.5)
+ */
+ if (multifragMaxRequestSize < settings->MultifragMaxRequestSize)
+ {
+ /*
+ * If it happens to be smaller we honor the client's value but
+ * have to disable RemoteFX
+ */
+ settings->RemoteFxCodec = FALSE;
+ settings->MultifragMaxRequestSize = multifragMaxRequestSize;
+ }
+ else
+ {
+ /* no need to increase server's max request size setting here */
+ }
+ }
+ else
+ {
+ settings->MultifragMaxRequestSize = multifragMaxRequestSize;
+ }
+ }
+ else
+ {
+ /*
+ * In client mode we keep up with the server's capabilites.
+ * In RemoteFX mode we MUST do this but it might also be useful to
+ * receive larger related bitmap updates.
+ */
+ if (multifragMaxRequestSize > settings->MultifragMaxRequestSize)
+ settings->MultifragMaxRequestSize = multifragMaxRequestSize;
+ }
+ return TRUE;
+}
+
+/*
+ * Read multifragment update capability set.
+ * msdn{cc240649}
+ */
+
+static BOOL rdp_read_multifragment_update_capability_set(wStream* s, rdpSettings* settings)
+{
+ UINT32 multifragMaxRequestSize = 0;
+
+ WINPR_ASSERT(settings);
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return FALSE;
+
+ Stream_Read_UINT32(s, multifragMaxRequestSize); /* MaxRequestSize (4 bytes) */
+ settings->MultifragMaxRequestSize = multifragMaxRequestSize;
+
+ return TRUE;
+}
+
+/*
+ * Write multifragment update capability set.
+ * msdn{cc240649}
+ */
+
+static BOOL rdp_write_multifragment_update_capability_set(wStream* s, rdpSettings* settings)
+{
+ WINPR_ASSERT(settings);
+ if (settings->ServerMode && settings->MultifragMaxRequestSize == 0)
+ {
+ /*
+ * In server mode we prefer to use the highest useful request size that
+ * will allow us to pack a complete screen update into a single fast
+ * path PDU using any of the supported codecs.
+ * However, the client is completely free to accept our proposed
+ * max request size or send a different value in the client-to-server
+ * multi-fragment update capability set and we have to accept that,
+ * unless we are using RemoteFX where the client MUST announce a value
+ * greater than or equal to the value we're sending here.
+ * See [MS-RDPRFX 1.5 capability #2]
+ */
+ UINT32 tileNumX = (settings->DesktopWidth + 63) / 64;
+ UINT32 tileNumY = (settings->DesktopHeight + 63) / 64;
+ settings->MultifragMaxRequestSize = tileNumX * tileNumY * 16384;
+ /* and add room for headers, regions, frame markers, etc. */
+ settings->MultifragMaxRequestSize += 16384;
+ }
+
+ if (!Stream_EnsureRemainingCapacity(s, 32))
+ return FALSE;
+ const size_t header = rdp_capability_set_start(s);
+ Stream_Write_UINT32(s, settings->MultifragMaxRequestSize); /* MaxRequestSize (4 bytes) */
+ return rdp_capability_set_finish(s, header, CAPSET_TYPE_MULTI_FRAGMENT_UPDATE);
+}
+
+#ifdef WITH_DEBUG_CAPABILITIES
+static BOOL rdp_print_multifragment_update_capability_set(wStream* s)
+{
+ UINT32 maxRequestSize = 0;
+ WLog_VRB(TAG,
+ "MultifragmentUpdateCapabilitySet (length %" PRIuz "):", Stream_GetRemainingLength(s));
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return FALSE;
+
+ Stream_Read_UINT32(s, maxRequestSize); /* maxRequestSize (4 bytes) */
+ WLog_VRB(TAG, "\tmaxRequestSize: 0x%08" PRIX32 "", maxRequestSize);
+ return TRUE;
+}
+#endif
+
+static BOOL rdp_apply_large_pointer_capability_set(rdpSettings* settings, const rdpSettings* src)
+{
+ WINPR_ASSERT(settings);
+ WINPR_ASSERT(src);
+
+ settings->LargePointerFlag = src->LargePointerFlag;
+ return TRUE;
+}
+
+/*
+ * Read large pointer capability set.
+ * msdn{cc240650}
+ */
+
+static BOOL rdp_read_large_pointer_capability_set(wStream* s, rdpSettings* settings)
+{
+ UINT16 largePointerSupportFlags = 0;
+
+ WINPR_ASSERT(settings);
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
+ return FALSE;
+
+ Stream_Read_UINT16(s, largePointerSupportFlags); /* largePointerSupportFlags (2 bytes) */
+ settings->LargePointerFlag &= largePointerSupportFlags;
+ if ((largePointerSupportFlags & ~(LARGE_POINTER_FLAG_96x96 | LARGE_POINTER_FLAG_384x384)) != 0)
+ {
+ WLog_WARN(
+ TAG,
+ "TS_LARGE_POINTER_CAPABILITYSET with unsupported flags %04X (all flags %04X) received",
+ largePointerSupportFlags & ~(LARGE_POINTER_FLAG_96x96 | LARGE_POINTER_FLAG_384x384),
+ largePointerSupportFlags);
+ }
+ return TRUE;
+}
+
+/*
+ * Write large pointer capability set.
+ * msdn{cc240650}
+ */
+
+static BOOL rdp_write_large_pointer_capability_set(wStream* s, const rdpSettings* settings)
+{
+ WINPR_ASSERT(settings);
+ if (!Stream_EnsureRemainingCapacity(s, 32))
+ return FALSE;
+
+ const size_t header = rdp_capability_set_start(s);
+ const UINT16 largePointerSupportFlags =
+ settings->LargePointerFlag & (LARGE_POINTER_FLAG_96x96 | LARGE_POINTER_FLAG_384x384);
+ Stream_Write_UINT16(s, largePointerSupportFlags); /* largePointerSupportFlags (2 bytes) */
+ return rdp_capability_set_finish(s, header, CAPSET_TYPE_LARGE_POINTER);
+}
+
+#ifdef WITH_DEBUG_CAPABILITIES
+static BOOL rdp_print_large_pointer_capability_set(wStream* s)
+{
+ UINT16 largePointerSupportFlags = 0;
+ WLog_VRB(TAG, "LargePointerCapabilitySet (length %" PRIuz "):", Stream_GetRemainingLength(s));
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
+ return FALSE;
+
+ Stream_Read_UINT16(s, largePointerSupportFlags); /* largePointerSupportFlags (2 bytes) */
+ WLog_VRB(TAG, "\tlargePointerSupportFlags: 0x%04" PRIX16 "", largePointerSupportFlags);
+ return TRUE;
+}
+#endif
+
+static BOOL rdp_apply_surface_commands_capability_set(rdpSettings* settings, const rdpSettings* src)
+{
+ WINPR_ASSERT(settings);
+ WINPR_ASSERT(src);
+
+ /* [MS-RDPBCGR] 2.2.7.2.9 Surface Commands Capability Set (TS_SURFCMDS_CAPABILITYSET)
+ *
+ * disable surface commands if the remote does not support fastpath
+ */
+ if (src->FastPathOutput)
+ {
+ settings->SurfaceCommandsEnabled = src->SurfaceCommandsEnabled;
+ settings->SurfaceFrameMarkerEnabled = src->SurfaceFrameMarkerEnabled;
+ }
+ else
+ {
+ settings->SurfaceCommandsEnabled = FALSE;
+ settings->SurfaceFrameMarkerEnabled = FALSE;
+ }
+
+ return TRUE;
+}
+
+/*
+ * Read surface commands capability set.
+ * msdn{dd871563}
+ */
+
+static BOOL rdp_read_surface_commands_capability_set(wStream* s, rdpSettings* settings)
+{
+ UINT32 cmdFlags = 0;
+
+ WINPR_ASSERT(settings);
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
+ return FALSE;
+
+ Stream_Read_UINT32(s, cmdFlags); /* cmdFlags (4 bytes) */
+ Stream_Seek_UINT32(s); /* reserved (4 bytes) */
+ settings->SurfaceCommandsEnabled = TRUE;
+ settings->SurfaceFrameMarkerEnabled = (cmdFlags & SURFCMDS_FRAME_MARKER) ? TRUE : FALSE;
+ return TRUE;
+}
+
+/*
+ * Write surface commands capability set.
+ * msdn{dd871563}
+ */
+
+static BOOL rdp_write_surface_commands_capability_set(wStream* s, const rdpSettings* settings)
+{
+ WINPR_ASSERT(settings);
+ if (!Stream_EnsureRemainingCapacity(s, 32))
+ return FALSE;
+
+ const size_t header = rdp_capability_set_start(s);
+ UINT32 cmdFlags = SURFCMDS_SET_SURFACE_BITS | SURFCMDS_STREAM_SURFACE_BITS;
+
+ if (settings->SurfaceFrameMarkerEnabled)
+ cmdFlags |= SURFCMDS_FRAME_MARKER;
+
+ Stream_Write_UINT32(s, cmdFlags); /* cmdFlags (4 bytes) */
+ Stream_Write_UINT32(s, 0); /* reserved (4 bytes) */
+ return rdp_capability_set_finish(s, header, CAPSET_TYPE_SURFACE_COMMANDS);
+}
+
+#ifdef WITH_DEBUG_CAPABILITIES
+static BOOL rdp_print_surface_commands_capability_set(wStream* s)
+{
+ UINT32 cmdFlags = 0;
+ UINT32 reserved = 0;
+
+ WLog_VRB(TAG,
+ "SurfaceCommandsCapabilitySet (length %" PRIuz "):", Stream_GetRemainingLength(s));
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
+ return FALSE;
+
+ Stream_Read_UINT32(s, cmdFlags); /* cmdFlags (4 bytes) */
+ Stream_Read_UINT32(s, reserved); /* reserved (4 bytes) */
+ WLog_VRB(TAG, "\tcmdFlags: 0x%08" PRIX32 "", cmdFlags);
+ WLog_VRB(TAG, "\treserved: 0x%08" PRIX32 "", reserved);
+ return TRUE;
+}
+
+static void rdp_print_bitmap_codec_guid(const GUID* guid)
+{
+ WINPR_ASSERT(guid);
+ WLog_VRB(TAG,
+ "%08" PRIX32 "%04" PRIX16 "%04" PRIX16 "%02" PRIX8 "%02" PRIX8 "%02" PRIX8 "%02" PRIX8
+ "%02" PRIX8 "%02" PRIX8 "%02" PRIX8 "%02" PRIX8 "",
+ guid->Data1, guid->Data2, guid->Data3, guid->Data4[0], guid->Data4[1], guid->Data4[2],
+ guid->Data4[3], guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]);
+}
+
+static char* rdp_get_bitmap_codec_guid_name(const GUID* guid)
+{
+ RPC_STATUS rpc_status = 0;
+
+ WINPR_ASSERT(guid);
+ if (UuidEqual(guid, &CODEC_GUID_REMOTEFX, &rpc_status))
+ return "CODEC_GUID_REMOTEFX";
+ else if (UuidEqual(guid, &CODEC_GUID_NSCODEC, &rpc_status))
+ return "CODEC_GUID_NSCODEC";
+ else if (UuidEqual(guid, &CODEC_GUID_IGNORE, &rpc_status))
+ return "CODEC_GUID_IGNORE";
+ else if (UuidEqual(guid, &CODEC_GUID_IMAGE_REMOTEFX, &rpc_status))
+ return "CODEC_GUID_IMAGE_REMOTEFX";
+
+#if defined(WITH_JPEG)
+ else if (UuidEqual(guid, &CODEC_GUID_JPEG, &rpc_status))
+ return "CODEC_GUID_JPEG";
+
+#endif
+ return "CODEC_GUID_UNKNOWN";
+}
+#endif
+
+static BOOL rdp_read_bitmap_codec_guid(wStream* s, GUID* guid)
+{
+ BYTE g[16] = { 0 };
+
+ WINPR_ASSERT(guid);
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 16))
+ return FALSE;
+ Stream_Read(s, g, 16);
+ guid->Data1 = ((UINT32)g[3] << 24U) | ((UINT32)g[2] << 16U) | (g[1] << 8U) | g[0];
+ guid->Data2 = (g[5] << 8U) | g[4];
+ guid->Data3 = (g[7] << 8U) | g[6];
+ guid->Data4[0] = g[8];
+ guid->Data4[1] = g[9];
+ guid->Data4[2] = g[10];
+ guid->Data4[3] = g[11];
+ guid->Data4[4] = g[12];
+ guid->Data4[5] = g[13];
+ guid->Data4[6] = g[14];
+ guid->Data4[7] = g[15];
+ return TRUE;
+}
+
+static void rdp_write_bitmap_codec_guid(wStream* s, const GUID* guid)
+{
+ BYTE g[16] = { 0 };
+ WINPR_ASSERT(guid);
+ g[0] = guid->Data1 & 0xFF;
+ g[1] = (guid->Data1 >> 8) & 0xFF;
+ g[2] = (guid->Data1 >> 16) & 0xFF;
+ g[3] = (guid->Data1 >> 24) & 0xFF;
+ g[4] = (guid->Data2) & 0xFF;
+ g[5] = (guid->Data2 >> 8) & 0xFF;
+ g[6] = (guid->Data3) & 0xFF;
+ g[7] = (guid->Data3 >> 8) & 0xFF;
+ g[8] = guid->Data4[0];
+ g[9] = guid->Data4[1];
+ g[10] = guid->Data4[2];
+ g[11] = guid->Data4[3];
+ g[12] = guid->Data4[4];
+ g[13] = guid->Data4[5];
+ g[14] = guid->Data4[6];
+ g[15] = guid->Data4[7];
+ Stream_Write(s, g, 16);
+}
+
+static BOOL rdp_apply_bitmap_codecs_capability_set(rdpSettings* settings, const rdpSettings* src)
+{
+ WINPR_ASSERT(settings);
+ WINPR_ASSERT(src);
+
+ if (settings->ServerMode)
+ {
+
+ settings->RemoteFxCodecId = src->RemoteFxCodecId;
+ settings->RemoteFxCaptureFlags = src->RemoteFxCaptureFlags;
+ settings->RemoteFxOnly = src->RemoteFxOnly;
+ settings->NSCodecAllowDynamicColorFidelity = src->NSCodecAllowDynamicColorFidelity;
+ settings->NSCodecAllowSubsampling = src->NSCodecAllowSubsampling;
+ settings->NSCodecColorLossLevel = src->NSCodecColorLossLevel;
+
+ /* only enable a codec if we've announced/enabled it before */
+ settings->RemoteFxCodec = settings->RemoteFxCodec && src->RemoteFxCodecId;
+ settings->RemoteFxImageCodec = settings->RemoteFxImageCodec && src->RemoteFxImageCodec;
+ freerdp_settings_set_bool(settings, FreeRDP_NSCodec, settings->NSCodec && src->NSCodec);
+ settings->JpegCodec = src->JpegCodec;
+ }
+ return TRUE;
+}
+
+/*
+ * Read bitmap codecs capability set.
+ * msdn{dd891377}
+ */
+
+static BOOL rdp_read_bitmap_codecs_capability_set(wStream* s, rdpSettings* settings, BOOL isServer)
+{
+ BYTE codecId = 0;
+ GUID codecGuid = { 0 };
+ RPC_STATUS rpc_status = 0;
+ BYTE bitmapCodecCount = 0;
+ UINT16 codecPropertiesLength = 0;
+
+ BOOL guidNSCodec = FALSE;
+ BOOL guidRemoteFx = FALSE;
+ BOOL guidRemoteFxImage = FALSE;
+
+ WINPR_ASSERT(settings);
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
+ return FALSE;
+
+ Stream_Read_UINT8(s, bitmapCodecCount); /* bitmapCodecCount (1 byte) */
+
+ while (bitmapCodecCount > 0)
+ {
+ wStream subbuffer = { 0 };
+
+ if (!rdp_read_bitmap_codec_guid(s, &codecGuid)) /* codecGuid (16 bytes) */
+ return FALSE;
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 3))
+ return FALSE;
+ Stream_Read_UINT8(s, codecId); /* codecId (1 byte) */
+ Stream_Read_UINT16(s, codecPropertiesLength); /* codecPropertiesLength (2 bytes) */
+
+ wStream* sub = Stream_StaticInit(&subbuffer, Stream_Pointer(s), codecPropertiesLength);
+ if (!Stream_SafeSeek(s, codecPropertiesLength))
+ return FALSE;
+
+ if (isServer)
+ {
+ if (UuidEqual(&codecGuid, &CODEC_GUID_REMOTEFX, &rpc_status))
+ {
+ UINT32 rfxCapsLength = 0;
+ UINT32 rfxPropsLength = 0;
+ UINT32 captureFlags = 0;
+ guidRemoteFx = TRUE;
+ settings->RemoteFxCodecId = codecId;
+ if (!Stream_CheckAndLogRequiredLength(TAG, sub, 12))
+ return FALSE;
+ Stream_Read_UINT32(sub, rfxPropsLength); /* length (4 bytes) */
+ Stream_Read_UINT32(sub, captureFlags); /* captureFlags (4 bytes) */
+ Stream_Read_UINT32(sub, rfxCapsLength); /* capsLength (4 bytes) */
+ settings->RemoteFxCaptureFlags = captureFlags;
+ settings->RemoteFxOnly = (captureFlags & CARDP_CAPS_CAPTURE_NON_CAC) ? FALSE : TRUE;
+
+ if (rfxCapsLength)
+ {
+ UINT16 blockType = 0;
+ UINT32 blockLen = 0;
+ UINT16 numCapsets = 0;
+ BYTE rfxCodecId = 0;
+ UINT16 capsetType = 0;
+ UINT16 numIcaps = 0;
+ UINT16 icapLen = 0;
+ /* TS_RFX_CAPS */
+ if (!Stream_CheckAndLogRequiredLength(TAG, sub, 21))
+ return FALSE;
+ Stream_Read_UINT16(sub, blockType); /* blockType (2 bytes) */
+ Stream_Read_UINT32(sub, blockLen); /* blockLen (4 bytes) */
+ Stream_Read_UINT16(sub, numCapsets); /* numCapsets (2 bytes) */
+
+ if (blockType != 0xCBC0)
+ return FALSE;
+
+ if (blockLen != 8)
+ return FALSE;
+
+ if (numCapsets != 1)
+ return FALSE;
+
+ /* TS_RFX_CAPSET */
+ Stream_Read_UINT16(sub, blockType); /* blockType (2 bytes) */
+ Stream_Read_UINT32(sub, blockLen); /* blockLen (4 bytes) */
+ Stream_Read_UINT8(sub, rfxCodecId); /* codecId (1 byte) */
+ Stream_Read_UINT16(sub, capsetType); /* capsetType (2 bytes) */
+ Stream_Read_UINT16(sub, numIcaps); /* numIcaps (2 bytes) */
+ Stream_Read_UINT16(sub, icapLen); /* icapLen (2 bytes) */
+
+ if (blockType != 0xCBC1)
+ return FALSE;
+
+ if (rfxCodecId != 1)
+ return FALSE;
+
+ if (capsetType != 0xCFC0)
+ return FALSE;
+
+ while (numIcaps--)
+ {
+ UINT16 version = 0;
+ UINT16 tileSize = 0;
+ BYTE codecFlags = 0;
+ BYTE colConvBits = 0;
+ BYTE transformBits = 0;
+ BYTE entropyBits = 0;
+ /* TS_RFX_ICAP */
+ if (!Stream_CheckAndLogRequiredLength(TAG, sub, 8))
+ return FALSE;
+ Stream_Read_UINT16(sub, version); /* version (2 bytes) */
+ Stream_Read_UINT16(sub, tileSize); /* tileSize (2 bytes) */
+ Stream_Read_UINT8(sub, codecFlags); /* flags (1 byte) */
+ Stream_Read_UINT8(sub, colConvBits); /* colConvBits (1 byte) */
+ Stream_Read_UINT8(sub, transformBits); /* transformBits (1 byte) */
+ Stream_Read_UINT8(sub, entropyBits); /* entropyBits (1 byte) */
+
+ if (version == 0x0009)
+ {
+ /* Version 0.9 */
+ if (tileSize != 0x0080)
+ return FALSE;
+ }
+ else if (version == 0x0100)
+ {
+ /* Version 1.0 */
+ if (tileSize != 0x0040)
+ return FALSE;
+ }
+ else
+ return FALSE;
+
+ if (colConvBits != 1)
+ return FALSE;
+
+ if (transformBits != 1)
+ return FALSE;
+ }
+ }
+ }
+ else if (UuidEqual(&codecGuid, &CODEC_GUID_IMAGE_REMOTEFX, &rpc_status))
+ {
+ /* Microsoft RDP servers ignore CODEC_GUID_IMAGE_REMOTEFX codec properties */
+ guidRemoteFxImage = TRUE;
+ if (!Stream_SafeSeek(sub, codecPropertiesLength)) /* codecProperties */
+ return FALSE;
+ }
+ else if (UuidEqual(&codecGuid, &CODEC_GUID_NSCODEC, &rpc_status))
+ {
+ BYTE colorLossLevel = 0;
+ BYTE fAllowSubsampling = 0;
+ BYTE fAllowDynamicFidelity = 0;
+ guidNSCodec = TRUE;
+ settings->NSCodecId = codecId;
+ if (!Stream_CheckAndLogRequiredLength(TAG, sub, 3))
+ return FALSE;
+ Stream_Read_UINT8(sub, fAllowDynamicFidelity); /* fAllowDynamicFidelity (1 byte) */
+ Stream_Read_UINT8(sub, fAllowSubsampling); /* fAllowSubsampling (1 byte) */
+ Stream_Read_UINT8(sub, colorLossLevel); /* colorLossLevel (1 byte) */
+
+ if (colorLossLevel < 1)
+ colorLossLevel = 1;
+
+ if (colorLossLevel > 7)
+ colorLossLevel = 7;
+
+ settings->NSCodecAllowDynamicColorFidelity = fAllowDynamicFidelity;
+ settings->NSCodecAllowSubsampling = fAllowSubsampling;
+ settings->NSCodecColorLossLevel = colorLossLevel;
+ }
+ else if (UuidEqual(&codecGuid, &CODEC_GUID_IGNORE, &rpc_status))
+ {
+ if (!Stream_SafeSeek(sub, codecPropertiesLength)) /* codecProperties */
+ return FALSE;
+ }
+ else
+ {
+ if (!Stream_SafeSeek(sub, codecPropertiesLength)) /* codecProperties */
+ return FALSE;
+ }
+ }
+ else
+ {
+ if (!Stream_SafeSeek(sub, codecPropertiesLength)) /* codecProperties */
+ return FALSE;
+ }
+
+ const size_t rest = Stream_GetRemainingLength(sub);
+ if (rest > 0)
+ {
+ WLog_ERR(TAG,
+ "error while reading codec properties: actual size: %" PRIuz
+ " expected size: %" PRIu32 "",
+ rest + codecPropertiesLength, codecPropertiesLength);
+ }
+ bitmapCodecCount--;
+
+ /* only enable a codec if we've announced/enabled it before */
+ settings->RemoteFxCodec = guidRemoteFx;
+ settings->RemoteFxImageCodec = guidRemoteFxImage;
+ freerdp_settings_set_bool(settings, FreeRDP_NSCodec, guidNSCodec);
+ settings->JpegCodec = FALSE;
+ }
+
+ return TRUE;
+}
+
+/*
+ * Write RemoteFX Client Capability Container.
+ */
+static BOOL rdp_write_rfx_client_capability_container(wStream* s, const rdpSettings* settings)
+{
+ UINT32 captureFlags = 0;
+ BYTE codecMode = 0;
+
+ WINPR_ASSERT(settings);
+ if (!Stream_EnsureRemainingCapacity(s, 64))
+ return FALSE;
+
+ captureFlags = settings->RemoteFxOnly ? 0 : CARDP_CAPS_CAPTURE_NON_CAC;
+ codecMode = settings->RemoteFxCodecMode;
+ Stream_Write_UINT16(s, 49); /* codecPropertiesLength */
+ /* TS_RFX_CLNT_CAPS_CONTAINER */
+ Stream_Write_UINT32(s, 49); /* length */
+ Stream_Write_UINT32(s, captureFlags); /* captureFlags */
+ Stream_Write_UINT32(s, 37); /* capsLength */
+ /* TS_RFX_CAPS */
+ Stream_Write_UINT16(s, CBY_CAPS); /* blockType */
+ Stream_Write_UINT32(s, 8); /* blockLen */
+ Stream_Write_UINT16(s, 1); /* numCapsets */
+ /* TS_RFX_CAPSET */
+ Stream_Write_UINT16(s, CBY_CAPSET); /* blockType */
+ Stream_Write_UINT32(s, 29); /* blockLen */
+ Stream_Write_UINT8(s, 0x01); /* codecId (MUST be set to 0x01) */
+ Stream_Write_UINT16(s, CLY_CAPSET); /* capsetType */
+ Stream_Write_UINT16(s, 2); /* numIcaps */
+ Stream_Write_UINT16(s, 8); /* icapLen */
+ /* TS_RFX_ICAP (RLGR1) */
+ Stream_Write_UINT16(s, CLW_VERSION_1_0); /* version */
+ Stream_Write_UINT16(s, CT_TILE_64x64); /* tileSize */
+ Stream_Write_UINT8(s, codecMode); /* flags */
+ Stream_Write_UINT8(s, CLW_COL_CONV_ICT); /* colConvBits */
+ Stream_Write_UINT8(s, CLW_XFORM_DWT_53_A); /* transformBits */
+ Stream_Write_UINT8(s, CLW_ENTROPY_RLGR1); /* entropyBits */
+ /* TS_RFX_ICAP (RLGR3) */
+ Stream_Write_UINT16(s, CLW_VERSION_1_0); /* version */
+ Stream_Write_UINT16(s, CT_TILE_64x64); /* tileSize */
+ Stream_Write_UINT8(s, codecMode); /* flags */
+ Stream_Write_UINT8(s, CLW_COL_CONV_ICT); /* colConvBits */
+ Stream_Write_UINT8(s, CLW_XFORM_DWT_53_A); /* transformBits */
+ Stream_Write_UINT8(s, CLW_ENTROPY_RLGR3); /* entropyBits */
+ return TRUE;
+}
+
+/*
+ * Write NSCODEC Client Capability Container.
+ */
+static BOOL rdp_write_nsc_client_capability_container(wStream* s, const rdpSettings* settings)
+{
+ BYTE colorLossLevel = 0;
+ BYTE fAllowSubsampling = 0;
+ BYTE fAllowDynamicFidelity = 0;
+
+ WINPR_ASSERT(settings);
+
+ fAllowDynamicFidelity = settings->NSCodecAllowDynamicColorFidelity;
+ fAllowSubsampling = settings->NSCodecAllowSubsampling;
+ colorLossLevel = settings->NSCodecColorLossLevel;
+
+ if (colorLossLevel < 1)
+ colorLossLevel = 1;
+
+ if (colorLossLevel > 7)
+ colorLossLevel = 7;
+
+ if (!Stream_EnsureRemainingCapacity(s, 8))
+ return FALSE;
+
+ Stream_Write_UINT16(s, 3); /* codecPropertiesLength */
+ /* TS_NSCODEC_CAPABILITYSET */
+ Stream_Write_UINT8(s, fAllowDynamicFidelity); /* fAllowDynamicFidelity (1 byte) */
+ Stream_Write_UINT8(s, fAllowSubsampling); /* fAllowSubsampling (1 byte) */
+ Stream_Write_UINT8(s, colorLossLevel); /* colorLossLevel (1 byte) */
+ return TRUE;
+}
+
+#if defined(WITH_JPEG)
+static BOOL rdp_write_jpeg_client_capability_container(wStream* s, const rdpSettings* settings)
+{
+ WINPR_ASSERT(settings);
+ if (!Stream_EnsureRemainingCapacity(s, 8))
+ return FALSE;
+
+ Stream_Write_UINT16(s, 1); /* codecPropertiesLength */
+ Stream_Write_UINT8(s, settings->JpegQuality);
+ return TRUE;
+}
+#endif
+
+/*
+ * Write RemoteFX Server Capability Container.
+ */
+static BOOL rdp_write_rfx_server_capability_container(wStream* s, const rdpSettings* settings)
+{
+ WINPR_UNUSED(settings);
+ WINPR_ASSERT(settings);
+
+ if (!Stream_EnsureRemainingCapacity(s, 8))
+ return FALSE;
+
+ Stream_Write_UINT16(s, 4); /* codecPropertiesLength */
+ Stream_Write_UINT32(s, 0); /* reserved */
+ return TRUE;
+}
+
+static BOOL rdp_write_jpeg_server_capability_container(wStream* s, const rdpSettings* settings)
+{
+ WINPR_UNUSED(settings);
+ WINPR_ASSERT(settings);
+
+ if (!Stream_EnsureRemainingCapacity(s, 8))
+ return FALSE;
+
+ Stream_Write_UINT16(s, 1); /* codecPropertiesLength */
+ Stream_Write_UINT8(s, 75);
+ return TRUE;
+}
+
+/*
+ * Write NSCODEC Server Capability Container.
+ */
+static BOOL rdp_write_nsc_server_capability_container(wStream* s, const rdpSettings* settings)
+{
+ WINPR_UNUSED(settings);
+ WINPR_ASSERT(settings);
+
+ if (!Stream_EnsureRemainingCapacity(s, 8))
+ return FALSE;
+
+ Stream_Write_UINT16(s, 4); /* codecPropertiesLength */
+ Stream_Write_UINT32(s, 0); /* reserved */
+ return TRUE;
+}
+
+/*
+ * Write bitmap codecs capability set.
+ * msdn{dd891377}
+ */
+
+static BOOL rdp_write_bitmap_codecs_capability_set(wStream* s, const rdpSettings* settings)
+{
+ WINPR_ASSERT(settings);
+ if (!Stream_EnsureRemainingCapacity(s, 64))
+ return FALSE;
+
+ const size_t header = rdp_capability_set_start(s);
+ BYTE bitmapCodecCount = 0;
+
+ if (settings->RemoteFxCodec)
+ bitmapCodecCount++;
+
+ if (freerdp_settings_get_bool(settings, FreeRDP_NSCodec))
+ bitmapCodecCount++;
+
+#if defined(WITH_JPEG)
+
+ if (settings->JpegCodec)
+ bitmapCodecCount++;
+
+#endif
+
+ if (settings->RemoteFxImageCodec)
+ bitmapCodecCount++;
+
+ Stream_Write_UINT8(s, bitmapCodecCount);
+
+ if (settings->RemoteFxCodec)
+ {
+ rdp_write_bitmap_codec_guid(s, &CODEC_GUID_REMOTEFX); /* codecGUID */
+
+ if (settings->ServerMode)
+ {
+ Stream_Write_UINT8(s, 0); /* codecID is defined by the client */
+
+ if (!rdp_write_rfx_server_capability_container(s, settings))
+ return FALSE;
+ }
+ else
+ {
+ Stream_Write_UINT8(s, RDP_CODEC_ID_REMOTEFX); /* codecID */
+
+ if (!rdp_write_rfx_client_capability_container(s, settings))
+ return FALSE;
+ }
+ }
+
+ if (freerdp_settings_get_bool(settings, FreeRDP_NSCodec))
+ {
+ rdp_write_bitmap_codec_guid(s, &CODEC_GUID_NSCODEC); /* codecGUID */
+
+ if (settings->ServerMode)
+ {
+ Stream_Write_UINT8(s, 0); /* codecID is defined by the client */
+
+ if (!rdp_write_nsc_server_capability_container(s, settings))
+ return FALSE;
+ }
+ else
+ {
+ Stream_Write_UINT8(s, RDP_CODEC_ID_NSCODEC); /* codecID */
+
+ if (!rdp_write_nsc_client_capability_container(s, settings))
+ return FALSE;
+ }
+ }
+
+#if defined(WITH_JPEG)
+
+ if (settings->JpegCodec)
+ {
+ rdp_write_bitmap_codec_guid(s, &CODEC_GUID_JPEG); /* codecGUID */
+
+ if (settings->ServerMode)
+ {
+ Stream_Write_UINT8(s, 0); /* codecID is defined by the client */
+
+ if (!rdp_write_jpeg_server_capability_container(s, settings))
+ return FALSE;
+ }
+ else
+ {
+ Stream_Write_UINT8(s, RDP_CODEC_ID_JPEG); /* codecID */
+
+ if (!rdp_write_jpeg_client_capability_container(s, settings))
+ return FALSE;
+ }
+ }
+
+#endif
+
+ if (settings->RemoteFxImageCodec)
+ {
+ rdp_write_bitmap_codec_guid(s, &CODEC_GUID_IMAGE_REMOTEFX); /* codecGUID */
+
+ if (settings->ServerMode)
+ {
+ Stream_Write_UINT8(s, 0); /* codecID is defined by the client */
+
+ if (!rdp_write_rfx_server_capability_container(s, settings))
+ return FALSE;
+ }
+ else
+ {
+ Stream_Write_UINT8(s, RDP_CODEC_ID_IMAGE_REMOTEFX); /* codecID */
+
+ if (!rdp_write_rfx_client_capability_container(s, settings))
+ return FALSE;
+ }
+ }
+
+ return rdp_capability_set_finish(s, header, CAPSET_TYPE_BITMAP_CODECS);
+}
+
+#ifdef WITH_DEBUG_CAPABILITIES
+static BOOL rdp_print_bitmap_codecs_capability_set(wStream* s)
+{
+ GUID codecGuid = { 0 };
+ BYTE bitmapCodecCount = 0;
+ BYTE codecId = 0;
+ UINT16 codecPropertiesLength = 0;
+
+ WLog_VRB(TAG, "BitmapCodecsCapabilitySet (length %" PRIuz "):", Stream_GetRemainingLength(s));
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
+ return FALSE;
+
+ Stream_Read_UINT8(s, bitmapCodecCount); /* bitmapCodecCount (1 byte) */
+ WLog_VRB(TAG, "\tbitmapCodecCount: %" PRIu8 "", bitmapCodecCount);
+
+ while (bitmapCodecCount > 0)
+ {
+ if (!rdp_read_bitmap_codec_guid(s, &codecGuid)) /* codecGuid (16 bytes) */
+ return FALSE;
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 3))
+ return FALSE;
+ Stream_Read_UINT8(s, codecId); /* codecId (1 byte) */
+ WLog_VRB(TAG, "\tcodecGuid: 0x");
+ rdp_print_bitmap_codec_guid(&codecGuid);
+ WLog_VRB(TAG, " (%s)", rdp_get_bitmap_codec_guid_name(&codecGuid));
+ WLog_VRB(TAG, "\tcodecId: %" PRIu8 "", codecId);
+ Stream_Read_UINT16(s, codecPropertiesLength); /* codecPropertiesLength (2 bytes) */
+ WLog_VRB(TAG, "\tcodecPropertiesLength: %" PRIu16 "", codecPropertiesLength);
+
+ if (!Stream_SafeSeek(s, codecPropertiesLength)) /* codecProperties */
+ return FALSE;
+ bitmapCodecCount--;
+ }
+
+ return TRUE;
+}
+#endif
+
+static BOOL rdp_apply_frame_acknowledge_capability_set(rdpSettings* settings,
+ const rdpSettings* src)
+{
+ WINPR_ASSERT(settings);
+ WINPR_ASSERT(src);
+
+ if (settings->ServerMode)
+ settings->FrameAcknowledge = src->FrameAcknowledge;
+
+ return TRUE;
+}
+
+/*
+ * Read frame acknowledge capability set.
+ */
+
+static BOOL rdp_read_frame_acknowledge_capability_set(wStream* s, rdpSettings* settings)
+{
+ WINPR_ASSERT(settings);
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return FALSE;
+
+ Stream_Read_UINT32(s, settings->FrameAcknowledge); /* (4 bytes) */
+
+ return TRUE;
+}
+
+/*
+ * Write frame acknowledge capability set.
+ */
+
+static BOOL rdp_write_frame_acknowledge_capability_set(wStream* s, const rdpSettings* settings)
+{
+ WINPR_ASSERT(settings);
+ if (!Stream_EnsureRemainingCapacity(s, 32))
+ return FALSE;
+
+ const size_t header = rdp_capability_set_start(s);
+ Stream_Write_UINT32(s, settings->FrameAcknowledge); /* (4 bytes) */
+ return rdp_capability_set_finish(s, header, CAPSET_TYPE_FRAME_ACKNOWLEDGE);
+}
+
+#ifdef WITH_DEBUG_CAPABILITIES
+static BOOL rdp_print_frame_acknowledge_capability_set(wStream* s)
+{
+ UINT32 frameAcknowledge = 0;
+ WLog_VRB(TAG,
+ "FrameAcknowledgeCapabilitySet (length %" PRIuz "):", Stream_GetRemainingLength(s));
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return FALSE;
+
+ Stream_Read_UINT32(s, frameAcknowledge); /* frameAcknowledge (4 bytes) */
+ WLog_VRB(TAG, "\tframeAcknowledge: 0x%08" PRIX32 "", frameAcknowledge);
+ return TRUE;
+}
+#endif
+
+static BOOL rdp_apply_bitmap_cache_v3_codec_id_capability_set(rdpSettings* settings,
+ const rdpSettings* src)
+{
+ WINPR_ASSERT(settings);
+ WINPR_ASSERT(src);
+
+ settings->BitmapCacheV3CodecId = src->BitmapCacheV3CodecId;
+ return TRUE;
+}
+
+static BOOL rdp_read_bitmap_cache_v3_codec_id_capability_set(wStream* s, rdpSettings* settings)
+{
+ BYTE bitmapCacheV3CodecId = 0;
+
+ WINPR_ASSERT(settings);
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
+ return FALSE;
+
+ Stream_Read_UINT8(s, bitmapCacheV3CodecId); /* bitmapCacheV3CodecId (1 byte) */
+ settings->BitmapCacheV3CodecId = bitmapCacheV3CodecId;
+ return TRUE;
+}
+
+static BOOL rdp_write_bitmap_cache_v3_codec_id_capability_set(wStream* s,
+ const rdpSettings* settings)
+{
+ WINPR_ASSERT(settings);
+ if (!Stream_EnsureRemainingCapacity(s, 32))
+ return FALSE;
+
+ const size_t header = rdp_capability_set_start(s);
+ if (settings->BitmapCacheV3CodecId > UINT8_MAX)
+ return FALSE;
+ Stream_Write_UINT8(s, (UINT8)settings->BitmapCacheV3CodecId);
+ return rdp_capability_set_finish(s, header, CAPSET_TYPE_BITMAP_CACHE_V3_CODEC_ID);
+}
+
+#ifdef WITH_DEBUG_CAPABILITIES
+static BOOL rdp_print_bitmap_cache_v3_codec_id_capability_set(wStream* s)
+{
+ BYTE bitmapCacheV3CodecId = 0;
+ WLog_VRB(TAG, "BitmapCacheV3CodecIdCapabilitySet (length %" PRIuz "):",
+ Stream_GetRemainingLength(s));
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
+ return FALSE;
+
+ Stream_Read_UINT8(s, bitmapCacheV3CodecId); /* bitmapCacheV3CodecId (1 byte) */
+ WLog_VRB(TAG, "\tbitmapCacheV3CodecId: 0x%02" PRIX8 "", bitmapCacheV3CodecId);
+ return TRUE;
+}
+
+BOOL rdp_print_capability_sets(wStream* s, size_t start, BOOL receiving)
+{
+ BOOL rc = FALSE;
+ UINT16 type = 0;
+ UINT16 length = 0;
+ UINT16 numberCapabilities = 0;
+
+ size_t pos = Stream_GetPosition(s);
+
+ Stream_SetPosition(s, start);
+ if (receiving)
+ {
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ goto fail;
+ }
+ else
+ {
+ if (!Stream_CheckAndLogRequiredCapacity(TAG, (s), 4))
+ goto fail;
+ }
+
+ Stream_Read_UINT16(s, numberCapabilities);
+ Stream_Seek(s, 2);
+
+ while (numberCapabilities > 0)
+ {
+ size_t rest = 0;
+ wStream subBuffer;
+ wStream* sub = NULL;
+
+ if (!rdp_read_capability_set_header(s, &length, &type))
+ goto fail;
+
+ WLog_VRB(TAG, "%s ", receiving ? "Receiving" : "Sending");
+ sub = Stream_StaticInit(&subBuffer, Stream_Pointer(s), length - 4);
+ if (!Stream_SafeSeek(s, length - 4))
+ goto fail;
+
+ switch (type)
+ {
+ case CAPSET_TYPE_GENERAL:
+ if (!rdp_print_general_capability_set(sub))
+ goto fail;
+
+ break;
+
+ case CAPSET_TYPE_BITMAP:
+ if (!rdp_print_bitmap_capability_set(sub))
+ goto fail;
+
+ break;
+
+ case CAPSET_TYPE_ORDER:
+ if (!rdp_print_order_capability_set(sub))
+ goto fail;
+
+ break;
+
+ case CAPSET_TYPE_BITMAP_CACHE:
+ if (!rdp_print_bitmap_cache_capability_set(sub))
+ goto fail;
+
+ break;
+
+ case CAPSET_TYPE_CONTROL:
+ if (!rdp_print_control_capability_set(sub))
+ goto fail;
+
+ break;
+
+ case CAPSET_TYPE_ACTIVATION:
+ if (!rdp_print_window_activation_capability_set(sub))
+ goto fail;
+
+ break;
+
+ case CAPSET_TYPE_POINTER:
+ if (!rdp_print_pointer_capability_set(sub))
+ goto fail;
+
+ break;
+
+ case CAPSET_TYPE_SHARE:
+ if (!rdp_print_share_capability_set(sub))
+ goto fail;
+
+ break;
+
+ case CAPSET_TYPE_COLOR_CACHE:
+ if (!rdp_print_color_cache_capability_set(sub))
+ goto fail;
+
+ break;
+
+ case CAPSET_TYPE_SOUND:
+ if (!rdp_print_sound_capability_set(sub))
+ goto fail;
+
+ break;
+
+ case CAPSET_TYPE_INPUT:
+ if (!rdp_print_input_capability_set(sub))
+ goto fail;
+
+ break;
+
+ case CAPSET_TYPE_FONT:
+ if (!rdp_print_font_capability_set(sub))
+ goto fail;
+
+ break;
+
+ case CAPSET_TYPE_BRUSH:
+ if (!rdp_print_brush_capability_set(sub))
+ goto fail;
+
+ break;
+
+ case CAPSET_TYPE_GLYPH_CACHE:
+ if (!rdp_print_glyph_cache_capability_set(sub))
+ goto fail;
+
+ break;
+
+ case CAPSET_TYPE_OFFSCREEN_CACHE:
+ if (!rdp_print_offscreen_bitmap_cache_capability_set(sub))
+ goto fail;
+
+ break;
+
+ case CAPSET_TYPE_BITMAP_CACHE_HOST_SUPPORT:
+ if (!rdp_print_bitmap_cache_host_support_capability_set(sub))
+ goto fail;
+
+ break;
+
+ case CAPSET_TYPE_BITMAP_CACHE_V2:
+ if (!rdp_print_bitmap_cache_v2_capability_set(sub))
+ goto fail;
+
+ break;
+
+ case CAPSET_TYPE_VIRTUAL_CHANNEL:
+ if (!rdp_print_virtual_channel_capability_set(sub))
+ goto fail;
+
+ break;
+
+ case CAPSET_TYPE_DRAW_NINE_GRID_CACHE:
+ if (!rdp_print_draw_nine_grid_cache_capability_set(sub))
+ goto fail;
+
+ break;
+
+ case CAPSET_TYPE_DRAW_GDI_PLUS:
+ if (!rdp_print_draw_gdiplus_cache_capability_set(sub))
+ goto fail;
+
+ break;
+
+ case CAPSET_TYPE_RAIL:
+ if (!rdp_print_remote_programs_capability_set(sub))
+ goto fail;
+
+ break;
+
+ case CAPSET_TYPE_WINDOW:
+ if (!rdp_print_window_list_capability_set(sub))
+ goto fail;
+
+ break;
+
+ case CAPSET_TYPE_COMP_DESK:
+ if (!rdp_print_desktop_composition_capability_set(sub))
+ goto fail;
+
+ break;
+
+ case CAPSET_TYPE_MULTI_FRAGMENT_UPDATE:
+ if (!rdp_print_multifragment_update_capability_set(sub))
+ goto fail;
+
+ break;
+
+ case CAPSET_TYPE_LARGE_POINTER:
+ if (!rdp_print_large_pointer_capability_set(sub))
+ goto fail;
+
+ break;
+
+ case CAPSET_TYPE_SURFACE_COMMANDS:
+ if (!rdp_print_surface_commands_capability_set(sub))
+ goto fail;
+
+ break;
+
+ case CAPSET_TYPE_BITMAP_CODECS:
+ if (!rdp_print_bitmap_codecs_capability_set(sub))
+ goto fail;
+
+ break;
+
+ case CAPSET_TYPE_FRAME_ACKNOWLEDGE:
+ if (!rdp_print_frame_acknowledge_capability_set(sub))
+ goto fail;
+
+ break;
+
+ case CAPSET_TYPE_BITMAP_CACHE_V3_CODEC_ID:
+ if (!rdp_print_bitmap_cache_v3_codec_id_capability_set(sub))
+ goto fail;
+
+ break;
+
+ default:
+ WLog_ERR(TAG, "unknown capability type %" PRIu16 "", type);
+ break;
+ }
+
+ rest = Stream_GetRemainingLength(sub);
+ if (rest > 0)
+ {
+ WLog_WARN(TAG,
+ "incorrect capability offset, type:0x%04" PRIX16 " %" PRIu16
+ " bytes expected, %" PRIuz "bytes remaining",
+ type, length, rest);
+ }
+
+ numberCapabilities--;
+ }
+
+ rc = TRUE;
+fail:
+ Stream_SetPosition(s, pos);
+ return rc;
+}
+#endif
+
+static BOOL rdp_apply_from_received(UINT16 type, rdpSettings* dst, const rdpSettings* src)
+{
+ switch (type)
+ {
+ case CAPSET_TYPE_GENERAL:
+ return rdp_apply_general_capability_set(dst, src);
+ case CAPSET_TYPE_BITMAP:
+ return rdp_apply_bitmap_capability_set(dst, src);
+ case CAPSET_TYPE_ORDER:
+ return rdp_apply_order_capability_set(dst, src);
+ case CAPSET_TYPE_POINTER:
+ return rdp_apply_pointer_capability_set(dst, src);
+ case CAPSET_TYPE_INPUT:
+ return rdp_apply_input_capability_set(dst, src);
+ case CAPSET_TYPE_VIRTUAL_CHANNEL:
+ return rdp_apply_virtual_channel_capability_set(dst, src);
+ case CAPSET_TYPE_SHARE:
+ return rdp_apply_share_capability_set(dst, src);
+ case CAPSET_TYPE_COLOR_CACHE:
+ return rdp_apply_color_cache_capability_set(dst, src);
+ case CAPSET_TYPE_FONT:
+ return rdp_apply_font_capability_set(dst, src);
+ case CAPSET_TYPE_DRAW_GDI_PLUS:
+ return rdp_apply_draw_gdiplus_cache_capability_set(dst, src);
+ case CAPSET_TYPE_RAIL:
+ return rdp_apply_remote_programs_capability_set(dst, src);
+ case CAPSET_TYPE_WINDOW:
+ return rdp_apply_window_list_capability_set(dst, src);
+ case CAPSET_TYPE_MULTI_FRAGMENT_UPDATE:
+ return rdp_apply_multifragment_update_capability_set(dst, src);
+ case CAPSET_TYPE_LARGE_POINTER:
+ return rdp_apply_large_pointer_capability_set(dst, src);
+ case CAPSET_TYPE_COMP_DESK:
+ return rdp_apply_desktop_composition_capability_set(dst, src);
+ case CAPSET_TYPE_SURFACE_COMMANDS:
+ return rdp_apply_surface_commands_capability_set(dst, src);
+ case CAPSET_TYPE_BITMAP_CODECS:
+ return rdp_apply_bitmap_codecs_capability_set(dst, src);
+ case CAPSET_TYPE_FRAME_ACKNOWLEDGE:
+ return rdp_apply_frame_acknowledge_capability_set(dst, src);
+ case CAPSET_TYPE_BITMAP_CACHE_V3_CODEC_ID:
+ return rdp_apply_bitmap_cache_v3_codec_id_capability_set(dst, src);
+ case CAPSET_TYPE_BITMAP_CACHE:
+ return rdp_apply_bitmap_cache_capability_set(dst, src);
+ case CAPSET_TYPE_BITMAP_CACHE_V2:
+ return rdp_apply_bitmap_cache_v2_capability_set(dst, src);
+ case CAPSET_TYPE_BRUSH:
+ return rdp_apply_brush_capability_set(dst, src);
+ case CAPSET_TYPE_GLYPH_CACHE:
+ return rdp_apply_glyph_cache_capability_set(dst, src);
+ case CAPSET_TYPE_OFFSCREEN_CACHE:
+ return rdp_apply_offscreen_bitmap_cache_capability_set(dst, src);
+ case CAPSET_TYPE_SOUND:
+ return rdp_apply_sound_capability_set(dst, src);
+ case CAPSET_TYPE_CONTROL:
+ return rdp_apply_control_capability_set(dst, src);
+ case CAPSET_TYPE_ACTIVATION:
+ return rdp_apply_window_activation_capability_set(dst, src);
+ case CAPSET_TYPE_DRAW_NINE_GRID_CACHE:
+ return rdp_apply_draw_nine_grid_cache_capability_set(dst, src);
+ case CAPSET_TYPE_BITMAP_CACHE_HOST_SUPPORT:
+ return rdp_apply_bitmap_cache_host_support_capability_set(dst, src);
+ default:
+ return TRUE;
+ }
+}
+
+BOOL rdp_read_capability_set(wStream* sub, UINT16 type, rdpSettings* settings, BOOL isServer)
+{
+ WINPR_ASSERT(settings);
+
+ if (type <= CAPSET_TYPE_FRAME_ACKNOWLEDGE)
+ {
+ size_t size = Stream_Length(sub);
+
+ WINPR_ASSERT(settings->ReceivedCapabilities);
+ settings->ReceivedCapabilities[type] = TRUE;
+
+ WINPR_ASSERT(settings->ReceivedCapabilityDataSizes);
+ settings->ReceivedCapabilityDataSizes[type] = size;
+
+ WINPR_ASSERT(settings->ReceivedCapabilityData);
+ void* tmp = realloc(settings->ReceivedCapabilityData[type], size);
+ if (!tmp && (size > 0))
+ return FALSE;
+ memcpy(tmp, Stream_Buffer(sub), size);
+ settings->ReceivedCapabilityData[type] = tmp;
+ }
+ else
+ WLog_WARN(TAG, "not handling capability type %" PRIu16 " yet", type);
+
+ BOOL treated = TRUE;
+
+ switch (type)
+ {
+ case CAPSET_TYPE_GENERAL:
+ if (!rdp_read_general_capability_set(sub, settings))
+ return FALSE;
+
+ break;
+
+ case CAPSET_TYPE_BITMAP:
+ if (!rdp_read_bitmap_capability_set(sub, settings))
+ return FALSE;
+
+ break;
+
+ case CAPSET_TYPE_ORDER:
+ if (!rdp_read_order_capability_set(sub, settings))
+ return FALSE;
+
+ break;
+
+ case CAPSET_TYPE_POINTER:
+ if (!rdp_read_pointer_capability_set(sub, settings))
+ return FALSE;
+
+ break;
+
+ case CAPSET_TYPE_INPUT:
+ if (!rdp_read_input_capability_set(sub, settings))
+ return FALSE;
+
+ break;
+
+ case CAPSET_TYPE_VIRTUAL_CHANNEL:
+ if (!rdp_read_virtual_channel_capability_set(sub, settings))
+ return FALSE;
+
+ break;
+
+ case CAPSET_TYPE_SHARE:
+ if (!rdp_read_share_capability_set(sub, settings))
+ return FALSE;
+
+ break;
+
+ case CAPSET_TYPE_COLOR_CACHE:
+ if (!rdp_read_color_cache_capability_set(sub, settings))
+ return FALSE;
+
+ break;
+
+ case CAPSET_TYPE_FONT:
+ if (!rdp_read_font_capability_set(sub, settings))
+ return FALSE;
+
+ break;
+
+ case CAPSET_TYPE_DRAW_GDI_PLUS:
+ if (!rdp_read_draw_gdiplus_cache_capability_set(sub, settings))
+ return FALSE;
+
+ break;
+
+ case CAPSET_TYPE_RAIL:
+ if (!rdp_read_remote_programs_capability_set(sub, settings))
+ return FALSE;
+
+ break;
+
+ case CAPSET_TYPE_WINDOW:
+ if (!rdp_read_window_list_capability_set(sub, settings))
+ return FALSE;
+
+ break;
+
+ case CAPSET_TYPE_MULTI_FRAGMENT_UPDATE:
+ if (!rdp_read_multifragment_update_capability_set(sub, settings))
+ return FALSE;
+
+ break;
+
+ case CAPSET_TYPE_LARGE_POINTER:
+ if (!rdp_read_large_pointer_capability_set(sub, settings))
+ return FALSE;
+
+ break;
+
+ case CAPSET_TYPE_COMP_DESK:
+ if (!rdp_read_desktop_composition_capability_set(sub, settings))
+ return FALSE;
+
+ break;
+
+ case CAPSET_TYPE_SURFACE_COMMANDS:
+ if (!rdp_read_surface_commands_capability_set(sub, settings))
+ return FALSE;
+
+ break;
+
+ case CAPSET_TYPE_BITMAP_CODECS:
+ if (!rdp_read_bitmap_codecs_capability_set(sub, settings, isServer))
+ return FALSE;
+
+ break;
+
+ case CAPSET_TYPE_FRAME_ACKNOWLEDGE:
+ if (!rdp_read_frame_acknowledge_capability_set(sub, settings))
+ return FALSE;
+
+ break;
+
+ case CAPSET_TYPE_BITMAP_CACHE_V3_CODEC_ID:
+ if (!rdp_read_bitmap_cache_v3_codec_id_capability_set(sub, settings))
+ return FALSE;
+
+ break;
+
+ default:
+ treated = FALSE;
+ break;
+ }
+
+ if (!treated)
+ {
+ if (isServer)
+ {
+ /* treating capabilities that are supposed to be send only from the client */
+ switch (type)
+ {
+ case CAPSET_TYPE_BITMAP_CACHE:
+ if (!rdp_read_bitmap_cache_capability_set(sub, settings))
+ return FALSE;
+
+ break;
+
+ case CAPSET_TYPE_BITMAP_CACHE_V2:
+ if (!rdp_read_bitmap_cache_v2_capability_set(sub, settings))
+ return FALSE;
+
+ break;
+
+ case CAPSET_TYPE_BRUSH:
+ if (!rdp_read_brush_capability_set(sub, settings))
+ return FALSE;
+
+ break;
+
+ case CAPSET_TYPE_GLYPH_CACHE:
+ if (!rdp_read_glyph_cache_capability_set(sub, settings))
+ return FALSE;
+
+ break;
+
+ case CAPSET_TYPE_OFFSCREEN_CACHE:
+ if (!rdp_read_offscreen_bitmap_cache_capability_set(sub, settings))
+ return FALSE;
+
+ break;
+
+ case CAPSET_TYPE_SOUND:
+ if (!rdp_read_sound_capability_set(sub, settings))
+ return FALSE;
+
+ break;
+
+ case CAPSET_TYPE_CONTROL:
+ if (!rdp_read_control_capability_set(sub, settings))
+ return FALSE;
+
+ break;
+
+ case CAPSET_TYPE_ACTIVATION:
+ if (!rdp_read_window_activation_capability_set(sub, settings))
+ return FALSE;
+
+ break;
+
+ case CAPSET_TYPE_DRAW_NINE_GRID_CACHE:
+ if (!rdp_read_draw_nine_grid_cache_capability_set(sub, settings))
+ return FALSE;
+
+ break;
+
+ default:
+ WLog_ERR(TAG, "capability %s(%" PRIu16 ") not expected from client",
+ get_capability_name(type), type);
+ return FALSE;
+ }
+ }
+ else
+ {
+ /* treating capabilities that are supposed to be send only from the server */
+ switch (type)
+ {
+ case CAPSET_TYPE_BITMAP_CACHE_HOST_SUPPORT:
+ if (!rdp_read_bitmap_cache_host_support_capability_set(sub, settings))
+ return FALSE;
+
+ break;
+
+ default:
+ WLog_ERR(TAG, "capability %s(%" PRIu16 ") not expected from server",
+ get_capability_name(type), type);
+ return FALSE;
+ }
+ }
+ }
+
+ const size_t rest = Stream_GetRemainingLength(sub);
+ if (rest > 0)
+ {
+ const size_t length = Stream_Capacity(sub);
+ WLog_ERR(TAG,
+ "incorrect offset, type:0x%04" PRIx16 " actual:%" PRIuz " expected:%" PRIuz "",
+ type, length - rest, length);
+ }
+ return TRUE;
+}
+
+static BOOL rdp_read_capability_sets(wStream* s, rdpSettings* settings, rdpSettings* rcvSettings,
+ UINT16 totalLength)
+{
+ BOOL rc = FALSE;
+ size_t start = 0;
+ size_t end = 0;
+ size_t len = 0;
+ UINT16 numberCapabilities = 0;
+ UINT16 count = 0;
+
+#ifdef WITH_DEBUG_CAPABILITIES
+ const size_t capstart = Stream_GetPosition(s);
+#endif
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(settings);
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return FALSE;
+
+ Stream_Read_UINT16(s, numberCapabilities); /* numberCapabilities (2 bytes) */
+ Stream_Seek(s, 2); /* pad2Octets (2 bytes) */
+ count = numberCapabilities;
+
+ start = Stream_GetPosition(s);
+ while (numberCapabilities > 0 && Stream_GetRemainingLength(s) >= 4)
+ {
+ UINT16 type = 0;
+ UINT16 length = 0;
+ wStream subbuffer;
+ wStream* sub = NULL;
+
+ if (!rdp_read_capability_set_header(s, &length, &type))
+ goto fail;
+ sub = Stream_StaticInit(&subbuffer, Stream_Pointer(s), length - 4);
+ if (!Stream_SafeSeek(s, length - 4))
+ goto fail;
+
+ if (!rdp_read_capability_set(sub, type, rcvSettings, settings->ServerMode))
+ goto fail;
+
+ if (!rdp_apply_from_received(type, settings, rcvSettings))
+ goto fail;
+ numberCapabilities--;
+ }
+
+ end = Stream_GetPosition(s);
+ len = end - start;
+
+ if (numberCapabilities)
+ {
+ WLog_ERR(TAG,
+ "strange we haven't read the number of announced capacity sets, read=%d "
+ "expected=%" PRIu16 "",
+ count - numberCapabilities, count);
+ }
+
+#ifdef WITH_DEBUG_CAPABILITIES
+ rdp_print_capability_sets(s, capstart, TRUE);
+#endif
+
+ if (len > totalLength)
+ {
+ WLog_ERR(TAG, "Capability length expected %" PRIu16 ", actual %" PRIdz, totalLength, len);
+ goto fail;
+ }
+ rc = freerdp_capability_buffer_copy(settings, rcvSettings);
+fail:
+ return rc;
+}
+
+BOOL rdp_recv_get_active_header(rdpRdp* rdp, wStream* s, UINT16* pChannelId, UINT16* length)
+{
+ UINT16 securityFlags = 0;
+
+ WINPR_ASSERT(rdp);
+ WINPR_ASSERT(rdp->context);
+
+ if (!rdp_read_header(rdp, s, length, pChannelId))
+ return FALSE;
+
+ if (freerdp_shall_disconnect_context(rdp->context))
+ return TRUE;
+
+ if (rdp->settings->UseRdpSecurityLayer)
+ {
+ if (!rdp_read_security_header(rdp, s, &securityFlags, length))
+ return FALSE;
+
+ if (securityFlags & SEC_ENCRYPT)
+ {
+ if (!rdp_decrypt(rdp, s, length, securityFlags))
+ return FALSE;
+ }
+ }
+
+ if (*pChannelId != MCS_GLOBAL_CHANNEL_ID)
+ {
+ UINT16 mcsMessageChannelId = rdp->mcs->messageChannelId;
+
+ if ((mcsMessageChannelId == 0) || (*pChannelId != mcsMessageChannelId))
+ {
+ WLog_ERR(TAG, "unexpected MCS channel id %04" PRIx16 " received", *pChannelId);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+BOOL rdp_recv_demand_active(rdpRdp* rdp, wStream* s, UINT16 pduSource, UINT16 length)
+{
+ UINT16 lengthSourceDescriptor = 0;
+ UINT16 lengthCombinedCapabilities = 0;
+
+ WINPR_ASSERT(rdp);
+ WINPR_ASSERT(rdp->settings);
+ WINPR_ASSERT(rdp->context);
+ WINPR_ASSERT(s);
+
+ rdp->settings->PduSource = pduSource;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
+ return FALSE;
+
+ Stream_Read_UINT32(s, rdp->settings->ShareId); /* shareId (4 bytes) */
+ Stream_Read_UINT16(s, lengthSourceDescriptor); /* lengthSourceDescriptor (2 bytes) */
+ Stream_Read_UINT16(s, lengthCombinedCapabilities); /* lengthCombinedCapabilities (2 bytes) */
+
+ if (!Stream_SafeSeek(s, lengthSourceDescriptor) ||
+ !Stream_CheckAndLogRequiredLength(TAG, s, 4)) /* sourceDescriptor */
+ return FALSE;
+
+ /* capabilitySets */
+ if (!rdp_read_capability_sets(s, rdp->settings, rdp->remoteSettings,
+ lengthCombinedCapabilities))
+ {
+ WLog_ERR(TAG, "rdp_read_capability_sets failed");
+ return FALSE;
+ }
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return FALSE;
+
+ UINT32 SessionId = 0;
+ Stream_Read_UINT32(s, SessionId); /* SessionId */
+
+ {
+ rdp_secondary_update_internal* secondary = secondary_update_cast(rdp->update->secondary);
+ secondary->glyph_v2 = (rdp->settings->GlyphSupportLevel > GLYPH_SUPPORT_FULL);
+ }
+
+ return tpkt_ensure_stream_consumed(s, length);
+}
+
+static BOOL rdp_write_demand_active(wStream* s, rdpSettings* settings)
+{
+ size_t bm = 0;
+ size_t em = 0;
+ size_t lm = 0;
+ UINT16 numberCapabilities = 0;
+ size_t lengthCombinedCapabilities = 0;
+
+ if (!Stream_EnsureRemainingCapacity(s, 64))
+ return FALSE;
+
+ Stream_Write_UINT32(s, settings->ShareId); /* shareId (4 bytes) */
+ Stream_Write_UINT16(s, 4); /* lengthSourceDescriptor (2 bytes) */
+ lm = Stream_GetPosition(s);
+ Stream_Seek_UINT16(s); /* lengthCombinedCapabilities (2 bytes) */
+ Stream_Write(s, "RDP", 4); /* sourceDescriptor */
+ bm = Stream_GetPosition(s);
+ Stream_Seek_UINT16(s); /* numberCapabilities (2 bytes) */
+ Stream_Write_UINT16(s, 0); /* pad2Octets (2 bytes) */
+ numberCapabilities = 14;
+
+ if (!rdp_write_general_capability_set(s, settings) ||
+ !rdp_write_bitmap_capability_set(s, settings) ||
+ !rdp_write_order_capability_set(s, settings) ||
+ !rdp_write_pointer_capability_set(s, settings) ||
+ !rdp_write_input_capability_set(s, settings) ||
+ !rdp_write_virtual_channel_capability_set(s, settings) ||
+ !rdp_write_share_capability_set(s, settings) ||
+ !rdp_write_font_capability_set(s, settings) ||
+ !rdp_write_multifragment_update_capability_set(s, settings) ||
+ !rdp_write_large_pointer_capability_set(s, settings) ||
+ !rdp_write_desktop_composition_capability_set(s, settings) ||
+ !rdp_write_surface_commands_capability_set(s, settings) ||
+ !rdp_write_bitmap_codecs_capability_set(s, settings) ||
+ !rdp_write_frame_acknowledge_capability_set(s, settings))
+ {
+ return FALSE;
+ }
+
+ if (freerdp_settings_get_bool(settings, FreeRDP_BitmapCachePersistEnabled))
+ {
+ numberCapabilities++;
+
+ if (!rdp_write_bitmap_cache_host_support_capability_set(s, settings))
+ return FALSE;
+ }
+
+ if (settings->RemoteApplicationMode)
+ {
+ numberCapabilities += 2;
+
+ if (!rdp_write_remote_programs_capability_set(s, settings) ||
+ !rdp_write_window_list_capability_set(s, settings))
+ return FALSE;
+ }
+
+ em = Stream_GetPosition(s);
+ Stream_SetPosition(s, lm); /* go back to lengthCombinedCapabilities */
+ lengthCombinedCapabilities = (em - bm);
+ if (lengthCombinedCapabilities > UINT16_MAX)
+ return FALSE;
+ Stream_Write_UINT16(
+ s, (UINT16)lengthCombinedCapabilities); /* lengthCombinedCapabilities (2 bytes) */
+ Stream_SetPosition(s, bm); /* go back to numberCapabilities */
+ Stream_Write_UINT16(s, numberCapabilities); /* numberCapabilities (2 bytes) */
+#ifdef WITH_DEBUG_CAPABILITIES
+ rdp_print_capability_sets(s, bm, FALSE);
+#endif
+ Stream_SetPosition(s, em);
+ Stream_Write_UINT32(s, 0); /* sessionId */
+ return TRUE;
+}
+
+BOOL rdp_send_demand_active(rdpRdp* rdp)
+{
+ wStream* s = rdp_send_stream_pdu_init(rdp);
+ BOOL status = 0;
+
+ if (!s)
+ return FALSE;
+
+ rdp->settings->ShareId = 0x10000 + rdp->mcs->userId;
+ status = rdp_write_demand_active(s, rdp->settings) &&
+ rdp_send_pdu(rdp, s, PDU_TYPE_DEMAND_ACTIVE, rdp->mcs->userId);
+ Stream_Release(s);
+ return status;
+}
+
+BOOL rdp_recv_confirm_active(rdpRdp* rdp, wStream* s, UINT16 pduLength)
+{
+ rdpSettings* settings = NULL;
+ UINT16 lengthSourceDescriptor = 0;
+ UINT16 lengthCombinedCapabilities = 0;
+
+ WINPR_ASSERT(rdp);
+ WINPR_ASSERT(s);
+ settings = rdp->settings;
+ WINPR_ASSERT(settings);
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 10))
+ return FALSE;
+
+ Stream_Seek_UINT32(s); /* shareId (4 bytes) */
+ Stream_Seek_UINT16(s); /* originatorId (2 bytes) */
+ Stream_Read_UINT16(s, lengthSourceDescriptor); /* lengthSourceDescriptor (2 bytes) */
+ Stream_Read_UINT16(s, lengthCombinedCapabilities); /* lengthCombinedCapabilities (2 bytes) */
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, lengthSourceDescriptor + 4U))
+ return FALSE;
+
+ Stream_Seek(s, lengthSourceDescriptor); /* sourceDescriptor */
+ if (!rdp_read_capability_sets(s, rdp->settings, rdp->remoteSettings,
+ lengthCombinedCapabilities))
+ return FALSE;
+
+ if (!settings->ReceivedCapabilities[CAPSET_TYPE_SURFACE_COMMANDS])
+ {
+ /* client does not support surface commands */
+ settings->SurfaceCommandsEnabled = FALSE;
+ settings->SurfaceFrameMarkerEnabled = FALSE;
+ }
+
+ if (!settings->ReceivedCapabilities[CAPSET_TYPE_FRAME_ACKNOWLEDGE])
+ {
+ /* client does not support frame acks */
+ settings->FrameAcknowledge = 0;
+ }
+
+ if (!settings->ReceivedCapabilities[CAPSET_TYPE_BITMAP_CACHE_V3_CODEC_ID])
+ {
+ /* client does not support bitmap cache v3 */
+ settings->BitmapCacheV3Enabled = FALSE;
+ }
+
+ if (!settings->ReceivedCapabilities[CAPSET_TYPE_BITMAP_CODECS])
+ {
+ /* client does not support bitmap codecs */
+ settings->RemoteFxCodec = FALSE;
+ freerdp_settings_set_bool(settings, FreeRDP_NSCodec, FALSE);
+ settings->JpegCodec = FALSE;
+ }
+
+ if (!settings->ReceivedCapabilities[CAPSET_TYPE_MULTI_FRAGMENT_UPDATE])
+ {
+ /* client does not support multi fragment updates - make sure packages are not fragmented */
+ settings->MultifragMaxRequestSize = FASTPATH_FRAGMENT_SAFE_SIZE;
+ }
+
+ if (!settings->ReceivedCapabilities[CAPSET_TYPE_LARGE_POINTER])
+ {
+ /* client does not support large pointers */
+ settings->LargePointerFlag = 0;
+ }
+
+ return tpkt_ensure_stream_consumed(s, pduLength);
+}
+
+static BOOL rdp_write_confirm_active(wStream* s, rdpSettings* settings)
+{
+ size_t bm = 0;
+ size_t em = 0;
+ size_t lm = 0;
+ UINT16 numberCapabilities = 0;
+ UINT16 lengthSourceDescriptor = 0;
+ size_t lengthCombinedCapabilities = 0;
+ BOOL ret = 0;
+
+ WINPR_ASSERT(settings);
+
+ lengthSourceDescriptor = sizeof(SOURCE_DESCRIPTOR);
+ Stream_Write_UINT32(s, settings->ShareId); /* shareId (4 bytes) */
+ Stream_Write_UINT16(s, 0x03EA); /* originatorId (2 bytes) */
+ Stream_Write_UINT16(s, lengthSourceDescriptor); /* lengthSourceDescriptor (2 bytes) */
+ lm = Stream_GetPosition(s);
+ Stream_Seek_UINT16(s); /* lengthCombinedCapabilities (2 bytes) */
+ Stream_Write(s, SOURCE_DESCRIPTOR, lengthSourceDescriptor); /* sourceDescriptor */
+ bm = Stream_GetPosition(s);
+ Stream_Seek_UINT16(s); /* numberCapabilities (2 bytes) */
+ Stream_Write_UINT16(s, 0); /* pad2Octets (2 bytes) */
+ /* Capability Sets */
+ numberCapabilities = 15;
+
+ if (!rdp_write_general_capability_set(s, settings) ||
+ !rdp_write_bitmap_capability_set(s, settings) ||
+ !rdp_write_order_capability_set(s, settings))
+ return FALSE;
+
+ if (settings->RdpVersion >= RDP_VERSION_5_PLUS)
+ ret = rdp_write_bitmap_cache_v2_capability_set(s, settings);
+ else
+ ret = rdp_write_bitmap_cache_capability_set(s, settings);
+
+ if (!ret)
+ return FALSE;
+
+ if (!rdp_write_pointer_capability_set(s, settings) ||
+ !rdp_write_input_capability_set(s, settings) ||
+ !rdp_write_brush_capability_set(s, settings) ||
+ !rdp_write_glyph_cache_capability_set(s, settings) ||
+ !rdp_write_virtual_channel_capability_set(s, settings) ||
+ !rdp_write_sound_capability_set(s, settings) ||
+ !rdp_write_share_capability_set(s, settings) ||
+ !rdp_write_font_capability_set(s, settings) ||
+ !rdp_write_control_capability_set(s, settings) ||
+ !rdp_write_color_cache_capability_set(s, settings) ||
+ !rdp_write_window_activation_capability_set(s, settings))
+ {
+ return FALSE;
+ }
+
+ if (settings->OffscreenSupportLevel)
+ {
+ numberCapabilities++;
+
+ if (!rdp_write_offscreen_bitmap_cache_capability_set(s, settings))
+ return FALSE;
+ }
+
+ if (settings->DrawNineGridEnabled)
+ {
+ numberCapabilities++;
+
+ if (!rdp_write_draw_nine_grid_cache_capability_set(s, settings))
+ return FALSE;
+ }
+
+ if (settings->ReceivedCapabilities[CAPSET_TYPE_LARGE_POINTER])
+ {
+ if (settings->LargePointerFlag)
+ {
+ numberCapabilities++;
+
+ if (!rdp_write_large_pointer_capability_set(s, settings))
+ return FALSE;
+ }
+ }
+
+ if (settings->RemoteApplicationMode)
+ {
+ numberCapabilities += 2;
+
+ if (!rdp_write_remote_programs_capability_set(s, settings) ||
+ !rdp_write_window_list_capability_set(s, settings))
+ return FALSE;
+ }
+
+ if (settings->ReceivedCapabilities[CAPSET_TYPE_MULTI_FRAGMENT_UPDATE])
+ {
+ numberCapabilities++;
+
+ if (!rdp_write_multifragment_update_capability_set(s, settings))
+ return FALSE;
+ }
+
+ if (settings->ReceivedCapabilities[CAPSET_TYPE_SURFACE_COMMANDS])
+ {
+ numberCapabilities++;
+
+ if (!rdp_write_surface_commands_capability_set(s, settings))
+ return FALSE;
+ }
+
+ if (settings->ReceivedCapabilities[CAPSET_TYPE_BITMAP_CODECS])
+ {
+ numberCapabilities++;
+
+ if (!rdp_write_bitmap_codecs_capability_set(s, settings))
+ return FALSE;
+ }
+
+ if (!settings->ReceivedCapabilities[CAPSET_TYPE_FRAME_ACKNOWLEDGE])
+ settings->FrameAcknowledge = 0;
+
+ if (settings->FrameAcknowledge)
+ {
+ numberCapabilities++;
+
+ if (!rdp_write_frame_acknowledge_capability_set(s, settings))
+ return FALSE;
+ }
+
+ if (settings->ReceivedCapabilities[CAPSET_TYPE_BITMAP_CACHE_V3_CODEC_ID])
+ {
+ if (settings->BitmapCacheV3CodecId != 0)
+ {
+ numberCapabilities++;
+
+ if (!rdp_write_bitmap_cache_v3_codec_id_capability_set(s, settings))
+ return FALSE;
+ }
+ }
+
+ em = Stream_GetPosition(s);
+ Stream_SetPosition(s, lm); /* go back to lengthCombinedCapabilities */
+ lengthCombinedCapabilities = (em - bm);
+ if (lengthCombinedCapabilities > UINT16_MAX)
+ return FALSE;
+ Stream_Write_UINT16(
+ s, (UINT16)lengthCombinedCapabilities); /* lengthCombinedCapabilities (2 bytes) */
+ Stream_SetPosition(s, bm); /* go back to numberCapabilities */
+ Stream_Write_UINT16(s, numberCapabilities); /* numberCapabilities (2 bytes) */
+#ifdef WITH_DEBUG_CAPABILITIES
+ rdp_print_capability_sets(s, bm, FALSE);
+#endif
+ Stream_SetPosition(s, em);
+
+ return TRUE;
+}
+
+BOOL rdp_send_confirm_active(rdpRdp* rdp)
+{
+ wStream* s = rdp_send_stream_pdu_init(rdp);
+ BOOL status = 0;
+
+ if (!s)
+ return FALSE;
+
+ status = rdp_write_confirm_active(s, rdp->settings) &&
+ rdp_send_pdu(rdp, s, PDU_TYPE_CONFIRM_ACTIVE, rdp->mcs->userId);
+ Stream_Release(s);
+ return status;
+}
+
+const char* rdp_input_flag_string(UINT16 flags, char* buffer, size_t len)
+{
+ char prefix[16] = { 0 };
+
+ _snprintf(prefix, sizeof(prefix), "[0x%04" PRIx16 "][", flags);
+ winpr_str_append(prefix, buffer, len, "");
+ if ((flags & INPUT_FLAG_SCANCODES) != 0)
+ winpr_str_append("INPUT_FLAG_SCANCODES", buffer, len, "|");
+ if ((flags & INPUT_FLAG_MOUSEX) != 0)
+ winpr_str_append("INPUT_FLAG_MOUSEX", buffer, len, "|");
+ if ((flags & INPUT_FLAG_FASTPATH_INPUT) != 0)
+ winpr_str_append("INPUT_FLAG_FASTPATH_INPUT", buffer, len, "|");
+ if ((flags & INPUT_FLAG_UNICODE) != 0)
+ winpr_str_append("INPUT_FLAG_UNICODE", buffer, len, "|");
+ if ((flags & INPUT_FLAG_FASTPATH_INPUT2) != 0)
+ winpr_str_append("INPUT_FLAG_FASTPATH_INPUT2", buffer, len, "|");
+ if ((flags & INPUT_FLAG_UNUSED1) != 0)
+ winpr_str_append("INPUT_FLAG_UNUSED1", buffer, len, "|");
+ if ((flags & INPUT_FLAG_MOUSE_RELATIVE) != 0)
+ winpr_str_append("INPUT_FLAG_MOUSE_RELATIVE", buffer, len, "|");
+ if ((flags & TS_INPUT_FLAG_MOUSE_HWHEEL) != 0)
+ winpr_str_append("TS_INPUT_FLAG_MOUSE_HWHEEL", buffer, len, "|");
+ if ((flags & TS_INPUT_FLAG_QOE_TIMESTAMPS) != 0)
+ winpr_str_append("TS_INPUT_FLAG_QOE_TIMESTAMPS", buffer, len, "|");
+ winpr_str_append("]", buffer, len, "");
+ return buffer;
+}