2011-07-12 19:06:39 +04:00
|
|
|
/**
|
2012-10-09 07:02:04 +04:00
|
|
|
* FreeRDP: A Remote Desktop Protocol Implementation
|
2011-07-12 19:06:39 +04:00
|
|
|
* Clipboard Virtual Channel
|
|
|
|
*
|
|
|
|
* Copyright 2009-2011 Jay Sorg
|
|
|
|
* Copyright 2010-2011 Vic Lee
|
2015-06-02 14:05:10 +03:00
|
|
|
* Copyright 2015 Thincast Technologies GmbH
|
|
|
|
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
2011-07-12 19:06:39 +04:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2012-08-15 01:09:01 +04:00
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
2012-10-09 07:42:01 +04:00
|
|
|
#include <winpr/crt.h>
|
2012-12-17 19:20:25 +04:00
|
|
|
#include <winpr/print.h>
|
2012-10-09 07:42:01 +04:00
|
|
|
|
2011-07-12 19:06:39 +04:00
|
|
|
#include <freerdp/types.h>
|
2011-11-01 09:09:38 +04:00
|
|
|
#include <freerdp/constants.h>
|
2012-10-09 04:33:58 +04:00
|
|
|
#include <freerdp/client/cliprdr.h>
|
2011-07-12 19:06:39 +04:00
|
|
|
|
|
|
|
#include "cliprdr_main.h"
|
|
|
|
#include "cliprdr_format.h"
|
|
|
|
|
2015-08-27 15:25:09 +03:00
|
|
|
/**
|
|
|
|
* Function description
|
|
|
|
*
|
|
|
|
* @return 0 on success, otherwise a Win32 error code
|
|
|
|
*/
|
|
|
|
UINT cliprdr_process_format_list(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags)
|
2011-11-01 09:09:38 +04:00
|
|
|
{
|
2014-11-20 22:28:05 +03:00
|
|
|
UINT32 index;
|
|
|
|
UINT32 position;
|
|
|
|
BOOL asciiNames;
|
|
|
|
int formatNameLength;
|
|
|
|
char* szFormatName;
|
|
|
|
WCHAR* wszFormatName;
|
|
|
|
CLIPRDR_FORMAT* formats = NULL;
|
|
|
|
CLIPRDR_FORMAT_LIST formatList;
|
|
|
|
CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
|
2015-08-27 15:25:09 +03:00
|
|
|
UINT error = CHANNEL_RC_OK;
|
2011-11-01 09:09:38 +04:00
|
|
|
|
2014-11-20 22:28:05 +03:00
|
|
|
if (!context->custom)
|
2015-06-02 14:05:10 +03:00
|
|
|
{
|
|
|
|
WLog_ERR(TAG, "context->custom not set!");
|
|
|
|
return ERROR_INTERNAL_ERROR;
|
|
|
|
}
|
2015-03-23 17:58:16 +03:00
|
|
|
|
2014-11-20 22:28:05 +03:00
|
|
|
asciiNames = (msgFlags & CB_ASCII_NAMES) ? TRUE : FALSE;
|
2015-03-23 17:58:16 +03:00
|
|
|
|
2014-11-20 22:28:05 +03:00
|
|
|
formatList.msgType = CB_FORMAT_LIST;
|
|
|
|
formatList.msgFlags = msgFlags;
|
|
|
|
formatList.dataLen = dataLen;
|
2011-11-01 09:09:38 +04:00
|
|
|
|
2014-11-20 22:28:05 +03:00
|
|
|
index = 0;
|
|
|
|
formatList.numFormats = 0;
|
|
|
|
position = Stream_GetPosition(s);
|
2015-03-23 17:58:16 +03:00
|
|
|
|
2014-12-20 21:07:30 +03:00
|
|
|
if (!formatList.dataLen)
|
|
|
|
{
|
|
|
|
/* empty format list */
|
|
|
|
formatList.formats = NULL;
|
|
|
|
formatList.numFormats = 0;
|
|
|
|
}
|
|
|
|
else if (!cliprdr->useLongFormatNames)
|
2011-11-01 09:09:38 +04:00
|
|
|
{
|
2014-11-20 22:28:05 +03:00
|
|
|
formatList.numFormats = (dataLen / 36);
|
2015-03-23 17:58:16 +03:00
|
|
|
|
2014-11-20 22:28:05 +03:00
|
|
|
if ((formatList.numFormats * 36) != dataLen)
|
2011-12-22 02:02:53 +04:00
|
|
|
{
|
2016-12-14 00:47:08 +03:00
|
|
|
WLog_ERR(TAG, "Invalid short format list length: %"PRIu32"", dataLen);
|
2015-06-02 14:05:10 +03:00
|
|
|
return ERROR_INTERNAL_ERROR;
|
2011-12-22 02:02:53 +04:00
|
|
|
}
|
2015-03-23 17:58:16 +03:00
|
|
|
|
2014-11-20 22:28:05 +03:00
|
|
|
if (formatList.numFormats)
|
|
|
|
formats = (CLIPRDR_FORMAT*) calloc(formatList.numFormats, sizeof(CLIPRDR_FORMAT));
|
2015-03-23 17:58:16 +03:00
|
|
|
|
2014-11-20 22:28:05 +03:00
|
|
|
if (!formats)
|
2015-06-02 14:05:10 +03:00
|
|
|
{
|
|
|
|
WLog_ERR(TAG, "calloc failed!");
|
|
|
|
return CHANNEL_RC_NO_MEMORY;
|
|
|
|
}
|
2015-03-23 17:58:16 +03:00
|
|
|
|
2014-11-20 22:28:05 +03:00
|
|
|
formatList.formats = formats;
|
2015-03-23 17:58:16 +03:00
|
|
|
|
2014-11-20 22:28:05 +03:00
|
|
|
while (dataLen)
|
2011-12-22 02:02:53 +04:00
|
|
|
{
|
2014-11-20 22:28:05 +03:00
|
|
|
Stream_Read_UINT32(s, formats[index].formatId); /* formatId (4 bytes) */
|
|
|
|
dataLen -= 4;
|
2015-03-23 17:58:16 +03:00
|
|
|
|
2014-11-20 22:28:05 +03:00
|
|
|
formats[index].formatName = NULL;
|
2015-03-23 17:58:16 +03:00
|
|
|
|
2016-03-03 18:21:12 +03:00
|
|
|
/* According to MS-RDPECLIP 2.2.3.1.1.1 formatName is "a 32-byte block containing
|
|
|
|
* the *null-terminated* name assigned to the Clipboard Format: (32 ASCII 8 characters
|
|
|
|
* or 16 Unicode characters)"
|
|
|
|
* However, both Windows RDSH and mstsc violate this specs as seen in the following
|
|
|
|
* example of a transferred short format name string: [R.i.c.h. .T.e.x.t. .F.o.r.m.a.t.]
|
|
|
|
* These are 16 unicode charaters - *without* terminating null !
|
|
|
|
*/
|
|
|
|
|
2014-11-20 22:28:05 +03:00
|
|
|
if (asciiNames)
|
|
|
|
{
|
|
|
|
szFormatName = (char*) Stream_Pointer(s);
|
2015-03-23 17:58:16 +03:00
|
|
|
|
2014-11-21 00:05:13 +03:00
|
|
|
if (szFormatName[0])
|
2014-11-20 22:28:05 +03:00
|
|
|
{
|
2016-03-03 18:21:12 +03:00
|
|
|
/* ensure null termination */
|
2014-11-21 00:05:13 +03:00
|
|
|
formats[index].formatName = (char*) malloc(32 + 1);
|
2015-06-02 14:05:10 +03:00
|
|
|
if (!formats[index].formatName)
|
|
|
|
{
|
2016-03-03 18:21:12 +03:00
|
|
|
WLog_ERR(TAG, "malloc failed!");
|
2015-06-02 14:05:10 +03:00
|
|
|
error = CHANNEL_RC_NO_MEMORY;
|
|
|
|
goto error_out;
|
|
|
|
}
|
2014-11-21 00:05:13 +03:00
|
|
|
CopyMemory(formats[index].formatName, szFormatName, 32);
|
|
|
|
formats[index].formatName[32] = '\0';
|
2014-11-20 22:28:05 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
wszFormatName = (WCHAR*) Stream_Pointer(s);
|
2015-03-23 17:58:16 +03:00
|
|
|
|
2014-11-21 00:05:13 +03:00
|
|
|
if (wszFormatName[0])
|
2014-11-20 22:28:05 +03:00
|
|
|
{
|
2016-03-03 18:21:12 +03:00
|
|
|
/* ConvertFromUnicode always returns a null-terminated
|
|
|
|
* string on success, even if the source string isn't.
|
|
|
|
*/
|
|
|
|
if (ConvertFromUnicode(CP_UTF8, 0, wszFormatName, 16,
|
|
|
|
&(formats[index].formatName), 0, NULL, NULL) < 1)
|
|
|
|
{
|
|
|
|
WLog_ERR(TAG, "failed to convert short clipboard format name");
|
|
|
|
error = ERROR_INTERNAL_ERROR;
|
|
|
|
goto error_out;
|
|
|
|
}
|
2014-11-20 22:28:05 +03:00
|
|
|
}
|
|
|
|
}
|
2015-03-23 17:58:16 +03:00
|
|
|
|
2014-11-20 22:28:05 +03:00
|
|
|
Stream_Seek(s, 32);
|
|
|
|
dataLen -= 32;
|
|
|
|
index++;
|
2011-12-22 02:02:53 +04:00
|
|
|
}
|
2011-11-01 09:09:38 +04:00
|
|
|
}
|
2014-11-20 22:28:05 +03:00
|
|
|
else
|
2011-09-23 07:37:17 +04:00
|
|
|
{
|
2013-11-24 06:35:44 +04:00
|
|
|
while (dataLen)
|
|
|
|
{
|
2014-11-20 22:28:05 +03:00
|
|
|
Stream_Seek(s, 4); /* formatId (4 bytes) */
|
2013-11-24 06:35:44 +04:00
|
|
|
dataLen -= 4;
|
2015-03-23 17:58:16 +03:00
|
|
|
|
2014-11-20 22:28:05 +03:00
|
|
|
wszFormatName = (WCHAR*) Stream_Pointer(s);
|
2015-03-23 17:58:16 +03:00
|
|
|
|
2014-11-20 22:28:05 +03:00
|
|
|
if (!wszFormatName[0])
|
|
|
|
formatNameLength = 0;
|
|
|
|
else
|
|
|
|
formatNameLength = _wcslen(wszFormatName);
|
2015-03-23 17:58:16 +03:00
|
|
|
|
2013-11-24 06:35:44 +04:00
|
|
|
Stream_Seek(s, (formatNameLength + 1) * 2);
|
|
|
|
dataLen -= ((formatNameLength + 1) * 2);
|
2015-03-23 17:58:16 +03:00
|
|
|
|
2014-10-17 05:45:47 +04:00
|
|
|
formatList.numFormats++;
|
2013-11-24 06:35:44 +04:00
|
|
|
}
|
2011-11-18 04:51:30 +04:00
|
|
|
|
2013-11-24 06:35:44 +04:00
|
|
|
dataLen = formatList.dataLen;
|
2014-11-20 22:28:05 +03:00
|
|
|
Stream_SetPosition(s, position);
|
2011-09-23 07:37:17 +04:00
|
|
|
|
2014-11-17 01:44:10 +03:00
|
|
|
if (formatList.numFormats)
|
|
|
|
formats = (CLIPRDR_FORMAT*) calloc(formatList.numFormats, sizeof(CLIPRDR_FORMAT));
|
2015-03-23 17:58:16 +03:00
|
|
|
|
2014-11-16 18:01:08 +03:00
|
|
|
if (!formats)
|
2015-06-02 14:05:10 +03:00
|
|
|
{
|
|
|
|
WLog_ERR(TAG, "calloc failed!");
|
|
|
|
return CHANNEL_RC_NO_MEMORY;
|
|
|
|
}
|
2014-11-16 18:01:08 +03:00
|
|
|
|
2013-11-24 06:35:44 +04:00
|
|
|
formatList.formats = formats;
|
|
|
|
|
|
|
|
while (dataLen)
|
|
|
|
{
|
2014-11-20 22:28:05 +03:00
|
|
|
Stream_Read_UINT32(s, formats[index].formatId); /* formatId (4 bytes) */
|
2013-11-24 06:35:44 +04:00
|
|
|
dataLen -= 4;
|
|
|
|
|
|
|
|
formats[index].formatName = NULL;
|
2015-03-23 17:58:16 +03:00
|
|
|
|
2014-11-20 22:28:05 +03:00
|
|
|
wszFormatName = (WCHAR*) Stream_Pointer(s);
|
2015-03-23 17:58:16 +03:00
|
|
|
|
2014-11-20 22:28:05 +03:00
|
|
|
if (!wszFormatName[0])
|
|
|
|
formatNameLength = 0;
|
|
|
|
else
|
|
|
|
formatNameLength = _wcslen(wszFormatName);
|
2013-11-24 06:35:44 +04:00
|
|
|
|
|
|
|
if (formatNameLength)
|
|
|
|
{
|
2016-03-03 18:21:12 +03:00
|
|
|
if (ConvertFromUnicode(CP_UTF8, 0, wszFormatName, -1,
|
|
|
|
&(formats[index].formatName), 0, NULL, NULL) < 1)
|
|
|
|
{
|
|
|
|
WLog_ERR(TAG, "failed to convert long clipboard format name");
|
|
|
|
error = ERROR_INTERNAL_ERROR;
|
|
|
|
goto error_out;
|
|
|
|
}
|
2013-11-24 06:35:44 +04:00
|
|
|
}
|
2015-03-23 17:58:16 +03:00
|
|
|
|
2014-11-20 22:28:05 +03:00
|
|
|
Stream_Seek(s, (formatNameLength + 1) * 2);
|
|
|
|
dataLen -= ((formatNameLength + 1) * 2);
|
2013-11-24 06:35:44 +04:00
|
|
|
|
|
|
|
index++;
|
|
|
|
}
|
2014-11-20 22:28:05 +03:00
|
|
|
}
|
2013-11-24 06:35:44 +04:00
|
|
|
|
2016-12-14 00:47:08 +03:00
|
|
|
WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatList: numFormats: %"PRIu32"",
|
2014-11-20 22:28:05 +03:00
|
|
|
formatList.numFormats);
|
2014-10-17 05:45:47 +04:00
|
|
|
|
2014-11-20 22:28:05 +03:00
|
|
|
if (context->ServerFormatList)
|
2015-06-02 14:05:10 +03:00
|
|
|
{
|
|
|
|
if ((error = context->ServerFormatList(context, &formatList)))
|
2016-12-14 00:47:08 +03:00
|
|
|
WLog_ERR(TAG, "ServerFormatList failed with error %"PRIu32"", error);
|
2015-06-02 14:05:10 +03:00
|
|
|
}
|
2013-11-24 06:35:44 +04:00
|
|
|
|
2015-06-02 14:05:10 +03:00
|
|
|
error_out:
|
2015-03-23 17:58:16 +03:00
|
|
|
if (formats)
|
2014-11-20 22:28:05 +03:00
|
|
|
{
|
2015-03-23 17:58:16 +03:00
|
|
|
for (index = 0; index < formatList.numFormats; index++)
|
|
|
|
{
|
2014-11-20 22:28:05 +03:00
|
|
|
free(formats[index].formatName);
|
2015-03-23 17:58:16 +03:00
|
|
|
}
|
2014-11-20 22:28:05 +03:00
|
|
|
|
2015-03-23 17:58:16 +03:00
|
|
|
free(formats);
|
|
|
|
}
|
2015-06-02 14:05:10 +03:00
|
|
|
return error;
|
2011-07-12 19:53:54 +04:00
|
|
|
}
|
2011-07-12 20:30:25 +04:00
|
|
|
|
2015-08-27 15:25:09 +03:00
|
|
|
/**
|
|
|
|
* Function description
|
|
|
|
*
|
|
|
|
* @return 0 on success, otherwise a Win32 error code
|
|
|
|
*/
|
|
|
|
UINT cliprdr_process_format_list_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags)
|
2011-09-23 07:37:17 +04:00
|
|
|
{
|
2014-11-20 22:28:05 +03:00
|
|
|
CLIPRDR_FORMAT_LIST_RESPONSE formatListResponse;
|
2013-11-24 06:35:44 +04:00
|
|
|
CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
|
2015-08-27 15:25:09 +03:00
|
|
|
UINT error = CHANNEL_RC_OK;
|
2013-11-24 06:35:44 +04:00
|
|
|
|
2014-10-16 06:48:18 +04:00
|
|
|
WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatListResponse");
|
|
|
|
|
2014-11-20 22:28:05 +03:00
|
|
|
if (!context->custom)
|
2015-06-02 14:05:10 +03:00
|
|
|
{
|
|
|
|
WLog_ERR(TAG, "context->custom not set!");
|
|
|
|
return ERROR_INTERNAL_ERROR;
|
|
|
|
}
|
2013-11-24 06:35:44 +04:00
|
|
|
|
2014-11-20 22:28:05 +03:00
|
|
|
formatListResponse.msgType = CB_FORMAT_LIST_RESPONSE;
|
|
|
|
formatListResponse.msgFlags = msgFlags;
|
|
|
|
formatListResponse.dataLen = dataLen;
|
2013-11-24 06:35:44 +04:00
|
|
|
|
2015-06-02 14:05:10 +03:00
|
|
|
IFCALLRET(context->ServerFormatListResponse, error, context, &formatListResponse);
|
|
|
|
if (error)
|
2016-12-14 00:47:08 +03:00
|
|
|
WLog_ERR(TAG, "ServerFormatListResponse failed with error %"PRIu32"!", error);
|
2015-06-02 14:05:10 +03:00
|
|
|
|
|
|
|
return error;
|
2011-09-23 07:37:17 +04:00
|
|
|
}
|
|
|
|
|
2015-08-27 15:25:09 +03:00
|
|
|
/**
|
|
|
|
* Function description
|
|
|
|
*
|
|
|
|
* @return 0 on success, otherwise a Win32 error code
|
|
|
|
*/
|
|
|
|
UINT cliprdr_process_format_data_request(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags)
|
2011-07-12 20:30:25 +04:00
|
|
|
{
|
2014-11-20 22:28:05 +03:00
|
|
|
CLIPRDR_FORMAT_DATA_REQUEST formatDataRequest;
|
2013-11-24 08:45:31 +04:00
|
|
|
CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
|
2015-08-27 15:25:09 +03:00
|
|
|
UINT error = CHANNEL_RC_OK;
|
2013-11-24 08:45:31 +04:00
|
|
|
|
2014-10-16 06:48:18 +04:00
|
|
|
WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatDataRequest");
|
|
|
|
|
2014-11-20 22:28:05 +03:00
|
|
|
if (!context->custom)
|
2015-06-02 14:05:10 +03:00
|
|
|
{
|
|
|
|
WLog_ERR(TAG, "context->custom not set!");
|
|
|
|
return ERROR_INTERNAL_ERROR;
|
|
|
|
}
|
2013-11-24 08:45:31 +04:00
|
|
|
|
2014-11-20 22:28:05 +03:00
|
|
|
formatDataRequest.msgType = CB_FORMAT_DATA_REQUEST;
|
|
|
|
formatDataRequest.msgFlags = msgFlags;
|
|
|
|
formatDataRequest.dataLen = dataLen;
|
2013-11-24 08:45:31 +04:00
|
|
|
|
2014-11-20 22:28:05 +03:00
|
|
|
Stream_Read_UINT32(s, formatDataRequest.requestedFormatId); /* requestedFormatId (4 bytes) */
|
2011-07-12 20:30:25 +04:00
|
|
|
|
2015-06-02 14:05:10 +03:00
|
|
|
|
|
|
|
IFCALLRET(context->ServerFormatDataRequest, error, context, &formatDataRequest);
|
|
|
|
if (error)
|
2016-12-14 00:47:08 +03:00
|
|
|
WLog_ERR(TAG, "ServerFormatDataRequest failed with error %"PRIu32"!", error);
|
2015-06-02 14:05:10 +03:00
|
|
|
|
|
|
|
return error;
|
2011-07-12 20:50:00 +04:00
|
|
|
}
|
|
|
|
|
2015-08-27 15:25:09 +03:00
|
|
|
/**
|
|
|
|
* Function description
|
|
|
|
*
|
|
|
|
* @return 0 on success, otherwise a Win32 error code
|
|
|
|
*/
|
|
|
|
UINT cliprdr_process_format_data_response(cliprdrPlugin* cliprdr, wStream* s, UINT32 dataLen, UINT16 msgFlags)
|
2011-07-12 20:50:00 +04:00
|
|
|
{
|
2014-11-20 22:28:05 +03:00
|
|
|
CLIPRDR_FORMAT_DATA_RESPONSE formatDataResponse;
|
2013-11-25 05:46:56 +04:00
|
|
|
CliprdrClientContext* context = cliprdr_get_client_interface(cliprdr);
|
2015-08-27 15:25:09 +03:00
|
|
|
UINT error = CHANNEL_RC_OK;
|
2015-03-23 17:58:16 +03:00
|
|
|
|
2014-10-16 06:48:18 +04:00
|
|
|
WLog_Print(cliprdr->log, WLOG_DEBUG, "ServerFormatDataResponse");
|
|
|
|
|
2014-11-20 22:28:05 +03:00
|
|
|
if (!context->custom)
|
2015-06-02 14:05:10 +03:00
|
|
|
{
|
|
|
|
WLog_ERR(TAG, "context->custom not set!");
|
|
|
|
return ERROR_INTERNAL_ERROR;
|
|
|
|
}
|
2015-03-23 17:58:16 +03:00
|
|
|
|
2014-11-20 22:28:05 +03:00
|
|
|
formatDataResponse.msgType = CB_FORMAT_DATA_RESPONSE;
|
|
|
|
formatDataResponse.msgFlags = msgFlags;
|
|
|
|
formatDataResponse.dataLen = dataLen;
|
|
|
|
formatDataResponse.requestedFormatData = NULL;
|
2015-03-23 17:58:16 +03:00
|
|
|
|
2014-11-20 22:28:05 +03:00
|
|
|
if (dataLen)
|
2015-10-02 10:29:00 +03:00
|
|
|
formatDataResponse.requestedFormatData = (BYTE*) Stream_Pointer(s);
|
2015-03-23 17:58:16 +03:00
|
|
|
|
2015-06-02 14:05:10 +03:00
|
|
|
IFCALLRET(context->ServerFormatDataResponse, error, context, &formatDataResponse);
|
|
|
|
if (error)
|
2016-12-14 00:47:08 +03:00
|
|
|
WLog_ERR(TAG, "ServerFormatDataResponse failed with error %"PRIu32"!", error);
|
2015-03-23 17:58:16 +03:00
|
|
|
|
2015-06-02 14:05:10 +03:00
|
|
|
return error;
|
2011-07-12 20:50:00 +04:00
|
|
|
}
|