Rabbit Remote Control 0.0.36
Loading...
Searching...
No Matches
ClipboardFreeRDP.cpp
1// Author: Kang Lin <kl222@126.com>
2// See: https://github.com/KangLin/Documents/blob/master/qt/clipboard.md
3// RDP protocol: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpeclip/fb9b7e0b-6db4-41c2-b83c-f889c1ee7688
4
5#include "ClipboardFreeRDP.h"
6#include <QClipboard>
7#include <QApplication>
8#include <QMimeData>
9#include <QtDebug>
10#include <QImage>
11#include <QBuffer>
12#include <QStandardPaths>
13#include <QDir>
14
15#include "ConnectFreeRDP.h"
16#include "ClipboardMimeData.h"
17#include "freerdp/version.h"
18
19static Q_LOGGING_CATEGORY(log, "FreeRDP.Clipboard")
20
21CClipboardFreeRDP::CClipboardFreeRDP(CConnectFreeRDP *parent) : QObject(parent),
22 m_pConnect(parent),
23 m_pCliprdrClientContext(nullptr),
24 m_pClipboard(nullptr),
25 m_FileCapabilityFlags(0),
26 m_bFileSupported(false),
27 m_bFileFormatsRegistered(false)
28{
29 qDebug(log) << "CClipboardFreeRDP::CClipboardFreeRDP()";
30 m_pClipboard = ClipboardCreate();
31 if (ClipboardGetFormatId(m_pClipboard, "text/uri-list"))
32 m_bFileFormatsRegistered = true;
33 wClipboardDelegate* pDelegate = ClipboardGetDelegate(m_pClipboard);
34 pDelegate->custom = this;
35 /* Set up a filesystem base path for local URI */
36 QString szPath = QStandardPaths::writableLocation(
37 QStandardPaths::TempLocation)
38 + QDir::separator() + "Rabbit"
39 + QDir::separator() + "RabbitRemoteControl";
40 qDebug(log) << "Delegate base path:" << szPath;
41
42 pDelegate->basePath = _strdup(szPath.toStdString().c_str());
43 pDelegate->ClipboardFileSizeSuccess = cb_clipboard_file_size_success;
44 pDelegate->ClipboardFileSizeFailure = cb_clipboard_file_size_failure;
45 pDelegate->ClipboardFileRangeSuccess = cb_clipboard_file_range_success;
46 pDelegate->ClipboardFileRangeFailure = cb_clipboard_file_range_failure;
47
48#if FREERDP_VERSION_MAJOR > 2 || (FREERDP_VERSION_MAJOR == 2 && FREERDP_VERSION_MINOR > 7)
49 pDelegate->IsFileNameComponentValid = cbIsFileNameComponentValid;
50#endif
51}
52
53CClipboardFreeRDP::~CClipboardFreeRDP()
54{
55 qDebug(log) << "CClipboardFreeRdp::~CClipboardFreeRdp()";
56
57 // Notify clipboard program has exited
58 emit sigServerFormatData(nullptr, 0, 0);
59 QByteArray data;
60 emit sigServerFileContentsRespose(-1, data);
61 ClipboardDestroy(m_pClipboard);
62}
63
64int CClipboardFreeRDP::Init(CliprdrClientContext *context, bool bEnable)
65{
66 if(!bEnable) return 0;
67
68 m_pCliprdrClientContext = context;
69 context->custom = this;
70
71 // See: [MS_RDPECLIP] 1.3.2.1 Initialization Sequence
72 // https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpeclip/a5cae3c9-170c-4154-992d-9ac8a149cc7e
73 context->ServerCapabilities = cb_cliprdr_server_capabilities;
74 context->MonitorReady = cb_cliprdr_monitor_ready;
75 context->ServerFormatList = cb_cliprdr_server_format_list;
76 context->ServerFormatListResponse = cb_cliprdr_server_format_list_response;
77 context->ServerFormatDataRequest = cb_cliprdr_server_format_data_request;
78 context->ServerFormatDataResponse = cb_cliprdr_server_format_data_response;
79 context->ServerFileContentsRequest = cb_cliprdr_server_file_contents_request;
80 //context->ServerFileContentsResponse = cb_cliprdr_server_file_contents_response;
81 return 0;
82}
83
84int CClipboardFreeRDP::UnInit(CliprdrClientContext *context, bool bEnable)
85{
86 context->custom = nullptr;
87 m_pCliprdrClientContext = nullptr;
88 return 0;
89}
90
91void CClipboardFreeRDP::slotClipBoardChanged()
92{
93 qDebug(log) << "CClipboardFreeRdp::slotClipBoardChanged";
94 // Whether it is the clipboard's QMimeData set by this connection
95 const QMimeData* pMimeType = QApplication::clipboard()->mimeData();
96 if(!pMimeType) return;
97
98 qint32 data = 0;
99 QVariant d = pMimeType->data(MIME_TYPE_RABBITREMOTECONTROL_PLUGINS_FREERDP);
100 if(d.isValid()) {
101 data = d.toInt();
102 if(!m_lstClipboardMimeDataId.isEmpty()
103 && m_lstClipboardMimeDataId.contains(data))
104 {//*
105 qDebug(log)
106 << "CClipboardFreeRdp::slotClipBoardChanged: clipboard is this owner"
107 << data << m_lstClipboardMimeDataId;//*/
108 return;
109 }
110 }
111
112 //*
113 qDebug(log) << "CClipboardFreeRdp::slotClipBoardChanged:"
114 << data << m_lstClipboardMimeDataId;//*/
115 m_lstClipboardMimeDataId.clear();
116 SendClientFormatList(m_pCliprdrClientContext);
117}
118
119UINT CClipboardFreeRDP::cb_cliprdr_server_capabilities(
120 CliprdrClientContext* context,
121 const CLIPRDR_CAPABILITIES* capabilities)
122{
123 qDebug(log) << "CClipboardFreeRdp::cb_cliprdr_server_capabilities";
124
125 int nRet = CHANNEL_RC_OK;
126
127 CClipboardFreeRDP* pThis = (CClipboardFreeRDP*)context->custom;
128 pThis->m_bFileSupported = FALSE;
129 const CLIPRDR_CAPABILITY_SET* caps;
130 const CLIPRDR_GENERAL_CAPABILITY_SET* generalCaps;
131 const BYTE* capsPtr = (const BYTE*)capabilities->capabilitySets;
132
133 for (UINT32 i = 0; i < capabilities->cCapabilitiesSets; i++)
134 {
135 caps = (const CLIPRDR_CAPABILITY_SET*)capsPtr;
136
137 if (caps->capabilitySetType == CB_CAPSTYPE_GENERAL)
138 {
139 generalCaps = (const CLIPRDR_GENERAL_CAPABILITY_SET*)caps;
140
141 if (generalCaps->generalFlags & CB_STREAM_FILECLIP_ENABLED)
142 {
143 // Support file clipboard
144 pThis->m_bFileSupported = TRUE;
145 }
146 }
147
148 capsPtr += caps->capabilitySetLength;
149 }
150 return nRet;
151}
152
154
155// 1.3.2.1 Initialization Sequence:
156// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpeclip/a5cae3c9-170c-4154-992d-9ac8a149cc7e
157UINT CClipboardFreeRDP::cb_cliprdr_monitor_ready(
158 CliprdrClientContext *context,
159 const CLIPRDR_MONITOR_READY *monitorReady)
160{
161 qDebug(log) << "CClipboardFreeRdp::cb_cliprdr_monitor_ready";
162 UINT nRet = CHANNEL_RC_OK;
163
164 if (!context || !context->ClientCapabilities)
165 {
166 Q_ASSERT(false);
167 return ERROR_INTERNAL_ERROR;
168 }
169 CClipboardFreeRDP* pThis = (CClipboardFreeRDP*)context->custom;
170
171 // Send client capabilities
172 CLIPRDR_CAPABILITIES capabilities;
173 CLIPRDR_GENERAL_CAPABILITY_SET generalCapabilitySet;
174 capabilities.cCapabilitiesSets = 1;
175 capabilities.capabilitySets = (CLIPRDR_CAPABILITY_SET*)&(generalCapabilitySet);
176 generalCapabilitySet.capabilitySetType = CB_CAPSTYPE_GENERAL;
177 generalCapabilitySet.capabilitySetLength = 12;
178 generalCapabilitySet.version = CB_CAPS_VERSION_2;
179 generalCapabilitySet.generalFlags = CB_USE_LONG_FORMAT_NAMES;
180
181 if (pThis->m_bFileSupported && pThis->m_bFileFormatsRegistered)
182 {
183 generalCapabilitySet.generalFlags |=
184 CB_STREAM_FILECLIP_ENABLED | CB_FILECLIP_NO_FILE_PATHS
185#if FREERDP_VERSION_MAJOR > 2 || (FREERDP_VERSION_MAJOR == 2 && FREERDP_VERSION_MINOR > 7)
186 | CB_HUGE_FILE_SUPPORT_ENABLED
187#endif
188 ;
189 }
190
191 pThis->m_FileCapabilityFlags = generalCapabilitySet.generalFlags;
192 if((nRet = context->ClientCapabilities(context, &capabilities))
193 != CHANNEL_RC_OK)
194 {
195 qCritical(log) << "Send Client Capabilities fail";
196 return nRet;
197 }
198
199 // Send client formats
200 return SendClientFormatList(context);
201}
202
203// See: [MS_RDPECLIP] 1.3.2.2 Data Transfer Sequences
204// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpeclip/395bc830-f2c2-40e5-a3f3-23e41183b777
205// Clipboard Formats: https://docs.microsoft.com/en-us/windows/win32/dataxchg/clipboard-formats
206UINT CClipboardFreeRDP::SendClientFormatList(CliprdrClientContext *context)
207{
208 qDebug(log) << "CClipboardFreeRdp::SendClientFormatList";
209 int nRet = CHANNEL_RC_OK;
210 int nLen = 0;
211 CLIPRDR_FORMAT* pFormats = nullptr;
212 CLIPRDR_FORMAT_LIST formatList = { 0 };
213 UINT32 numFormats = 0;
214
215 if(!context) return nRet;
216
217 CClipboardFreeRDP* pThis = (CClipboardFreeRDP*)context->custom;
218 QClipboard *clipboard = QApplication::clipboard();
219 if(!clipboard)
220 {
221 qDebug(log) << "clipboard is null";
222 return nRet;
223 }
224
225 QVector<UINT32> formatIds;
226 const QMimeData* pData = clipboard->mimeData();
227 if(!pData || pData->formats().isEmpty())
228 {
229 qDebug(log) << "clipboard->mimeData is null";
230 return nRet;
231 }
232 auto clipFormats = pData->formats();
233 if(!clipFormats.isEmpty())
234 {
235 nLen = clipFormats.length()
236 + ClipboardCountRegisteredFormats(pThis->m_pClipboard);
237 pFormats = new CLIPRDR_FORMAT[nLen];
238 }
239 if(!pFormats)
240 {
241 qCritical(log) << "Failed to allocate"
242 << nLen << "CLIPRDR_FORMAT structs";
243 return CHANNEL_RC_NO_MEMORY;
244 }
245 memset(pFormats, 0, sizeof(CLIPRDR_FORMAT) * nLen);
246 qDebug(log) << "Clipboard formats:" << clipFormats;
247 foreach(auto f, pData->formats())
248 {
249 UINT32 id = ClipboardRegisterFormat(
250 pThis->m_pClipboard, f.toStdString().c_str());
251 if(!formatIds.contains(id))
252 {
253 pFormats[numFormats].formatName = _strdup(f.toStdString().c_str());
254 pFormats[numFormats].formatId = id;
255 formatIds.push_back(pFormats[numFormats].formatId);
256 numFormats++;
257 }
258 }
259
260 if(pData->hasUrls())
261 {
262 UINT32 id = ClipboardRegisterFormat(
263 pThis->m_pClipboard, "FileGroupDescriptorW");
264 if(!formatIds.contains(id))
265 {
266 pFormats[numFormats].formatName = _strdup("FileGroupDescriptorW");
267 pFormats[numFormats].formatId = id;
268 formatIds.push_back(pFormats[numFormats].formatId);
269 numFormats++;
270 }
271 id = ClipboardRegisterFormat(pThis->m_pClipboard, "FileContents");
272 if(!formatIds.contains(id))
273 {
274 pFormats[numFormats].formatName = _strdup("FileContents");
275 pFormats[numFormats].formatId = id;
276 formatIds.push_back(pFormats[numFormats].formatId);
277 numFormats++;
278 }
279 id = ClipboardRegisterFormat(pThis->m_pClipboard, "text/uri-list");
280 if(!formatIds.contains(id))
281 {
282 pFormats[numFormats].formatName = _strdup("text/uri-list");
283 pFormats[numFormats].formatId = id;
284 formatIds.push_back(pFormats[numFormats].formatId);
285 numFormats++;
286 }
287 }
288 if(pData->hasImage())
289 {
290 // if(!formatIds.contains(CF_BITMAP))
291 // {
292 // pFormats[numFormats].formatId = CF_BITMAP;
293 // pFormats[numFormats].formatName = nullptr;
294 // formatIds.push_back(pFormats[numFormats].formatId);
295 // numFormats++;
296 // }
297 if(!formatIds.contains(CF_DIB))
298 {
299 pFormats[numFormats].formatId = CF_DIB;
300 pFormats[numFormats].formatName = nullptr;
301 formatIds.push_back(pFormats[numFormats].formatId);
302 numFormats++;
303 }
304 if(!formatIds.contains(CF_DIBV5))
305 {
306 pFormats[numFormats].formatId = CF_DIBV5;
307 pFormats[numFormats].formatName = nullptr;
308 formatIds.push_back(pFormats[numFormats].formatId);
309 numFormats++;
310 }
311 }
312 if(pData->hasHtml())
313 {
314 UINT32 id = ClipboardRegisterFormat(
315 pThis->m_pClipboard, "text/html");
316 if(!formatIds.contains(id))
317 {
318 pFormats[numFormats].formatId = id;
319 pFormats[numFormats].formatName = _strdup("text/html");
320 formatIds.push_back(pFormats[numFormats].formatId);
321 numFormats++;
322 }
323 id = ClipboardRegisterFormat(
324 pThis->m_pClipboard, "HTML Format");
325 if(!formatIds.contains(id))
326 {
327 pFormats[numFormats].formatId = id;
328 pFormats[numFormats].formatName = _strdup("HTML Format");
329 formatIds.push_back(pFormats[numFormats].formatId);
330 numFormats++;
331 }
332 }
333 if(pData->hasText())
334 {
335 if(!formatIds.contains(CF_TEXT))
336 {
337 pFormats[numFormats].formatId = CF_TEXT;
338 pFormats[numFormats].formatName = nullptr;
339 formatIds.push_back(pFormats[numFormats].formatId);
340 numFormats++;
341 }
342 if(!formatIds.contains(CF_OEMTEXT))
343 {
344 pFormats[numFormats].formatId = CF_OEMTEXT;
345 pFormats[numFormats].formatName = nullptr;
346 formatIds.push_back(pFormats[numFormats].formatId);
347 numFormats++;
348 }
349 if(!formatIds.contains(CF_UNICODETEXT))
350 {
351 pFormats[numFormats].formatId = CF_UNICODETEXT;
352 pFormats[numFormats].formatName = nullptr;
353 formatIds.push_back(pFormats[numFormats].formatId);
354 numFormats++;
355 }
356 if(!formatIds.contains(CF_LOCALE))
357 {
358 pFormats[numFormats].formatId = CF_LOCALE;
359 pFormats[numFormats].formatName = nullptr;
360 formatIds.push_back(pFormats[numFormats].formatId);
361 numFormats++;
362 }
363 }
364 //*
365 QString szFormats;
366 for(int i = 0; i < numFormats; i++)
367 {
368 szFormats += " id:" + QString::number(pFormats[i].formatId);
369 if(pFormats[i].formatName)
370 {
371 szFormats += "(";
372 szFormats += pFormats[i].formatName;
373 szFormats += ");";
374 }
375 szFormats += "\n";
376 }
377 qDebug(log, "SendClientFormatList formats: %d:%s",
378 numFormats,
379 szFormats.toStdString().c_str());//*/
380#if FREERDP_VERSION_MAJOR >= 3
381 formatList.common.msgFlags = CB_RESPONSE_OK;
382 formatList.common.msgType = CB_FORMAT_LIST;
383#else
384 formatList.msgFlags = CB_RESPONSE_OK;
385 formatList.msgType = CB_FORMAT_LIST;
386#endif
387 formatList.numFormats = numFormats;
388 formatList.formats = pFormats;
389
390
391 /* Ensure all pending requests are answered. */
392 SendFormatDataResponse(context, NULL, 0);
393
394 Q_ASSERT(context->ClientFormatList);
395 nRet = context->ClientFormatList(context, &formatList);
396 qDebug(log) << "SendClientFormatList nRet:" << nRet;
397 for(UINT32 i = 0; i < numFormats; i++)
398 if(pFormats[i].formatName)
399 {
400 //qDebug(log) << pFormats[i].formatName;
401 free(pFormats[i].formatName);
402 }
403 delete []pFormats;
404 return nRet;
405}
406
408
409UINT CClipboardFreeRDP::cb_cliprdr_server_format_data_request(
410 CliprdrClientContext* context,
411 const CLIPRDR_FORMAT_DATA_REQUEST* formatDataRequest)
412{
413 qDebug(log)
414 << "CClipboardFreeRdp::cb_cliprdr_server_format_data_request";
415
416 int nRet = CHANNEL_RC_OK;
417 bool bSuucess = false;
418 QString mimeType;
419 BYTE* pDstData = NULL;
420 UINT32 dstSize = 0;
421 UINT32 dstFormatId = formatDataRequest->requestedFormatId;
422 UINT32 scrFormatID = 0;
423 if(!context) return ERROR_INTERNAL_ERROR;
424
425 CClipboardFreeRDP* pThis = (CClipboardFreeRDP*)context->custom;
426 qDebug(log) << "server format date request formatID:"
427 << dstFormatId;
428 QClipboard *clipboard = QApplication::clipboard();
429 if(!clipboard) return ERROR_INTERNAL_ERROR;
430
431 switch(dstFormatId)
432 {
433 case CF_TEXT:
434 case CF_OEMTEXT:
435 case CF_UNICODETEXT:
436 case CF_LOCALE:
437 {
438 if(clipboard->mimeData()->hasText())
439 {
440 QString szData = clipboard->text();
441 bSuucess = ClipboardSetData(
442 pThis->m_pClipboard, CF_UNICODETEXT,
443 szData.data(), (szData.size() + 1) * sizeof(QChar));
444 }
445 break;
446 }
447 case CF_DIB:
448 case CF_DIBV5:
449 //case CF_BITMAP:
450 if(clipboard->mimeData()->hasImage())
451 {
452 QImage img = clipboard->image();
453 if(img.isNull())
454 break;
455 QByteArray d;
456 QBuffer buffer(&d);
457 if(buffer.open(QIODevice::WriteOnly))
458 {
459 img.save(&buffer, "BMP");
460 buffer.close();
461 }
462 if(!d.isEmpty())
463 bSuucess = ClipboardSetData(
464 pThis->m_pClipboard,
465 ClipboardGetFormatId(pThis->m_pClipboard, "image/bmp"),
466 (BYTE*)d.data(), d.length());
467 }
468 break;
469 default:
470 if(ClipboardGetFormatId(pThis->m_pClipboard, "HTML Format")
471 == dstFormatId)
472 {
473 mimeType = "text/html";
474 scrFormatID = ClipboardGetFormatId(pThis->m_pClipboard,
475 "text/html");
476 } else if(ClipboardGetFormatId(pThis->m_pClipboard,
477 "FileGroupDescriptorW")
478 == dstFormatId)
479 {
480 mimeType = "text/uri-list";
481 scrFormatID = ClipboardGetFormatId(pThis->m_pClipboard,
482 "text/uri-list");
483 } else {
484 mimeType = ClipboardGetFormatName(pThis->m_pClipboard, dstFormatId);
485 scrFormatID = dstFormatId;
486 }
487 if(!mimeType.isEmpty() && scrFormatID > 0)
488 {
489 QByteArray data = clipboard->mimeData()->data(mimeType);
490 qDebug(log) << "mimeData:" << data << data.length();
491 if(!data.isEmpty())
492 bSuucess = ClipboardSetData(pThis->m_pClipboard, scrFormatID,
493 data.data(), data.size());
494 }
495 break;
496 }
497
498 if(bSuucess)
499 pDstData = (BYTE*)ClipboardGetData(pThis->m_pClipboard,
500 dstFormatId, &dstSize);
501
502 if(!pDstData)
503 {
504 qCritical(log)
505 << "ClipboardGetData fail: dstFormatId:" << dstFormatId
506 << ", srcFormatId:" << scrFormatID;
507 nRet = SendFormatDataResponse(context, NULL, 0);
508 return nRet;
509 }
510
511 /*
512 * File lists require a bit of postprocessing to convert them from WinPR's FILDESCRIPTOR
513 * format to CLIPRDR_FILELIST expected by the server.
514 *
515 * We check for "FileGroupDescriptorW" format being registered (i.e., nonzero) in order
516 * to not process CF_RAW as a file list in case WinPR does not support file transfers.
517 */
518 if(dstFormatId
519 && (ClipboardGetFormatId(pThis->m_pClipboard, "FileGroupDescriptorW")
520 == dstFormatId))
521 {
522 UINT error = NO_ERROR;
523 FILEDESCRIPTORW* file_array = (FILEDESCRIPTORW*)pDstData;
524 UINT32 file_count = dstSize / sizeof(FILEDESCRIPTORW);
525
526 error = cliprdr_serialize_file_list_ex(
527 pThis->m_FileCapabilityFlags, file_array,
528 file_count, &pDstData, &dstSize);
529 if (error)
530 qCritical(log)
531 << "failed to serialize CLIPRDR_FILELIST:" << error;
532 free(file_array);
533 }
534
535 nRet = SendFormatDataResponse(context, pDstData, dstSize);
536
537 if(pDstData)
538 free(pDstData);
539
540 /*
541 qDebug(log) <<
542 "cb_cliprdr_server_format_data_request end. nRet:" << nRet;//*/
543
544 return nRet;
545}
546
547UINT CClipboardFreeRDP::SendFormatDataResponse(CliprdrClientContext *context,
548 const BYTE *data, size_t size)
549{
550 qDebug(log) << Q_FUNC_INFO;
551 CLIPRDR_FORMAT_DATA_RESPONSE response = { 0 };
552#if FREERDP_VERSION_MAJOR >= 3
553 response.common.msgFlags = (data) ? CB_RESPONSE_OK : CB_RESPONSE_FAIL;
554 response.common.dataLen = size;
555#else
556 response.msgFlags = (data) ? CB_RESPONSE_OK : CB_RESPONSE_FAIL;
557 response.dataLen = size;
558#endif
559 response.requestedFormatData = data;
560 return context->ClientFormatDataResponse(context, &response);
561}
562
564
565UINT CClipboardFreeRDP::cb_clipboard_file_size_success(
566 wClipboardDelegate* delegate,
567 const wClipboardFileSizeRequest* request,
568 UINT64 fileSize)
569{
570 qDebug(log) << "CClipboardFreeRDP::cb_clipboard_file_size_success";
571 CLIPRDR_FILE_CONTENTS_RESPONSE response = { 0 };
572 CClipboardFreeRDP* pThis = (CClipboardFreeRDP*)delegate->custom;
573#if FREERDP_VERSION_MAJOR >= 3
574 response.common.msgFlags = CB_RESPONSE_OK;
575#else
576 response.msgFlags = CB_RESPONSE_OK;
577#endif
578 response.streamId = request->streamId;
579 response.cbRequested = sizeof(UINT64);
580 response.requestedData = (BYTE*)&fileSize;
581 return pThis->m_pCliprdrClientContext->ClientFileContentsResponse(
582 pThis->m_pCliprdrClientContext, &response);
583}
584
585UINT CClipboardFreeRDP::cb_clipboard_file_size_failure(
586 wClipboardDelegate* delegate,
587 const wClipboardFileSizeRequest* request,
588 UINT errorCode)
589{
590 qDebug(log) << "CClipboardFreeRDP::cb_clipboard_file_size_failure";
591 CLIPRDR_FILE_CONTENTS_RESPONSE response = { 0 };
592 CClipboardFreeRDP* pThis = (CClipboardFreeRDP*)delegate->custom;
593 WINPR_UNUSED(errorCode);
594#if FREERDP_VERSION_MAJOR >= 3
595 response.common.msgFlags = CB_RESPONSE_FAIL;
596#else
597 response.msgFlags = CB_RESPONSE_FAIL;
598#endif
599 response.streamId = request->streamId;
600 return pThis->m_pCliprdrClientContext->ClientFileContentsResponse(
601 pThis->m_pCliprdrClientContext, &response);
602}
603
604UINT CClipboardFreeRDP::cb_clipboard_file_range_success(
605 wClipboardDelegate* delegate,
606 const wClipboardFileRangeRequest* request,
607 const BYTE* data, UINT32 size)
608{
609 qDebug(log) << "CClipboardFreeRDP::cb_clipboard_file_range_success";
610 CLIPRDR_FILE_CONTENTS_RESPONSE response = { 0 };
611 CClipboardFreeRDP* pThis = (CClipboardFreeRDP*)delegate->custom;
612#if FREERDP_VERSION_MAJOR >= 3
613 response.common.msgFlags = CB_RESPONSE_OK;
614#else
615 response.msgFlags = CB_RESPONSE_OK;
616#endif
617 response.streamId = request->streamId;
618 response.cbRequested = size;
619 response.requestedData = (BYTE*)data;
620 return pThis->m_pCliprdrClientContext->ClientFileContentsResponse(
621 pThis->m_pCliprdrClientContext, &response);
622}
623
624UINT CClipboardFreeRDP::cb_clipboard_file_range_failure(
625 wClipboardDelegate* delegate,
626 const wClipboardFileRangeRequest* request,
627 UINT errorCode)
628{
629 qDebug(log) << "CClipboardFreeRDP::cb_clipboard_file_range_failure";
630 CLIPRDR_FILE_CONTENTS_RESPONSE response = { 0 };
631 CClipboardFreeRDP* pThis = (CClipboardFreeRDP*)delegate->custom;
632 WINPR_UNUSED(errorCode);
633#if FREERDP_VERSION_MAJOR >= 3
634 response.common.msgFlags = CB_RESPONSE_FAIL;
635#else
636 response.msgFlags = CB_RESPONSE_FAIL;
637#endif
638 response.streamId = request->streamId;
639 return pThis->m_pCliprdrClientContext->ClientFileContentsResponse(
640 pThis->m_pCliprdrClientContext, &response);
641}
642
643BOOL CClipboardFreeRDP::cbIsFileNameComponentValid(LPCWSTR lpFileName)
644{
645 qDebug(log) << "CClipboardFreeRDP::cbIsFileNameComponentValid:" << lpFileName;
646 LPCWSTR c;
647
648 if (!lpFileName)
649 return FALSE;
650
651 if (lpFileName[0] == L'\0')
652 return FALSE;
653
654 /* Reserved characters */
655 for (c = lpFileName; *c; ++c)
656 {
657 if (*c == L'/')
658 return FALSE;
659 }
660
661 return TRUE;
662}
663
664UINT CClipboardFreeRDP::SendFileContentsFailure(
665 CliprdrClientContext* context,
666 const CLIPRDR_FILE_CONTENTS_REQUEST* fileContentsRequest)
667{
668 qDebug(log) << Q_FUNC_INFO;
669 CLIPRDR_FILE_CONTENTS_RESPONSE response = { 0 };
670#if FREERDP_VERSION_MAJOR >= 3
671 response.common.msgFlags = CB_RESPONSE_FAIL;
672#else
673 response.msgFlags = CB_RESPONSE_FAIL;
674#endif
675 response.streamId = fileContentsRequest->streamId;
676 return context->ClientFileContentsResponse(context, &response);
677}
678
679UINT CClipboardFreeRDP::ServerFileRangeRequest(
680 const CLIPRDR_FILE_CONTENTS_REQUEST* fileContentsRequest)
681{
682 qDebug(log) << Q_FUNC_INFO;
683 wClipboardFileRangeRequest request = { 0 };
684 request.streamId = fileContentsRequest->streamId;
685 request.listIndex = fileContentsRequest->listIndex;
686 request.nPositionLow = fileContentsRequest->nPositionLow;
687 request.nPositionHigh = fileContentsRequest->nPositionHigh;
688 request.cbRequested = fileContentsRequest->cbRequested;
689 wClipboardDelegate* pDelegate = ClipboardGetDelegate(m_pClipboard);
690 if(pDelegate)
691 return pDelegate->ClientRequestFileRange(pDelegate, &request);
692 return E_FAIL;
693}
694
695UINT CClipboardFreeRDP::ServerFileSizeRequest(
696 const CLIPRDR_FILE_CONTENTS_REQUEST* fileContentsRequest)
697{
698 qDebug(log) << Q_FUNC_INFO;
699 wClipboardFileSizeRequest request = { 0 };
700 request.streamId = fileContentsRequest->streamId;
701 request.listIndex = fileContentsRequest->listIndex;
702
703 wClipboardDelegate* pDelegate = ClipboardGetDelegate(m_pClipboard);
704 if(!pDelegate) return E_FAIL;
705
706 if (fileContentsRequest->cbRequested != sizeof(UINT64))
707 {
708 qWarning(log) << "unexpected FILECONTENTS_SIZE request:"
709 << fileContentsRequest->cbRequested << " bytes";
710 }
711
712 return pDelegate->ClientRequestFileSize(pDelegate, &request);
713}
714
715UINT CClipboardFreeRDP::cb_cliprdr_server_file_contents_request(
716 CliprdrClientContext* context,
717 const CLIPRDR_FILE_CONTENTS_REQUEST* fileContentsRequest)
718{
719 qDebug(log) <<
720 "CClipboardFreeRdp::cb_cliprdr_server_file_contents_request";
721 int nRet = CHANNEL_RC_OK;
722
723 UINT error = NO_ERROR;
724
725 if(!context) return nRet;
726
727 CClipboardFreeRDP* pThis = (CClipboardFreeRDP*)context->custom;
728
729 /*
730 * MS-RDPECLIP 2.2.5.3 File Contents Request PDU (CLIPRDR_FILECONTENTS_REQUEST):
731 * The FILECONTENTS_SIZE and FILECONTENTS_RANGE flags MUST NOT be set at the same time.
732 */
733 if ((fileContentsRequest->dwFlags
734 & (FILECONTENTS_SIZE | FILECONTENTS_RANGE))
735 == (FILECONTENTS_SIZE | FILECONTENTS_RANGE))
736 {
737 qCritical(log) << "invalid CLIPRDR_FILECONTENTS_REQUEST.dwFlags";
738 return SendFileContentsFailure(context, fileContentsRequest);
739 }
740
741 if (fileContentsRequest->dwFlags & FILECONTENTS_SIZE)
742 error = pThis->ServerFileSizeRequest(fileContentsRequest);
743
744 if (fileContentsRequest->dwFlags & FILECONTENTS_RANGE)
745 error = pThis->ServerFileRangeRequest(fileContentsRequest);
746
747 if (error)
748 {
749 qCritical(log)
750 << "failed to handle CLIPRDR_FILECONTENTS_REQUEST:" << error;
751 return SendFileContentsFailure(context, fileContentsRequest);
752 }
753
754 return nRet;
755}
756
758UINT CClipboardFreeRDP::cb_cliprdr_server_format_list(
759 CliprdrClientContext* context,
760 const CLIPRDR_FORMAT_LIST* formatList)
761{
762 qDebug(log) << "CClipboardFreeRdp::cb_cliprdr_server_format_list";
763 int nRet = CHANNEL_RC_OK;
764
765 CClipboardFreeRDP* pThis = (CClipboardFreeRDP*)context->custom;
766 CClipboardMimeData* pMimeData = nullptr;
767 if(formatList->numFormats < 0)
768 {
769 return nRet;
770 }
771 // The pMimeData is freed by QApplication::clipboard()
772 pMimeData = new CClipboardMimeData(context);
773 if(!pMimeData) return nRet;
774 if(pMimeData->SetFormat(formatList))
775 {
776 pMimeData->deleteLater();
777 return nRet;
778 }
779
780 bool check = false;
781 check = connect(pThis,
782 SIGNAL(sigServerFormatData(const BYTE*, UINT32, UINT32)),
783 pMimeData,
784 SLOT(slotServerFormatData(const BYTE*, UINT32, UINT32)),
785 Qt::DirectConnection);
786 Q_ASSERT(check);
787 check = connect(pThis,
788 SIGNAL(sigServerFileContentsRespose(UINT32, QByteArray&)),
789 pMimeData,
790 SLOT(slotServerFileContentsRespose(UINT32, QByteArray&)),
791 Qt::DirectConnection);
792 Q_ASSERT(check);
793 check = connect(pMimeData,
794 SIGNAL(sigSendDataRequest(CliprdrClientContext*, UINT32)),
795 pThis,
796 SLOT(slotSendFormatDataRequest(CliprdrClientContext*, UINT32)));
797 Q_ASSERT(check);
798
799 pThis->m_lstClipboardMimeDataId.push_back(pMimeData->GetId());
800 emit pThis->m_pConnect->sigSetClipboard(pMimeData);
801 return nRet;
802}
803
804UINT CClipboardFreeRDP::cb_cliprdr_server_format_list_response(
805 CliprdrClientContext* context,
806 const CLIPRDR_FORMAT_LIST_RESPONSE* pformatListResponse)
807{
808#if FREERDP_VERSION_MAJOR >= 3
809 qDebug(log)
810 << "CClipboardFreeRdp::cb_cliprdr_server_format_list_response:type:"
811 << pformatListResponse->common.msgType
812 << ";flag:" << pformatListResponse->common.msgFlags
813 << ";datalen:" << pformatListResponse->common.dataLen;
814#else
815 qDebug(log)
816 << "CClipboardFreeRdp::cb_cliprdr_server_format_list_response:type:"
817 << pformatListResponse->msgType
818 << ";flag:" << pformatListResponse->msgFlags
819 << ";datalen:" << pformatListResponse->dataLen;
820#endif
821
822 if (
823#if FREERDP_VERSION_MAJOR >= 3
824 pformatListResponse->common.msgFlags
825#else
826 pformatListResponse->msgFlags
827#endif
828 != CB_RESPONSE_OK)
829 qDebug(log) << "The server is not support the format";
830 return CHANNEL_RC_OK;
831}
832
833UINT CClipboardFreeRDP::slotSendFormatDataRequest(CliprdrClientContext* context,
834 UINT32 formatId)
835{
836 UINT rc = CHANNEL_RC_OK;
837 CLIPRDR_FORMAT_DATA_REQUEST formatDataRequest;
838 qDebug(log) << Q_FUNC_INFO;
839 if (!context || !context->ClientFormatDataRequest)
840 return ERROR_INTERNAL_ERROR;
841
842 CClipboardFreeRDP* pThis = (CClipboardFreeRDP*)context->custom;
843 pThis->m_RequestFormatId = formatId;
844 formatDataRequest.requestedFormatId = formatId;
845 rc = context->ClientFormatDataRequest(context, &formatDataRequest);
846
847 return rc;
848}
849
850UINT CClipboardFreeRDP::cb_cliprdr_server_format_data_response(
851 CliprdrClientContext* context,
852 const CLIPRDR_FORMAT_DATA_RESPONSE* formatDataResponse)
853{
854 qDebug(log) << "CClipboardFreeRdp::cb_cliprdr_server_format_data_response";
855 int nRet = CHANNEL_RC_OK;
856 if(!context) return nRet;
857
858 CClipboardFreeRDP* pThis = (CClipboardFreeRDP*)context->custom;
859
860 emit pThis->sigServerFormatData(formatDataResponse->requestedFormatData,
861#if FREERDP_VERSION_MAJOR >= 3
862 formatDataResponse->common.dataLen,
863#else
864 formatDataResponse->dataLen,
865#endif
866 pThis->m_RequestFormatId);
867 return nRet;
868}
869
870UINT CClipboardFreeRDP::cb_cliprdr_server_file_contents_response(
871 CliprdrClientContext* context,
872 const CLIPRDR_FILE_CONTENTS_RESPONSE* fileContentsResponse)
873{
874 qDebug(log) << "CClipboardFreeRdp::cb_cliprdr_server_file_contents_response";
875 int nRet = CHANNEL_RC_OK;
876
877 if (!context || !fileContentsResponse)
878 return ERROR_INTERNAL_ERROR;
879
880 if (
881#if FREERDP_VERSION_MAJOR >= 3
882 fileContentsResponse->common.msgFlags
883#else
884 fileContentsResponse->msgFlags
885#endif
886 != CB_RESPONSE_OK)
887 {
888 qDebug(log)
889 << "File contents response error";
890
891 return nRet;
892 }
893
894 CClipboardFreeRDP* pThis = (CClipboardFreeRDP*)context->custom;
895 if(0 == fileContentsResponse->cbRequested)
896 {
897 qDebug(log) << "CClipboardFreeRdp::cb_cliprdr_server_file_contents_response size is zero.";
898 QByteArray data;
899 emit pThis->sigServerFileContentsRespose(
900 fileContentsResponse->streamId,
901 data);
902 } else {
903 QByteArray data((char*)fileContentsResponse->requestedData,
904 fileContentsResponse->cbRequested);
905 emit pThis->sigServerFileContentsRespose(
906 fileContentsResponse->streamId,
907 data);
908 }
909 return nRet;
910}
void sigServerFormatData(const BYTE *pData, UINT32 nLen, UINT32 formatId)
Notify clipboard get data from server.