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"
24#include "ConnectFreeRDP.h"
25#include "RabbitCommonTools.h"
26#include "ConvertKeyCode.h"
30#include <QApplication>
32#include <QSslCertificate>
33#include <QInputDialog>
34#include <QMutexLocker>
36#include <QPrinterInfo>
38#include <QSerialPortInfo>
39#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
40 #include <QSoundEffect>
45#ifdef WINPR_HAVE_POLL_H
52#include <sys/select.h>
56#if FREERDP_VERSION_MAJOR >= 3
57#include "ConnectLayerQTcpSocket.h"
59#include "ConnectLayerSSHTunnel.h"
63static Q_LOGGING_CATEGORY(log,
"FreeRDP.Connect")
64static Q_LOGGING_CATEGORY(logKey, "FreeRDP.Connect.Key")
65static Q_LOGGING_CATEGORY(logMouse, "FreeRDP.Connect.Mouse")
69 , m_pConnecter(pConnecter)
71 , m_pParameter(
nullptr)
74 , m_writeEvent(
nullptr)
75#if FREERDP_VERSION_MAJOR >= 3
76 , m_pConnectLayer(
nullptr)
79 , m_pThreadSSH(
nullptr)
82 qDebug(log) << Q_FUNC_INFO;
83 m_pParameter = qobject_cast<CParameterFreeRDP*>(pConnecter->GetParameter());
84 Q_ASSERT(m_pParameter);
87CConnectFreeRDP::~CConnectFreeRDP()
89 qDebug(log) << Q_FUNC_INFO;
100 qDebug(log) << Q_FUNC_INFO;
103 m_writeEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
105 qCritical(log) <<
"CreateEvent failed";
106 return OnInitReturnValue::Fail;
108 ZeroMemory(&m_ClientEntryPoints,
sizeof(RDP_CLIENT_ENTRY_POINTS));
109 m_ClientEntryPoints.Version = RDP_CLIENT_INTERFACE_VERSION;
110 m_ClientEntryPoints.Size =
sizeof(RDP_CLIENT_ENTRY_POINTS);
112 m_ClientEntryPoints.GlobalInit = cbGlobalInit;
113 m_ClientEntryPoints.GlobalUninit = cbGlobalUninit;
114 m_ClientEntryPoints.ClientNew = cbClientNew;
115 m_ClientEntryPoints.ClientFree = cbClientFree;
116 m_ClientEntryPoints.ClientStart = cbClientStart;
117 m_ClientEntryPoints.ClientStop = cbClientStop;
120 auto pRdpContext = freerdp_client_context_new(&m_ClientEntryPoints);
124 m_pContext->pThis =
this;
126 qCritical(log) <<
"freerdp_client_context_new fail";
127 return OnInitReturnValue::Fail;
130 rdpSettings* settings = pRdpContext->settings;
132 qCritical(log) <<
"settings is null";
133 return OnInitReturnValue::Fail;
146 char* argv[]= {(
char*)QApplication::applicationFilePath().toStdString().c_str()};
147 int argc =
sizeof(argv) /
sizeof(
char*);
148 nRet = freerdp_client_settings_parse_command_line(settings, argc, argv, TRUE);
151 nRet = freerdp_client_settings_command_line_status_print(settings, nRet, argc, argv);
152 return OnInitReturnValue::Fail;
155#if FREERDP_VERSION_MAJOR >= 3
156 if (!stream_dump_register_handlers(pRdpContext,
157 CONNECTION_STATE_MCS_CREATE_REQUEST,
159 return OnInitReturnValue::Fail;
162 auto &user = m_pParameter->m_Net.
m_User;
163 if(!user.GetUser().isEmpty())
164 freerdp_settings_set_string(
165 settings, FreeRDP_Username,
166 user.GetUser().toStdString().c_str());
167 if(!user.GetPassword().isEmpty())
168 freerdp_settings_set_string(
169 settings, FreeRDP_Password,
170 user.GetPassword().toStdString().c_str());
172 freerdp_settings_set_bool(
173 settings, FreeRDP_RedirectClipboard, m_pParameter->GetClipboard());
175#if FREERDP_VERSION_MAJOR >= 3
176 bool bOnlyView = m_pParameter->GetOnlyView();
177 freerdp_settings_set_bool(
178 settings, FreeRDP_SuspendInput, bOnlyView);
181 freerdp_settings_set_uint32(settings, FreeRDP_DesktopWidth,
182 m_pParameter->GetDesktopWidth());
183 freerdp_settings_set_uint32(settings, FreeRDP_DesktopHeight,
184 m_pParameter->GetDesktopHeight());
185 freerdp_settings_set_uint32(settings, FreeRDP_ColorDepth,
186 m_pParameter->GetColorDepth());
188 freerdp_settings_set_bool(settings, FreeRDP_UseMultimon,
189 m_pParameter->GetUseMultimon());
191 if(m_pParameter->GetReconnectInterval()) {
192 freerdp_settings_set_bool(
193 settings, FreeRDP_AutoReconnectionEnabled,
true);
194 freerdp_settings_set_uint32(
196 FreeRDP_AutoReconnectMaxRetries,
197 m_pParameter->GetReconnectInterval());
200 freerdp_settings_set_bool(
201 settings, FreeRDP_AutoReconnectionEnabled,
false);
205 RedirectionMicrophone();
207 RedirectionPrinter();
212 switch(m_pParameter->m_Proxy.GetUsedType())
214 case CParameterProxy::TYPE::System:
216 case CParameterProxy::TYPE::Http:
217 case CParameterProxy::TYPE::SockesV5:
220 if (!freerdp_settings_set_uint32(settings, FreeRDP_ProxyType, PROXY_TYPE_SOCKS))
221 return OnInitReturnValue::Fail;
222 if(CParameterProxy::TYPE::Http == m_pParameter->m_Proxy.GetUsedType()) {
223 net = &m_pParameter->m_Proxy.m_Http;
224 if (!freerdp_settings_set_uint32(settings, FreeRDP_ProxyType, PROXY_TYPE_HTTP))
225 return OnInitReturnValue::Fail;
228 if (!freerdp_settings_set_string(settings, FreeRDP_ProxyHostname,
229 net->GetHost().toStdString().c_str()))
230 return OnInitReturnValue::Fail;
231 if (!freerdp_settings_set_uint16(settings, FreeRDP_ProxyPort, net->GetPort()))
232 return OnInitReturnValue::Fail;
234 if(((user.GetUsedType() == CParameterUser::TYPE::UserPassword)
235 && (user.GetPassword().isEmpty() || user.GetUser().isEmpty()))
236 || ((user.GetUsedType() == CParameterUser::TYPE::PublicKey)
237 && user.GetPassphrase().isEmpty())) {
238 int nRet = QDialog::Rejected;
240 if(QDialog::Accepted != nRet)
242 return OnInitReturnValue::Fail;
245 if (!freerdp_settings_set_string(settings, FreeRDP_ProxyUsername,
246 user.GetUser().toStdString().c_str()))
247 return OnInitReturnValue::Fail;
248 if (!freerdp_settings_set_string(settings, FreeRDP_ProxyPassword,
249 user.GetPassword().toStdString().c_str()))
250 return OnInitReturnValue::Fail;
261 case CParameterProxy::TYPE::None:
263 if(!m_pParameter->GetDomain().isEmpty())
264 freerdp_settings_set_string(
265 settings, FreeRDP_Domain,
266 m_pParameter->GetDomain().toStdString().c_str());
267 if(m_pParameter->m_Net.GetHost().isEmpty())
270 szErr = tr(
"The server is empty, please input it");
271 qCritical(log) << szErr;
273 emit
sigError(-1, szErr.toStdString().c_str());
274 return OnInitReturnValue::Fail;
276 auto &net = m_pParameter->m_Net;
277 freerdp_settings_set_string(
278 settings, FreeRDP_ServerHostname,
279 net.GetHost().toStdString().c_str());
280 freerdp_settings_set_uint32(
281 settings, FreeRDP_ServerPort,
284 nRet = freerdp_client_start(pRdpContext);
287 qCritical(log) <<
"freerdp_client_start fail";
288 return OnInitReturnValue::Fail;
293 case CParameterProxy::TYPE::SSHTunnel:
295#if FREERDP_VERSION_MAJOR >= 3
297 if(!m_pConnectLayer)
return OnInitReturnValue::Fail;
298 nRet = m_pConnectLayer->Initialize(pRdpContext);
299 if(nRet)
return OnInitReturnValue::Fail;
302 return InitSSHTunnelPipe();
307 qCritical(log) <<
"Don't support proxy type:" << m_pParameter->m_Proxy.GetUsedType();
311 return OnInitReturnValue::UseOnProcess;
316 qDebug(log) << Q_FUNC_INFO;
320 CloseHandle(m_writeEvent);
321 m_writeEvent =
nullptr;
325 rdpContext* pRdpContext = (rdpContext*)m_pContext;
326 if(!freerdp_disconnect(pRdpContext->instance))
327 qCritical(log) <<
"freerdp_disconnect fail";
329 if(freerdp_client_stop(pRdpContext))
330 qCritical(log) <<
"freerdp_client_stop fail";
332 freerdp_client_context_free(pRdpContext);
333 m_pContext =
nullptr;
336#if FREERDP_VERSION_MAJOR >= 3
337 if(m_pConnectLayer) {
338 m_pConnectLayer->Clean();
339 m_pConnectLayer->deleteLater();
340 m_pConnectLayer =
nullptr;
345 CleanSSHTunnelPipe();
387 rdpContext* pRdpContext = (rdpContext*)m_pContext;
389 if(
nullptr == freerdp_settings_get_string(pRdpContext->settings, FreeRDP_ServerHostname))
397 nCount += freerdp_get_event_handles(pRdpContext, &handles[nCount],
398 ARRAYSIZE(handles) - nCount);
401 qCritical(log) <<
"freerdp_get_event_handles failed";
407 handles[nCount++] = m_writeEvent;
409 DWORD waitStatus = WaitForMultipleObjects(nCount, handles, FALSE, nTimeout);
412 ResetEvent(m_writeEvent);
414 if (waitStatus == WAIT_FAILED)
416 qCritical(log) <<
"WaitForMultipleObjects: WAIT_FAILED";
421 if(waitStatus == WAIT_TIMEOUT)
428 if (!freerdp_check_event_handles(pRdpContext))
432 UINT32 err = freerdp_get_last_error(pRdpContext);
434 szErr =
"freerdp_check_event_handles fail.";
436 szErr += QString::number(err);
438 szErr += freerdp_get_last_error_category(err);
440 szErr += freerdp_get_last_error_name(err);
442 szErr += freerdp_get_last_error_string(err);
443 qCritical(log) << szErr;
467#if FREERDP_VERSION_MAJOR >= 3
468 if(freerdp_shall_disconnect_context(pRdpContext))
470 if(freerdp_shall_disconnect(pRdpContext->instance))
473 qCritical(log) <<
"freerdp_shall_disconnect false";
481void CConnectFreeRDP::slotClipBoardChanged()
483 qDebug(log) << Q_FUNC_INFO;
484 if(m_pParameter && m_pParameter->GetOnlyView())
return;
485 if(m_pParameter->GetClipboard())
486 m_ClipBoard.slotClipBoardChanged();
489BOOL CConnectFreeRDP::cbGlobalInit()
491 qDebug(log) << Q_FUNC_INFO;
495void CConnectFreeRDP::cbGlobalUninit()
497 qDebug(log) << Q_FUNC_INFO;
500BOOL CConnectFreeRDP::cbClientNew(freerdp *instance, rdpContext *context)
502 qDebug(log) << Q_FUNC_INFO;
505 instance->PostDisconnect = cb_post_disconnect;
508#if FREERDP_VERSION_MAJOR < 3
509 instance->Authenticate = cb_authenticate;
510 instance->GatewayAuthenticate = cb_GatewayAuthenticate;
512 instance->AuthenticateEx = cb_authenticate_ex;
513 instance->ChooseSmartcard = cb_choose_smartcard;
518 instance->PresentGatewayMessage = cb_present_gateway_message;
520 instance->LogonErrorInfo = cb_logon_error_info;
525void CConnectFreeRDP::cbClientFree(freerdp *instance, rdpContext *context)
527 qDebug(log) << Q_FUNC_INFO;
530int CConnectFreeRDP::cbClientStart(rdpContext *context)
532 qDebug(log) << Q_FUNC_INFO;
535 if (!context || !context->settings)
537 freerdp* instance = freerdp_client_get_instance(context);
541 auto settings = context->settings;
543 QString szDomainHost;
545 szDomainHost = freerdp_settings_get_string(settings, FreeRDP_ServerHostname);
546 nPort = freerdp_settings_get_uint32(settings, FreeRDP_ServerPort);
548 auto &net = pThis->m_pParameter->m_Net;
549 szServer = net.GetHost() +
":" + QString::number(net.GetPort());
550 auto &proxy = pThis->m_pParameter->m_Proxy;
551 switch(proxy.GetUsedType()) {
552 case CParameterProxy::TYPE::SockesV5:
554 auto &sockesV5 = proxy.m_SockesV5;
555 szServer = sockesV5.GetHost() +
":" + QString::number(sockesV5.GetPort())
559 case CParameterProxy::TYPE::Http:
561 auto &http = proxy.m_Http;
562 szServer = http.GetHost() +
":" + QString::number(http.GetPort())
566 case CParameterProxy::TYPE::SSHTunnel:
568 auto &sshNet = proxy.m_SSH.m_Net;
569#if FREERDP_VERSION_MAJOR < 3
570 szServer = szDomainHost +
":" + QString::number(nPort)
571 +
" -> " + sshNet.GetHost() +
":" + QString::number(sshNet.GetPort())
574 szServer = sshNet.GetHost() +
":" + QString::number(sshNet.GetPort())
583 BOOL status = freerdp_connect(instance);
585 QString szInfo = tr(
"Connected to ") + szServer;
586 qInfo(log) << szInfo;
590 UINT32 nRet = freerdp_get_last_error(context);
593 szErr = tr(
"Connect to ") + szServer + tr(
" fail.");
595 szErr += QString::number(nRet) +
" - ";
596 szErr += freerdp_get_last_error_name(nRet);
601 szErr += freerdp_get_last_error_string(nRet);
605 case FREERDP_ERROR_CONNECT_LOGON_FAILURE:
607 szErr = tr(
"Logon to ") + szServer;
608 szErr += tr(
" fail. Please check that the username and password are correct.") +
"\n";
611 case FREERDP_ERROR_CONNECT_WRONG_PASSWORD:
613 szErr = tr(
"Logon to ") + szServer;
614 szErr += tr(
" fail. Please check password are correct.") +
"\n";
617 case FREERDP_ERROR_AUTHENTICATION_FAILED:
619 szErr = tr(
"Logon to ") + szServer;
620 szErr += tr(
" authentication fail. please add a CA certificate to the store.") +
"\n";
623 case FREERDP_ERROR_CONNECT_TRANSPORT_FAILED:
625 szErr = tr(
"Logon to ") + szServer;
626 szErr += tr(
" connect transport layer fail.") +
"\n\n";
627 szErr += tr(
"Please:") +
"\n";
628 szErr += tr(
"1. Check for any network related issues") +
"\n";
629 szErr += tr(
"2. Check you have proper security settings ('NLA' enabled is required for most connections nowadays)") +
"\n";
630 szErr +=
" " + tr(
"If you do not know the server security settings, contact your server administrator.") +
"\n";
631 szErr += tr(
"3. Check the certificate is proper (and guacd properly checks that)") +
"\n";
634 case FREERDP_ERROR_SECURITY_NEGO_CONNECT_FAILED:
636 szErr += tr(
"Please check you have proper security settings.") +
"\n";
637 szErr += tr(
"If you do not know the server security settings, contact your server administrator.");
639 case FREERDP_ERROR_CONNECT_CANCELLED:
640 szErr = tr(
"The connect was canceled.") +
"\n\n" + szErr;
647 qCritical(log) << szErr;
648 emit pThis->
sigError(nRet, szErr.toStdString().c_str());
654int CConnectFreeRDP::cbClientStop(rdpContext *context)
657 qDebug(log) << Q_FUNC_INFO;
658#if FREERDP_VERSION_MAJOR >= 3
659 nRet = freerdp_client_common_stop(context);
661 BOOL bRet = freerdp_abort_connect(context->instance);
663 { qCritical(log) <<
"freerdp_abort_connect fail";
690 qDebug(log) << Q_FUNC_INFO;
691 rdpChannels* channels =
nullptr;
692 rdpSettings* settings =
nullptr;
693 rdpContext* context = instance->context;
695 if (!instance || !instance->context || !instance->context->settings)
699 if(!pThis)
return FALSE;
700 settings = instance->context->settings;
701 channels = context->channels;
703 if(!channels || !pParameter)
707#if defined (Q_OS_WIN)
708 if (!freerdp_settings_set_uint32(
709 settings, FreeRDP_OsMajorType, OSMAJORTYPE_WINDOWS))
711 if (!freerdp_settings_set_uint32(
712 settings, FreeRDP_OsMinorType, OSMINORTYPE_WINDOWS_NT))
714#elif defined(Q_OS_ANDROID)
715 if (!freerdp_settings_set_uint32(
716 settings, FreeRDP_OsMajorType, OSMAJORTYPE_ANDROID))
718 if (!freerdp_settings_set_uint32(
719 settings, FreeRDP_OsMinorType, OSMINORTYPE_UNSPECIFIED))
721#elif defined(Q_OS_IOS)
722 if (!freerdp_settings_set_uint32(
723 settings, FreeRDP_OsMajorType, OSMAJORTYPE_IOS))
725 if (!freerdp_settings_set_uint32(
726 settings, FreeRDP_OsMinorType, OSMINORTYPE_UNSPECIFIED))
728#elif defined (Q_OS_UNIX)
729 if (!freerdp_settings_set_uint32(
730 settings, FreeRDP_OsMajorType, OSMAJORTYPE_UNIX))
732 if (!freerdp_settings_set_uint32(
733 settings, FreeRDP_OsMinorType, OSMINORTYPE_NATIVE_XSERVER))
736 if (!freerdp_settings_set_uint32(
737 settings, FreeRDP_OsMajorType, OSMAJORTYPE_UNSPECIFIED))
739 if (!freerdp_settings_set_uint32(
740 settings, FreeRDP_OsMinorType, OSMINORTYPE_UNSPECIFIED))
745 PubSub_SubscribeChannelConnected(instance->context->pubSub,
746 OnChannelConnectedEventHandler);
747 PubSub_SubscribeChannelDisconnected(instance->context->pubSub,
748 OnChannelDisconnectedEventHandler);
750#if FREERDP_VERSION_MAJOR < 3
751 if (!freerdp_client_load_addins(channels, instance->context->settings))
754 #if defined(Q_OS_LINUX) || (defined(Q_OS_WIN) && defined(WITH_WINDOWS_CERT_STORE))
755 if (!freerdp_settings_set_bool(settings, FreeRDP_CertificateCallbackPreferPEM, TRUE))
760 if(!freerdp_settings_set_bool(
761 settings, FreeRDP_NegotiateSecurityLayer,
762 pParameter->GetNegotiateSecurityLayer()))
764 CParameterFreeRDP::Security security = pParameter->GetSecurity();
766 if(!freerdp_settings_set_bool(
767 settings, FreeRDP_RdpSecurity,
768 CParameterFreeRDP::Security::RDP & security))
770 if (!freerdp_settings_set_bool(
771 settings, FreeRDP_UseRdpSecurityLayer,
772 CParameterFreeRDP::Security::RDP & security))
775 if(!freerdp_settings_set_bool(
776 settings, FreeRDP_TlsSecurity,
777 CParameterFreeRDP::Security::TLS & security))
779 if(!freerdp_settings_set_bool(
780 settings, FreeRDP_NlaSecurity,
781 CParameterFreeRDP::Security::NLA & security))
783 if(!freerdp_settings_set_bool(
784 settings, FreeRDP_ExtSecurity,
785 CParameterFreeRDP::Security::NLA_Ext & security))
787#if FREERDP_VERSION_MAJOR >= 3
788 if(!freerdp_settings_set_bool(
789 settings, FreeRDP_AadSecurity,
790 CParameterFreeRDP::Security::RDSAAD & security))
792 if(!freerdp_settings_set_bool(
793 settings, FreeRDP_RdstlsSecurity,
794 CParameterFreeRDP::Security::RDSTLS & security))
796 freerdp_settings_set_uint16(settings, FreeRDP_TLSMinVersion,
797 pParameter->GetTlsVersion());
801 if (freerdp_settings_get_bool(settings, FreeRDP_AuthenticationOnly))
804 auto &user = pParameter->m_Net.
m_User;
805 if(!freerdp_settings_get_string(settings, FreeRDP_Username)) {
806 if(user.GetUser().isEmpty()) {
807 if(user.GetUser().isEmpty()) {
809 qWarning(log) <<
"Auth-only, but no user name set. Will be call instance->Authenticate.";
812 freerdp_settings_set_string(
813 settings, FreeRDP_Username,
814 user.GetUser().toStdString().c_str());
816 if (!freerdp_settings_get_string(settings, FreeRDP_Password)) {
817 if (user.GetPassword().isEmpty()) {
819 qWarning(log) <<
"auth-only, but no password set. Will be call instance->Authenticate";
821 freerdp_settings_set_string(
822 settings, FreeRDP_Password,
823 user.GetPassword().toStdString().c_str());
825#if FREERDP_VERSION_MAJOR >= 3
826 if (!freerdp_settings_set_bool(settings, FreeRDP_DeactivateClientDecoding, TRUE))
829 }
else if(freerdp_settings_get_bool(settings, FreeRDP_CredentialsFromStdin)){
831 }
else if(freerdp_settings_get_bool(settings, FreeRDP_SmartcardLogon)) {
842 UINT32 width = pParameter->GetDesktopWidth();
843 UINT32 height = pParameter->GetDesktopHeight();
844 if ((width < 64) || (height < 64) ||
845 (width > 4096) || (height > 4096))
847 QString szErr = tr(
"Invalid dimensions:")
848 + QString::number(width)
849 +
"*" + QString::number(height);
850 qCritical(log) << szErr;
854 qInfo(log) <<
"Init desktop size " << width <<
"*" << height;
858 <<
"width:" << freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth)
859 <<
"height:" << freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight)
860 <<
"ColorDepth:" << freerdp_settings_get_uint32(settings, FreeRDP_ColorDepth);
871 if(!freerdp_set_connection_type(settings, pParameter->GetConnectType()))
873 freerdp_settings_set_uint32(
874 settings, FreeRDP_PerformanceFlags, pParameter->GetPerformanceFlags());
875 freerdp_performance_flags_split(settings);
880const char* CConnectFreeRDP::GetTitle(freerdp* instance)
882 const char* windowTitle;
885 const char* name =
nullptr;
888 rdpSettings* settings = instance->context->settings;
893 windowTitle = freerdp_settings_get_string(settings, FreeRDP_WindowTitle);
897#if FREERDP_VERSION_MAJOR >= 3
898 name = freerdp_settings_get_server_name(settings);
900 name = pThis->m_pParameter->m_Net.GetHost().toStdString().c_str();
902 port = freerdp_settings_get_uint32(settings, FreeRDP_ServerPort);
904 addPort = (port != 3389);
906 char buffer[MAX_PATH + 64] = { 0 };
909 sprintf_s(buffer,
sizeof(buffer),
"%s", name);
911 sprintf_s(buffer,
sizeof(buffer),
"%s:%" PRIu32, name, port);
913 freerdp_settings_set_string(settings, FreeRDP_WindowTitle, buffer);
914 return freerdp_settings_get_string(settings, FreeRDP_WindowTitle);
924 qDebug(log) << Q_FUNC_INFO;
926 rdpContext* context = instance->context;
927 rdpSettings* settings = instance->context->settings;
928 rdpUpdate* update = instance->context->update;
931 const char* pWindowTitle = GetTitle(instance);
934 WCHAR* windowTitle = NULL;
935#if FREERDP_VERSION_MAJOR >= 3
936 windowTitle = ConvertUtf8ToWCharAlloc(pWindowTitle, NULL);
938 ConvertToUnicode(CP_UTF8, 0, pWindowTitle, -1, &windowTitle, 0);
942 QString title = QString::fromUtf16((
const char16_t*)windowTitle);
944 if(pThis->m_pParameter->GetServerName().isEmpty())
945 emit pThis->sigServerName(title);
949 int desktopWidth = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
950 int desktopHeight = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
951 emit pThis->sigSetDesktopSize(desktopWidth, desktopHeight);
953 if (!gdi_init(instance, PIXEL_FORMAT_BGRA32))
956 if(!pThis->CreateImage(instance->context))
959 Q_ASSERT(instance->context->cache);
962 if(pThis->m_Cursor.RegisterPointer(context->graphics))
965 update->BeginPaint = cb_begin_paint;
966 update->EndPaint = cb_end_paint;
967 update->DesktopResize = cb_desktop_resize;
969 update->PlaySound = cb_play_bell_sound;
971 update->SetKeyboardIndicators = cb_keyboard_set_indicators;
972 update->SetKeyboardImeStatus = cb_keyboard_set_ime_status;
978void CConnectFreeRDP::cb_post_disconnect(freerdp* instance)
980 qDebug(log) << Q_FUNC_INFO;
981 rdpContext* context =
nullptr;
983 if (!instance || !instance->context)
986 context = instance->context;
988 PubSub_UnsubscribeChannelConnected(instance->context->pubSub,
989 OnChannelConnectedEventHandler);
990 PubSub_UnsubscribeChannelDisconnected(instance->context->pubSub,
991 OnChannelDisconnectedEventHandler);
995int CConnectFreeRDP::cb_logon_error_info(freerdp* instance, UINT32 data, UINT32 type)
998 const char* str_data = freerdp_get_logon_error_info_data(data);
999 const char* str_type = freerdp_get_logon_error_info_type(type);
1000 QString szErr = tr(
"FreeRDP logon info: [");
1004 qDebug(log) << szErr;
1010void CConnectFreeRDP::OnChannelConnectedEventHandler(
void *context,
1011 #
if FREERDP_VERSION_MAJOR >= 3
1014 ChannelConnectedEventArgs *e)
1016 rdpContext* pContext = (rdpContext*)context;
1018 if (strcmp(e->name, CLIPRDR_SVC_CHANNEL_NAME) == 0) {
1019 qDebug(log) <<
"channel" << e->name <<
"connected";
1020 pThis->m_ClipBoard.Init((CliprdrClientContext*)e->pInterface,
1021 pThis->m_pParameter->GetClipboard());
1023#if FREERDP_VERSION_MAJOR >= 3
1025 freerdp_client_OnChannelConnectedEventHandler(pContext, e);
1027 else if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0)
1029 if (freerdp_settings_get_bool(pContext->settings, FreeRDP_SoftwareGdi)) {
1030 rdpGdi* gdi = pContext->gdi;
1032 gdi_graphics_pipeline_init(gdi, (RdpgfxClientContext*) e->pInterface);
1035 qDebug(log,
"Unimplemented: channel %s connected but libfreerdp is in HardwareGdi mode\n", e->name);
1037 else if (strcmp(e->name, GEOMETRY_DVC_CHANNEL_NAME) == 0)
1039 gdi_video_geometry_init(pContext->gdi, (GeometryClientContext*)e->pInterface);
1041 else if (strcmp(e->name, VIDEO_DATA_DVC_CHANNEL_NAME) == 0)
1043 gdi_video_data_init(pContext->gdi, (VideoClientContext*)e->pInterface);
1045 qDebug(log) <<
"Unimplemented: channel" << e->name <<
"connected but we can’t use it";
1049void CConnectFreeRDP::OnChannelDisconnectedEventHandler(
void *context,
1050 #
if FREERDP_VERSION_MAJOR >= 3
1053 ChannelDisconnectedEventArgs *e)
1055 rdpContext* pContext = (rdpContext*)context;
1058 if (strcmp(e->name, CLIPRDR_SVC_CHANNEL_NAME) == 0) {
1059 qDebug(log) <<
"channel" << e->name <<
"disconnected";
1060 pThis->m_ClipBoard.UnInit((CliprdrClientContext*)e->pInterface,
1061 pThis->m_pParameter->GetClipboard());
1063#if FREERDP_VERSION_MAJOR >= 3
1065 freerdp_client_OnChannelDisconnectedEventHandler(pContext, e);
1067 else if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0)
1069 if (freerdp_settings_get_bool(pContext->settings, FreeRDP_SoftwareGdi)) {
1070 rdpGdi* gdi = pContext->gdi;
1071 gdi_graphics_pipeline_uninit(gdi, (RdpgfxClientContext*) e->pInterface);
1074 qDebug(log,
"Unimplemented: channel %s connected but libfreerdp is in HardwareGdi mode\n", e->name);
1077 qDebug(log) <<
"Unimplemented: channel" << e->name <<
"disconnected but we can’t use it";
1082UINT32 CConnectFreeRDP::GetImageFormat(QImage::Format format)
1085#if (QT_VERSION >= QT_VERSION_CHECK(5,2,0))
1086 case QImage::Format_RGBA8888:
1087 return PIXEL_FORMAT_RGBA32;
1088 case QImage::Format_RGBX8888:
1089 return PIXEL_FORMAT_RGBX32;
1091 case QImage::Format_RGB16:
1092 return PIXEL_FORMAT_RGB16;
1093 case QImage::Format_ARGB32:
1094 return PIXEL_FORMAT_BGRA32;
1095 case QImage::Format_RGB32:
1096 return PIXEL_FORMAT_BGRA32;
1103UINT32 CConnectFreeRDP::GetImageFormat()
1105 return GetImageFormat(m_Image.format());
1108BOOL CConnectFreeRDP::CreateImage(rdpContext *context)
1111 ClientContext* pContext = (ClientContext*)context;
1113 rdpGdi* gdi = context->gdi;
1114 Q_ASSERT(pThis && gdi);
1115 pThis->m_Image = QImage(gdi->primary_buffer,
1116 static_cast<int>(gdi->width),
1117 static_cast<int>(gdi->height),
1118 QImage::Format_ARGB32);
1122#if FREERDP_VERSION_MAJOR >= 3
1124static CREDUI_INFOW wfUiInfo = {
sizeof(CREDUI_INFOW), NULL, L
"Enter your credentials",
1125 L
"Remote Desktop Security", NULL };
1128BOOL CConnectFreeRDP::cb_authenticate_ex(freerdp* instance,
1129 char** username,
char** password,
1130 char** domain, rdp_auth_reason reason)
1132 qDebug(log) << Q_FUNC_INFO <<
"reason:" << reason;
1136 if(!username || !password || !domain)
return FALSE;
1138 rdpContext* pContext = (rdpContext*)instance->context;
1143 WCHAR UserNameW[CREDUI_MAX_USERNAME_LENGTH + 1] = { 0 };
1144 WCHAR UserW[CREDUI_MAX_USERNAME_LENGTH + 1] = { 0 };
1145 WCHAR DomainW[CREDUI_MAX_DOMAIN_TARGET_LENGTH + 1] = { 0 };
1146 WCHAR PasswordW[CREDUI_MAX_PASSWORD_LENGTH + 1] = { 0 };
1148 WINPR_ASSERT(instance);
1149 WINPR_ASSERT(instance->context);
1150 WINPR_ASSERT(instance->context->settings);
1152 WINPR_ASSERT(username);
1153 WINPR_ASSERT(domain);
1154 WINPR_ASSERT(password);
1156 const WCHAR auth[] = L
"Target credentials requested";
1157 const WCHAR authPin[] = L
"PIN requested";
1158 const WCHAR gwAuth[] = L
"Gateway credentials requested";
1159 const WCHAR* titleW = auth;
1162 dwFlags = CREDUI_FLAGS_DO_NOT_PERSIST | CREDUI_FLAGS_EXCLUDE_CERTIFICATES |
1163 CREDUI_FLAGS_USERNAME_TARGET_CREDENTIALS;
1170 if ((*username) && (*password))
1173 case AUTH_SMARTCARD_PIN:
1174 dwFlags &= ~CREDUI_FLAGS_USERNAME_TARGET_CREDENTIALS;
1175 dwFlags |= CREDUI_FLAGS_PASSWORD_ONLY_OK | CREDUI_FLAGS_KEEP_USERNAME;
1180 *username = _strdup(
"PIN");
1193 ConvertUtf8ToWChar(*username, UserNameW, ARRAYSIZE(UserNameW));
1194 ConvertUtf8ToWChar(*username, UserW, ARRAYSIZE(UserW));
1198 ConvertUtf8ToWChar(*password, PasswordW, ARRAYSIZE(PasswordW));
1201 ConvertUtf8ToWChar(*domain, DomainW, ARRAYSIZE(DomainW));
1203 if (_wcsnlen(PasswordW, ARRAYSIZE(PasswordW)) == 0)
1205 status = CredUIPromptForCredentialsW(&wfUiInfo, titleW, NULL, 0, UserNameW,
1206 ARRAYSIZE(UserNameW), PasswordW,
1207 ARRAYSIZE(PasswordW), &fSave, dwFlags);
1208 if (status != NO_ERROR)
1211 "CredUIPromptForCredentials unexpected status: 0x%08lX",
1216 if ((dwFlags & CREDUI_FLAGS_KEEP_USERNAME) == 0)
1218 status = CredUIParseUserNameW(UserNameW, UserW, ARRAYSIZE(UserW), DomainW,
1219 ARRAYSIZE(DomainW));
1220 if (status != NO_ERROR)
1222 CHAR User[CREDUI_MAX_USERNAME_LENGTH + 1] = { 0 };
1223 CHAR UserName[CREDUI_MAX_USERNAME_LENGTH + 1] = { 0 };
1224 CHAR Domain[CREDUI_MAX_DOMAIN_TARGET_LENGTH + 1] = { 0 };
1226 ConvertWCharNToUtf8(UserNameW, ARRAYSIZE(UserNameW), UserName, ARRAYSIZE(UserName));
1227 ConvertWCharNToUtf8(UserW, ARRAYSIZE(UserW), User, ARRAYSIZE(User));
1228 ConvertWCharNToUtf8(DomainW, ARRAYSIZE(DomainW), Domain, ARRAYSIZE(Domain));
1230 "Failed to parse UserName: %s into User: %s Domain: %s",
1231 UserName, User, Domain);
1237 *username = ConvertWCharNToUtf8Alloc(UserW, ARRAYSIZE(UserW), NULL);
1240 qCritical(log) <<
"ConvertWCharNToUtf8Alloc failed" << status;
1244 if (_wcsnlen(DomainW, ARRAYSIZE(DomainW)) > 0)
1245 *domain = ConvertWCharNToUtf8Alloc(DomainW, ARRAYSIZE(DomainW), NULL);
1247 *domain = _strdup(
"\0");
1252 qCritical(log) <<
"strdup failed" << status;
1256 *password = ConvertWCharNToUtf8Alloc(PasswordW, ARRAYSIZE(PasswordW), NULL);
1265 return cb_authenticate(instance, username, password, domain);
1270BOOL CConnectFreeRDP::cb_choose_smartcard(freerdp* instance,
1271 SmartcardCertInfo** cert_list,
1273 DWORD* choice, BOOL gateway)
1275 rdpContext* pContext = (rdpContext*)instance->context;
1277 QString msg(
"Multiple smartcards are available for use:\n");
1278 for (DWORD i = 0; i < count; i++)
1280 const SmartcardCertInfo* cert = cert_list[i];
1281 char* reader = ConvertWCharToUtf8Alloc(cert->reader, NULL);
1282 char* container_name = ConvertWCharToUtf8Alloc(cert->containerName, NULL);
1284 msg += QString::number(i) +
" ";
1285 msg += QString(container_name) +
"\n\t";
1286 msg +=
"Reader: " + QString(reader) +
"\n\t";
1287 msg +=
"User: " + QString(cert->userHint) + +
"@" + QString(cert->domainHint) +
"\n\t";
1288 msg +=
"Subject: " + QString(cert->subject) +
"\n\t";
1289 msg +=
"Issuer: " + QString(cert->issuer) +
"\n\t";
1290 msg +=
"UPN: " + QString(cert->upn) +
"\n";
1293 free(container_name);
1296 msg +=
"\nChoose a smartcard to use for ";
1298 msg +=
"gateway authentication";
1302 msg +=
"(0 - " + QString::number(count - 1) +
")";
1310 int n = num.toInt(&ok);
1320#ifdef WITH_WINDOWS_CERT_STORE
1328static void wf_report_error(
char* wszMessage, DWORD dwErrCode)
1330 LPSTR pwszMsgBuf = NULL;
1332 if (NULL != wszMessage && 0 != *wszMessage)
1334 WLog_ERR(TAG,
"%s", wszMessage);
1337 FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
1342 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
1352 if (NULL != pwszMsgBuf)
1354 WLog_ERR(TAG,
"Error: 0x%08x (%d) %s", dwErrCode, dwErrCode, pwszMsgBuf);
1355 LocalFree(pwszMsgBuf);
1359 WLog_ERR(TAG,
"Error: 0x%08x (%d)", dwErrCode, dwErrCode);
1363static DWORD wf_is_x509_certificate_trusted(
const char* common_name,
const char* subject,
1364 const char* issuer,
const char* fingerprint)
1366 HRESULT hr = CRYPT_E_NOT_FOUND;
1368 DWORD dwChainFlags = CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;
1369 PCCERT_CONTEXT pCert = NULL;
1370 HCERTCHAINENGINE hChainEngine = NULL;
1371 PCCERT_CHAIN_CONTEXT pChainContext = NULL;
1373 CERT_ENHKEY_USAGE EnhkeyUsage = { 0 };
1374 CERT_USAGE_MATCH CertUsage = { 0 };
1375 CERT_CHAIN_PARA ChainPara = { 0 };
1376 CERT_CHAIN_POLICY_PARA ChainPolicy = { 0 };
1377 CERT_CHAIN_POLICY_STATUS PolicyStatus = { 0 };
1378 CERT_CHAIN_ENGINE_CONFIG EngineConfig = { 0 };
1380 DWORD derPubKeyLen = WINPR_ASSERTING_INT_CAST(uint32_t, strlen(fingerprint));
1381 char* derPubKey = calloc(derPubKeyLen,
sizeof(
char));
1382 if (NULL == derPubKey)
1384 WLog_ERR(TAG,
"Could not allocate derPubKey");
1391 if (!CryptStringToBinaryA(fingerprint, 0, CRYPT_STRING_BASE64HEADER, derPubKey, &derPubKeyLen,
1394 WLog_ERR(TAG,
"CryptStringToBinary failed. Err: %d", GetLastError());
1401 EnhkeyUsage.cUsageIdentifier = 0;
1402 EnhkeyUsage.rgpszUsageIdentifier = NULL;
1404 CertUsage.dwType = USAGE_MATCH_TYPE_AND;
1405 CertUsage.Usage = EnhkeyUsage;
1407 ChainPara.cbSize =
sizeof(ChainPara);
1408 ChainPara.RequestedUsage = CertUsage;
1410 ChainPolicy.cbSize =
sizeof(ChainPolicy);
1412 PolicyStatus.cbSize =
sizeof(PolicyStatus);
1414 EngineConfig.cbSize =
sizeof(EngineConfig);
1415 EngineConfig.dwUrlRetrievalTimeout = 0;
1417 pCert = CertCreateCertificateContext(X509_ASN_ENCODING, derPubKey, derPubKeyLen);
1420 WLog_ERR(TAG,
"FAILED: Certificate could not be parsed.");
1424 dwChainFlags |= CERT_CHAIN_ENABLE_PEER_TRUST;
1433 if (!CertCreateCertificateChainEngine(&EngineConfig, &hChainEngine))
1435 hr = HRESULT_FROM_WIN32(GetLastError());
1442 if (!CertGetCertificateChain(hChainEngine,
1453 hr = HRESULT_FROM_WIN32(GetLastError());
1460 if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_BASE,
1465 hr = HRESULT_FROM_WIN32(GetLastError());
1469 if (PolicyStatus.dwError != S_OK)
1471 wf_report_error(
"CertVerifyCertificateChainPolicy: Chain Status", PolicyStatus.dwError);
1472 hr = PolicyStatus.dwError;
1478 if (PolicyStatus.dwError == CRYPT_E_NO_REVOCATION_CHECK ||
1479 PolicyStatus.dwError == CRYPT_E_REVOCATION_OFFLINE)
1487 WLog_INFO(TAG,
"CertVerifyCertificateChainPolicy succeeded for %s (%s) issued by %s",
1488 common_name, subject, issuer);
1495 WLog_INFO(TAG,
"CertVerifyCertificateChainPolicy failed for %s (%s) issued by %s",
1496 common_name, subject, issuer);
1497 wf_report_error(NULL, hr);
1502 if (NULL != pChainContext)
1504 CertFreeCertificateChain(pChainContext);
1507 if (NULL != hChainEngine)
1509 CertFreeCertificateChainEngine(hChainEngine);
1514 CertFreeCertificateContext(pCert);
1523BOOL CConnectFreeRDP::cb_authenticate(freerdp* instance,
char** username,
1524 char** password,
char** domain)
1526 qDebug(log) << Q_FUNC_INFO;
1529 rdpContext* pContext = (rdpContext*)instance->context;
1531 if(!username || !password || !domain)
return FALSE;
1532 if(*username && *password )
return TRUE;
1534 int nRet = QDialog::Rejected;
1536 nRet, pThis->m_pParameter);
1537 if(QDialog::Accepted == nRet)
1539 QString szPassword = pThis->m_pParameter->m_Net.
m_User.GetPassword();
1540 QString szName = pThis->m_pParameter->m_Net.
m_User.GetUser();
1541 QString szDomain = pThis->m_pParameter->GetDomain();
1542 if(!szDomain.isEmpty() && domain)
1543 *domain = _strdup(szDomain.toStdString().c_str());
1544 if(!szName.isEmpty() && username)
1545 *username = _strdup(szName.toStdString().c_str());
1546 if(!szPassword.isEmpty() && password)
1547 *password = _strdup(szPassword.toStdString().c_str());
1554BOOL CConnectFreeRDP::cb_GatewayAuthenticate(freerdp *instance,
1555 char **username,
char **password,
char **domain)
1557 qDebug(log) << Q_FUNC_INFO;
1561 rdpContext* pContext = (rdpContext*)instance->context;
1563 if(!username || !password || !domain)
return FALSE;
1564 if(*username && *password )
return TRUE;
1566 int nRet = QDialog::Rejected;
1567 emit pThis->
sigBlockShowWidget(
"CDlgGetUserPasswordFreeRDP", nRet, pThis->m_pParameter);
1568 if(QDialog::Accepted == nRet)
1570 QString szPassword = pThis->m_pParameter->m_Net.
m_User.GetPassword();
1571 QString szName = pThis->m_pParameter->m_Net.
m_User.GetUser();
1572 QString szDomain = pThis->m_pParameter->GetDomain();
1573 if(!szDomain.isEmpty() && domain)
1574 *domain = _strdup(szDomain.toStdString().c_str());
1575 if(!szName.isEmpty() && username)
1576 *username = _strdup(szName.toStdString().c_str());
1577 if(!szPassword.isEmpty() && password)
1578 *password = _strdup(szPassword.toStdString().c_str());
1586 const BYTE* data,
size_t length,
1587 const char* hostname, UINT16 port, DWORD flags)
1589 qDebug(log) << Q_FUNC_INFO;
1590 rdpContext* pContext = (rdpContext*)instance->context;
1591 QSslCertificate cert(QByteArray((
const char*)data, length));
1592#if FREERDP_VERSION_MAJOR >= 3
1596 if (flags & VERIFY_CERT_FLAG_FP_IS_PEM) {
1598 instance, hostname, port,
1599 cert.issuerDisplayName().toStdString().c_str(),
1600 cert.subjectDisplayName().toStdString().c_str(),
1601 cert.issuerDisplayName().toStdString().c_str(),
1607 instance, hostname, port,
1608 cert.issuerDisplayName().toStdString().c_str(),
1609 cert.subjectDisplayName().toStdString().c_str(),
1610 cert.issuerDisplayName().toStdString().c_str(),
1611 cert.serialNumber().toStdString().c_str(),
1615static QString pem_cert_fingerprint(
const char* pem, DWORD flags)
1617 QString szFingerPrint;
1618#if FREERDP_VERSION_MAJOR >= 3
1622 if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
1624 rdpCertificate* cert = freerdp_certificate_new_from_pem(pem);
1628 char* fp = freerdp_certificate_get_fingerprint(cert);
1629 char* start = freerdp_certificate_get_validity(cert, TRUE);
1630 char* end = freerdp_certificate_get_validity(cert, FALSE);
1631 freerdp_certificate_free(cert);
1633 szFingerPrint = QObject::tr(
"Valid from: ") + QString(start) +
"\n";
1634 szFingerPrint += QObject::tr(
"Valid to: ") + QString(end) +
"\n";
1635 szFingerPrint += QObject::tr(
"Fingerprint: ") + QString(fp) +
"\n";
1642 szFingerPrint = QObject::tr(
"Fingerprint: ") + QString(pem) +
"\n";
1643 return szFingerPrint;
1662 const char *host, UINT16 port,
1663 const char *common_name,
const char *subject,
1664 const char *issuer,
const char *fingerprint, DWORD flags)
1666 qDebug(log) << Q_FUNC_INFO;
1668 rdpContext* pContext = (rdpContext*)instance->context;
1675 emit pThis->sigServerName(common_name);
1678 if(!pThis->m_pParameter->GetShowVerifyDiaglog()) {
1684#if FREERDP_VERSION_MAJOR >= 3
1685#if defined(Q_OS_WIN) && defined(WITH_WINDOWS_CERT_STORE)
1686 if (flags & VERIFY_CERT_FLAG_FP_IS_PEM && !(flags & VERIFY_CERT_FLAG_MISMATCH))
1688 if (wf_is_x509_certificate_trusted(common_name, subject, issuer, fingerprint) == S_OK)
1696 QString szType = tr(
"RDP-Server");
1697 if (flags & VERIFY_CERT_FLAG_GATEWAY)
1698 szType = tr(
"RDP-Gateway");
1699 if (flags & VERIFY_CERT_FLAG_REDIRECT)
1700 szType = tr(
"RDP-Redirect");
1702 QString title(tr(
"Verify certificate"));
1705 message += szType + tr(
": %1:%2").arg(host, QString::number(port)) +
"\n";
1706 message += tr(
"Common name: ") + common_name +
"\n";
1707 message += tr(
"Subject: ") + subject +
"\n";
1708 message += tr(
"Issuer: ") + issuer +
"\n";
1709 message += pem_cert_fingerprint(fingerprint, flags);
1711 if(VERIFY_CERT_FLAG_CHANGED & flags) {
1712 message += tr(
"The above X.509 certificate is changed.\n"
1713 "It is possible that the server has changed its certificate, "
1714 "or Maybe it was attacked."
1715 "Please look at the OpenSSL documentation on "
1716 "how to add a private CA to the store.");
1718 message += tr(
"The above X.509 certificate could not be verified.\n"
1719 "Possibly because you do not have the CA certificate "
1720 "in your certificate store, or the certificate has expired.\n"
1721 "Please look at the OpenSSL documentation on "
1722 "how to add a private CA to the store.");
1726 message += tr(
"Yes - trusted") +
"\n";
1727 message += tr(
"Ignore - temporary trusted") +
"\n";
1728 message += tr(
"No - no trusted") +
"\n";
1730 QMessageBox::StandardButton nRet = QMessageBox::StandardButton::No;
1731 QMessageBox::StandardButtons buttons = QMessageBox::Yes | QMessageBox::Ignore | QMessageBox::No;
1732 bool bCheckBox =
false;
1734 tr(
"Don't show again"));
1735 pThis->m_pParameter->SetShowVerifyDiaglog(!bCheckBox);
1741 case QMessageBox::StandardButton::Yes:
1743 case QMessageBox::StandardButton::Ignore:
1771 const char *host, UINT16 port,
1772 const char *common_name,
const char *subject,
1773 const char *issuer,
const char *fingerprint,
1774 const char *old_subject,
const char *old_issuer,
1775 const char *old_fingerprint, DWORD flags)
1777 qDebug(log) << Q_FUNC_INFO;
1778 rdpContext* pContext = (rdpContext*)instance->context;
1781 emit pThis->sigServerName(common_name);
1783 if(!pThis->m_pParameter->GetShowVerifyDiaglog()) {
1789 QString szType = tr(
"RDP-Server");
1790 if (flags & VERIFY_CERT_FLAG_GATEWAY)
1791 szType = tr(
"RDP-Gateway");
1792 if (flags & VERIFY_CERT_FLAG_REDIRECT)
1793 szType = tr(
"RDP-Redirect");
1795 QString title(tr(
"Verify changed certificate"));
1797 message += szType + tr(
": %1:%2").arg(host, QString::number(port)) +
"\n";
1798 message += tr(
"New Certificate details:") +
"\n";
1799 message +=
" " + tr(
"name: ") + common_name +
"\n";
1800 message +=
" " + tr(
"subject: ") + subject +
"\n";
1801 message +=
" " + tr(
"issuer: ") + issuer +
"\n";
1802 message +=
" " + pem_cert_fingerprint(fingerprint, flags) +
"\n";
1803 message += tr(
"Old Certificate details:") +
"\n";
1804 message +=
" " + tr(
"subject: ") + old_subject +
"\n";
1805 message +=
" " + tr(
"issuer: ") + old_issuer +
"\n";
1806 message +=
" " + pem_cert_fingerprint(old_fingerprint, flags) +
"\n";
1808 message += tr(
"The above X.509 certificate could not be verified, "
1809 "possibly because you do not have the CA certificate "
1810 "in your certificate store, or the certificate has expired. "
1811 "Please look at the OpenSSL documentation on "
1812 "how to add a private CA to the store.");
1815 message += tr(
"Yes - trusted") +
"\n";
1816 message += tr(
"Ignore - temporary trusted") +
"\n";
1817 message += tr(
"No - no trusted") +
"\n";
1819 bool bCheckBox =
false;
1820 QMessageBox::StandardButton nRet = QMessageBox::StandardButton::No;
1821 QMessageBox::StandardButtons buttons = QMessageBox::Yes | QMessageBox::Ignore | QMessageBox::No;
1823 tr(
"Don't show again"));
1824 pThis->m_pParameter->SetShowVerifyDiaglog(!bCheckBox);
1831 case QMessageBox::StandardButton::Yes:
1833 case QMessageBox::StandardButton::Ignore:
1844BOOL CConnectFreeRDP::cb_present_gateway_message(
1845 freerdp* instance, UINT32 type, BOOL isDisplayMandatory,
1846 BOOL isConsentMandatory,
size_t length,
const WCHAR* message)
1848 qDebug(log) << Q_FUNC_INFO;
1850 if (!isDisplayMandatory && !isConsentMandatory)
1854 if (type == GATEWAY_MESSAGE_CONSENT && isConsentMandatory)
1856 QString msgType = (type == GATEWAY_MESSAGE_CONSENT)
1857 ? tr(
"Consent message") : tr(
"Service message");
1859#if FREERDP_VERSION_MAJOR >= 3
1860 char* pMsg = ConvertWCharToUtf8Alloc(message, NULL);
1866 msgType += QString::fromStdWString((
wchar_t*)message);
1869 msgType += tr(
"I understand and agree to the terms of this policy (Y/N)");
1871 rdpContext* pContext = (rdpContext*)instance->context;
1873 QMessageBox::StandardButton nRet = QMessageBox::No;
1874 bool bCheckBox =
false;
1876 QMessageBox::Yes|QMessageBox::No,
1879 case QMessageBox::Yes:
1887 return client_cli_present_gateway_message(
1888 instance, type, isDisplayMandatory,
1889 isConsentMandatory, length, message);
1894BOOL CConnectFreeRDP::cb_begin_paint(rdpContext *context)
1898 if (!context || !context->gdi || !context->gdi->primary
1899 || !context->gdi->primary->hdc)
1902 hdc = context->gdi->primary->hdc;
1904 if (!hdc || !hdc->hwnd || !hdc->hwnd->invalid)
1907 hdc->hwnd->invalid->null = TRUE;
1908 hdc->hwnd->ninvalid = 0;
1912BOOL CConnectFreeRDP::UpdateBuffer(INT32 x, INT32 y, INT32 w, INT32 h)
1914 if(x > m_Image.width() || y > m_Image.height()) {
1915 qCritical(log) <<
"The width and height out of range."
1916 <<
"Image width:" << m_Image.width()
1917 <<
"Image height:" << m_Image.height()
1918 <<
"w:" << w <<
"h:" << h;
1922 QRect rect(x, y, w, h);
1923 QImage img = m_Image.copy(rect);
1929BOOL CConnectFreeRDP::cb_end_paint(rdpContext *context)
1932 ClientContext* pContext = (ClientContext*)context;
1936 REGION16 invalidRegion;
1937 RECTANGLE_16 invalidRect;
1938 const RECTANGLE_16* extents;
1942 if (!context || !context->gdi || !context->gdi->primary
1943 || !context->gdi->primary->hdc)
1946 hdc = context->gdi->primary->hdc;
1948 if (!hdc || !hdc->hwnd || !hdc->hwnd->invalid)
1951 rdpGdi* gdi = context->gdi;
1952 if (gdi->suppressOutput)
1955 HGDI_WND hwnd = context->gdi->primary->hdc->hwnd;
1956 ninvalid = hwnd->ninvalid;
1957 cinvalid = hwnd->cinvalid;
1961 region16_init(&invalidRegion);
1963 for (i = 0; i < ninvalid; i++)
1965 if(cinvalid[i].null)
1967 qWarning(log) <<
"is null region" << cinvalid[i].x << cinvalid[i].y
1968 << cinvalid[i].w << cinvalid[i].h;
1971 invalidRect.left = cinvalid[i].x;
1972 invalidRect.top = cinvalid[i].y;
1973 invalidRect.right = cinvalid[i].x + cinvalid[i].w;
1974 invalidRect.bottom = cinvalid[i].y + cinvalid[i].h;
1975 region16_union_rect(&invalidRegion, &invalidRegion, &invalidRect);
1978 if (!region16_is_empty(&invalidRegion))
1980 extents = region16_extents(&invalidRegion);
1982 pThis->UpdateBuffer(extents->left,
1984 extents->right - extents->left,
1985 extents->bottom - extents->top);
1988 region16_uninit(&invalidRegion);
1993BOOL CConnectFreeRDP::cb_desktop_resize(rdpContext* context)
1995 qDebug(log) << Q_FUNC_INFO;
1996 ClientContext* pContext = (ClientContext*)context;
1998 rdpSettings* settings;
1999 if (!context || !context->settings)
2001 settings = context->settings;
2002 int desktopWidth = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
2003 int desktopHeight = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
2005 if(!gdi_resize(context->gdi, desktopWidth, desktopHeight))
2007 if(!pThis->CreateImage(context))
2010 emit pThis->sigSetDesktopSize(desktopWidth, desktopHeight);
2011 pThis->UpdateBuffer(0, 0, desktopWidth, desktopHeight);
2015BOOL CConnectFreeRDP::cb_play_bell_sound(rdpContext *context,
const PLAY_SOUND_UPDATE *play_sound)
2017 qDebug(log) << Q_FUNC_INFO;
2018 ClientContext* pContext = (ClientContext*)context;
2020 WINPR_UNUSED(play_sound);
2021 QApplication::beep();
2025#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
2026 QSoundEffect effect;
2027 effect.setSource(QUrl::fromLocalFile(szFile));
2032 QSound::play(szFile);
2038BOOL CConnectFreeRDP::cb_keyboard_set_indicators(rdpContext *context, UINT16 led_flags)
2040 qDebug(log) << Q_FUNC_INFO;
2041 ClientContext* pContext = (ClientContext*)context;
2044 int state = CFrmViewer::LED_STATE::Unknown;
2046 if (led_flags & KBD_SYNC_NUM_LOCK)
2047 state |= CFrmViewer::LED_STATE::NumLock;
2048 if (led_flags & KBD_SYNC_CAPS_LOCK)
2049 state |= CFrmViewer::LED_STATE::CapsLock;
2050 if (led_flags & KBD_SYNC_SCROLL_LOCK)
2051 state |= CFrmViewer::LED_STATE::ScrollLock;
2053 emit pThis->sigUpdateLedState(state);
2059BOOL CConnectFreeRDP::cb_keyboard_set_ime_status(
2060 rdpContext* context, UINT16 imeId, UINT32 imeState, UINT32 imeConvMode)
2066 "KeyboardSetImeStatus(unitId=%04" PRIx16
", imeState=%08" PRIx32
2067 ", imeConvMode=%08" PRIx32
") ignored",
2068 imeId, imeState, imeConvMode);
2076 SetEvent(m_writeEvent);
2081bool CConnectFreeRDP::SendMouseEvent(UINT16 flags, QPoint pos,
bool isExtended)
2083 if(m_pParameter && m_pParameter->GetOnlyView())
return true;
2084 if(!m_pContext)
return false;
2086#if FREERDP_VERSION_MAJOR >= 3
2088 freerdp_client_send_extended_button_event(
2089 &m_pContext->Context, FALSE, flags, pos.x(), pos.y());
2091 freerdp_client_send_button_event(
2092 &m_pContext->Context, FALSE, flags, pos.x(), pos.y());
2094 if(!m_pContext->Context.input)
return false;
2095 return freerdp_input_send_mouse_event(
2096 m_pContext->Context.input, flags, pos.x(), pos.y());
2101void CConnectFreeRDP::wheelEvent(QWheelEvent *event)
2103 qDebug(logMouse) << Q_FUNC_INFO << event;
2104 if(!m_pContext)
return;
2105 if(m_pParameter && m_pParameter->GetOnlyView())
return;
2109#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
2110 pos =
event->position();
2114 QPoint p =
event->angleDelta();
2117 flags |= PTR_FLAGS_WHEEL | p.y();
2121 flags |= PTR_FLAGS_WHEEL | PTR_FLAGS_WHEEL_NEGATIVE | -p.y();
2126 flags |= PTR_FLAGS_HWHEEL | PTR_FLAGS_WHEEL_NEGATIVE | -p.x();
2130 flags |= PTR_FLAGS_HWHEEL | p.x();
2132#if FREERDP_VERSION_MAJOR >= 3
2133 freerdp_client_send_wheel_event(&m_pContext->Context, flags);
2138 freerdp_input_send_mouse_event(
2139 m_pContext->Context.input, flags, pos.x(), pos.y());
2144void CConnectFreeRDP::mouseMoveEvent(QMouseEvent *event)
2146 qDebug(logMouse) << Q_FUNC_INFO <<
event <<
event->buttons() <<
event->button();
2147 if(!m_pContext)
return;
2148 if(m_pParameter && m_pParameter->GetOnlyView())
return;
2149 UINT16 flags = PTR_FLAGS_MOVE;
2150 SendMouseEvent(flags, event->pos(),
false);
2153void CConnectFreeRDP::mousePressEvent(QMouseEvent *event)
2155 qDebug(logMouse) << Q_FUNC_INFO <<
event <<
event->buttons() <<
event->button();
2156 if(!m_pContext)
return;
2157 if(m_pParameter && m_pParameter->GetOnlyView())
return;
2160 bool isExtended =
false;
2161 Qt::MouseButton button =
event->button();
2162 if (button & Qt::MouseButton::LeftButton)
2164 flags = PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON1;
2166 else if (button & Qt::MouseButton::RightButton)
2168 flags = PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON2;
2170 else if (button & Qt::MouseButton::MiddleButton)
2172 flags = PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON3;
2174 else if (button & Qt::MouseButton::ForwardButton)
2176 flags = PTR_XFLAGS_DOWN | PTR_XFLAGS_BUTTON2;
2179 else if (button & Qt::MouseButton::BackButton)
2181 flags = PTR_XFLAGS_DOWN | PTR_XFLAGS_BUTTON1;
2186 SendMouseEvent(flags, event->pos(), isExtended);
2190void CConnectFreeRDP::mouseReleaseEvent(QMouseEvent *event)
2192 qDebug(logMouse) << Q_FUNC_INFO <<
event <<
event->buttons() <<
event->button();
2193 if(!m_pContext)
return;
2194 if(m_pParameter && m_pParameter->GetOnlyView())
return;
2197 bool isExtended =
false;
2198 Qt::MouseButton button =
event->button();
2199 if (button & Qt::MouseButton::LeftButton)
2201 flags = PTR_FLAGS_BUTTON1;
2203 else if (button & Qt::MouseButton::MiddleButton)
2205 flags = PTR_FLAGS_BUTTON3;
2207 else if (button & Qt::MouseButton::RightButton)
2209 flags = PTR_FLAGS_BUTTON2;
2211 else if (button & Qt::MouseButton::ForwardButton)
2213 flags = PTR_XFLAGS_BUTTON2;
2216 else if (button & Qt::MouseButton::BackButton)
2218 flags = PTR_XFLAGS_BUTTON1;
2223 SendMouseEvent(flags, event->pos(), isExtended);
2227void CConnectFreeRDP::keyPressEvent(QKeyEvent *event)
2229 qDebug(logKey) << Q_FUNC_INFO << event;
2230 if(!m_pContext)
return;
2231 if(m_pParameter && m_pParameter->GetOnlyView())
return;
2234 if(RDP_SCANCODE_UNKNOWN != k)
2235#if FREERDP_VERSION_MAJOR >= 3
2236 freerdp_input_send_keyboard_event_ex(
2237 m_pContext->Context.context.input,
true,
true, k);
2239 freerdp_input_send_keyboard_event_ex(
2240 m_pContext->Context.input,
true, k);
2244void CConnectFreeRDP::keyReleaseEvent(QKeyEvent *event)
2246 qDebug(logKey) << Q_FUNC_INFO << event;
2247 if(!m_pContext)
return;
2248 if(m_pParameter && m_pParameter->GetOnlyView())
return;
2250 if(RDP_SCANCODE_UNKNOWN != k)
2251#if FREERDP_VERSION_MAJOR >= 3
2252 freerdp_input_send_keyboard_event_ex(
2253 m_pContext->Context.context.input,
false,
false, k);
2255 freerdp_input_send_keyboard_event_ex(
2256 m_pContext->Context.input,
false, k);
2260int CConnectFreeRDP::RedirectionSound()
2262 rdpContext* pRdpContext = (rdpContext*)m_pContext;
2263 freerdp* instance = freerdp_client_get_instance(pRdpContext);
2264 rdpSettings* settings = instance->context->settings;
2267 if(m_pParameter->GetRedirectionSound()
2268 == CParameterFreeRDP::RedirecionSoundType::Disable)
2271 freerdp_settings_set_bool(settings, FreeRDP_AudioPlayback, FALSE);
2272 freerdp_settings_set_bool(settings, FreeRDP_RemoteConsoleAudio, FALSE);
2274 }
else if(m_pParameter->GetRedirectionSound()
2275 == CParameterFreeRDP::RedirecionSoundType::Local)
2277 freerdp_settings_set_bool(settings, FreeRDP_AudioPlayback, TRUE);
2278 freerdp_settings_set_bool(settings, FreeRDP_AudioCapture, TRUE);
2279 }
else if(m_pParameter->GetRedirectionSound()
2280 == CParameterFreeRDP::RedirecionSoundType::Remote)
2282 freerdp_settings_set_bool(settings, FreeRDP_RemoteConsoleAudio, TRUE);
2297 ptr.p = CommandLineParseCommaSeparatedValuesEx(
"rdpsnd",
2298 m_pParameter->GetRedirectionSoundParameters().toStdString().c_str(),
2300 BOOL status = freerdp_client_add_static_channel(settings, count,
2301 #
if FREERDP_VERSION_MAJOR < 3
2309 status = freerdp_client_add_dynamic_channel(settings, count,
2310 #
if FREERDP_VERSION_MAJOR < 3
2320 qCritical(log) <<
"Load rdpsnd fail";
2327int CConnectFreeRDP::RedirectionMicrophone()
2329 if(m_pParameter->GetRedirectionSound()
2330 == CParameterFreeRDP::RedirecionSoundType::Remote)
2332 if(!m_pParameter->GetRedirectionMicrophone())
2335 rdpContext* pRdpContext = (rdpContext*)m_pContext;
2336 freerdp* instance = freerdp_client_get_instance(pRdpContext);
2338 rdpSettings* settings = instance->context->settings;
2341 freerdp_settings_set_bool(settings, FreeRDP_AudioCapture, TRUE);
2352 ptr.p = CommandLineParseCommaSeparatedValuesEx(
"audin",
2353 m_pParameter->GetRedirectionMicrophoneParameters().toStdString().c_str(),
2355 BOOL status = freerdp_client_add_dynamic_channel(settings, count,
2356 #
if FREERDP_VERSION_MAJOR < 3
2365 qCritical(log) <<
"Load audin fail";
2372int CConnectFreeRDP::RedirectionDriver()
2374 QStringList lstDrives = m_pParameter->GetRedirectionDrives();
2375 if(lstDrives.isEmpty())
2378 rdpContext* pRdpContext = (rdpContext*)m_pContext;
2379 freerdp* instance = freerdp_client_get_instance(pRdpContext);
2380 rdpSettings* settings = instance->context->settings;
2383 foreach (
auto drive, lstDrives) {
2385 char* pDrive = _strdup(drive.toStdString().c_str());
2386 const char* argvDrive[] = {
"drive", pDrive};
2387 int count =
sizeof(argvDrive) /
sizeof(
const char*);
2388 BOOL status = freerdp_client_add_device_channel(settings, count,
2389 #
if FREERDP_VERSION_MAJOR < 3
2393 if(pDrive) free(pDrive);
2396 qCritical(log) <<
"Load drive fail";
2404int CConnectFreeRDP::RedirectionPrinter()
2406 if(!m_pParameter->GetRedirectionPrinter())
2409 rdpContext* pRdpContext = (rdpContext*)m_pContext;
2410 freerdp* instance = freerdp_client_get_instance(pRdpContext);
2411 rdpSettings* settings = instance->context->settings;
2414 QStringList printerList = QPrinterInfo::availablePrinterNames();
2415 if(printerList.isEmpty())
2417 qCritical(log) <<
"The printer is empty";
2420 qDebug(log) << printerList;
2423 const char* argvPrinter[] = {
"printer",
nullptr,
nullptr};
2424 int count =
sizeof(argvPrinter) /
sizeof(
const char*);
2425 BOOL status = freerdp_client_add_device_channel(settings, count,
2426 #
if FREERDP_VERSION_MAJOR < 3
2431 qCritical(log) <<
"Load printer fail";
2438int CConnectFreeRDP::RedirectionSerial()
2442 rdpContext* pRdpContext = (rdpContext*)m_pContext;
2443 freerdp* instance = freerdp_client_get_instance(pRdpContext);
2444 rdpSettings* settings = instance->context->settings;
2447 QList<QSerialPortInfo> lstSerial = QSerialPortInfo::availablePorts();
2450 foreach (
auto serial, lstSerial) {
2453 qDebug(log) <<
"systemLocation:" << serial.systemLocation()
2454 <<
"portName:" << serial.portName()
2455 <<
"serialNumber:" << serial.serialNumber();
2456 char* pSerial = _strdup(serial.systemLocation().toStdString().c_str());
2457 char* pName = _strdup(serial.portName().toStdString().c_str());
2458 const char* argvSerial[] = {
"serial", pName, pSerial};
2459 int count =
sizeof(argvSerial) /
sizeof(
const char*);
2460 BOOL status = freerdp_client_add_device_channel(settings, count,
2461 #
if FREERDP_VERSION_MAJOR < 3
2465 if(pSerial) free(pSerial);
2466 if(pName) free(pName);
2470 qCritical(log) <<
"Load drive fail";
2478void CConnectFreeRDP::slotConnectProxyServer(QString szHost, quint16 nPort)
2480 qDebug(log) <<
"Connect proxy server:" << szHost +
":" + QString::number(nPort);
2481 rdpContext* pContext = (rdpContext*)m_pContext;
2482 rdpSettings* settings = pContext->settings;
2484 qCritical(log) <<
"settings is null";
2487 freerdp_settings_set_string(
2488 settings, FreeRDP_ServerHostname,
2489 szHost.toStdString().c_str());
2490 freerdp_settings_set_uint32(
2491 settings, FreeRDP_ServerPort,
2494 int nRet = freerdp_client_start(pContext);
2497 qCritical(log) <<
"freerdp_client_start fail";
2499 qDebug(log) <<
"Connect proxy server:" << szHost +
":" + QString::number(nPort) <<
"end";
2503CConnect::OnInitReturnValue CConnectFreeRDP::InitSSHTunnelPipe()
2508 &m_pParameter->m_Proxy.m_SSH, &m_pParameter->m_Net,
this);
2510 return OnInitReturnValue::Fail;
2511 bool check = connect(m_pThreadSSH, SIGNAL(sigServer(QString, quint16)),
2512 this, SLOT(slotConnectProxyServer(QString, quint16)));
2514 check = connect(m_pThreadSSH, SIGNAL(
sigError(
int,QString)),
2515 this, SIGNAL(
sigError(
int,QString)));
2520 m_pThreadSSH->start();
2521 return OnInitReturnValue::UseOnProcess;
2524int CConnectFreeRDP::CleanSSHTunnelPipe()
2528 m_pThreadSSH->Exit();
2529 m_pThreadSSH =
nullptr;
Remote desktop connect interface.
void sigUpdateRect(const QRect &r, const QImage &image)
Notify the CFrmView update image.
virtual OnInitReturnValue OnInit() override
Specific plug-in realizes connection initialization.
virtual int OnClean() override
Clean.
static int cb_verify_x509_certificate(freerdp *instance, const BYTE *data, size_t length, const char *hostname, UINT16 port, DWORD flags)
Callback used if user interaction is required to accept a certificate.
static DWORD cb_verify_changed_certificate_ex(freerdp *instance, const char *host, UINT16 port, const char *common_name, const char *subject, const char *issuer, const char *fingerprint, const char *old_subject, const char *old_issuer, const char *old_fingerprint, DWORD flags)
Callback set in the rdp_freerdp structure, and used to make a certificate validation when a stored ce...
virtual int WakeUp() override
Wake up Connect thread(background thread)
static DWORD cb_verify_certificate_ex(freerdp *instance, const char *host, UINT16 port, const char *common_name, const char *subject, const char *issuer, const char *fingerprint, DWORD flags)
Callback set in the rdp_freerdp structure, and used to make a certificate validation when the connect...
virtual int OnProcess() override
Specific operation processing of plug-in connection.
static BOOL cb_post_connect(freerdp *instance)
Callback given to freerdp_connect() to perform post-connection operations.
static BOOL cb_pre_connect(freerdp *instance)
Callback given to freerdp_connect() to process the pre-connect operations.
void sigError(const int nError, const QString &szError=QString())
Triggered when an error is generated.
void sigInformation(const QString &szInfo)
Triggering from a background thread displays information in the main thread without blocking the back...
void sigBlockShowMessageBox(const QString &szTitle, const QString &szMessage, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton &nRet, bool &checkBox, QString checkBoxContext=QString())
Block background threads and display message dialogs in foreground threads (QMessageBox)
void sigShowMessageBox(const QString &szTitle, const QString &szMessage, const QMessageBox::Icon &icon=QMessageBox::Information)
Trigger the display of a message dialog (QMessageBox) in the main thread from a background thread wit...
void sigBlockInputDialog(const QString &szTitle, const QString &szLable, const QString &szMessage, QString &szText)
Block background threads and display input dialogs in foreground threads (QInputDialog)
void sigConnected()
Emitted when the plugin is successfully connected.
void sigDisconnect()
Notify the user to call disconnect.
void sigBlockShowWidget(const QString &className, int &nRet, void *pContext)
Blocks the background thread and displays the window in the foreground thread.
static UINT32 QtToScanCode(int key, Qt::KeyboardModifiers modifiers)
CConvertKeyCode::QtToScanCode.
[Declare CParameterFreeRDP]
Basic network parameters.
CParameterUser m_User
[Instance user]
void sigChanged()
emit when the parameter changes Usually if required, the corresponding parameter corresponds to a cha...
Data is forwarded over a local socket and SSH tunnel.