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