Rabbit Remote Control 0.0.36
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#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
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]
Basic network parameters.
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.