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