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