玉兔远程控制 0.0.35
载入中...
搜索中...
未找到
ConnectFreeRDP.cpp
1// Author: Kang Lin <kl222@126.com>
2// https://learn.microsoft.com/zh-cn/windows-server/remote/remote-desktop-services/welcome-to-rds
3// X.509 Public Key Certificates: https://learn.microsoft.com/zh-cn/windows/win32/seccertenroll/about-x-509-public-key-certificates
4// Cryptography: https://learn.microsoft.com/zh-cn/windows/win32/seccrypto/cryptography-portal
5
6#include "ConnectFreeRDP.h"
7
8#undef PEN_FLAG_INVERTED
9#include "freerdp/client.h"
10#include "freerdp/client/channels.h"
11#include "freerdp/channels/rdpei.h"
12#include "freerdp/channels/rdpdr.h"
13#include "freerdp/channels/disp.h"
14#include "freerdp/channels/tsmf.h"
15#include "freerdp/channels/rdpsnd.h"
16#include "freerdp/client/encomsp.h"
17#include "freerdp/gdi/gfx.h"
18#include "freerdp/settings.h"
19#include "freerdp/locale/keyboard.h"
20#include "freerdp/channels/rdpgfx.h"
21#include "freerdp/channels/cliprdr.h"
22#include "freerdp/client/cmdline.h"
23#include <freerdp/gdi/video.h>
24
25#include "RabbitCommonTools.h"
26#include "ConvertKeyCode.h"
27
28#include <memory.h>
29#include <QDebug>
30#include <QApplication>
31#include <QScreen>
32#include <QSslCertificate>
33#include <QInputDialog>
34#include <QMutexLocker>
35#include <QPainter>
36#include <QPrinterInfo>
37#include <QSerialPort>
38#include <QSerialPortInfo>
39#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
40 #include <QSoundEffect>
41#else
42 #include <QSound>
43#endif
44
45static Q_LOGGING_CATEGORY(log, "FreeRDP.Connect")
46static Q_LOGGING_CATEGORY(logKey, "FreeRDP.Connect.Key")
47static Q_LOGGING_CATEGORY(logMouse, "FreeRDP.Connect.Mouse")
48
50 : CConnectDesktop(pConnecter),
51 m_pContext(nullptr),
52 m_pParameter(nullptr),
53 m_ClipBoard(this),
54 m_Cursor(this),
55 m_writeEvent(nullptr)
56#ifdef HAVE_LIBSSH
57 ,m_pThread(nullptr)
58#endif
59{
60 qDebug(log) << Q_FUNC_INFO;
61 m_pParameter = qobject_cast<CParameterFreeRDP*>(pConnecter->GetParameter());
62 Q_ASSERT(m_pParameter);
63}
64
65CConnectFreeRDP::~CConnectFreeRDP()
66{
67 qDebug(log) << Q_FUNC_INFO;
68}
69
70/*
71 * \return
72 * \li OnInitReturnValue::Fail: error
73 * \li OnInitReturnValue::UseOnProcess: Use OnProcess (non-Qt event loop)
74 * \li OnInitReturnValue::NotUseOnProcess: Don't use OnProcess (qt event loop)
75 */
76CConnect::OnInitReturnValue CConnectFreeRDP::OnInit()
77{
78 qDebug(log) << Q_FUNC_INFO;
79 int nRet = 0;
80
81 m_writeEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
82 if(!m_writeEvent)
83 qCritical(log) << "CreateEvent failed";
84
85 ZeroMemory(&m_ClientEntryPoints, sizeof(RDP_CLIENT_ENTRY_POINTS));
86 m_ClientEntryPoints.Version = RDP_CLIENT_INTERFACE_VERSION;
87 m_ClientEntryPoints.Size = sizeof(RDP_CLIENT_ENTRY_POINTS);
88 //m_ClientEntryPoints.settings = m_pParameter->m_pSettings;
89 m_ClientEntryPoints.GlobalInit = cbGlobalInit;
90 m_ClientEntryPoints.GlobalUninit = cbGlobalUninit;
91 m_ClientEntryPoints.ClientNew = cbClientNew;
92 m_ClientEntryPoints.ClientFree = cbClientFree;
93 m_ClientEntryPoints.ClientStart = cbClientStart;
94 m_ClientEntryPoints.ClientStop = cbClientStop;
95 m_ClientEntryPoints.ContextSize = sizeof(ClientContext);
96
97 auto pRdpContext = freerdp_client_context_new(&m_ClientEntryPoints);
98 if(pRdpContext)
99 {
100 m_pContext = (ClientContext*)pRdpContext;
101 m_pContext->pThis = this;
102 } else {
103 qCritical(log) << "freerdp_client_context_new fail";
104 return OnInitReturnValue::Fail;
105 }
106
107 rdpSettings* settings = pRdpContext->settings;
108 if(!settings) {
109 qCritical(log) << "settings is null";
110 return OnInitReturnValue::Fail;
111 }
112
113 //*
114 // Initial FreeRDP default parameters.
115 // See: https://github.com/KangLin/RabbitRemoteControl/issues/27
116 // Set default parameters: FreeRDP_SupportGraphicsPipeline and FreeRDP_RemoteFxCodec
117 // See:
118 // - [[MS-RDPBCGR]: Remote Desktop Protocol: Basic Connectivity and Graphics Remoting](https://learn.microsoft.com/zh-cn/openspecs/windows_protocols/ms-rdpbcgr/5073f4ed-1e93-45e1-b039-6e30c385867c)
119 // - [[MS-RDPRFX]: Remote Desktop Protocol: RemoteFX Codec Extension](https://learn.microsoft.com/zh-cn/openspecs/windows_protocols/ms-rdprfx/62495a4a-a495-46ea-b459-5cde04c44549)
120 // - [[MS-RDPEGFX]: Remote Desktop Protocol: Graphics Pipeline Extension](https://learn.microsoft.com/zh-cn/openspecs/windows_protocols/ms-rdpegfx/da5c75f9-cd99-450c-98c4-014a496942b0)
121 // https://www.cswamp.com/post/37
122 // Set connection parameters. See: FREERDP_API BOOL freerdp_set_connection_type(rdpSettings* settings, UINT32 type);
123 char* argv[]= {(char*)QApplication::applicationFilePath().toStdString().c_str()};
124 int argc = sizeof(argv) / sizeof(char*);
125 nRet = freerdp_client_settings_parse_command_line(settings, argc, argv, TRUE);
126 if (nRet)
127 {
128 nRet = freerdp_client_settings_command_line_status_print(settings, nRet, argc, argv);
129 return OnInitReturnValue::Fail;
130 } //*/
131
132#if FreeRDP_VERSION_MAJOR >= 3
133 if (!stream_dump_register_handlers(pRdpContext,
134 CONNECTION_STATE_MCS_CREATE_REQUEST,
135 FALSE))
136 return OnInitReturnValue::Fail;
137#endif
138
139 auto &user = m_pParameter->m_Net.m_User;
140 if(!user.GetUser().isEmpty())
141 freerdp_settings_set_string(
142 settings, FreeRDP_Username,
143 user.GetUser().toStdString().c_str());
144 if(!user.GetPassword().isEmpty())
145 freerdp_settings_set_string(
146 settings, FreeRDP_Password,
147 user.GetPassword().toStdString().c_str());
148
149 freerdp_settings_set_bool(
150 settings, FreeRDP_RedirectClipboard, m_pParameter->GetClipboard());
151
152#if FreeRDP_VERSION_MAJOR >= 3
153 bool bOnlyView = m_pParameter->GetOnlyView();
154 freerdp_settings_set_bool(
155 settings, FreeRDP_SuspendInput, bOnlyView);
156#endif
157
158 freerdp_settings_set_uint32(settings, FreeRDP_DesktopWidth,
159 m_pParameter->GetDesktopWidth());
160 freerdp_settings_set_uint32(settings, FreeRDP_DesktopHeight,
161 m_pParameter->GetDesktopHeight());
162 freerdp_settings_set_uint32(settings, FreeRDP_ColorDepth,
163 m_pParameter->GetColorDepth());
164
165 freerdp_settings_set_bool(settings, FreeRDP_UseMultimon,
166 m_pParameter->GetUseMultimon());
167
168 if(m_pParameter->GetReconnectInterval()) {
169 freerdp_settings_set_bool(
170 settings, FreeRDP_AutoReconnectionEnabled, true);
171 freerdp_settings_set_uint32(
172 settings,
173 FreeRDP_AutoReconnectMaxRetries,
174 m_pParameter->GetReconnectInterval());
175 }
176 else
177 freerdp_settings_set_bool(
178 settings, FreeRDP_AutoReconnectionEnabled, false);
179
180 //*Load channel
181 RedirectionSound();
182 RedirectionMicrophone();
183 RedirectionDriver();
184 RedirectionPrinter();
185 RedirectionSerial();
186 //*/
187
188 // Set proxy
189 switch(m_pParameter->m_Proxy.GetUsedType())
190 {
191 case CParameterProxy::TYPE::None:
192 {
193 if(!m_pParameter->GetDomain().isEmpty())
194 freerdp_settings_set_string(
195 settings, FreeRDP_Domain,
196 m_pParameter->GetDomain().toStdString().c_str());
197 if(m_pParameter->m_Net.GetHost().isEmpty())
198 {
199 QString szErr;
200 szErr = tr("The server is empty, please input it");
201 qCritical(log) << szErr;
202 emit sigShowMessageBox(tr("Error"), szErr, QMessageBox::Critical);
203 emit sigError(-1, szErr.toStdString().c_str());
204 return OnInitReturnValue::Fail;
205 }
206 auto &net = m_pParameter->m_Net;
207 freerdp_settings_set_string(
208 settings, FreeRDP_ServerHostname,
209 net.GetHost().toStdString().c_str());
210 freerdp_settings_set_uint32(
211 settings, FreeRDP_ServerPort,
212 net.GetPort());
213
214 nRet = freerdp_client_start(pRdpContext);
215 if(nRet)
216 {
217 qCritical(log) << "freerdp_client_start fail";
218 return OnInitReturnValue::Fail;
219 }
220 break;
221 }
222#ifdef HAVE_LIBSSH
223 case CParameterProxy::TYPE::SSHTunnel:
224 {
225 // Set SSH parameters
226 QSharedPointer<CParameterChannelSSH> parameter(new CParameterChannelSSH());
227 auto &ssh = m_pParameter->m_Proxy.m_SSH;
228 parameter->setServer(ssh.GetHost());
229 parameter->setPort(ssh.GetPort());
230 auto &user = ssh.m_User;
231 parameter->SetUser(user.GetUser());
232 parameter->SetUseSystemFile(user.GetUseSystemFile());
233 if(CParameterUser::TYPE::UserPassword == user.GetUsedType()) {
234 parameter->SetAuthenticationMethod(SSH_AUTH_METHOD_PASSWORD);
235 parameter->SetPassword(user.GetPassword());
236 }
237 if(CParameterUser::TYPE::PublicKey == user.GetUsedType()) {
238 parameter->SetAuthenticationMethod(SSH_AUTH_METHOD_PUBLICKEY);
239 parameter->SetPublicKeyFile(user.GetPublicKeyFile());
240 parameter->SetPrivateKeyFile(user.GetPrivateKeyFile());
241 parameter->SetPassphrase(user.GetPassphrase());
242 }
243 auto &net = m_pParameter->m_Net;
244 parameter->SetRemoteHost(net.GetHost());
245 parameter->SetRemotePort(net.GetPort());
246
247 // Start ssh thread
248 if(!m_pThread)
249 m_pThread = new CSSHTunnelThread(parameter);
250 if(!m_pThread)
251 return OnInitReturnValue::Fail;
252 bool check = connect(m_pThread, SIGNAL(sigServer(QString, quint16)),
253 this, SLOT(slotConnectProxyServer(QString, quint16)));
254 Q_ASSERT(check);
255 check = connect(m_pThread, SIGNAL(sigError(int,QString)),
256 this, SIGNAL(sigError(int,QString)));
257 Q_ASSERT(check);
258 check = connect(m_pThread, SIGNAL(sigDisconnect()),
259 this, SIGNAL(sigDisconnect()));
260 Q_ASSERT(check);
261 m_pThread->start();
262 break;
263 }
264#endif
265 default:
266 break;
267 };
268
269 return OnInitReturnValue::UseOnProcess;
270}
271
273{
274 qDebug(log) << Q_FUNC_INFO;
275 int nRet = 0;
276
277#ifdef HAVE_LIBSSH
278 if(m_pThread)
279 {
280 m_pThread->Exit();
281 m_pThread = nullptr;
282 }
283#endif
284 if(m_writeEvent)
285 {
286 CloseHandle(m_writeEvent);
287 m_writeEvent = nullptr;
288 }
289 if(m_pContext)
290 {
291 rdpContext* pRdpContext = (rdpContext*)m_pContext;
292 if(!freerdp_disconnect(pRdpContext->instance))
293 qCritical(log) << "freerdp_disconnect fail";
294
295 if(freerdp_client_stop(pRdpContext))
296 qCritical(log) << "freerdp_client_stop fail";
297
298 freerdp_client_context_free(pRdpContext);
299 m_pContext = nullptr;
300 }
301 return nRet;
302}
303
318{
319 //qDebug(log) << Q_FUNC_INFO;
320 int nRet = 0;
321 HANDLE handles[64];
322 rdpContext* pRdpContext = (rdpContext*)m_pContext;
323
324 if(nullptr == freerdp_settings_get_string(pRdpContext->settings, FreeRDP_ServerHostname))
325 {
326 return 50;
327 }
328
329 do {
330
331 DWORD nCount = 0;
332 nCount = freerdp_get_event_handles(pRdpContext, &handles[nCount],
333 ARRAYSIZE(handles) - nCount);
334 if (nCount == 0)
335 {
336 qCritical(log) << "freerdp_get_event_handles failed";
337 nRet = -2;
338 break;
339 }
340
341 handles[nCount] = m_writeEvent;
342 nCount++;
343
344 DWORD waitStatus = WaitForMultipleObjects(nCount, handles, FALSE, 500);
345
346 ResetEvent(m_writeEvent);
347
348 if (waitStatus == WAIT_FAILED)
349 {
350 qCritical(log) << "WaitForMultipleObjects: WAIT_FAILED";
351 nRet = -3;
352 break;
353 }
354
355 if(waitStatus == WAIT_TIMEOUT)
356 {
357 //qDebug(log) << "WaitForMultipleObjects timeout";
358 nRet = 0;
359 break;
360 }
361
362 if (!freerdp_check_event_handles(pRdpContext))
363 {
364 nRet = -5;
365
366 UINT32 err = freerdp_get_last_error(pRdpContext);
367 QString szErr;
368 szErr = "freerdp_check_event_handles fail.";
369 szErr += " [";
370 szErr += QString::number(err);
371 szErr += " - ";
372 szErr += freerdp_get_last_error_category(err);
373 szErr += " - ";
374 szErr += freerdp_get_last_error_name(err);
375 szErr += "] ";
376 szErr += freerdp_get_last_error_string(err);
377 qCritical(log) << szErr;
378 emit sigError(err, szErr);
379
380 /*/ Reconnect
381 freerdp *instance = pRdpContext->instance;
382 if (client_auto_reconnect(instance))
383 {
384 nRet = 0;
385 break;
386 }
387
388 err = freerdp_get_last_error(pRdpContext);
389 szErr = "client_auto_reconnect[";
390 szErr += QString::number(err);
391 szErr += "]";
392 szErr += freerdp_get_last_error_category(err);
393 szErr += "-";
394 szErr += freerdp_get_last_error_name(err);
395 szErr += ":";
396 szErr += freerdp_get_last_error_string(err);
397 qCritical(log) << szErr;
398 emit sigError(err, szErr);//*/
399 }
400
401#if FreeRDP_VERSION_MAJOR >= 3
402 if(freerdp_shall_disconnect_context(pRdpContext))
403#else
404 if(freerdp_shall_disconnect(pRdpContext->instance))
405#endif
406 {
407 qCritical(log) << "freerdp_shall_disconnect false";
408 nRet = -7;
409 }
410 } while(false);
411
412 return nRet;
413}
414
415void CConnectFreeRDP::slotClipBoardChanged()
416{
417 qDebug(log) << Q_FUNC_INFO;
418 if(m_pParameter && m_pParameter->GetOnlyView()) return;
419 if(m_pParameter->GetClipboard())
420 m_ClipBoard.slotClipBoardChanged();
421}
422
423BOOL CConnectFreeRDP::cbGlobalInit()
424{
425 qDebug(log) << Q_FUNC_INFO;
426 return TRUE;
427}
428
429void CConnectFreeRDP::cbGlobalUninit()
430{
431 qDebug(log) << Q_FUNC_INFO;
432}
433
434BOOL CConnectFreeRDP::cbClientNew(freerdp *instance, rdpContext *context)
435{
436 qDebug(log) << Q_FUNC_INFO;
437 instance->PreConnect = cb_pre_connect;
438 instance->PostConnect = cb_post_connect;
439 instance->PostDisconnect = cb_post_disconnect;
440
441 // Because it is already set in the parameters
442#if FreeRDP_VERSION_MAJOR < 3
443 instance->Authenticate = cb_authenticate;
444 instance->GatewayAuthenticate = cb_GatewayAuthenticate;
445#else
446 instance->AuthenticateEx = cb_authenticate_ex;
447 instance->ChooseSmartcard = cb_choose_smartcard;
448#endif
449 instance->VerifyX509Certificate = cb_verify_x509_certificate;
450 instance->VerifyCertificateEx = cb_verify_certificate_ex;
451 instance->VerifyChangedCertificateEx = cb_verify_changed_certificate_ex;
452 instance->PresentGatewayMessage = cb_present_gateway_message;
453
454 instance->LogonErrorInfo = cb_logon_error_info;
455
456 return TRUE;
457}
458
459void CConnectFreeRDP::cbClientFree(freerdp *instance, rdpContext *context)
460{
461 qDebug(log) << Q_FUNC_INFO;
462}
463
464int CConnectFreeRDP::cbClientStart(rdpContext *context)
465{
466 qDebug(log) << Q_FUNC_INFO;
467 int nRet = 0;
468
469 if (!context || !context->settings)
470 return -1;
471 freerdp* instance = freerdp_client_get_instance(context);
472 if(!instance)
473 return -2;
474 CConnectFreeRDP* pThis = ((ClientContext*)context)->pThis;
475 auto settings = context->settings;
476
477 QString szHost;
478 quint16 nPort;
479 szHost = freerdp_settings_get_string(settings, FreeRDP_ServerHostname);
480 nPort = freerdp_settings_get_uint32(settings, FreeRDP_ServerPort);
481 QString szServer;
482 auto &net = pThis->m_pParameter->m_Net;
483 szServer = net.GetHost() + ":" + QString::number(net.GetPort());
484 auto &proxy = pThis->m_pParameter->m_Proxy;
485 switch(proxy.GetUsedType()) {
486 case CParameterProxy::TYPE::SSHTunnel:
487 {
488 auto &sshNet = proxy.m_SSH;
489 szServer = szHost + ":" + QString::number(nPort)
490 + " <-> " + sshNet.GetHost() + ":" + QString::number(sshNet.GetPort())
491 + " <-> " + szServer;
492 break;
493 }
494 default:
495 break;
496 }
497
498 BOOL status = freerdp_connect(instance);
499 if (status) {
500 QString szInfo = tr("Connect to ") + szServer;
501 qInfo(log) << szInfo;
502 emit pThis->sigInformation(szInfo);
503 } else {
504 //DWORD dwErrCode = freerdp_error_info(instance);
505 UINT32 nErr = freerdp_get_last_error(context);
506
507 QString szErr;
508 szErr = tr("Connect to ") + szServer + tr(" fail.");
509 szErr += "\n[";
510 szErr += QString::number(nErr) + " - ";
511 szErr += freerdp_get_last_error_name(nErr);
512 szErr += "] ";
513 /*szErr += "[";
514 szErr += freerdp_get_last_error_category(nErr);
515 szErr += "] ";*/
516 szErr += freerdp_get_last_error_string(nErr);
517 //szErr += "]";
518
519 switch(nErr) {
520 case FREERDP_ERROR_CONNECT_LOGON_FAILURE:
521 {
522 nRet = -3;
523 szErr = tr("Logon to ") + szServer;
524 szErr += tr(" fail. Please check that the username and password are correct.") + "\n";
525 break;
526 }
527 case FREERDP_ERROR_CONNECT_WRONG_PASSWORD:
528 {
529 nRet = -4;
530 szErr = tr("Logon to ") + szServer;
531 szErr += tr(" fail. Please check password are correct.") + "\n";
532 break;
533 }
534 case FREERDP_ERROR_AUTHENTICATION_FAILED:
535 {
536 nRet = -5;
537 szErr = tr("Logon to ") + szServer;
538 szErr += tr(" authentication fail. please add a CA certificate to the store.") + "\n";
539 break;
540 }
541 case FREERDP_ERROR_CONNECT_TRANSPORT_FAILED:
542 {
543 nRet = -6;
544 szErr = tr("Logon to ") + szServer;
545 szErr += tr(" connect transport layer fail.") + "\n\n";
546 szErr += tr("Please:") + "\n";
547 szErr += tr("1. Check for any network related issues") + "\n";
548 szErr += tr("2. Check you have proper security settings ('NLA' enabled is required for most connections nowadays)") + "\n";
549 szErr += " " + tr("If you do not know the server security settings, contact your server administrator.") + "\n";
550 szErr += tr("3. Check the certificate is proper (and guacd properly checks that)") + "\n";
551 break;
552 }
553 case FREERDP_ERROR_SECURITY_NEGO_CONNECT_FAILED:
554 nRet = -7;
555 szErr += "\n\n";
556 szErr += tr("Please check you have proper security settings.") + "\n";
557 szErr += tr("If you do not know the server security settings, contact your server administrator.");
558 break;
559 case FREERDP_ERROR_CONNECT_CANCELLED:
560 nRet = -8;
561 szErr = tr("The connect was canceled.") + "\n\n" + szErr;
562 break;
563 default:
564 nRet = -9;
565 }
566
567 emit pThis->sigShowMessageBox(tr("Error"), szErr, QMessageBox::Critical);
568 qCritical(log) << szErr;
569 emit pThis->sigError(nRet, szErr.toStdString().c_str());
570 }
571
572 return nRet;
573}
574
575int CConnectFreeRDP::cbClientStop(rdpContext *context)
576{
577 int nRet = 0;
578 qDebug(log) << Q_FUNC_INFO;
579#if FreeRDP_VERSION_MAJOR >= 3
580 nRet = freerdp_client_common_stop(context);
581#else
582 BOOL bRet = freerdp_abort_connect(context->instance);
583 if(!bRet)
584 { qCritical(log) << "freerdp_abort_connect fail";
585 nRet = -1;
586 }
587#endif
588 return nRet;
589}
590
609BOOL CConnectFreeRDP::cb_pre_connect(freerdp* instance)
610{
611 qDebug(log) << Q_FUNC_INFO;
612 rdpChannels* channels = nullptr;
613 rdpSettings* settings = nullptr;
614 rdpContext* context = instance->context;
615
616 if (!instance || !instance->context || !instance->context->settings)
617 return FALSE;
618
619 CConnectFreeRDP* pThis = ((ClientContext*)context)->pThis;
620 if(!pThis) return FALSE;
621 settings = instance->context->settings;
622 channels = context->channels;
623 CParameterFreeRDP* pParameter = pThis->m_pParameter;
624 if(!channels || !pParameter)
625 return FALSE;
626
627 /* Optional OS identifier sent to server */
628#if defined (Q_OS_WIN)
629 if (!freerdp_settings_set_uint32(
630 settings, FreeRDP_OsMajorType, OSMAJORTYPE_WINDOWS))
631 return FALSE;
632 if (!freerdp_settings_set_uint32(
633 settings, FreeRDP_OsMinorType, OSMINORTYPE_WINDOWS_NT))
634 return FALSE;
635#elif defined(Q_OS_ANDROID)
636 if (!freerdp_settings_set_uint32(
637 settings, FreeRDP_OsMajorType, OSMAJORTYPE_ANDROID))
638 return FALSE;
639 if (!freerdp_settings_set_uint32(
640 settings, FreeRDP_OsMinorType, OSMINORTYPE_UNSPECIFIED))
641 return FALSE;
642#elif defined(Q_OS_IOS)
643 if (!freerdp_settings_set_uint32(
644 settings, FreeRDP_OsMajorType, OSMAJORTYPE_IOS))
645 return FALSE;
646 if (!freerdp_settings_set_uint32(
647 settings, FreeRDP_OsMinorType, OSMINORTYPE_UNSPECIFIED))
648 return FALSE;
649#elif defined (Q_OS_UNIX)
650 if (!freerdp_settings_set_uint32(
651 settings, FreeRDP_OsMajorType, OSMAJORTYPE_UNIX))
652 return FALSE;
653 if (!freerdp_settings_set_uint32(
654 settings, FreeRDP_OsMinorType, OSMINORTYPE_NATIVE_XSERVER))
655 return FALSE;
656#else
657 if (!freerdp_settings_set_uint32(
658 settings, FreeRDP_OsMajorType, OSMAJORTYPE_UNSPECIFIED))
659 return FALSE;
660 if (!freerdp_settings_set_uint32(
661 settings, FreeRDP_OsMinorType, OSMINORTYPE_UNSPECIFIED))
662 return FALSE;
663#endif
664
665 // Subscribe channel event
666 PubSub_SubscribeChannelConnected(instance->context->pubSub,
667 OnChannelConnectedEventHandler);
668 PubSub_SubscribeChannelDisconnected(instance->context->pubSub,
669 OnChannelDisconnectedEventHandler);
670
671#if FreeRDP_VERSION_MAJOR < 3
672 if (!freerdp_client_load_addins(channels, instance->context->settings))
673 return FALSE;
674#else
675 #if defined(Q_OS_LINUX) || (defined(Q_OS_WIN) && defined(WITH_WINDOWS_CERT_STORE))
676 if (!freerdp_settings_set_bool(settings, FreeRDP_CertificateCallbackPreferPEM, TRUE))
677 return FALSE;
678 #endif
679#endif
680
681 if(!freerdp_settings_set_bool(
682 settings, FreeRDP_NegotiateSecurityLayer,
683 pParameter->GetNegotiateSecurityLayer()))
684 return FALSE;
685 CParameterFreeRDP::Security security = pParameter->GetSecurity();
686 // [5.3 Standard RDP Security](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/8e8b2cca-c1fa-456c-8ecb-a82fc60b2322)
687 if(!freerdp_settings_set_bool(
688 settings, FreeRDP_RdpSecurity,
689 CParameterFreeRDP::Security::RDP & security))
690 return FALSE;
691 if (!freerdp_settings_set_bool(
692 settings, FreeRDP_UseRdpSecurityLayer,
693 CParameterFreeRDP::Security::RDP & security))
694 return FALSE;
695 // [5.4 Enhanced RDP Security](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/592a0337-dc91-4de3-a901-e1829665291d)
696 if(!freerdp_settings_set_bool(
697 settings, FreeRDP_TlsSecurity,
698 CParameterFreeRDP::Security::TLS & security))
699 return FALSE;
700 if(!freerdp_settings_set_bool(
701 settings, FreeRDP_NlaSecurity,
702 CParameterFreeRDP::Security::NLA & security))
703 return FALSE;
704 if(!freerdp_settings_set_bool(
705 settings, FreeRDP_ExtSecurity,
706 CParameterFreeRDP::Security::NLA_Ext & security))
707 return FALSE;
708#if FreeRDP_VERSION_MAJOR >= 3
709 if(!freerdp_settings_set_bool(
710 settings, FreeRDP_AadSecurity,
711 CParameterFreeRDP::Security::RDSAAD & security))
712 return FALSE;
713 if(!freerdp_settings_set_bool(
714 settings, FreeRDP_RdstlsSecurity,
715 CParameterFreeRDP::Security::RDSTLS & security))
716 return FALSE;
717#endif
718
719 freerdp_settings_set_uint16(settings, FreeRDP_TLSMinVersion,
720 pParameter->GetTlsVersion());
721
722 // Check authentication parameters
723 if (freerdp_settings_get_bool(settings, FreeRDP_AuthenticationOnly))
724 {
725 /* Check +auth-only has a username and password. */
726 auto &user = pParameter->m_Net.m_User;
727 if(!freerdp_settings_get_string(settings, FreeRDP_Username)) {
728 if(user.GetUser().isEmpty()) {
729 if(user.GetUser().isEmpty()) {
730 // Will be call instance->Authenticate = cb_authenticate
731 qWarning(log) << "Auth-only, but no user name set. Will be call instance->Authenticate.";
732 }
733 } else
734 freerdp_settings_set_string(
735 settings, FreeRDP_Username,
736 user.GetUser().toStdString().c_str());
737 }
738 if (!freerdp_settings_get_string(settings, FreeRDP_Password)) {
739 if (user.GetPassword().isEmpty()) {
740 // Will be call instance->Authenticate = cb_authenticate
741 qWarning(log) << "auth-only, but no password set. Will be call instance->Authenticate";
742 } else
743 freerdp_settings_set_string(
744 settings, FreeRDP_Password,
745 user.GetPassword().toStdString().c_str());
746 }
747#if FreeRDP_VERSION_MAJOR >= 3
748 if (!freerdp_settings_set_bool(settings, FreeRDP_DeactivateClientDecoding, TRUE))
749 return FALSE;
750#endif
751 } else if(freerdp_settings_get_bool(settings, FreeRDP_CredentialsFromStdin)){
752 // Because the pragram is GUI. so don't process it.
753 } else if(freerdp_settings_get_bool(settings, FreeRDP_SmartcardLogon)) {
754 // TODO: add FreeRDP_SmartcardLogon !
755 }
756
757 /* TODO: Check Keyboard layout
758 UINT32 rc = freerdp_keyboard_init(
759 freerdp_settings_get_uint32(settings, FreeRDP_KeyboardLayout));
760 freerdp_settings_set_uint32(settings, FreeRDP_KeyboardLayout, rc);
761 //*/
762
763 // Check desktop size, it is set in parameter
764 UINT32 width = pParameter->GetDesktopWidth();
765 UINT32 height = pParameter->GetDesktopHeight();
766 if ((width < 64) || (height < 64) ||
767 (width > 4096) || (height > 4096))
768 {
769 QString szErr = tr("Invalid dimensions:")
770 + QString::number(width)
771 + "*" + QString::number(height);
772 qCritical(log) << szErr;
773 //emit pThis->sigShowMessageBox(tr("Error"), szErr);
774 return FALSE;
775 } else {
776 qInfo(log) << "Init desktop size " << width << "*" << height;
777 }
778
779 qDebug(log)
780 << "width:" << freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth)
781 << "height:" << freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight)
782 << "ColorDepth:" << freerdp_settings_get_uint32(settings, FreeRDP_ColorDepth);
783
784 // Initial FreeRDP graph codec
785 // See: https://github.com/KangLin/RabbitRemoteControl/issues/27
786 // Set default parameters FreeRDP_SupportGraphicsPipeline and FreeRDP_RemoteFxCodec in auto detec connect type
787 // See:
788 // - [[MS-RDPBCGR]: Remote Desktop Protocol: Basic Connectivity and Graphics Remoting](https://learn.microsoft.com/zh-cn/openspecs/windows_protocols/ms-rdpbcgr/5073f4ed-1e93-45e1-b039-6e30c385867c)
789 // - [[MS-RDPRFX]: Remote Desktop Protocol: RemoteFX Codec Extension](https://learn.microsoft.com/zh-cn/openspecs/windows_protocols/ms-rdprfx/62495a4a-a495-46ea-b459-5cde04c44549)
790 // - [[MS-RDPEGFX]: Remote Desktop Protocol: Graphics Pipeline Extension](https://learn.microsoft.com/zh-cn/openspecs/windows_protocols/ms-rdpegfx/da5c75f9-cd99-450c-98c4-014a496942b0)
791 // https://www.cswamp.com/post/37
792 // - [Remote Desktop Protocol (RDP) 10 AVC/H.264 improvements in Windows 10 and Windows Server 2016 Technical Preview](https://techcommunity.microsoft.com/blog/microsoft-security-blog/remote-desktop-protocol-rdp-10-avch-264-improvements-in-windows-10-and-windows-s/249588)
793 if(!freerdp_set_connection_type(settings, pParameter->GetConnectType()))
794 return FALSE;
795 freerdp_settings_set_uint32(
796 settings, FreeRDP_PerformanceFlags, pParameter->GetPerformanceFlags());
797 freerdp_performance_flags_split(settings);
798
799 return TRUE;
800}
801
802const char* CConnectFreeRDP::GetTitle(freerdp* instance)
803{
804 const char* windowTitle;
805 UINT32 port;
806 BOOL addPort;
807 const char* name = nullptr;
808
809 CConnectFreeRDP* pThis = ((ClientContext*)instance->context)->pThis;
810 rdpSettings* settings = instance->context->settings;
811
812 if (!settings)
813 return nullptr;
814
815 windowTitle = freerdp_settings_get_string(settings, FreeRDP_WindowTitle);
816 if (windowTitle)
817 return windowTitle;
818
819#if FreeRDP_VERSION_MAJOR >= 3
820 name = freerdp_settings_get_server_name(settings);
821#else
822 name = pThis->m_pParameter->m_Net.GetHost().toStdString().c_str();
823#endif
824 port = freerdp_settings_get_uint32(settings, FreeRDP_ServerPort);
825
826 addPort = (port != 3389);
827
828 char buffer[MAX_PATH + 64] = { 0 };
829
830 if (!addPort)
831 sprintf_s(buffer, sizeof(buffer), "%s", name);
832 else
833 sprintf_s(buffer, sizeof(buffer), "%s:%" PRIu32, name, port);
834
835 freerdp_settings_set_string(settings, FreeRDP_WindowTitle, buffer);
836 return freerdp_settings_get_string(settings, FreeRDP_WindowTitle);
837}
838
844BOOL CConnectFreeRDP::cb_post_connect(freerdp* instance)
845{
846 qDebug(log) << Q_FUNC_INFO;
847
848 rdpContext* context = instance->context;
849 rdpSettings* settings = instance->context->settings;
850 rdpUpdate* update = instance->context->update;
851 CConnectFreeRDP* pThis = ((ClientContext*)instance->context)->pThis;
852
853 const char* pWindowTitle = GetTitle(instance);
854 if(pWindowTitle)
855 {
856 WCHAR* windowTitle = NULL;
857#if FreeRDP_VERSION_MAJOR >= 3
858 windowTitle = ConvertUtf8ToWCharAlloc(pWindowTitle, NULL);
859#else
860 ConvertToUnicode(CP_UTF8, 0, pWindowTitle, -1, &windowTitle, 0);
861#endif
862 if(windowTitle)
863 {
864 QString title = QString::fromUtf16((const char16_t*)windowTitle);
865 delete windowTitle;
866 if(pThis->m_pParameter->GetServerName().isEmpty())
867 emit pThis->sigServerName(title);
868 }
869 }
870
871 int desktopWidth = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
872 int desktopHeight = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
873 emit pThis->sigSetDesktopSize(desktopWidth, desktopHeight);
874
875 if (!gdi_init(instance, PIXEL_FORMAT_BGRA32))
876 return FALSE;
877
878 if(!pThis->CreateImage(instance->context))
879 return FALSE;
880
881 Q_ASSERT(instance->context->cache);
882
883 // Register cursor pointer
884 if(pThis->m_Cursor.RegisterPointer(context->graphics))
885 return FALSE;
886
887 update->BeginPaint = cb_begin_paint;
888 update->EndPaint = cb_end_paint;
889 update->DesktopResize = cb_desktop_resize;
890
891 update->PlaySound = cb_play_bell_sound;
892
893 update->SetKeyboardIndicators = cb_keyboard_set_indicators;
894 update->SetKeyboardImeStatus = cb_keyboard_set_ime_status;
895
896 emit pThis->sigConnected();
897 return TRUE;
898}
899
900void CConnectFreeRDP::cb_post_disconnect(freerdp* instance)
901{
902 qDebug(log) << Q_FUNC_INFO;
903 rdpContext* context = nullptr;
904
905 if (!instance || !instance->context)
906 return;
907
908 context = instance->context;
909
910 PubSub_UnsubscribeChannelConnected(instance->context->pubSub,
911 OnChannelConnectedEventHandler);
912 PubSub_UnsubscribeChannelDisconnected(instance->context->pubSub,
913 OnChannelDisconnectedEventHandler);
914 gdi_free(instance);
915}
916
917int CConnectFreeRDP::cb_logon_error_info(freerdp* instance, UINT32 data, UINT32 type)
918{
919 CConnectFreeRDP* pThis = ((ClientContext*)instance->context)->pThis;
920 const char* str_data = freerdp_get_logon_error_info_data(data);
921 const char* str_type = freerdp_get_logon_error_info_type(type);
922 QString szErr = tr("FreeRDP logon info: [");
923 szErr += str_type;
924 szErr += "] ";
925 szErr += str_data;
926 qDebug(log) << szErr;
927 emit pThis->sigInformation(szErr);
928 emit pThis->sigError(type, szErr);
929 return 1;
930}
931
932void CConnectFreeRDP::OnChannelConnectedEventHandler(void *context,
933 #if FreeRDP_VERSION_MAJOR >= 3
934 const
935 #endif
936 ChannelConnectedEventArgs *e)
937{
938 rdpContext* pContext = (rdpContext*)context;
939 CConnectFreeRDP* pThis = ((ClientContext*)context)->pThis;
940 if (strcmp(e->name, CLIPRDR_SVC_CHANNEL_NAME) == 0) {
941 qDebug(log) << "channel" << e->name << "connected";
942 pThis->m_ClipBoard.Init((CliprdrClientContext*)e->pInterface,
943 pThis->m_pParameter->GetClipboard());
944 }
945#if FreeRDP_VERSION_MAJOR >= 3
946 else
947 freerdp_client_OnChannelConnectedEventHandler(pContext, e);
948#else
949 else if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0)
950 {
951 if (freerdp_settings_get_bool(pContext->settings, FreeRDP_SoftwareGdi)) {
952 rdpGdi* gdi = pContext->gdi;
953 // See: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/da5c75f9-cd99-450c-98c4-014a496942b0
954 gdi_graphics_pipeline_init(gdi, (RdpgfxClientContext*) e->pInterface);
955 }
956 else
957 qDebug(log, "Unimplemented: channel %s connected but libfreerdp is in HardwareGdi mode\n", e->name);
958 }
959 else if (strcmp(e->name, GEOMETRY_DVC_CHANNEL_NAME) == 0)
960 {
961 gdi_video_geometry_init(pContext->gdi, (GeometryClientContext*)e->pInterface);
962 }
963 else if (strcmp(e->name, VIDEO_DATA_DVC_CHANNEL_NAME) == 0)
964 {
965 gdi_video_data_init(pContext->gdi, (VideoClientContext*)e->pInterface);
966 } else
967 qDebug(log) << "Unimplemented: channel" << e->name << "connected but we can’t use it";
968#endif
969}
970
971void CConnectFreeRDP::OnChannelDisconnectedEventHandler(void *context,
972 #if FreeRDP_VERSION_MAJOR >= 3
973 const
974 #endif
975 ChannelDisconnectedEventArgs *e)
976{
977 rdpContext* pContext = (rdpContext*)context;
978 CConnectFreeRDP* pThis = ((ClientContext*)context)->pThis;
979
980 if (strcmp(e->name, CLIPRDR_SVC_CHANNEL_NAME) == 0) {
981 qDebug(log) << "channel" << e->name << "disconnected";
982 pThis->m_ClipBoard.UnInit((CliprdrClientContext*)e->pInterface,
983 pThis->m_pParameter->GetClipboard());
984 }
985#if FreeRDP_VERSION_MAJOR >= 3
986 else
987 freerdp_client_OnChannelDisconnectedEventHandler(pContext, e);
988#else
989 else if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0)
990 {
991 if (freerdp_settings_get_bool(pContext->settings, FreeRDP_SoftwareGdi)) {
992 rdpGdi* gdi = pContext->gdi;
993 gdi_graphics_pipeline_uninit(gdi, (RdpgfxClientContext*) e->pInterface);
994 }
995 else
996 qDebug(log, "Unimplemented: channel %s connected but libfreerdp is in HardwareGdi mode\n", e->name);
997 }
998 else
999 qDebug(log) << "Unimplemented: channel" << e->name << "disconnected but we can’t use it";
1000#endif
1001
1002}
1003
1004UINT32 CConnectFreeRDP::GetImageFormat(QImage::Format format)
1005{
1006 switch (format) {
1007#if (QT_VERSION >= QT_VERSION_CHECK(5,2,0))
1008 case QImage::Format_RGBA8888:
1009 return PIXEL_FORMAT_RGBA32;
1010 case QImage::Format_RGBX8888:
1011 return PIXEL_FORMAT_RGBX32;
1012#endif
1013 case QImage::Format_RGB16:
1014 return PIXEL_FORMAT_RGB16;
1015 case QImage::Format_ARGB32:
1016 return PIXEL_FORMAT_BGRA32;
1017 case QImage::Format_RGB32:
1018 return PIXEL_FORMAT_BGRA32;
1019 default:
1020 break;
1021 }
1022 return 0;
1023}
1024
1025UINT32 CConnectFreeRDP::GetImageFormat()
1026{
1027 return GetImageFormat(m_Image.format());
1028}
1029
1030BOOL CConnectFreeRDP::CreateImage(rdpContext *context)
1031{
1032 Q_ASSERT(context);
1033 ClientContext* pContext = (ClientContext*)context;
1034 CConnectFreeRDP* pThis = pContext->pThis;
1035 rdpGdi* gdi = context->gdi;
1036 Q_ASSERT(pThis && gdi);
1037 pThis->m_Image = QImage(gdi->primary_buffer,
1038 static_cast<int>(gdi->width),
1039 static_cast<int>(gdi->height),
1040 QImage::Format_ARGB32);
1041 return TRUE;
1042}
1043
1044#if FreeRDP_VERSION_MAJOR >= 3
1045#ifdef Q_OS_WINDOWS
1046static CREDUI_INFOW wfUiInfo = { sizeof(CREDUI_INFOW), NULL, L"Enter your credentials",
1047 L"Remote Desktop Security", NULL };
1048#endif
1049
1050BOOL CConnectFreeRDP::cb_authenticate_ex(freerdp* instance,
1051 char** username, char** password,
1052 char** domain, rdp_auth_reason reason)
1053{
1054 qDebug(log) << Q_FUNC_INFO << "reason:" << reason;
1055 if(!instance)
1056 return FALSE;
1057
1058 if(!username || !password || !domain) return FALSE;
1059
1060 rdpContext* pContext = (rdpContext*)instance->context;
1061#ifdef Q_OS_WINDOWS
1062 BOOL fSave;
1063 DWORD status;
1064 DWORD dwFlags;
1065 WCHAR UserNameW[CREDUI_MAX_USERNAME_LENGTH + 1] = { 0 };
1066 WCHAR UserW[CREDUI_MAX_USERNAME_LENGTH + 1] = { 0 };
1067 WCHAR DomainW[CREDUI_MAX_DOMAIN_TARGET_LENGTH + 1] = { 0 };
1068 WCHAR PasswordW[CREDUI_MAX_PASSWORD_LENGTH + 1] = { 0 };
1069
1070 WINPR_ASSERT(instance);
1071 WINPR_ASSERT(instance->context);
1072 WINPR_ASSERT(instance->context->settings);
1073
1074 WINPR_ASSERT(username);
1075 WINPR_ASSERT(domain);
1076 WINPR_ASSERT(password);
1077
1078 const WCHAR auth[] = L"Target credentials requested";
1079 const WCHAR authPin[] = L"PIN requested";
1080 const WCHAR gwAuth[] = L"Gateway credentials requested";
1081 const WCHAR* titleW = auth;
1082
1083 fSave = FALSE;
1084 dwFlags = CREDUI_FLAGS_DO_NOT_PERSIST | CREDUI_FLAGS_EXCLUDE_CERTIFICATES |
1085 CREDUI_FLAGS_USERNAME_TARGET_CREDENTIALS;
1086 switch (reason)
1087 {
1088 case AUTH_NLA:
1089 break;
1090 case AUTH_TLS:
1091 case AUTH_RDP:
1092 if ((*username) && (*password))
1093 return TRUE;
1094 break;
1095 case AUTH_SMARTCARD_PIN:
1096 dwFlags &= ~CREDUI_FLAGS_USERNAME_TARGET_CREDENTIALS;
1097 dwFlags |= CREDUI_FLAGS_PASSWORD_ONLY_OK | CREDUI_FLAGS_KEEP_USERNAME;
1098 titleW = authPin;
1099 if (*password)
1100 return TRUE;
1101 if (!(*username))
1102 *username = _strdup("PIN");
1103 break;
1104 case GW_AUTH_HTTP:
1105 case GW_AUTH_RDG:
1106 case GW_AUTH_RPC:
1107 titleW = gwAuth;
1108 break;
1109 default:
1110 return FALSE;
1111 }
1112
1113 if (*username)
1114 {
1115 ConvertUtf8ToWChar(*username, UserNameW, ARRAYSIZE(UserNameW));
1116 ConvertUtf8ToWChar(*username, UserW, ARRAYSIZE(UserW));
1117 }
1118
1119 if (*password)
1120 ConvertUtf8ToWChar(*password, PasswordW, ARRAYSIZE(PasswordW));
1121
1122 if (*domain)
1123 ConvertUtf8ToWChar(*domain, DomainW, ARRAYSIZE(DomainW));
1124
1125 if (_wcsnlen(PasswordW, ARRAYSIZE(PasswordW)) == 0)
1126 {
1127 status = CredUIPromptForCredentialsW(&wfUiInfo, titleW, NULL, 0, UserNameW,
1128 ARRAYSIZE(UserNameW), PasswordW,
1129 ARRAYSIZE(PasswordW), &fSave, dwFlags);
1130 if (status != NO_ERROR)
1131 {
1132 qCritical(log,
1133 "CredUIPromptForCredentials unexpected status: 0x%08lX",
1134 status);
1135 return FALSE;
1136 }
1137
1138 if ((dwFlags & CREDUI_FLAGS_KEEP_USERNAME) == 0)
1139 {
1140 status = CredUIParseUserNameW(UserNameW, UserW, ARRAYSIZE(UserW), DomainW,
1141 ARRAYSIZE(DomainW));
1142 if (status != NO_ERROR)
1143 {
1144 CHAR User[CREDUI_MAX_USERNAME_LENGTH + 1] = { 0 };
1145 CHAR UserName[CREDUI_MAX_USERNAME_LENGTH + 1] = { 0 };
1146 CHAR Domain[CREDUI_MAX_DOMAIN_TARGET_LENGTH + 1] = { 0 };
1147
1148 ConvertWCharNToUtf8(UserNameW, ARRAYSIZE(UserNameW), UserName, ARRAYSIZE(UserName));
1149 ConvertWCharNToUtf8(UserW, ARRAYSIZE(UserW), User, ARRAYSIZE(User));
1150 ConvertWCharNToUtf8(DomainW, ARRAYSIZE(DomainW), Domain, ARRAYSIZE(Domain));
1151 qCritical(log,
1152 "Failed to parse UserName: %s into User: %s Domain: %s",
1153 UserName, User, Domain);
1154 return FALSE;
1155 }
1156 }
1157 }
1158
1159 *username = ConvertWCharNToUtf8Alloc(UserW, ARRAYSIZE(UserW), NULL);
1160 if (!(*username))
1161 {
1162 qCritical(log) << "ConvertWCharNToUtf8Alloc failed" << status;
1163 return FALSE;
1164 }
1165
1166 if (_wcsnlen(DomainW, ARRAYSIZE(DomainW)) > 0)
1167 *domain = ConvertWCharNToUtf8Alloc(DomainW, ARRAYSIZE(DomainW), NULL);
1168 else
1169 *domain = _strdup("\0");
1170
1171 if (!(*domain))
1172 {
1173 free(*username);
1174 qCritical(log) << "strdup failed" << status;
1175 return FALSE;
1176 }
1177
1178 *password = ConvertWCharNToUtf8Alloc(PasswordW, ARRAYSIZE(PasswordW), NULL);
1179 if (!(*password))
1180 {
1181 free(*username);
1182 free(*domain);
1183 return FALSE;
1184 }
1185 return TRUE;
1186#else
1187 return cb_authenticate(instance, username, password, domain);
1188#endif //#ifdef Q_OS_WINDOWS
1189}
1190
1191//TODO: to be continue!!!
1192BOOL CConnectFreeRDP::cb_choose_smartcard(freerdp* instance,
1193 SmartcardCertInfo** cert_list,
1194 DWORD count,
1195 DWORD* choice, BOOL gateway)
1196{
1197 rdpContext* pContext = (rdpContext*)instance->context;
1198 CConnectFreeRDP* pThis = ((ClientContext*)pContext)->pThis;
1199 QString msg("Multiple smartcards are available for use:\n");
1200 for (DWORD i = 0; i < count; i++)
1201 {
1202 const SmartcardCertInfo* cert = cert_list[i];
1203 char* reader = ConvertWCharToUtf8Alloc(cert->reader, NULL);
1204 char* container_name = ConvertWCharToUtf8Alloc(cert->containerName, NULL);
1205
1206 msg += QString::number(i) + " ";
1207 msg += QString(container_name) + "\n\t";
1208 msg += "Reader: " + QString(reader) + "\n\t";
1209 msg += "User: " + QString(cert->userHint) + + "@" + QString(cert->domainHint) + "\n\t";
1210 msg += "Subject: " + QString(cert->subject) + "\n\t";
1211 msg += "Issuer: " + QString(cert->issuer) + "\n\t";
1212 msg += "UPN: " + QString(cert->upn) + "\n";
1213
1214 free(reader);
1215 free(container_name);
1216 }
1217
1218 msg += "\nChoose a smartcard to use for ";
1219 if(gateway)
1220 msg += "gateway authentication";
1221 else
1222 msg += "logon";
1223
1224 msg += "(0 - " + QString::number(count - 1) + ")";
1225
1226 QString num;
1227 emit pThis->sigBlockInputDialog(tr("Choose"), tr("Please choose smartcard"),
1228 msg, num);
1229 if(!num.isEmpty())
1230 {
1231 bool ok = false;
1232 int n = num.toInt(&ok);
1233 if(ok)
1234 {
1235 *choice = n;
1236 return TRUE;
1237 }
1238 }
1239 return FALSE;
1240}
1241
1242#ifdef WITH_WINDOWS_CERT_STORE
1243/* https://stackoverflow.com/questions/1231178/load-an-pem-encoded-x-509-certificate-into-windows-cryptoapi/3803333#3803333
1244 */
1245/* https://github.com/microsoft/Windows-classic-samples/blob/main/Samples/Win7Samples/security/cryptoapi/peertrust/cpp/peertrust.cpp
1246 */
1247/* https://stackoverflow.com/questions/7340504/whats-the-correct-way-to-verify-an-ssl-certificate-in-win32
1248 */
1249
1250static void wf_report_error(char* wszMessage, DWORD dwErrCode)
1251{
1252 LPSTR pwszMsgBuf = NULL;
1253
1254 if (NULL != wszMessage && 0 != *wszMessage)
1255 {
1256 WLog_ERR(TAG, "%s", wszMessage);
1257 }
1258
1259 FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
1260 NULL, // Location of message
1261 // definition ignored
1262 dwErrCode, // Message identifier for
1263 // the requested message
1264 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Language identifier for
1265 // the requested message
1266 (LPSTR)&pwszMsgBuf, // Buffer that receives
1267 // the formatted message
1268 0, // Size of output buffer
1269 // not needed as allocate
1270 // buffer flag is set
1271 NULL // Array of insert values
1272 );
1273
1274 if (NULL != pwszMsgBuf)
1275 {
1276 WLog_ERR(TAG, "Error: 0x%08x (%d) %s", dwErrCode, dwErrCode, pwszMsgBuf);
1277 LocalFree(pwszMsgBuf);
1278 }
1279 else
1280 {
1281 WLog_ERR(TAG, "Error: 0x%08x (%d)", dwErrCode, dwErrCode);
1282 }
1283}
1284
1285static DWORD wf_is_x509_certificate_trusted(const char* common_name, const char* subject,
1286 const char* issuer, const char* fingerprint)
1287{
1288 HRESULT hr = CRYPT_E_NOT_FOUND;
1289
1290 DWORD dwChainFlags = CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;
1291 PCCERT_CONTEXT pCert = NULL;
1292 HCERTCHAINENGINE hChainEngine = NULL;
1293 PCCERT_CHAIN_CONTEXT pChainContext = NULL;
1294
1295 CERT_ENHKEY_USAGE EnhkeyUsage = { 0 };
1296 CERT_USAGE_MATCH CertUsage = { 0 };
1297 CERT_CHAIN_PARA ChainPara = { 0 };
1298 CERT_CHAIN_POLICY_PARA ChainPolicy = { 0 };
1299 CERT_CHAIN_POLICY_STATUS PolicyStatus = { 0 };
1300 CERT_CHAIN_ENGINE_CONFIG EngineConfig = { 0 };
1301
1302 DWORD derPubKeyLen = WINPR_ASSERTING_INT_CAST(uint32_t, strlen(fingerprint));
1303 char* derPubKey = calloc(derPubKeyLen, sizeof(char));
1304 if (NULL == derPubKey)
1305 {
1306 WLog_ERR(TAG, "Could not allocate derPubKey");
1307 goto CleanUp;
1308 }
1309
1310 /*
1311 * Convert from PEM format to DER format - removes header and footer and decodes from base64
1312 */
1313 if (!CryptStringToBinaryA(fingerprint, 0, CRYPT_STRING_BASE64HEADER, derPubKey, &derPubKeyLen,
1314 NULL, NULL))
1315 {
1316 WLog_ERR(TAG, "CryptStringToBinary failed. Err: %d", GetLastError());
1317 goto CleanUp;
1318 }
1319
1320 //---------------------------------------------------------
1321 // Initialize data structures for chain building.
1322
1323 EnhkeyUsage.cUsageIdentifier = 0;
1324 EnhkeyUsage.rgpszUsageIdentifier = NULL;
1325
1326 CertUsage.dwType = USAGE_MATCH_TYPE_AND;
1327 CertUsage.Usage = EnhkeyUsage;
1328
1329 ChainPara.cbSize = sizeof(ChainPara);
1330 ChainPara.RequestedUsage = CertUsage;
1331
1332 ChainPolicy.cbSize = sizeof(ChainPolicy);
1333
1334 PolicyStatus.cbSize = sizeof(PolicyStatus);
1335
1336 EngineConfig.cbSize = sizeof(EngineConfig);
1337 EngineConfig.dwUrlRetrievalTimeout = 0;
1338
1339 pCert = CertCreateCertificateContext(X509_ASN_ENCODING, derPubKey, derPubKeyLen);
1340 if (NULL == pCert)
1341 {
1342 WLog_ERR(TAG, "FAILED: Certificate could not be parsed.");
1343 goto CleanUp;
1344 }
1345
1346 dwChainFlags |= CERT_CHAIN_ENABLE_PEER_TRUST;
1347
1348 // When this flag is set, end entity certificates in the
1349 // Trusted People store are trusted without doing any chain building
1350 // This optimizes the chain building process.
1351
1352 //---------------------------------------------------------
1353 // Create chain engine.
1354
1355 if (!CertCreateCertificateChainEngine(&EngineConfig, &hChainEngine))
1356 {
1357 hr = HRESULT_FROM_WIN32(GetLastError());
1358 goto CleanUp;
1359 }
1360
1361 //-------------------------------------------------------------------
1362 // Build a chain using CertGetCertificateChain
1363
1364 if (!CertGetCertificateChain(hChainEngine, // use the default chain engine
1365 pCert, // pointer to the end certificate
1366 NULL, // use the default time
1367 NULL, // search no additional stores
1368 &ChainPara, // use AND logic and enhanced key usage
1369 // as indicated in the ChainPara
1370 // data structure
1371 dwChainFlags,
1372 NULL, // currently reserved
1373 &pChainContext)) // return a pointer to the chain created
1374 {
1375 hr = HRESULT_FROM_WIN32(GetLastError());
1376 goto CleanUp;
1377 }
1378
1379 //---------------------------------------------------------------
1380 // Verify that the chain complies with policy
1381
1382 if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_BASE, // use the base policy
1383 pChainContext, // pointer to the chain
1384 &ChainPolicy,
1385 &PolicyStatus)) // return a pointer to the policy status
1386 {
1387 hr = HRESULT_FROM_WIN32(GetLastError());
1388 goto CleanUp;
1389 }
1390
1391 if (PolicyStatus.dwError != S_OK)
1392 {
1393 wf_report_error("CertVerifyCertificateChainPolicy: Chain Status", PolicyStatus.dwError);
1394 hr = PolicyStatus.dwError;
1395 // Instruction: If the PolicyStatus.dwError is CRYPT_E_NO_REVOCATION_CHECK or
1396 // CRYPT_E_REVOCATION_OFFLINE, it indicates errors in obtaining
1397 // revocation information. These can be ignored since the retrieval of
1398 // revocation information depends on network availability
1399
1400 if (PolicyStatus.dwError == CRYPT_E_NO_REVOCATION_CHECK ||
1401 PolicyStatus.dwError == CRYPT_E_REVOCATION_OFFLINE)
1402 {
1403 hr = S_OK;
1404 }
1405
1406 goto CleanUp;
1407 }
1408
1409 WLog_INFO(TAG, "CertVerifyCertificateChainPolicy succeeded for %s (%s) issued by %s",
1410 common_name, subject, issuer);
1411
1412 hr = S_OK;
1413CleanUp:
1414
1415 if (FAILED(hr))
1416 {
1417 WLog_INFO(TAG, "CertVerifyCertificateChainPolicy failed for %s (%s) issued by %s",
1418 common_name, subject, issuer);
1419 wf_report_error(NULL, hr);
1420 }
1421
1422 free(derPubKey);
1423
1424 if (NULL != pChainContext)
1425 {
1426 CertFreeCertificateChain(pChainContext);
1427 }
1428
1429 if (NULL != hChainEngine)
1430 {
1431 CertFreeCertificateChainEngine(hChainEngine);
1432 }
1433
1434 if (NULL != pCert)
1435 {
1436 CertFreeCertificateContext(pCert);
1437 }
1438
1439 return (DWORD)hr;
1440}
1441#endif
1442
1443#endif //#if FreeRDP_VERSION_MAJOR >= 3
1444
1445BOOL CConnectFreeRDP::cb_authenticate(freerdp* instance, char** username,
1446 char** password, char** domain)
1447{
1448 qDebug(log) << Q_FUNC_INFO;
1449 if(!instance)
1450 return FALSE;
1451 rdpContext* pContext = (rdpContext*)instance->context;
1452 CConnectFreeRDP* pThis = ((ClientContext*)pContext)->pThis;
1453 if(!username || !password || !domain) return FALSE;
1454 if(*username && *password ) return TRUE;
1455
1456 int nRet = QDialog::Rejected;
1457 emit pThis->sigBlockShowWidget("CDlgGetUserPasswordFreeRDP",
1458 nRet, pThis->m_pParameter);
1459 if(QDialog::Accepted == nRet)
1460 {
1461 QString szPassword = pThis->m_pParameter->m_Net.m_User.GetPassword();
1462 QString szName = pThis->m_pParameter->m_Net.m_User.GetUser();
1463 QString szDomain = pThis->m_pParameter->GetDomain();
1464 if(!szDomain.isEmpty() && domain)
1465 *domain = _strdup(szDomain.toStdString().c_str());
1466 if(!szName.isEmpty() && username)
1467 *username = _strdup(szName.toStdString().c_str());
1468 if(!szPassword.isEmpty() && password)
1469 *password = _strdup(szPassword.toStdString().c_str());
1470 } else
1471 return FALSE;
1472
1473 return TRUE;
1474}
1475
1476BOOL CConnectFreeRDP::cb_GatewayAuthenticate(freerdp *instance,
1477 char **username, char **password, char **domain)
1478{
1479 qDebug(log) << Q_FUNC_INFO;
1480 if(!instance)
1481 return FALSE;
1482
1483 rdpContext* pContext = (rdpContext*)instance->context;
1484 CConnectFreeRDP* pThis = ((ClientContext*)pContext)->pThis;
1485 if(!username || !password || !domain) return FALSE;
1486 if(*username && *password ) return TRUE;
1487
1488 int nRet = QDialog::Rejected;
1489 emit pThis->sigBlockShowWidget("CDlgGetUserPasswordFreeRDP", nRet, pThis->m_pParameter);
1490 if(QDialog::Accepted == nRet)
1491 {
1492 QString szPassword = pThis->m_pParameter->m_Net.m_User.GetPassword();
1493 QString szName = pThis->m_pParameter->m_Net.m_User.GetUser();
1494 QString szDomain = pThis->m_pParameter->GetDomain();
1495 if(!szDomain.isEmpty() && domain)
1496 *domain = _strdup(szDomain.toStdString().c_str());
1497 if(!szName.isEmpty() && username)
1498 *username = _strdup(szName.toStdString().c_str());
1499 if(!szPassword.isEmpty() && password)
1500 *password = _strdup(szPassword.toStdString().c_str());
1501 } else
1502 return FALSE;
1503
1504 return TRUE;
1505}
1506
1508 const BYTE* data, size_t length,
1509 const char* hostname, UINT16 port, DWORD flags)
1510{
1511 qDebug(log) << Q_FUNC_INFO;
1512 rdpContext* pContext = (rdpContext*)instance->context;
1513 QSslCertificate cert(QByteArray((const char*)data, length));
1514#if FreeRDP_VERSION_MAJOR >= 3
1515 /* Newer versions of FreeRDP allow exposing the whole PEM by setting
1516 * FreeRDP_CertificateCallbackPreferPEM to TRUE
1517 */
1518 if (flags & VERIFY_CERT_FLAG_FP_IS_PEM) {
1520 instance, hostname, port,
1521 cert.issuerDisplayName().toStdString().c_str(),
1522 cert.subjectDisplayName().toStdString().c_str(),
1523 cert.issuerDisplayName().toStdString().c_str(),
1524 (const char*)data,
1525 flags);
1526 } else
1527#endif
1529 instance, hostname, port,
1530 cert.issuerDisplayName().toStdString().c_str(),
1531 cert.subjectDisplayName().toStdString().c_str(),
1532 cert.issuerDisplayName().toStdString().c_str(),
1533 cert.serialNumber().toStdString().c_str(),
1534 flags);
1535}
1536
1537static QString pem_cert_fingerprint(const char* pem, DWORD flags)
1538{
1539 QString szFingerPrint;
1540#if FreeRDP_VERSION_MAJOR >= 3
1541 /* Newer versions of FreeRDP allow exposing the whole PEM by setting
1542 * FreeRDP_CertificateCallbackPreferPEM to TRUE
1543 */
1544 if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
1545 {
1546 rdpCertificate* cert = freerdp_certificate_new_from_pem(pem);
1547 if (!cert)
1548 return NULL;
1549
1550 char* fp = freerdp_certificate_get_fingerprint(cert);
1551 char* start = freerdp_certificate_get_validity(cert, TRUE);
1552 char* end = freerdp_certificate_get_validity(cert, FALSE);
1553 freerdp_certificate_free(cert);
1554
1555 szFingerPrint = QObject::tr("Valid from: ") + QString(start) + "\n";
1556 szFingerPrint += QObject::tr("Valid to: ") + QString(end) + "\n";
1557 szFingerPrint += QObject::tr("Fingerprint: ") + QString(fp) + "\n";
1558
1559 free(fp);
1560 free(start);
1561 free(end);
1562 } else
1563#endif
1564 szFingerPrint = QObject::tr("Fingerprint: ") + QString(pem) + "\n";
1565 return szFingerPrint;
1566}
1567
1584 const char *host, UINT16 port,
1585 const char *common_name, const char *subject,
1586 const char *issuer, const char *fingerprint, DWORD flags)
1587{
1588 qDebug(log) << Q_FUNC_INFO;
1589
1590 rdpContext* pContext = (rdpContext*)instance->context;
1591 Q_ASSERT(pContext);
1592 CConnectFreeRDP* pThis = ((ClientContext*)pContext)->pThis;
1593 Q_ASSERT(pThis);
1594 if(common_name)
1595 {
1596 //pThis->m_pParameter->SetServerName(common_name);
1597 emit pThis->sigServerName(common_name);
1598 }
1599
1600 if(!pThis->m_pParameter->GetShowVerifyDiaglog()) {
1601 /* return 1 to accept and store a certificate, 2 to accept
1602 * a certificate only for this session, 0 otherwise */
1603 return 2;
1604 }
1605
1606#if FreeRDP_VERSION_MAJOR >= 3
1607#if defined(Q_OS_WIN) && defined(WITH_WINDOWS_CERT_STORE)
1608 if (flags & VERIFY_CERT_FLAG_FP_IS_PEM && !(flags & VERIFY_CERT_FLAG_MISMATCH))
1609 {
1610 if (wf_is_x509_certificate_trusted(common_name, subject, issuer, fingerprint) == S_OK)
1611 {
1612 return 2;
1613 }
1614 }
1615#endif
1616#endif
1617
1618 QString szType = tr("RDP-Server");
1619 if (flags & VERIFY_CERT_FLAG_GATEWAY)
1620 szType = tr("RDP-Gateway");
1621 if (flags & VERIFY_CERT_FLAG_REDIRECT)
1622 szType = tr("RDP-Redirect");
1623
1624 QString title(tr("Verify certificate"));
1625 QString message;
1626
1627 message += szType + tr(": %1:%2").arg(host, QString::number(port)) + "\n";
1628 message += tr("Common name: ") + common_name + "\n";
1629 message += tr("Subject: ") + subject + "\n";
1630 message += tr("Issuer: ") + issuer + "\n";
1631 message += pem_cert_fingerprint(fingerprint, flags);
1632 message += "\n";
1633 if(VERIFY_CERT_FLAG_CHANGED & flags) {
1634 message += tr("The above X.509 certificate is changed.\n"
1635 "It is possible that the server has changed its certificate, "
1636 "or Maybe it was attacked."
1637 "Please look at the OpenSSL documentation on "
1638 "how to add a private CA to the store.");
1639 } else {
1640 message += tr("The above X.509 certificate could not be verified.\n"
1641 "Possibly because you do not have the CA certificate "
1642 "in your certificate store, or the certificate has expired.\n"
1643 "Please look at the OpenSSL documentation on "
1644 "how to add a private CA to the store.");
1645 }
1646 message += "\n";
1647 message += "\n";
1648 message += tr("Yes - trusted") + "\n";
1649 message += tr("Ignore - temporary trusted") + "\n";
1650 message += tr("No - no trusted") + "\n";
1651
1652 QMessageBox::StandardButton nRet = QMessageBox::StandardButton::No;
1653 QMessageBox::StandardButtons buttons = QMessageBox::Yes | QMessageBox::Ignore | QMessageBox::No;
1654 bool bCheckBox = false;
1655 emit pThis->sigBlockShowMessageBox(title, message, buttons, nRet, bCheckBox,
1656 tr("Don't show again"));
1657 pThis->m_pParameter->SetShowVerifyDiaglog(!bCheckBox);
1658 emit pThis->m_pParameter->sigChanged();
1659 /* return 1 to accept and store a certificate, 2 to accept
1660 * a certificate only for this session, 0 otherwise */
1661 switch(nRet)
1662 {
1663 case QMessageBox::StandardButton::Yes:
1664 return 1;
1665 case QMessageBox::StandardButton::Ignore:
1666 return 2;
1667 default:
1668 return 0;
1669 }
1670 return 2;
1671}
1672
1693 const char *host, UINT16 port,
1694 const char *common_name, const char *subject,
1695 const char *issuer, const char *fingerprint,
1696 const char *old_subject, const char *old_issuer,
1697 const char *old_fingerprint, DWORD flags)
1698{
1699 qDebug(log) << Q_FUNC_INFO;
1700 rdpContext* pContext = (rdpContext*)instance->context;
1701 CConnectFreeRDP* pThis = ((ClientContext*)pContext)->pThis;
1702 if(common_name)
1703 emit pThis->sigServerName(common_name);
1704
1705 if(!pThis->m_pParameter->GetShowVerifyDiaglog()) {
1706 /* return 1 to accept and store a certificate, 2 to accept
1707 * a certificate only for this session, 0 otherwise */
1708 return 2;
1709 }
1710
1711 QString szType = tr("RDP-Server");
1712 if (flags & VERIFY_CERT_FLAG_GATEWAY)
1713 szType = tr("RDP-Gateway");
1714 if (flags & VERIFY_CERT_FLAG_REDIRECT)
1715 szType = tr("RDP-Redirect");
1716
1717 QString title(tr("Verify changed certificate"));
1718 QString message;
1719 message += szType + tr(": %1:%2").arg(host, QString::number(port)) + "\n";
1720 message += tr("New Certificate details:") + "\n";
1721 message += " " + tr("name: ") + common_name + "\n";
1722 message += " " + tr("subject: ") + subject + "\n";
1723 message += " " + tr("issuer: ") + issuer + "\n";
1724 message += " " + pem_cert_fingerprint(fingerprint, flags) + "\n";
1725 message += tr("Old Certificate details:") + "\n";
1726 message += " " + tr("subject: ") + old_subject + "\n";
1727 message += " " + tr("issuer: ") + old_issuer + "\n";
1728 message += " " + pem_cert_fingerprint(old_fingerprint, flags) + "\n";
1729 message += "\n";
1730 message += tr("The above X.509 certificate could not be verified, "
1731 "possibly because you do not have the CA certificate "
1732 "in your certificate store, or the certificate has expired. "
1733 "Please look at the OpenSSL documentation on "
1734 "how to add a private CA to the store.");
1735 message += "\n";
1736 message += "\n";
1737 message += tr("Yes - trusted") + "\n";
1738 message += tr("Ignore - temporary trusted") + "\n";
1739 message += tr("No - no trusted") + "\n";
1740
1741 bool bCheckBox = false;
1742 QMessageBox::StandardButton nRet = QMessageBox::StandardButton::No;
1743 QMessageBox::StandardButtons buttons = QMessageBox::Yes | QMessageBox::Ignore | QMessageBox::No;
1744 emit pThis->sigBlockShowMessageBox(title, message, buttons, nRet, bCheckBox,
1745 tr("Don't show again"));
1746 pThis->m_pParameter->SetShowVerifyDiaglog(!bCheckBox);
1747 emit pThis->m_pParameter->sigChanged();
1748
1749 /* return 1 to accept and store a certificate, 2 to accept
1750 * a certificate only for this session, 0 otherwise */
1751 switch(nRet)
1752 {
1753 case QMessageBox::StandardButton::Yes:
1754 return 1;
1755 case QMessageBox::StandardButton::Ignore:
1756 return 2;
1757 default:
1758 return 0;
1759 }
1760
1761 /* return 1 to accept and store a certificate, 2 to accept
1762 * a certificate only for this session, 0 otherwise */
1763 return 2;
1764}
1765
1766BOOL CConnectFreeRDP::cb_present_gateway_message(
1767 freerdp* instance, UINT32 type, BOOL isDisplayMandatory,
1768 BOOL isConsentMandatory, size_t length, const WCHAR* message)
1769{
1770 qDebug(log) << Q_FUNC_INFO;
1771
1772 if (!isDisplayMandatory && !isConsentMandatory)
1773 return TRUE;
1774
1775 /* special handling for consent messages (show modal dialog) */
1776 if (type == GATEWAY_MESSAGE_CONSENT && isConsentMandatory)
1777 {
1778 QString msgType = (type == GATEWAY_MESSAGE_CONSENT)
1779 ? tr("Consent message") : tr("Service message");
1780 msgType += "\n";
1781#if FreeRDP_VERSION_MAJOR >= 3
1782 char* pMsg = ConvertWCharToUtf8Alloc(message, NULL);
1783 if(pMsg) {
1784 msgType += pMsg;
1785 free(pMsg);
1786 }
1787#else
1788 msgType += QString::fromStdWString((wchar_t*)message);
1789#endif
1790 msgType += "\n";
1791 msgType += tr("I understand and agree to the terms of this policy (Y/N)");
1792
1793 rdpContext* pContext = (rdpContext*)instance->context;
1794 CConnectFreeRDP* pThis = ((ClientContext*)pContext)->pThis;
1795 QMessageBox::StandardButton nRet = QMessageBox::No;
1796 bool bCheckBox = false;
1797 emit pThis->sigBlockShowMessageBox(tr("Gateway message"), msgType,
1798 QMessageBox::Yes|QMessageBox::No,
1799 nRet, bCheckBox);
1800 switch (nRet) {
1801 case QMessageBox::Yes:
1802 return TRUE;
1803 break;
1804 default:
1805 return FALSE;
1806 }
1807 }
1808 else
1809 return client_cli_present_gateway_message(
1810 instance, type, isDisplayMandatory,
1811 isConsentMandatory, length, message);
1812
1813 return TRUE;
1814}
1815
1816BOOL CConnectFreeRDP::cb_begin_paint(rdpContext *context)
1817{
1818 HGDI_DC hdc;
1819
1820 if (!context || !context->gdi || !context->gdi->primary
1821 || !context->gdi->primary->hdc)
1822 return FALSE;
1823
1824 hdc = context->gdi->primary->hdc;
1825
1826 if (!hdc || !hdc->hwnd || !hdc->hwnd->invalid)
1827 return FALSE;
1828
1829 hdc->hwnd->invalid->null = TRUE;
1830 hdc->hwnd->ninvalid = 0;
1831 return TRUE;
1832}
1833
1834BOOL CConnectFreeRDP::UpdateBuffer(INT32 x, INT32 y, INT32 w, INT32 h)
1835{
1836 if(x > m_Image.width() || y > m_Image.height()) {
1837 qCritical(log) << "The width and height out of range."
1838 << "Image width:" << m_Image.width()
1839 << "Image height:" << m_Image.height()
1840 << "w:" << w << "h:" << h;
1841 return FALSE;
1842 }
1843
1844 QRect rect(x, y, w, h);
1845 QImage img = m_Image.copy(rect);
1846 //qDebug(log) << "Image:" << rect << img.rect() << img;
1847 emit sigUpdateRect(rect, img);
1848 return TRUE;
1849}
1850
1851BOOL CConnectFreeRDP::cb_end_paint(rdpContext *context)
1852{
1853 //qDebug(log) << Q_FUNC_INFO;
1854 ClientContext* pContext = (ClientContext*)context;
1855 CConnectFreeRDP* pThis = pContext->pThis;
1856 INT32 ninvalid;
1857 HGDI_RGN cinvalid;
1858 REGION16 invalidRegion;
1859 RECTANGLE_16 invalidRect;
1860 const RECTANGLE_16* extents;
1861 HGDI_DC hdc;
1862 int i = 0;
1863
1864 if (!context || !context->gdi || !context->gdi->primary
1865 || !context->gdi->primary->hdc)
1866 return FALSE;
1867
1868 hdc = context->gdi->primary->hdc;
1869
1870 if (!hdc || !hdc->hwnd || !hdc->hwnd->invalid)
1871 return FALSE;
1872
1873 rdpGdi* gdi = context->gdi;
1874 if (gdi->suppressOutput)
1875 return TRUE;
1876
1877 HGDI_WND hwnd = context->gdi->primary->hdc->hwnd;
1878 ninvalid = hwnd->ninvalid; //无效区数量
1879 cinvalid = hwnd->cinvalid; //无效区数组
1880 if (ninvalid < 1)
1881 return TRUE;
1882
1883 region16_init(&invalidRegion);
1884
1885 for (i = 0; i < ninvalid; i++)
1886 {
1887 if(cinvalid[i].null)
1888 {
1889 qWarning(log) << "is null region" << cinvalid[i].x << cinvalid[i].y
1890 << cinvalid[i].w << cinvalid[i].h;
1891 continue;
1892 }
1893 invalidRect.left = cinvalid[i].x;
1894 invalidRect.top = cinvalid[i].y;
1895 invalidRect.right = cinvalid[i].x + cinvalid[i].w;
1896 invalidRect.bottom = cinvalid[i].y + cinvalid[i].h;
1897 region16_union_rect(&invalidRegion, &invalidRegion, &invalidRect);
1898 }
1899
1900 if (!region16_is_empty(&invalidRegion))
1901 {
1902 extents = region16_extents(&invalidRegion);
1903 //qDebug(log) << extents->left << extents->top << extents->right << extents->bottom;
1904 pThis->UpdateBuffer(extents->left,
1905 extents->top,
1906 extents->right - extents->left,
1907 extents->bottom - extents->top);
1908 }
1909
1910 region16_uninit(&invalidRegion);
1911
1912 return TRUE;
1913}
1914
1915BOOL CConnectFreeRDP::cb_desktop_resize(rdpContext* context)
1916{
1917 qDebug(log) << Q_FUNC_INFO;
1918 ClientContext* pContext = (ClientContext*)context;
1919 CConnectFreeRDP* pThis = pContext->pThis;
1920 rdpSettings* settings;
1921 if (!context || !context->settings)
1922 return FALSE;
1923 settings = context->settings;
1924 int desktopWidth = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
1925 int desktopHeight = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
1926
1927 if(!gdi_resize(context->gdi, desktopWidth, desktopHeight))
1928 return FALSE;
1929 if(!pThis->CreateImage(context))
1930 return FALSE;
1931
1932 emit pThis->sigSetDesktopSize(desktopWidth, desktopHeight);
1933 pThis->UpdateBuffer(0, 0, desktopWidth, desktopHeight);
1934 return TRUE;
1935}
1936
1937BOOL CConnectFreeRDP::cb_play_bell_sound(rdpContext *context, const PLAY_SOUND_UPDATE *play_sound)
1938{
1939 qDebug(log) << Q_FUNC_INFO;
1940 ClientContext* pContext = (ClientContext*)context;
1941 CConnectFreeRDP* pThis = pContext->pThis;
1942 WINPR_UNUSED(play_sound);
1943 QApplication::beep();
1944 return TRUE;
1945
1946 QString szFile;
1947#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
1948 QSoundEffect effect;
1949 effect.setSource(QUrl::fromLocalFile(szFile));
1950// effect.setLoopCount(1);
1951// effect.setVolume(1);
1952 effect.play();
1953#else
1954 QSound::play(szFile);
1955#endif
1956 return TRUE;
1957}
1958
1959/* This function is called to update the keyboard indicator LED */
1960BOOL CConnectFreeRDP::cb_keyboard_set_indicators(rdpContext *context, UINT16 led_flags)
1961{
1962 qDebug(log) << Q_FUNC_INFO;
1963 ClientContext* pContext = (ClientContext*)context;
1964 CConnectFreeRDP* pThis = pContext->pThis;
1965
1966 int state = CFrmViewer::LED_STATE::Unknown;
1967
1968 if (led_flags & KBD_SYNC_NUM_LOCK)
1969 state |= CFrmViewer::LED_STATE::NumLock;
1970 if (led_flags & KBD_SYNC_CAPS_LOCK)
1971 state |= CFrmViewer::LED_STATE::CapsLock;
1972 if (led_flags & KBD_SYNC_SCROLL_LOCK)
1973 state |= CFrmViewer::LED_STATE::ScrollLock;
1974
1975 emit pThis->sigUpdateLedState(state);
1976
1977 return TRUE;
1978}
1979
1980/* This function is called to set the IME state */
1981BOOL CConnectFreeRDP::cb_keyboard_set_ime_status(
1982 rdpContext* context, UINT16 imeId, UINT32 imeState, UINT32 imeConvMode)
1983{
1984 if (!context)
1985 return FALSE;
1986
1987 qWarning(log,
1988 "KeyboardSetImeStatus(unitId=%04" PRIx16 ", imeState=%08" PRIx32
1989 ", imeConvMode=%08" PRIx32 ") ignored",
1990 imeId, imeState, imeConvMode);
1991 return TRUE;
1992}
1993
1995{
1996 //qDebug(log) << Q_FUNC_INFO;
1997 SetEvent(m_writeEvent);
1998 return 0;
1999}
2000
2001// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/2c1ced34-340a-46cd-be6e-fc8cab7c3b17
2002bool CConnectFreeRDP::SendMouseEvent(UINT16 flags, QPoint pos, bool isExtended)
2003{
2004 if(m_pParameter && m_pParameter->GetOnlyView()) return true;
2005 if(!m_pContext) return false;
2006
2007#if FreeRDP_VERSION_MAJOR >= 3
2008 if(isExtended)
2009 freerdp_client_send_extended_button_event(
2010 &m_pContext->Context, FALSE, flags, pos.x(), pos.y());
2011 else
2012 freerdp_client_send_button_event(
2013 &m_pContext->Context, FALSE, flags, pos.x(), pos.y());
2014#else
2015 if(!m_pContext->Context.input) return false;
2016 return freerdp_input_send_mouse_event(
2017 m_pContext->Context.input, flags, pos.x(), pos.y());
2018#endif
2019 return true;
2020}
2021
2022void CConnectFreeRDP::wheelEvent(QWheelEvent *event)
2023{
2024 qDebug(logMouse) << Q_FUNC_INFO << event;
2025 if(!m_pContext) return;
2026 if(m_pParameter && m_pParameter->GetOnlyView()) return;
2027
2028 UINT16 flags = 0;
2029 QPointF pos;
2030#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
2031 pos = event->position();
2032#else
2033 pos = event->pos();
2034#endif
2035 QPoint p = event->angleDelta();
2036 if(p.y() > 0)
2037 {
2038 flags |= PTR_FLAGS_WHEEL | p.y();
2039 }
2040 if(p.y() < 0)
2041 {
2042 flags |= PTR_FLAGS_WHEEL | PTR_FLAGS_WHEEL_NEGATIVE | -p.y();
2043 }
2044
2045 if(p.x() < 0)
2046 {
2047 flags |= PTR_FLAGS_HWHEEL | PTR_FLAGS_WHEEL_NEGATIVE | -p.x();
2048 }
2049 if(p.x() > 0)
2050 {
2051 flags |= PTR_FLAGS_HWHEEL | p.x();
2052 }
2053#if FreeRDP_VERSION_MAJOR >= 3
2054 freerdp_client_send_wheel_event(&m_pContext->Context, flags);
2055 /*
2056 freerdp_client_send_button_event(
2057 &m_pContext->Context, FALSE, flags, pos.x(), pos.y());//*/
2058#else
2059 freerdp_input_send_mouse_event(
2060 m_pContext->Context.input, flags, pos.x(), pos.y());
2061#endif
2062}
2063
2064// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/2c1ced34-340a-46cd-be6e-fc8cab7c3b17
2065void CConnectFreeRDP::mouseMoveEvent(QMouseEvent *event)
2066{
2067 qDebug(logMouse) << Q_FUNC_INFO << event << event->buttons() << event->button();
2068 if(!m_pContext) return;
2069 if(m_pParameter && m_pParameter->GetOnlyView()) return;
2070 UINT16 flags = PTR_FLAGS_MOVE;
2071 SendMouseEvent(flags, event->pos(), false);
2072}
2073
2074void CConnectFreeRDP::mousePressEvent(QMouseEvent *event)
2075{
2076 qDebug(logMouse) << Q_FUNC_INFO << event << event->buttons() << event->button();
2077 if(!m_pContext) return;
2078 if(m_pParameter && m_pParameter->GetOnlyView()) return;
2079
2080 UINT16 flags = 0;
2081 bool isExtended = false;
2082 Qt::MouseButton button = event->button();
2083 if (button & Qt::MouseButton::LeftButton)
2084 {
2085 flags = PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON1;
2086 }
2087 else if (button & Qt::MouseButton::RightButton)
2088 {
2089 flags = PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON2;
2090 }
2091 else if (button & Qt::MouseButton::MiddleButton)
2092 {
2093 flags = PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON3;
2094 }
2095 else if (button & Qt::MouseButton::ForwardButton)
2096 {
2097 flags = PTR_XFLAGS_DOWN | PTR_XFLAGS_BUTTON2;
2098 isExtended = true;
2099 }
2100 else if (button & Qt::MouseButton::BackButton)
2101 {
2102 flags = PTR_XFLAGS_DOWN | PTR_XFLAGS_BUTTON1;
2103 isExtended = true;
2104 }
2105 if (flags != 0)
2106 {
2107 SendMouseEvent(flags, event->pos(), isExtended);
2108 }
2109}
2110
2111void CConnectFreeRDP::mouseReleaseEvent(QMouseEvent *event)
2112{
2113 qDebug(logMouse) << Q_FUNC_INFO << event << event->buttons() << event->button();
2114 if(!m_pContext) return;
2115 if(m_pParameter && m_pParameter->GetOnlyView()) return;
2116
2117 UINT16 flags = 0;
2118 bool isExtended = false;
2119 Qt::MouseButton button = event->button();
2120 if (button & Qt::MouseButton::LeftButton)
2121 {
2122 flags = PTR_FLAGS_BUTTON1;
2123 }
2124 else if (button & Qt::MouseButton::MiddleButton)
2125 {
2126 flags = PTR_FLAGS_BUTTON3;
2127 }
2128 else if (button & Qt::MouseButton::RightButton)
2129 {
2130 flags = PTR_FLAGS_BUTTON2;
2131 }
2132 else if (button & Qt::MouseButton::ForwardButton)
2133 {
2134 flags = PTR_XFLAGS_BUTTON2;
2135 isExtended = true;
2136 }
2137 else if (button & Qt::MouseButton::BackButton)
2138 {
2139 flags = PTR_XFLAGS_BUTTON1;
2140 isExtended = true;
2141 }
2142 if (flags != 0)
2143 {
2144 SendMouseEvent(flags, event->pos(), isExtended);
2145 }
2146}
2147
2148void CConnectFreeRDP::keyPressEvent(QKeyEvent *event)
2149{
2150 qDebug(logKey) << Q_FUNC_INFO << event;
2151 if(!m_pContext) return;
2152 if(m_pParameter && m_pParameter->GetOnlyView()) return;
2153 // Convert to rdp scan code freerdp/scancode.h
2154 UINT32 k = CConvertKeyCode::QtToScanCode(event->key(), event->modifiers());
2155 if(RDP_SCANCODE_UNKNOWN != k)
2156#if FreeRDP_VERSION_MAJOR >= 3
2157 freerdp_input_send_keyboard_event_ex(
2158 m_pContext->Context.context.input, true, true, k);
2159#else
2160 freerdp_input_send_keyboard_event_ex(
2161 m_pContext->Context.input, true, k);
2162#endif
2163}
2164
2165void CConnectFreeRDP::keyReleaseEvent(QKeyEvent *event)
2166{
2167 qDebug(logKey) << Q_FUNC_INFO << event;
2168 if(!m_pContext) return;
2169 if(m_pParameter && m_pParameter->GetOnlyView()) return;
2170 UINT32 k = CConvertKeyCode::QtToScanCode(event->key(), event->modifiers());
2171 if(RDP_SCANCODE_UNKNOWN != k)
2172#if FreeRDP_VERSION_MAJOR >= 3
2173 freerdp_input_send_keyboard_event_ex(
2174 m_pContext->Context.context.input, false, false, k);
2175#else
2176 freerdp_input_send_keyboard_event_ex(
2177 m_pContext->Context.input, false, k);
2178#endif
2179}
2180
2181int CConnectFreeRDP::RedirectionSound()
2182{
2183 rdpContext* pRdpContext = (rdpContext*)m_pContext;
2184 freerdp* instance = freerdp_client_get_instance(pRdpContext);
2185 rdpSettings* settings = instance->context->settings;
2186 Q_ASSERT(settings);
2187
2188 if(m_pParameter->GetRedirectionSound()
2189 == CParameterFreeRDP::RedirecionSoundType::Disable)
2190 {
2191 /* Disable sound */
2192 freerdp_settings_set_bool(settings, FreeRDP_AudioPlayback, FALSE);
2193 freerdp_settings_set_bool(settings, FreeRDP_RemoteConsoleAudio, FALSE);
2194 return 0;
2195 } else if(m_pParameter->GetRedirectionSound()
2196 == CParameterFreeRDP::RedirecionSoundType::Local)
2197 {
2198 freerdp_settings_set_bool(settings, FreeRDP_AudioPlayback, TRUE);
2199 freerdp_settings_set_bool(settings, FreeRDP_AudioCapture, TRUE);
2200 } else if(m_pParameter->GetRedirectionSound()
2201 == CParameterFreeRDP::RedirecionSoundType::Remote)
2202 {
2203 freerdp_settings_set_bool(settings, FreeRDP_RemoteConsoleAudio, TRUE);
2204 return 0;
2205 }
2206
2207 // Sound:
2208 // rdpsnd channel parameters format see: rdpsnd_process_addin_args in FreeRDP/channels/rdpsnd/client/rdpsnd_main.c
2209 // rdpsnd devices see: rdpsnd_process_connect in FreeRDP/channels/rdpsnd/client/rdpsnd_main.c
2210 // Format ag: /rdpsnd:sys:oss,dev:1,format:1
2211 //
2212 size_t count = 0;
2213 union
2214 {
2215 char** p;
2216 const char** pc;
2217 } ptr;
2218 ptr.p = CommandLineParseCommaSeparatedValuesEx("rdpsnd",
2219 m_pParameter->GetRedirectionSoundParameters().toStdString().c_str(),
2220 &count);
2221 BOOL status = freerdp_client_add_static_channel(settings, count,
2222 #if FreeRDP_VERSION_MAJOR < 3
2223 ptr.p
2224 #else
2225 ptr.pc
2226 #endif
2227 );
2228 if (status)
2229 {
2230 status = freerdp_client_add_dynamic_channel(settings, count,
2231 #if FreeRDP_VERSION_MAJOR < 3
2232 ptr.p
2233 #else
2234 ptr.pc
2235 #endif
2236 );
2237 }
2238
2239 if(!status)
2240 {
2241 qCritical(log) << "Load rdpsnd fail";
2242 return -1;
2243 }
2244
2245 return 0;
2246}
2247
2248int CConnectFreeRDP::RedirectionMicrophone()
2249{
2250 if(m_pParameter->GetRedirectionSound()
2251 == CParameterFreeRDP::RedirecionSoundType::Remote)
2252 return 0;
2253 if(!m_pParameter->GetRedirectionMicrophone())
2254 return 0;
2255
2256 rdpContext* pRdpContext = (rdpContext*)m_pContext;
2257 freerdp* instance = freerdp_client_get_instance(pRdpContext);
2258
2259 rdpSettings* settings = instance->context->settings;
2260 Q_ASSERT(settings);
2261
2262 freerdp_settings_set_bool(settings, FreeRDP_AudioCapture, TRUE);
2263
2264 // Microphone:
2265 // Audin channel parameters format see: audin_process_addin_args in FreeRDP/channels/audin/client/audin_main.c
2266 // Audin channel devices see: audin_DVCPluginEntry in FreeRDP/channels/audin/client/audin_main.c
2267 size_t count = 0;
2268 union
2269 {
2270 char** p;
2271 const char** pc;
2272 } ptr;
2273 ptr.p = CommandLineParseCommaSeparatedValuesEx("audin",
2274 m_pParameter->GetRedirectionMicrophoneParameters().toStdString().c_str(),
2275 &count);
2276 BOOL status = freerdp_client_add_dynamic_channel(settings, count,
2277 #if FreeRDP_VERSION_MAJOR < 3
2278 ptr.p
2279 #else
2280 ptr.pc
2281 #endif
2282 );
2283
2284 if(!status)
2285 {
2286 qCritical(log) << "Load audin fail";
2287 return -1;
2288 }
2289
2290 return 0;
2291}
2292
2293int CConnectFreeRDP::RedirectionDriver()
2294{
2295 QStringList lstDrives = m_pParameter->GetRedirectionDrives();
2296 if(lstDrives.isEmpty())
2297 return 0;
2298
2299 rdpContext* pRdpContext = (rdpContext*)m_pContext;
2300 freerdp* instance = freerdp_client_get_instance(pRdpContext);
2301 rdpSettings* settings = instance->context->settings;
2302 Q_ASSERT(settings);
2303
2304 foreach (auto drive, lstDrives) {
2305 // Format: /drive:name,path
2306 char* pDrive = _strdup(drive.toStdString().c_str());
2307 const char* argvDrive[] = {"drive", pDrive};
2308 int count = sizeof(argvDrive) / sizeof(const char*);
2309 BOOL status = freerdp_client_add_device_channel(settings, count,
2310 #if FreeRDP_VERSION_MAJOR < 3
2311 (char**)
2312 #endif
2313 argvDrive);
2314 if(pDrive) free(pDrive);
2315 if(!status)
2316 {
2317 qCritical(log) << "Load drive fail";
2318 return -1;
2319 }
2320 }
2321
2322 return 0;
2323}
2324
2325int CConnectFreeRDP::RedirectionPrinter()
2326{
2327 if(!m_pParameter->GetRedirectionPrinter())
2328 return 0;
2329
2330 rdpContext* pRdpContext = (rdpContext*)m_pContext;
2331 freerdp* instance = freerdp_client_get_instance(pRdpContext);
2332 rdpSettings* settings = instance->context->settings;
2333 Q_ASSERT(settings);
2334 // 获取系统的打印机列表,并在combobox中显示
2335 QStringList printerList = QPrinterInfo::availablePrinterNames();
2336 if(printerList.isEmpty())
2337 {
2338 qCritical(log) << "The printer is empty";
2339 return -1;
2340 }
2341 qDebug(log) << printerList;
2342
2343 // Format: /printer:<device>,<driver>,[default]
2344 const char* argvPrinter[] = {"printer", nullptr, nullptr};
2345 int count = sizeof(argvPrinter) / sizeof(const char*);
2346 BOOL status = freerdp_client_add_device_channel(settings, count,
2347 #if FreeRDP_VERSION_MAJOR < 3
2348 (char**)
2349 #endif
2350 argvPrinter);
2351 if(!status) {
2352 qCritical(log) << "Load printer fail";
2353 return -2;
2354 }
2355
2356 return 0;
2357}
2358
2359int CConnectFreeRDP::RedirectionSerial()
2360{
2361 //TODO: FreeRDP don't support
2362 return 0;
2363 rdpContext* pRdpContext = (rdpContext*)m_pContext;
2364 freerdp* instance = freerdp_client_get_instance(pRdpContext);
2365 rdpSettings* settings = instance->context->settings;
2366 Q_ASSERT(settings);
2367
2368 QList<QSerialPortInfo> lstSerial = QSerialPortInfo::availablePorts();
2369
2370 int nNum = 1;
2371 foreach (auto serial, lstSerial) {
2372 // Format: /serial:<name>,<device>,[SerCx2|SerCx|Serial],[permissive]
2373 // ag: /serial:COM1,/dev/ttyS0
2374 qDebug(log) << "systemLocation:" << serial.systemLocation()
2375 << "portName:" << serial.portName()
2376 << "serialNumber:" << serial.serialNumber();
2377 char* pSerial = _strdup(serial.systemLocation().toStdString().c_str());
2378 char* pName = _strdup(serial.portName().toStdString().c_str());
2379 const char* argvSerial[] = {"serial", pName, pSerial};
2380 int count = sizeof(argvSerial) / sizeof(const char*);
2381 BOOL status = freerdp_client_add_device_channel(settings, count,
2382 #if FreeRDP_VERSION_MAJOR < 3
2383 (char**)
2384 #endif
2385 argvSerial);
2386 if(pSerial) free(pSerial);
2387 if(pName) free(pName);
2388
2389 if(!status)
2390 {
2391 qCritical(log) << "Load drive fail";
2392 return -1;
2393 }
2394 }
2395
2396 return 0;
2397}
2398
2399void CConnectFreeRDP::slotConnectProxyServer(QString szHost, quint16 nPort)
2400{
2401 qDebug(log) << "CConnectFreeRDP::slotConnectProxyServer" << nPort;
2402 rdpContext* pContext = (rdpContext*)m_pContext;
2403 rdpSettings* settings = pContext->settings;
2404 if(!settings) {
2405 qCritical(log) << "settings is null";
2406 }
2407
2408 freerdp_settings_set_string(
2409 settings, FreeRDP_ServerHostname,
2410 szHost.toStdString().c_str());
2411 freerdp_settings_set_uint32(
2412 settings, FreeRDP_ServerPort,
2413 nPort);
2414
2415 int nRet = freerdp_client_start(pContext);
2416 if(nRet)
2417 {
2418 qCritical(log) << "freerdp_client_start fail";
2419 }
2420 qDebug(log) << "CConnectFreeRDP::slotConnectProxyServer end";
2421}
远程桌面连接接口。它由协议插件实现。
void sigUpdateRect(const QRect &r, const QImage &image)
通知视图,图像更新
virtual OnInitReturnValue OnInit() override
具体的插件实现连接初始化
virtual int OnClean() override
清理
static int cb_verify_x509_certificate(freerdp *instance, const BYTE *data, size_t length, const char *hostname, UINT16 port, DWORD flags)
Callback used if user interaction is required to accept a certificate.
static DWORD cb_verify_changed_certificate_ex(freerdp *instance, const char *host, UINT16 port, const char *common_name, const char *subject, const char *issuer, const char *fingerprint, const char *old_subject, const char *old_issuer, const char *old_fingerprint, DWORD flags)
Callback set in the rdp_freerdp structure, and used to make a certificate validation when a stored ce...
virtual int WakeUp() override
唤醒连接线程(后台线程)
static DWORD cb_verify_certificate_ex(freerdp *instance, const char *host, UINT16 port, const char *common_name, const char *subject, const char *issuer, const char *fingerprint, DWORD flags)
Callback set in the rdp_freerdp structure, and used to make a certificate validation when the connect...
virtual int OnProcess() override
插件连接的具体操作处理。因为此插件是非Qt事件,所以在此函数中等待。
static BOOL cb_post_connect(freerdp *instance)
Callback given to freerdp_connect() to perform post-connection operations.
static BOOL cb_pre_connect(freerdp *instance)
Callback given to freerdp_connect() to process the pre-connect operations.
void sigError(const int nError, const QString &szError=QString())
当有错误产生时触发
void sigInformation(const QString &szInfo)
从后台线程中触发在主线程中显示信息,不阻塞后台线程
void sigBlockShowMessageBox(const QString &szTitle, const QString &szMessage, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton &nRet, bool &checkBox, QString checkBoxContext=QString())
阻塞后台线程,并在前台线程中显示消息对话框(QMessageBox)
void sigShowMessageBox(const QString &szTitle, const QString &szMessage, const QMessageBox::Icon &icon=QMessageBox::Information)
从后台线程中触发在主线程中显示消息对话框(QMessageBox),不阻塞后台线程
void sigBlockInputDialog(const QString &szTitle, const QString &szLable, const QString &szMessage, QString &szText)
阻塞后台线程,并在前台线程中显示输入对话框 (QInputDialog)
void sigConnected()
当插件连接成功后触发。仅由插件触发
void sigDisconnect()
通知用户断开连接。仅由插件触发。 当从插件中需要要断开连接时触发。例如:对端断开连接、重置连接或者连接出错。
void sigBlockShowWidget(const QString &className, int &nRet, void *pContext)
阻塞后台线程,并在前台线程中显示窗口。
static UINT32 QtToScanCode(int key, Qt::KeyboardModifiers modifiers)
CConvertKeyCode::QtToScanCode
[Declare CParameterFreeRDP]
CParameterUser m_User
[Instance user]
void sigChanged()
当参数改变时,触发 通常如果需要,则相应的参数会对应一个改变事件。
实现通过本地 SOCKET 与 SSH 隧道转发数据。适用于库没有实现传输层接口,只有 socket 的情况。