Rabbit Remote Control 0.0.35
Loading...
Searching...
No Matches
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}
Remote desktop connect interface.
void sigUpdateRect(const QRect &r, const QImage &image)
Notify the CFrmView update image.
virtual OnInitReturnValue OnInit() override
Specific plug-in realizes connection initialization.
virtual int OnClean() override
Clean.
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
Wake up Connect thread(background thread)
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
Specific operation processing of plug-in connection.
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())
Triggered when an error is generated.
void sigInformation(const QString &szInfo)
Triggering from a background thread displays information in the main thread without blocking the back...
void sigBlockShowMessageBox(const QString &szTitle, const QString &szMessage, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton &nRet, bool &checkBox, QString checkBoxContext=QString())
Block background threads and display message dialogs in foreground threads (QMessageBox)
void sigShowMessageBox(const QString &szTitle, const QString &szMessage, const QMessageBox::Icon &icon=QMessageBox::Information)
Trigger the display of a message dialog (QMessageBox) in the main thread from a background thread wit...
void sigBlockInputDialog(const QString &szTitle, const QString &szLable, const QString &szMessage, QString &szText)
Block background threads and display input dialogs in foreground threads (QInputDialog)
void sigConnected()
Emitted when the plugin is successfully connected.
void sigDisconnect()
Notify the user to call disconnect.
void sigBlockShowWidget(const QString &className, int &nRet, void *pContext)
Blocks the background thread and displays the window in the foreground thread.
static UINT32 QtToScanCode(int key, Qt::KeyboardModifiers modifiers)
CConvertKeyCode::QtToScanCode.
[Declare CParameterFreeRDP]
CParameterUser m_User
[Instance user]
void sigChanged()
emit when the parameter changes Usually if required, the corresponding parameter corresponds to a cha...
Data is forwarded over a local socket and SSH tunnel.