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