summaryrefslogtreecommitdiffstats
path: root/libfreerdp/utils/cliprdr_utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'libfreerdp/utils/cliprdr_utils.c')
-rw-r--r--libfreerdp/utils/cliprdr_utils.c258
1 files changed, 258 insertions, 0 deletions
diff --git a/libfreerdp/utils/cliprdr_utils.c b/libfreerdp/utils/cliprdr_utils.c
new file mode 100644
index 0000000..5835ab6
--- /dev/null
+++ b/libfreerdp/utils/cliprdr_utils.c
@@ -0,0 +1,258 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Clipboard Virtual Channel Extension
+ *
+ * Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ * Copyright 2022 Armin Novak <anovak@thincast.com
+ * Copyright 2022 Thincast Technologies GmbH
+ *
+ * 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/stream.h>
+#include <freerdp/utils/cliprdr_utils.h>
+#include <freerdp/channels/cliprdr.h>
+
+#include <freerdp/log.h>
+#define TAG FREERDP_TAG("utils." CLIPRDR_SVC_CHANNEL_NAME)
+
+#define CLIPRDR_FILEDESCRIPTOR_SIZE (4 + 32 + 4 + 16 + 8 + 8 + 520)
+#define CLIPRDR_MAX_FILE_SIZE (2U * 1024 * 1024 * 1024)
+
+static UINT64 filetime_to_uint64(FILETIME value)
+{
+ UINT64 converted = 0;
+ converted |= (UINT32)value.dwHighDateTime;
+ converted <<= 32;
+ converted |= (UINT32)value.dwLowDateTime;
+ return converted;
+}
+
+static FILETIME uint64_to_filetime(UINT64 value)
+{
+ FILETIME converted;
+ converted.dwLowDateTime = (UINT32)(value >> 0);
+ converted.dwHighDateTime = (UINT32)(value >> 32);
+ return converted;
+}
+
+/**
+ * Parse a packed file list.
+ *
+ * The resulting array must be freed with the `free()` function.
+ *
+ * @param [in] format_data packed `CLIPRDR_FILELIST` to parse.
+ * @param [in] format_data_length length of `format_data` in bytes.
+ * @param [out] file_descriptor_array parsed array of `FILEDESCRIPTOR` structs.
+ * @param [out] file_descriptor_count number of elements in `file_descriptor_array`.
+ *
+ * @returns 0 on success, otherwise a Win32 error code.
+ */
+UINT cliprdr_parse_file_list(const BYTE* format_data, UINT32 format_data_length,
+ FILEDESCRIPTORW** file_descriptor_array, UINT32* file_descriptor_count)
+{
+ UINT result = NO_ERROR;
+ UINT32 count = 0;
+ wStream sbuffer;
+ wStream* s = NULL;
+
+ if (!format_data || !file_descriptor_array || !file_descriptor_count)
+ return ERROR_BAD_ARGUMENTS;
+
+ s = Stream_StaticConstInit(&sbuffer, format_data, format_data_length);
+ if (!s)
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ {
+ result = ERROR_INCORRECT_SIZE;
+ goto out;
+ }
+
+ Stream_Read_UINT32(s, count); /* cItems (4 bytes) */
+
+ if (!Stream_CheckAndLogRequiredLengthOfSize(TAG, s, count, CLIPRDR_FILEDESCRIPTOR_SIZE))
+ {
+ result = ERROR_INCORRECT_SIZE;
+ goto out;
+ }
+
+ *file_descriptor_count = count;
+ *file_descriptor_array = calloc(count, sizeof(FILEDESCRIPTORW));
+ if (!*file_descriptor_array)
+ {
+ result = ERROR_NOT_ENOUGH_MEMORY;
+ goto out;
+ }
+
+ for (UINT32 i = 0; i < count; i++)
+ {
+ FILEDESCRIPTORW* file = &((*file_descriptor_array)[i]);
+
+ if (!cliprdr_read_filedescriptor(s, file))
+ goto out;
+ }
+
+ if (Stream_GetRemainingLength(s) > 0)
+ WLog_WARN(TAG, "packed file list has %" PRIuz " excess bytes",
+ Stream_GetRemainingLength(s));
+out:
+
+ return result;
+}
+
+BOOL cliprdr_read_filedescriptor(wStream* s, FILEDESCRIPTORW* file)
+{
+ UINT64 tmp = 0;
+ WINPR_ASSERT(file);
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, sizeof(FILEDESCRIPTORW)))
+ return FALSE;
+
+ Stream_Read_UINT32(s, file->dwFlags); /* flags (4 bytes) */
+ Stream_Read_UINT32(s, file->clsid.Data1);
+ Stream_Read_UINT16(s, file->clsid.Data2);
+ Stream_Read_UINT16(s, file->clsid.Data3);
+ Stream_Read(s, &file->clsid.Data4, sizeof(file->clsid.Data4));
+ Stream_Read_INT32(s, file->sizel.cx);
+ Stream_Read_INT32(s, file->sizel.cy);
+ Stream_Read_INT32(s, file->pointl.x);
+ Stream_Read_INT32(s, file->pointl.y);
+ Stream_Read_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */
+ Stream_Read_UINT64(s, tmp); /* ftCreationTime (8 bytes) */
+ file->ftCreationTime = uint64_to_filetime(tmp);
+ Stream_Read_UINT64(s, tmp); /* ftLastAccessTime (8 bytes) */
+ file->ftLastAccessTime = uint64_to_filetime(tmp);
+ Stream_Read_UINT64(s, tmp); /* lastWriteTime (8 bytes) */
+ file->ftLastWriteTime = uint64_to_filetime(tmp);
+ Stream_Read_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */
+ Stream_Read_UINT32(s, file->nFileSizeLow); /* fileSizeLow (4 bytes) */
+ Stream_Read_UTF16_String(s, file->cFileName,
+ ARRAYSIZE(file->cFileName)); /* cFileName (520 bytes) */
+ return TRUE;
+}
+
+BOOL cliprdr_write_filedescriptor(wStream* s, const FILEDESCRIPTORW* file)
+{
+ WINPR_ASSERT(file);
+
+ if (!Stream_EnsureRemainingCapacity(s, sizeof(FILEDESCRIPTORW)))
+ return FALSE;
+
+ Stream_Write_UINT32(s, file->dwFlags); /* flags (4 bytes) */
+
+ Stream_Write_UINT32(s, file->clsid.Data1);
+ Stream_Write_UINT16(s, file->clsid.Data2);
+ Stream_Write_UINT16(s, file->clsid.Data3);
+ Stream_Write(s, &file->clsid.Data4, sizeof(file->clsid.Data4));
+ Stream_Write_INT32(s, file->sizel.cx);
+ Stream_Write_INT32(s, file->sizel.cy);
+ Stream_Write_INT32(s, file->pointl.x);
+ Stream_Write_INT32(s, file->pointl.y);
+ Stream_Write_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */
+ Stream_Write_UINT64(s, filetime_to_uint64(file->ftCreationTime));
+ Stream_Write_UINT64(s, filetime_to_uint64(file->ftLastAccessTime));
+ Stream_Write_UINT64(s, filetime_to_uint64(file->ftLastWriteTime)); /* lastWriteTime (8 bytes) */
+ Stream_Write_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */
+ Stream_Write_UINT32(s, file->nFileSizeLow); /* fileSizeLow (4 bytes) */
+ Stream_Write_UTF16_String(s, file->cFileName,
+ ARRAYSIZE(file->cFileName)); /* cFileName (520 bytes) */
+ return TRUE;
+}
+
+/**
+ * Serialize a packed file list.
+ *
+ * The resulting format data must be freed with the `free()` function.
+ *
+ * @param [in] file_descriptor_array array of `FILEDESCRIPTOR` structs to serialize.
+ * @param [in] file_descriptor_count number of elements in `file_descriptor_array`.
+ * @param [out] format_data serialized CLIPRDR_FILELIST.
+ * @param [out] format_data_length length of `format_data` in bytes.
+ *
+ * @returns 0 on success, otherwise a Win32 error code.
+ */
+UINT cliprdr_serialize_file_list(const FILEDESCRIPTORW* file_descriptor_array,
+ UINT32 file_descriptor_count, BYTE** format_data,
+ UINT32* format_data_length)
+{
+ return cliprdr_serialize_file_list_ex(CB_STREAM_FILECLIP_ENABLED, file_descriptor_array,
+ file_descriptor_count, format_data, format_data_length);
+}
+
+UINT cliprdr_serialize_file_list_ex(UINT32 flags, const FILEDESCRIPTORW* file_descriptor_array,
+ UINT32 file_descriptor_count, BYTE** format_data,
+ UINT32* format_data_length)
+{
+ UINT result = NO_ERROR;
+ size_t len = 0;
+ wStream* s = NULL;
+
+ if (!file_descriptor_array || !format_data || !format_data_length)
+ return ERROR_BAD_ARGUMENTS;
+
+ if ((flags & CB_STREAM_FILECLIP_ENABLED) == 0)
+ {
+ WLog_WARN(TAG, "No file clipboard support annouonced!");
+ return ERROR_BAD_ARGUMENTS;
+ }
+
+ s = Stream_New(NULL, 4 + file_descriptor_count * CLIPRDR_FILEDESCRIPTOR_SIZE);
+ if (!s)
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ Stream_Write_UINT32(s, file_descriptor_count); /* cItems (4 bytes) */
+
+ for (UINT32 i = 0; i < file_descriptor_count; i++)
+ {
+ const FILEDESCRIPTORW* file = &file_descriptor_array[i];
+
+ /*
+ * There is a known issue with Windows server getting stuck in
+ * an infinite loop when downloading files that are larger than
+ * 2 gigabytes. Do not allow clients to send such file lists.
+ *
+ * https://support.microsoft.com/en-us/help/2258090
+ */
+ if ((flags & CB_HUGE_FILE_SUPPORT_ENABLED) == 0)
+ {
+ if ((file->nFileSizeHigh > 0) || (file->nFileSizeLow >= CLIPRDR_MAX_FILE_SIZE))
+ {
+ WLog_ERR(TAG, "cliprdr does not support files over 2 GB");
+ result = ERROR_FILE_TOO_LARGE;
+ goto error;
+ }
+ }
+
+ if (!cliprdr_write_filedescriptor(s, file))
+ goto error;
+ }
+
+ Stream_SealLength(s);
+
+ Stream_GetBuffer(s, *format_data);
+ Stream_GetLength(s, len);
+ if (len > UINT32_MAX)
+ goto error;
+
+ *format_data_length = (UINT32)len;
+
+ Stream_Free(s, FALSE);
+
+ return result;
+
+error:
+ Stream_Free(s, TRUE);
+
+ return result;
+}