Blame channels/cliprdr/client/cliprdr_format.c

Packit Service fa4841
/**
Packit Service fa4841
 * FreeRDP: A Remote Desktop Protocol Implementation
Packit Service fa4841
 * Clipboard Virtual Channel
Packit Service fa4841
 *
Packit Service fa4841
 * Copyright 2009-2011 Jay Sorg
Packit Service fa4841
 * Copyright 2010-2011 Vic Lee
Packit Service fa4841
 * Copyright 2015 Thincast Technologies GmbH
Packit Service fa4841
 * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
Packit Service fa4841
 *
Packit Service fa4841
 * Licensed under the Apache License, Version 2.0 (the "License");
Packit Service fa4841
 * you may not use this file except in compliance with the License.
Packit Service fa4841
 * You may obtain a copy of the License at
Packit Service fa4841
 *
Packit Service fa4841
 *     http://www.apache.org/licenses/LICENSE-2.0
Packit Service fa4841
 *
Packit Service fa4841
 * Unless required by applicable law or agreed to in writing, software
Packit Service fa4841
 * distributed under the License is distributed on an "AS IS" BASIS,
Packit Service fa4841
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit Service fa4841
 * See the License for the specific language governing permissions and
Packit Service fa4841
 * limitations under the License.
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
#ifdef HAVE_CONFIG_H
Packit Service fa4841
#include "config.h"
Packit Service fa4841
#endif
Packit Service fa4841
Packit Service fa4841
#include <winpr/crt.h>
Packit Service fa4841
#include <winpr/print.h>
Packit Service fa4841
Packit Service fa4841
#include <freerdp/types.h>
Packit Service fa4841
#include <freerdp/constants.h>
Packit Service fa4841
#include <freerdp/client/cliprdr.h>
Packit Service fa4841
Packit Service fa4841
#include "cliprdr_main.h"
Packit Service fa4841
#include "cliprdr_format.h"
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Function description
Packit Service fa4841
 *
Packit Service fa4841
 * @return 0 on success, otherwise a Win32 error code
Packit Service fa4841
 */
Packit Service bb5c11
UINT cliprdr_process_format_list(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags)
Packit Service fa4841
{
Packit Service bb5c11
	UINT32 index;
Packit Service bb5c11
	size_t position;
Packit Service bb5c11
	BOOL asciiNames;
Packit Service bb5c11
	int formatNameLength;
Packit Service bb5c11
	char* szFormatName;
Packit Service bb5c11
	WCHAR* wszFormatName;
Packit Service bb5c11
	CLIPRDR_FORMAT* formats = NULL;
Packit Service bb5c11
	CLIPRDR_FORMAT_LIST formatList;
Packit Service fa4841
	CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
Packit Service fa4841
	UINT error = CHANNEL_RC_OK;
Packit Service fa4841
Packit Service fa4841
	if (!context->custom)
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "context->custom not set!");
Packit Service fa4841
		return ERROR_INTERNAL_ERROR;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service bb5c11
	asciiNames = (msgFlags & CB_ASCII_NAMES) ? TRUE : FALSE;
Packit Service bb5c11
Packit Service fa4841
	formatList.msgType = CB_FORMAT_LIST;
Packit Service fa4841
	formatList.msgFlags = msgFlags;
Packit Service fa4841
	formatList.dataLen = dataLen;
Packit Service fa4841
Packit Service bb5c11
	index = 0;
Packit Service bb5c11
	formatList.numFormats = 0;
Packit Service bb5c11
	position = Stream_GetPosition(s);
Packit Service bb5c11
Packit Service bb5c11
	if (!formatList.dataLen)
Packit Service bb5c11
	{
Packit Service bb5c11
		/* empty format list */
Packit Service bb5c11
		formatList.formats = NULL;
Packit Service bb5c11
		formatList.numFormats = 0;
Packit Service bb5c11
	}
Packit Service bb5c11
	else if (!cliprdr->useLongFormatNames)
Packit Service bb5c11
	{
Packit Service bb5c11
		formatList.numFormats = (dataLen / 36);
Packit Service bb5c11
Packit Service bb5c11
		if ((formatList.numFormats * 36) != dataLen)
Packit Service bb5c11
		{
Packit Service bb5c11
			WLog_ERR(TAG, "Invalid short format list length: %"PRIu32"", dataLen);
Packit Service bb5c11
			return ERROR_INTERNAL_ERROR;
Packit Service bb5c11
		}
Packit Service bb5c11
Packit Service bb5c11
		if (formatList.numFormats)
Packit Service bb5c11
			formats = (CLIPRDR_FORMAT*) calloc(formatList.numFormats, sizeof(CLIPRDR_FORMAT));
Packit Service bb5c11
Packit Service bb5c11
		if (!formats)
Packit Service bb5c11
		{
Packit Service bb5c11
			WLog_ERR(TAG, "calloc failed!");
Packit Service bb5c11
			return CHANNEL_RC_NO_MEMORY;
Packit Service bb5c11
		}
Packit Service bb5c11
Packit Service bb5c11
		formatList.formats = formats;
Packit Service bb5c11
Packit Service bb5c11
		while (dataLen)
Packit Service bb5c11
		{
Packit Service bb5c11
			Stream_Read_UINT32(s, formats[index].formatId); /* formatId (4 bytes) */
Packit Service bb5c11
			dataLen -= 4;
Packit Service bb5c11
Packit Service bb5c11
			formats[index].formatName = NULL;
Packit Service bb5c11
Packit Service bb5c11
			/* According to MS-RDPECLIP 2.2.3.1.1.1 formatName is "a 32-byte block containing
Packit Service bb5c11
			 * the *null-terminated* name assigned to the Clipboard Format: (32 ASCII 8 characters
Packit Service bb5c11
			 * or 16 Unicode characters)"
Packit Service bb5c11
			 * However, both Windows RDSH and mstsc violate this specs as seen in the following
Packit Service bb5c11
			 * example of a transferred short format name string: [R.i.c.h. .T.e.x.t. .F.o.r.m.a.t.]
Packit Service bb5c11
			 * These are 16 unicode charaters - *without* terminating null !
Packit Service bb5c11
			 */
Packit Service bb5c11
Packit Service bb5c11
			if (asciiNames)
Packit Service bb5c11
			{
Packit Service bb5c11
				szFormatName = (char*) Stream_Pointer(s);
Packit Service bb5c11
Packit Service bb5c11
				if (szFormatName[0])
Packit Service bb5c11
				{
Packit Service bb5c11
					/* ensure null termination */
Packit Service bb5c11
					formats[index].formatName = (char*) malloc(32 + 1);
Packit Service bb5c11
					if (!formats[index].formatName)
Packit Service bb5c11
					{
Packit Service bb5c11
						WLog_ERR(TAG, "malloc failed!");
Packit Service bb5c11
						error = CHANNEL_RC_NO_MEMORY;
Packit Service bb5c11
						goto error_out;
Packit Service bb5c11
					}
Packit Service bb5c11
					CopyMemory(formats[index].formatName, szFormatName, 32);
Packit Service bb5c11
					formats[index].formatName[32] = '\0';
Packit Service bb5c11
				}
Packit Service bb5c11
			}
Packit Service bb5c11
			else
Packit Service bb5c11
			{
Packit Service bb5c11
				wszFormatName = (WCHAR*) Stream_Pointer(s);
Packit Service bb5c11
Packit Service bb5c11
				if (wszFormatName[0])
Packit Service bb5c11
				{
Packit Service bb5c11
					/* ConvertFromUnicode always returns a null-terminated
Packit Service bb5c11
					 * string on success, even if the source string isn't.
Packit Service bb5c11
					 */
Packit Service bb5c11
					if (ConvertFromUnicode(CP_UTF8, 0, wszFormatName, 16,
Packit Service bb5c11
						&(formats[index].formatName), 0, NULL, NULL) < 1)
Packit Service bb5c11
					{
Packit Service bb5c11
						WLog_ERR(TAG, "failed to convert short clipboard format name");
Packit Service bb5c11
						error = ERROR_INTERNAL_ERROR;
Packit Service bb5c11
						goto error_out;
Packit Service bb5c11
					}
Packit Service bb5c11
				}
Packit Service bb5c11
			}
Packit Service bb5c11
Packit Service bb5c11
			Stream_Seek(s, 32);
Packit Service bb5c11
			dataLen -= 32;
Packit Service bb5c11
			index++;
Packit Service bb5c11
		}
Packit Service bb5c11
	}
Packit Service bb5c11
	else
Packit Service bb5c11
	{
Packit Service bb5c11
		while (dataLen)
Packit Service bb5c11
		{
Packit Service bb5c11
			Stream_Seek(s, 4); /* formatId (4 bytes) */
Packit Service bb5c11
			dataLen -= 4;
Packit Service bb5c11
Packit Service bb5c11
			wszFormatName = (WCHAR*) Stream_Pointer(s);
Packit Service bb5c11
Packit Service bb5c11
			if (!wszFormatName[0])
Packit Service bb5c11
				formatNameLength = 0;
Packit Service bb5c11
			else
Packit Service bb5c11
				formatNameLength = _wcslen(wszFormatName);
Packit Service bb5c11
Packit Service bb5c11
			Stream_Seek(s, (formatNameLength + 1) * 2);
Packit Service bb5c11
			dataLen -= ((formatNameLength + 1) * 2);
Packit Service bb5c11
Packit Service bb5c11
			formatList.numFormats++;
Packit Service bb5c11
		}
Packit Service bb5c11
Packit Service bb5c11
		dataLen = formatList.dataLen;
Packit Service bb5c11
		Stream_SetPosition(s, position);
Packit Service bb5c11
Packit Service bb5c11
		if (formatList.numFormats)
Packit Service bb5c11
			formats = (CLIPRDR_FORMAT*) calloc(formatList.numFormats, sizeof(CLIPRDR_FORMAT));
Packit Service bb5c11
Packit Service bb5c11
		if (!formats)
Packit Service bb5c11
		{
Packit Service bb5c11
			WLog_ERR(TAG, "calloc failed!");
Packit Service bb5c11
			return CHANNEL_RC_NO_MEMORY;
Packit Service bb5c11
		}
Packit Service bb5c11
Packit Service bb5c11
		formatList.formats = formats;
Packit Service bb5c11
Packit Service bb5c11
		while (dataLen)
Packit Service bb5c11
		{
Packit Service bb5c11
			Stream_Read_UINT32(s, formats[index].formatId); /* formatId (4 bytes) */
Packit Service bb5c11
			dataLen -= 4;
Packit Service bb5c11
Packit Service bb5c11
			formats[index].formatName = NULL;
Packit Service bb5c11
Packit Service bb5c11
			wszFormatName = (WCHAR*) Stream_Pointer(s);
Packit Service bb5c11
Packit Service bb5c11
			if (!wszFormatName[0])
Packit Service bb5c11
				formatNameLength = 0;
Packit Service bb5c11
			else
Packit Service bb5c11
				formatNameLength = _wcslen(wszFormatName);
Packit Service fa4841
Packit Service bb5c11
			if (formatNameLength)
Packit Service bb5c11
			{
Packit Service bb5c11
				if (ConvertFromUnicode(CP_UTF8, 0, wszFormatName, -1,
Packit Service bb5c11
					&(formats[index].formatName), 0, NULL, NULL) < 1)
Packit Service bb5c11
				{
Packit Service bb5c11
					WLog_ERR(TAG, "failed to convert long clipboard format name");
Packit Service bb5c11
					error = ERROR_INTERNAL_ERROR;
Packit Service bb5c11
					goto error_out;
Packit Service bb5c11
				}
Packit Service bb5c11
			}
Packit Service bb5c11
Packit Service bb5c11
			Stream_Seek(s, (formatNameLength + 1) * 2);
Packit Service bb5c11
			dataLen -= ((formatNameLength + 1) * 2);
Packit Service bb5c11
Packit Service bb5c11
			index++;
Packit Service bb5c11
		}
Packit Service bb5c11
	}
Packit Service bb5c11
Packit Service bb5c11
	WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatList: numFormats: %"PRIu32"",
Packit Service bb5c11
			formatList.numFormats);
Packit Service fa4841
Packit Service fa4841
	if (context->ServerFormatList)
Packit Service fa4841
	{
Packit Service fa4841
		if ((error = context->ServerFormatList(context, &formatList)))
Packit Service bb5c11
			WLog_ERR(TAG, "ServerFormatList failed with error %"PRIu32"", error);
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
error_out:
Packit Service bb5c11
	if (formats)
Packit Service bb5c11
	{
Packit Service bb5c11
		for (index = 0; index < formatList.numFormats; index++)
Packit Service bb5c11
		{
Packit Service bb5c11
			free(formats[index].formatName);
Packit Service bb5c11
		}
Packit Service bb5c11
Packit Service bb5c11
		free(formats);
Packit Service bb5c11
	}
Packit Service fa4841
	return error;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Function description
Packit Service fa4841
 *
Packit Service fa4841
 * @return 0 on success, otherwise a Win32 error code
Packit Service fa4841
 */
Packit Service bb5c11
UINT cliprdr_process_format_list_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags)
Packit Service fa4841
{
Packit Service bb5c11
	CLIPRDR_FORMAT_LIST_RESPONSE formatListResponse;
Packit Service fa4841
	CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
Packit Service fa4841
	UINT error = CHANNEL_RC_OK;
Packit Service fa4841
Packit Service fa4841
	WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatListResponse");
Packit Service fa4841
Packit Service fa4841
	if (!context->custom)
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "context->custom not set!");
Packit Service fa4841
		return ERROR_INTERNAL_ERROR;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	formatListResponse.msgType = CB_FORMAT_LIST_RESPONSE;
Packit Service fa4841
	formatListResponse.msgFlags = msgFlags;
Packit Service fa4841
	formatListResponse.dataLen = dataLen;
Packit Service fa4841
Packit Service fa4841
	IFCALLRET(context->ServerFormatListResponse, error, context, &formatListResponse);
Packit Service fa4841
	if (error)
Packit Service bb5c11
		WLog_ERR(TAG, "ServerFormatListResponse failed with error %"PRIu32"!", error);
Packit Service fa4841
Packit Service fa4841
	return error;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Function description
Packit Service fa4841
 *
Packit Service fa4841
 * @return 0 on success, otherwise a Win32 error code
Packit Service fa4841
 */
Packit Service bb5c11
UINT cliprdr_process_format_data_request(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags)
Packit Service fa4841
{
Packit Service fa4841
	CLIPRDR_FORMAT_DATA_REQUEST formatDataRequest;
Packit Service fa4841
	CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
Packit Service fa4841
	UINT error = CHANNEL_RC_OK;
Packit Service fa4841
Packit Service fa4841
	WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatDataRequest");
Packit Service fa4841
Packit Service fa4841
	if (!context->custom)
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "context->custom not set!");
Packit Service fa4841
		return ERROR_INTERNAL_ERROR;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	formatDataRequest.msgType = CB_FORMAT_DATA_REQUEST;
Packit Service fa4841
	formatDataRequest.msgFlags = msgFlags;
Packit Service fa4841
	formatDataRequest.dataLen = dataLen;
Packit Service fa4841
Packit Service bb5c11
	Stream_Read_UINT32(s, formatDataRequest.requestedFormatId); /* requestedFormatId (4 bytes) */
Packit Service bb5c11
Packit Service fa4841
Packit Service fa4841
	IFCALLRET(context->ServerFormatDataRequest, error, context, &formatDataRequest);
Packit Service fa4841
	if (error)
Packit Service bb5c11
		WLog_ERR(TAG, "ServerFormatDataRequest failed with error %"PRIu32"!", error);
Packit Service fa4841
Packit Service fa4841
	return error;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Function description
Packit Service fa4841
 *
Packit Service fa4841
 * @return 0 on success, otherwise a Win32 error code
Packit Service fa4841
 */
Packit Service bb5c11
UINT cliprdr_process_format_data_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags)
Packit Service fa4841
{
Packit Service fa4841
	CLIPRDR_FORMAT_DATA_RESPONSE formatDataResponse;
Packit Service fa4841
	CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
Packit Service fa4841
	UINT error = CHANNEL_RC_OK;
Packit Service fa4841
Packit Service fa4841
	WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatDataResponse");
Packit Service fa4841
Packit Service fa4841
	if (!context->custom)
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "context->custom not set!");
Packit Service fa4841
		return ERROR_INTERNAL_ERROR;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	formatDataResponse.msgType = CB_FORMAT_DATA_RESPONSE;
Packit Service fa4841
	formatDataResponse.msgFlags = msgFlags;
Packit Service fa4841
	formatDataResponse.dataLen = dataLen;
Packit Service bb5c11
	formatDataResponse.requestedFormatData = NULL;
Packit Service fa4841
Packit Service bb5c11
	if (dataLen)
Packit Service bb5c11
		formatDataResponse.requestedFormatData = (BYTE*) Stream_Pointer(s);
Packit Service fa4841
Packit Service fa4841
	IFCALLRET(context->ServerFormatDataResponse, error, context, &formatDataResponse);
Packit Service fa4841
	if (error)
Packit Service bb5c11
		WLog_ERR(TAG, "ServerFormatDataResponse failed with error %"PRIu32"!", error);
Packit Service fa4841
Packit Service fa4841
	return error;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static UINT64 filetime_to_uint64(FILETIME value)
Packit Service fa4841
{
Packit Service fa4841
	UINT64 converted = 0;
Packit Service bb5c11
	converted |= (UINT32) value.dwHighDateTime;
Packit Service fa4841
	converted <<= 32;
Packit Service bb5c11
	converted |= (UINT32) value.dwLowDateTime;
Packit Service fa4841
	return converted;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static FILETIME uint64_to_filetime(UINT64 value)
Packit Service fa4841
{
Packit Service fa4841
	FILETIME converted;
Packit Service bb5c11
	converted.dwLowDateTime = (UINT32) (value >> 0);
Packit Service bb5c11
	converted.dwHighDateTime = (UINT32) (value >> 32);
Packit Service fa4841
	return converted;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
#define CLIPRDR_FILEDESCRIPTOR_SIZE (4 + 32 + 4 + 16 + 8 + 8 + 520)
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Parse a packed file list.
Packit Service fa4841
 *
Packit Service fa4841
 * The resulting array must be freed with the `free()` function.
Packit Service fa4841
 *
Packit Service fa4841
 * @param [in]  format_data            packed `CLIPRDR_FILELIST` to parse.
Packit Service fa4841
 * @param [in]  format_data_length     length of `format_data` in bytes.
Packit Service fa4841
 * @param [out] file_descriptor_array  parsed array of `FILEDESCRIPTOR` structs.
Packit Service fa4841
 * @param [out] file_descriptor_count  number of elements in `file_descriptor_array`.
Packit Service fa4841
 *
Packit Service fa4841
 * @returns 0 on success, otherwise a Win32 error code.
Packit Service fa4841
 */
Packit Service fa4841
UINT cliprdr_parse_file_list(const BYTE* format_data, UINT32 format_data_length,
Packit Service bb5c11
		FILEDESCRIPTOR** file_descriptor_array, UINT32* file_descriptor_count)
Packit Service fa4841
{
Packit Service fa4841
	UINT result = NO_ERROR;
Packit Service fa4841
	UINT32 i;
Packit Service fa4841
	UINT32 count = 0;
Packit Service fa4841
	wStream* s = NULL;
Packit Service fa4841
Packit Service fa4841
	if (!format_data || !file_descriptor_array || !file_descriptor_count)
Packit Service fa4841
		return ERROR_BAD_ARGUMENTS;
Packit Service fa4841
Packit Service bb5c11
	s = Stream_New((BYTE*) format_data, format_data_length);
Packit Service fa4841
	if (!s)
Packit Service fa4841
		return ERROR_NOT_ENOUGH_MEMORY;
Packit Service fa4841
Packit Service fa4841
	if (Stream_GetRemainingLength(s) < 4)
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "invalid packed file list");
Packit Service fa4841
Packit Service fa4841
		result = ERROR_INCORRECT_SIZE;
Packit Service fa4841
		goto out;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	Stream_Read_UINT32(s, count); /* cItems (4 bytes) */
Packit Service fa4841
Packit Service fa4841
	if (Stream_GetRemainingLength(s) / CLIPRDR_FILEDESCRIPTOR_SIZE < count)
Packit Service fa4841
	{
Packit Service bb5c11
		WLog_ERR(TAG, "packed file list is too short: expected %"PRIuz", have %"PRIuz,
Packit Service bb5c11
			((size_t) count) * CLIPRDR_FILEDESCRIPTOR_SIZE,
Packit Service bb5c11
			Stream_GetRemainingLength(s));
Packit Service fa4841
Packit Service fa4841
		result = ERROR_INCORRECT_SIZE;
Packit Service fa4841
		goto out;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	*file_descriptor_count = count;
Packit Service fa4841
	*file_descriptor_array = calloc(count, sizeof(FILEDESCRIPTOR));
Packit Service fa4841
	if (!*file_descriptor_array)
Packit Service fa4841
	{
Packit Service fa4841
		result = ERROR_NOT_ENOUGH_MEMORY;
Packit Service fa4841
		goto out;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	for (i = 0; i < count; i++)
Packit Service fa4841
	{
Packit Service fa4841
		int c;
Packit Service fa4841
		UINT64 lastWriteTime;
Packit Service fa4841
		FILEDESCRIPTOR* file = &((*file_descriptor_array)[i]);
Packit Service fa4841
Packit Service bb5c11
		Stream_Read_UINT32(s, file->dwFlags); /* flags (4 bytes) */
Packit Service bb5c11
		Stream_Seek(s, 32); /* reserved1 (32 bytes) */
Packit Service fa4841
		Stream_Read_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */
Packit Service bb5c11
		Stream_Seek(s, 16); /* reserved2 (16 bytes) */
Packit Service bb5c11
		Stream_Read_UINT64(s, lastWriteTime); /* lastWriteTime (8 bytes) */
Packit Service fa4841
		file->ftLastWriteTime = uint64_to_filetime(lastWriteTime);
Packit Service fa4841
		Stream_Read_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */
Packit Service bb5c11
		Stream_Read_UINT32(s, file->nFileSizeLow); /* fileSizeLow (4 bytes) */
Packit Service bb5c11
		for (c = 0; c < 260; c++) /* cFileName (520 bytes) */
Packit Service fa4841
			Stream_Read_UINT16(s, file->cFileName[c]);
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	if (Stream_GetRemainingLength(s) > 0)
Packit Service bb5c11
		WLog_WARN(TAG, "packed file list has %"PRIuz" excess bytes",
Packit Service bb5c11
			Stream_GetRemainingLength(s));
Packit Service fa4841
out:
Packit Service fa4841
	Stream_Free(s, FALSE);
Packit Service fa4841
Packit Service fa4841
	return result;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
#define CLIPRDR_MAX_FILE_SIZE (2U * 1024 * 1024 * 1024)
Packit Service fa4841
Packit Service fa4841
/**
Packit Service fa4841
 * Serialize a packed file list.
Packit Service fa4841
 *
Packit Service fa4841
 * The resulting format data must be freed with the `free()` function.
Packit Service fa4841
 *
Packit Service fa4841
 * @param [in]  file_descriptor_array  array of `FILEDESCRIPTOR` structs to serialize.
Packit Service fa4841
 * @param [in]  file_descriptor_count  number of elements in `file_descriptor_array`.
Packit Service fa4841
 * @param [out] format_data            serialized CLIPRDR_FILELIST.
Packit Service fa4841
 * @param [out] format_data_length     length of `format_data` in bytes.
Packit Service fa4841
 *
Packit Service fa4841
 * @returns 0 on success, otherwise a Win32 error code.
Packit Service fa4841
 */
Packit Service fa4841
UINT cliprdr_serialize_file_list(const FILEDESCRIPTOR* file_descriptor_array,
Packit Service bb5c11
		UINT32 file_descriptor_count, BYTE** format_data, UINT32* format_data_length)
Packit Service fa4841
{
Packit Service fa4841
	UINT result = NO_ERROR;
Packit Service fa4841
	UINT32 i;
Packit Service fa4841
	wStream* s = NULL;
Packit Service fa4841
Packit Service fa4841
	if (!file_descriptor_array || !format_data || !format_data_length)
Packit Service fa4841
		return ERROR_BAD_ARGUMENTS;
Packit Service fa4841
Packit Service fa4841
	s = Stream_New(NULL, 4 + file_descriptor_count * CLIPRDR_FILEDESCRIPTOR_SIZE);
Packit Service fa4841
	if (!s)
Packit Service fa4841
		return ERROR_NOT_ENOUGH_MEMORY;
Packit Service fa4841
Packit Service fa4841
	Stream_Write_UINT32(s, file_descriptor_count); /* cItems (4 bytes) */
Packit Service fa4841
Packit Service fa4841
	for (i = 0; i < file_descriptor_count; i++)
Packit Service fa4841
	{
Packit Service fa4841
		int c;
Packit Service fa4841
		UINT64 lastWriteTime;
Packit Service fa4841
		const FILEDESCRIPTOR* file = &file_descriptor_array[i];
Packit Service fa4841
Packit Service fa4841
		/*
Packit Service fa4841
		 * There is a known issue with Windows server getting stuck in
Packit Service fa4841
		 * an infinite loop when downloading files that are larger than
Packit Service fa4841
		 * 2 gigabytes. Do not allow clients to send such file lists.
Packit Service fa4841
		 *
Packit Service fa4841
		 * https://support.microsoft.com/en-us/help/2258090
Packit Service fa4841
		 */
Packit Service fa4841
		if ((file->nFileSizeHigh > 0) || (file->nFileSizeLow >= CLIPRDR_MAX_FILE_SIZE))
Packit Service fa4841
		{
Packit Service fa4841
			WLog_ERR(TAG, "cliprdr does not support files over 2 GB");
Packit Service fa4841
			result = ERROR_FILE_TOO_LARGE;
Packit Service fa4841
			goto error;
Packit Service fa4841
		}
Packit Service fa4841
Packit Service bb5c11
		Stream_Write_UINT32(s, file->dwFlags); /* flags (4 bytes) */
Packit Service bb5c11
		Stream_Zero(s, 32); /* reserved1 (32 bytes) */
Packit Service fa4841
		Stream_Write_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */
Packit Service bb5c11
		Stream_Zero(s, 16); /* reserved2 (16 bytes) */
Packit Service fa4841
		lastWriteTime = filetime_to_uint64(file->ftLastWriteTime);
Packit Service bb5c11
		Stream_Write_UINT64(s, lastWriteTime); /* lastWriteTime (8 bytes) */
Packit Service fa4841
		Stream_Write_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */
Packit Service bb5c11
		Stream_Write_UINT32(s, file->nFileSizeLow); /* fileSizeLow (4 bytes) */
Packit Service bb5c11
		for (c = 0; c < 260; c++) /* cFileName (520 bytes) */
Packit Service fa4841
			Stream_Write_UINT16(s, file->cFileName[c]);
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	Stream_SealLength(s);
Packit Service fa4841
Packit Service fa4841
	Stream_GetBuffer(s, *format_data);
Packit Service fa4841
	Stream_GetLength(s, *format_data_length);
Packit Service fa4841
Packit Service fa4841
	Stream_Free(s, FALSE);
Packit Service fa4841
Packit Service fa4841
	return result;
Packit Service fa4841
Packit Service fa4841
error:
Packit Service fa4841
	Stream_Free(s, TRUE);
Packit Service fa4841
Packit Service fa4841
	return result;
Packit Service fa4841
}