Blame channels/cliprdr/client/cliprdr_format.c

Packit 1fb8d4
/**
Packit 1fb8d4
 * FreeRDP: A Remote Desktop Protocol Implementation
Packit 1fb8d4
 * Clipboard Virtual Channel
Packit 1fb8d4
 *
Packit 1fb8d4
 * Copyright 2009-2011 Jay Sorg
Packit 1fb8d4
 * Copyright 2010-2011 Vic Lee
Packit 1fb8d4
 * Copyright 2015 Thincast Technologies GmbH
Packit 1fb8d4
 * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
Packit 1fb8d4
 *
Packit 1fb8d4
 * Licensed under the Apache License, Version 2.0 (the "License");
Packit 1fb8d4
 * you may not use this file except in compliance with the License.
Packit 1fb8d4
 * You may obtain a copy of the License at
Packit 1fb8d4
 *
Packit 1fb8d4
 *     http://www.apache.org/licenses/LICENSE-2.0
Packit 1fb8d4
 *
Packit 1fb8d4
 * Unless required by applicable law or agreed to in writing, software
Packit 1fb8d4
 * distributed under the License is distributed on an "AS IS" BASIS,
Packit 1fb8d4
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit 1fb8d4
 * See the License for the specific language governing permissions and
Packit 1fb8d4
 * limitations under the License.
Packit 1fb8d4
 */
Packit 1fb8d4
Packit 1fb8d4
#ifdef HAVE_CONFIG_H
Packit 1fb8d4
#include "config.h"
Packit 1fb8d4
#endif
Packit 1fb8d4
Packit 1fb8d4
#include <winpr/crt.h>
Packit 1fb8d4
#include <winpr/print.h>
Packit 1fb8d4
Packit 1fb8d4
#include <freerdp/types.h>
Packit 1fb8d4
#include <freerdp/constants.h>
Packit 1fb8d4
#include <freerdp/client/cliprdr.h>
Packit 1fb8d4
Packit 1fb8d4
#include "cliprdr_main.h"
Packit 1fb8d4
#include "cliprdr_format.h"
Packit Service 5a9772
#include "../cliprdr_common.h"
Packit 1fb8d4
Packit 1fb8d4
/**
Packit 1fb8d4
 * Function description
Packit 1fb8d4
 *
Packit 1fb8d4
 * @return 0 on success, otherwise a Win32 error code
Packit 1fb8d4
 */
Packit Service 5a9772
UINT cliprdr_process_format_list(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen,
Packit Service 5a9772
                                 UINT16 msgFlags)
Packit 1fb8d4
{
Packit Service 5a9772
	CLIPRDR_FORMAT_LIST formatList = { 0 };
Packit 1fb8d4
	CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
Packit 1fb8d4
	UINT error = CHANNEL_RC_OK;
Packit 1fb8d4
Packit 1fb8d4
	if (!context->custom)
Packit 1fb8d4
	{
Packit 1fb8d4
		WLog_ERR(TAG, "context->custom not set!");
Packit 1fb8d4
		return ERROR_INTERNAL_ERROR;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	formatList.msgType = CB_FORMAT_LIST;
Packit 1fb8d4
	formatList.msgFlags = msgFlags;
Packit 1fb8d4
	formatList.dataLen = dataLen;
Packit 1fb8d4
Packit Service 5a9772
	if ((error = cliprdr_read_format_list(s, &formatList, cliprdr->useLongFormatNames)))
Packit Service 5a9772
		goto error_out;
Packit 1fb8d4
Packit Service 5a9772
	WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatList: numFormats: %" PRIu32 "",
Packit Service 5a9772
	           formatList.numFormats);
Packit 1fb8d4
Packit 1fb8d4
	if (context->ServerFormatList)
Packit 1fb8d4
	{
Packit 1fb8d4
		if ((error = context->ServerFormatList(context, &formatList)))
Packit Service 5a9772
			WLog_ERR(TAG, "ServerFormatList failed with error %" PRIu32 "", error);
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
error_out:
Packit Service 5a9772
	cliprdr_free_format_list(&formatList);
Packit 1fb8d4
	return error;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
/**
Packit 1fb8d4
 * Function description
Packit 1fb8d4
 *
Packit 1fb8d4
 * @return 0 on success, otherwise a Win32 error code
Packit 1fb8d4
 */
Packit Service 5a9772
UINT cliprdr_process_format_list_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen,
Packit Service 5a9772
                                          UINT16 msgFlags)
Packit 1fb8d4
{
Packit Service 5a9772
	CLIPRDR_FORMAT_LIST_RESPONSE formatListResponse = { 0 };
Packit 1fb8d4
	CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
Packit 1fb8d4
	UINT error = CHANNEL_RC_OK;
Packit 1fb8d4
Packit 1fb8d4
	WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatListResponse");
Packit 1fb8d4
Packit 1fb8d4
	if (!context->custom)
Packit 1fb8d4
	{
Packit 1fb8d4
		WLog_ERR(TAG, "context->custom not set!");
Packit 1fb8d4
		return ERROR_INTERNAL_ERROR;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	formatListResponse.msgType = CB_FORMAT_LIST_RESPONSE;
Packit 1fb8d4
	formatListResponse.msgFlags = msgFlags;
Packit 1fb8d4
	formatListResponse.dataLen = dataLen;
Packit 1fb8d4
Packit 1fb8d4
	IFCALLRET(context->ServerFormatListResponse, error, context, &formatListResponse);
Packit 1fb8d4
	if (error)
Packit Service 5a9772
		WLog_ERR(TAG, "ServerFormatListResponse failed with error %" PRIu32 "!", error);
Packit 1fb8d4
Packit 1fb8d4
	return error;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
/**
Packit 1fb8d4
 * Function description
Packit 1fb8d4
 *
Packit 1fb8d4
 * @return 0 on success, otherwise a Win32 error code
Packit 1fb8d4
 */
Packit Service 5a9772
UINT cliprdr_process_format_data_request(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen,
Packit Service 5a9772
                                         UINT16 msgFlags)
Packit 1fb8d4
{
Packit 1fb8d4
	CLIPRDR_FORMAT_DATA_REQUEST formatDataRequest;
Packit 1fb8d4
	CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
Packit 1fb8d4
	UINT error = CHANNEL_RC_OK;
Packit 1fb8d4
Packit 1fb8d4
	WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatDataRequest");
Packit 1fb8d4
Packit 1fb8d4
	if (!context->custom)
Packit 1fb8d4
	{
Packit 1fb8d4
		WLog_ERR(TAG, "context->custom not set!");
Packit 1fb8d4
		return ERROR_INTERNAL_ERROR;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	formatDataRequest.msgType = CB_FORMAT_DATA_REQUEST;
Packit 1fb8d4
	formatDataRequest.msgFlags = msgFlags;
Packit 1fb8d4
	formatDataRequest.dataLen = dataLen;
Packit 1fb8d4
Packit Service 5a9772
	if ((error = cliprdr_read_format_data_request(s, &formatDataRequest)))
Packit Service 5a9772
		return error;
Packit 1fb8d4
Packit Service 5a9772
	context->lastRequestedFormatId = formatDataRequest.requestedFormatId;
Packit 1fb8d4
	IFCALLRET(context->ServerFormatDataRequest, error, context, &formatDataRequest);
Packit 1fb8d4
	if (error)
Packit Service 5a9772
		WLog_ERR(TAG, "ServerFormatDataRequest failed with error %" PRIu32 "!", error);
Packit 1fb8d4
Packit 1fb8d4
	return error;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
/**
Packit 1fb8d4
 * Function description
Packit 1fb8d4
 *
Packit 1fb8d4
 * @return 0 on success, otherwise a Win32 error code
Packit 1fb8d4
 */
Packit Service 5a9772
UINT cliprdr_process_format_data_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen,
Packit Service 5a9772
                                          UINT16 msgFlags)
Packit 1fb8d4
{
Packit 1fb8d4
	CLIPRDR_FORMAT_DATA_RESPONSE formatDataResponse;
Packit 1fb8d4
	CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
Packit 1fb8d4
	UINT error = CHANNEL_RC_OK;
Packit 1fb8d4
Packit 1fb8d4
	WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatDataResponse");
Packit 1fb8d4
Packit 1fb8d4
	if (!context->custom)
Packit 1fb8d4
	{
Packit 1fb8d4
		WLog_ERR(TAG, "context->custom not set!");
Packit 1fb8d4
		return ERROR_INTERNAL_ERROR;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	formatDataResponse.msgType = CB_FORMAT_DATA_RESPONSE;
Packit 1fb8d4
	formatDataResponse.msgFlags = msgFlags;
Packit 1fb8d4
	formatDataResponse.dataLen = dataLen;
Packit 1fb8d4
Packit Service 5a9772
	if ((error = cliprdr_read_format_data_response(s, &formatDataResponse)))
Packit Service 5a9772
		return error;
Packit 1fb8d4
Packit 1fb8d4
	IFCALLRET(context->ServerFormatDataResponse, error, context, &formatDataResponse);
Packit 1fb8d4
	if (error)
Packit Service 5a9772
		WLog_ERR(TAG, "ServerFormatDataResponse failed with error %" PRIu32 "!", error);
Packit 1fb8d4
Packit 1fb8d4
	return error;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static UINT64 filetime_to_uint64(FILETIME value)
Packit 1fb8d4
{
Packit 1fb8d4
	UINT64 converted = 0;
Packit Service 5a9772
	converted |= (UINT32)value.dwHighDateTime;
Packit 1fb8d4
	converted <<= 32;
Packit Service 5a9772
	converted |= (UINT32)value.dwLowDateTime;
Packit 1fb8d4
	return converted;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static FILETIME uint64_to_filetime(UINT64 value)
Packit 1fb8d4
{
Packit 1fb8d4
	FILETIME converted;
Packit Service 5a9772
	converted.dwLowDateTime = (UINT32)(value >> 0);
Packit Service 5a9772
	converted.dwHighDateTime = (UINT32)(value >> 32);
Packit 1fb8d4
	return converted;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
#define CLIPRDR_FILEDESCRIPTOR_SIZE (4 + 32 + 4 + 16 + 8 + 8 + 520)
Packit 1fb8d4
Packit 1fb8d4
/**
Packit 1fb8d4
 * Parse a packed file list.
Packit 1fb8d4
 *
Packit 1fb8d4
 * The resulting array must be freed with the `free()` function.
Packit 1fb8d4
 *
Packit 1fb8d4
 * @param [in]  format_data            packed `CLIPRDR_FILELIST` to parse.
Packit 1fb8d4
 * @param [in]  format_data_length     length of `format_data` in bytes.
Packit 1fb8d4
 * @param [out] file_descriptor_array  parsed array of `FILEDESCRIPTOR` structs.
Packit 1fb8d4
 * @param [out] file_descriptor_count  number of elements in `file_descriptor_array`.
Packit 1fb8d4
 *
Packit 1fb8d4
 * @returns 0 on success, otherwise a Win32 error code.
Packit 1fb8d4
 */
Packit 1fb8d4
UINT cliprdr_parse_file_list(const BYTE* format_data, UINT32 format_data_length,
Packit Service 5a9772
                             FILEDESCRIPTOR** file_descriptor_array, UINT32* file_descriptor_count)
Packit 1fb8d4
{
Packit 1fb8d4
	UINT result = NO_ERROR;
Packit 1fb8d4
	UINT32 i;
Packit 1fb8d4
	UINT32 count = 0;
Packit 1fb8d4
	wStream* s = NULL;
Packit 1fb8d4
Packit 1fb8d4
	if (!format_data || !file_descriptor_array || !file_descriptor_count)
Packit 1fb8d4
		return ERROR_BAD_ARGUMENTS;
Packit 1fb8d4
Packit Service 5a9772
	s = Stream_New((BYTE*)format_data, format_data_length);
Packit 1fb8d4
	if (!s)
Packit 1fb8d4
		return ERROR_NOT_ENOUGH_MEMORY;
Packit 1fb8d4
Packit 1fb8d4
	if (Stream_GetRemainingLength(s) < 4)
Packit 1fb8d4
	{
Packit 1fb8d4
		WLog_ERR(TAG, "invalid packed file list");
Packit 1fb8d4
Packit 1fb8d4
		result = ERROR_INCORRECT_SIZE;
Packit 1fb8d4
		goto out;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	Stream_Read_UINT32(s, count); /* cItems (4 bytes) */
Packit 1fb8d4
Packit 1fb8d4
	if (Stream_GetRemainingLength(s) / CLIPRDR_FILEDESCRIPTOR_SIZE < count)
Packit 1fb8d4
	{
Packit Service 5a9772
		WLog_ERR(TAG, "packed file list is too short: expected %" PRIuz ", have %" PRIuz,
Packit Service 5a9772
		         ((size_t)count) * CLIPRDR_FILEDESCRIPTOR_SIZE, Stream_GetRemainingLength(s));
Packit 1fb8d4
Packit 1fb8d4
		result = ERROR_INCORRECT_SIZE;
Packit 1fb8d4
		goto out;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	*file_descriptor_count = count;
Packit 1fb8d4
	*file_descriptor_array = calloc(count, sizeof(FILEDESCRIPTOR));
Packit 1fb8d4
	if (!*file_descriptor_array)
Packit 1fb8d4
	{
Packit 1fb8d4
		result = ERROR_NOT_ENOUGH_MEMORY;
Packit 1fb8d4
		goto out;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	for (i = 0; i < count; i++)
Packit 1fb8d4
	{
Packit 1fb8d4
		int c;
Packit 1fb8d4
		UINT64 lastWriteTime;
Packit 1fb8d4
		FILEDESCRIPTOR* file = &((*file_descriptor_array)[i]);
Packit 1fb8d4
Packit Service 5a9772
		Stream_Read_UINT32(s, file->dwFlags);          /* flags (4 bytes) */
Packit Service 5a9772
		Stream_Seek(s, 32);                            /* reserved1 (32 bytes) */
Packit 1fb8d4
		Stream_Read_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */
Packit Service 5a9772
		Stream_Seek(s, 16);                            /* reserved2 (16 bytes) */
Packit Service 5a9772
		Stream_Read_UINT64(s, lastWriteTime);          /* lastWriteTime (8 bytes) */
Packit 1fb8d4
		file->ftLastWriteTime = uint64_to_filetime(lastWriteTime);
Packit 1fb8d4
		Stream_Read_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */
Packit Service 5a9772
		Stream_Read_UINT32(s, file->nFileSizeLow);  /* fileSizeLow (4 bytes) */
Packit Service 5a9772
		for (c = 0; c < 260; c++)                   /* cFileName (520 bytes) */
Packit 1fb8d4
			Stream_Read_UINT16(s, file->cFileName[c]);
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (Stream_GetRemainingLength(s) > 0)
Packit Service 5a9772
		WLog_WARN(TAG, "packed file list has %" PRIuz " excess bytes",
Packit Service 5a9772
		          Stream_GetRemainingLength(s));
Packit 1fb8d4
out:
Packit 1fb8d4
	Stream_Free(s, FALSE);
Packit 1fb8d4
Packit 1fb8d4
	return result;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
#define CLIPRDR_MAX_FILE_SIZE (2U * 1024 * 1024 * 1024)
Packit 1fb8d4
Packit 1fb8d4
/**
Packit 1fb8d4
 * Serialize a packed file list.
Packit 1fb8d4
 *
Packit 1fb8d4
 * The resulting format data must be freed with the `free()` function.
Packit 1fb8d4
 *
Packit 1fb8d4
 * @param [in]  file_descriptor_array  array of `FILEDESCRIPTOR` structs to serialize.
Packit 1fb8d4
 * @param [in]  file_descriptor_count  number of elements in `file_descriptor_array`.
Packit 1fb8d4
 * @param [out] format_data            serialized CLIPRDR_FILELIST.
Packit 1fb8d4
 * @param [out] format_data_length     length of `format_data` in bytes.
Packit 1fb8d4
 *
Packit 1fb8d4
 * @returns 0 on success, otherwise a Win32 error code.
Packit 1fb8d4
 */
Packit 1fb8d4
UINT cliprdr_serialize_file_list(const FILEDESCRIPTOR* file_descriptor_array,
Packit Service 5a9772
                                 UINT32 file_descriptor_count, BYTE** format_data,
Packit Service 5a9772
                                 UINT32* format_data_length)
Packit 1fb8d4
{
Packit 1fb8d4
	UINT result = NO_ERROR;
Packit 1fb8d4
	UINT32 i;
Packit 1fb8d4
	wStream* s = NULL;
Packit 1fb8d4
Packit 1fb8d4
	if (!file_descriptor_array || !format_data || !format_data_length)
Packit 1fb8d4
		return ERROR_BAD_ARGUMENTS;
Packit 1fb8d4
Packit 1fb8d4
	s = Stream_New(NULL, 4 + file_descriptor_count * CLIPRDR_FILEDESCRIPTOR_SIZE);
Packit 1fb8d4
	if (!s)
Packit 1fb8d4
		return ERROR_NOT_ENOUGH_MEMORY;
Packit 1fb8d4
Packit 1fb8d4
	Stream_Write_UINT32(s, file_descriptor_count); /* cItems (4 bytes) */
Packit 1fb8d4
Packit 1fb8d4
	for (i = 0; i < file_descriptor_count; i++)
Packit 1fb8d4
	{
Packit 1fb8d4
		int c;
Packit 1fb8d4
		UINT64 lastWriteTime;
Packit 1fb8d4
		const FILEDESCRIPTOR* file = &file_descriptor_array[i];
Packit 1fb8d4
Packit 1fb8d4
		/*
Packit 1fb8d4
		 * There is a known issue with Windows server getting stuck in
Packit 1fb8d4
		 * an infinite loop when downloading files that are larger than
Packit 1fb8d4
		 * 2 gigabytes. Do not allow clients to send such file lists.
Packit 1fb8d4
		 *
Packit 1fb8d4
		 * https://support.microsoft.com/en-us/help/2258090
Packit 1fb8d4
		 */
Packit 1fb8d4
		if ((file->nFileSizeHigh > 0) || (file->nFileSizeLow >= CLIPRDR_MAX_FILE_SIZE))
Packit 1fb8d4
		{
Packit 1fb8d4
			WLog_ERR(TAG, "cliprdr does not support files over 2 GB");
Packit 1fb8d4
			result = ERROR_FILE_TOO_LARGE;
Packit 1fb8d4
			goto error;
Packit 1fb8d4
		}
Packit 1fb8d4
Packit Service 5a9772
		Stream_Write_UINT32(s, file->dwFlags);          /* flags (4 bytes) */
Packit Service 5a9772
		Stream_Zero(s, 32);                             /* reserved1 (32 bytes) */
Packit 1fb8d4
		Stream_Write_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */
Packit Service 5a9772
		Stream_Zero(s, 16);                             /* reserved2 (16 bytes) */
Packit 1fb8d4
		lastWriteTime = filetime_to_uint64(file->ftLastWriteTime);
Packit Service 5a9772
		Stream_Write_UINT64(s, lastWriteTime);       /* lastWriteTime (8 bytes) */
Packit 1fb8d4
		Stream_Write_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */
Packit Service 5a9772
		Stream_Write_UINT32(s, file->nFileSizeLow);  /* fileSizeLow (4 bytes) */
Packit Service 5a9772
		for (c = 0; c < 260; c++)                    /* cFileName (520 bytes) */
Packit 1fb8d4
			Stream_Write_UINT16(s, file->cFileName[c]);
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	Stream_SealLength(s);
Packit 1fb8d4
Packit 1fb8d4
	Stream_GetBuffer(s, *format_data);
Packit 1fb8d4
	Stream_GetLength(s, *format_data_length);
Packit 1fb8d4
Packit 1fb8d4
	Stream_Free(s, FALSE);
Packit 1fb8d4
Packit 1fb8d4
	return result;
Packit 1fb8d4
Packit 1fb8d4
error:
Packit 1fb8d4
	Stream_Free(s, TRUE);
Packit 1fb8d4
Packit 1fb8d4
	return result;
Packit 1fb8d4
}