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