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