File indexing completed on 2024-04-28 04:58:53

0001 /*
0002  * SPDX-FileCopyrightText: 2023 Arjen Hiemstra <ahiemstra@heimr.nl>
0003  * SPDX-License-Identifier: GPL-2.0-or-later
0004  */
0005 
0006 #include "rdpsession.h"
0007 
0008 #include <memory>
0009 
0010 #include <QKeyEvent>
0011 #include <QMouseEvent>
0012 
0013 #include <KLocalizedString>
0014 #include <KMessageBox>
0015 #include <KMessageDialog>
0016 #include <KPasswordDialog>
0017 
0018 #include <freerdp/addin.h>
0019 #include <freerdp/channels/rdpgfx.h>
0020 #include <freerdp/client.h>
0021 #include <freerdp/client/channels.h>
0022 #include <freerdp/client/cliprdr.h>
0023 #include <freerdp/client/cmdline.h>
0024 #include <freerdp/client/rdpgfx.h>
0025 #include <freerdp/event.h>
0026 #include <freerdp/freerdp.h>
0027 #include <freerdp/gdi/gdi.h>
0028 #include <freerdp/gdi/gfx.h>
0029 #include <freerdp/input.h>
0030 #include <winpr/synch.h>
0031 #ifdef Q_OS_UNIX
0032 #include <freerdp/locale/keyboard.h>
0033 #endif
0034 
0035 #include "rdpview.h"
0036 
0037 #include "krdc_debug.h"
0038 
0039 BOOL preConnect(freerdp *rdp)
0040 {
0041     auto session = reinterpret_cast<RdpContext *>(rdp->context)->session;
0042     if (session->onPreConnect()) {
0043         return TRUE;
0044     }
0045     return FALSE;
0046 }
0047 
0048 BOOL postConnect(freerdp *rdp)
0049 {
0050     auto session = reinterpret_cast<RdpContext *>(rdp->context)->session;
0051     if (session->onPostConnect()) {
0052         return TRUE;
0053     }
0054     return FALSE;
0055 }
0056 
0057 void postDisconnect(freerdp *rdp)
0058 {
0059     auto session = reinterpret_cast<RdpContext *>(rdp->context)->session;
0060     session->onPostDisconnect();
0061 }
0062 
0063 BOOL authenticate(freerdp *rdp, char **username, char **password, char **domain)
0064 {
0065     auto session = reinterpret_cast<RdpContext *>(rdp->context)->session;
0066     if (session->onAuthenticate(username, password, domain)) {
0067         return TRUE;
0068     }
0069 
0070     return FALSE;
0071 }
0072 
0073 DWORD verifyChangedCertificate(freerdp *rdp,
0074                                const char *host,
0075                                UINT16 port,
0076                                const char *common_name,
0077                                const char *subject,
0078                                const char *issuer,
0079                                const char *new_fingerprint,
0080                                const char *old_subject,
0081                                const char *old_issuer,
0082                                const char *old_fingerprint,
0083                                DWORD flags)
0084 {
0085     auto session = reinterpret_cast<RdpContext *>(rdp->context)->session;
0086 
0087     Certificate oldCertificate;
0088     oldCertificate.host = QString::fromLocal8Bit(host);
0089     oldCertificate.port = port;
0090     oldCertificate.commonName = QString::fromLocal8Bit(common_name);
0091     oldCertificate.subject = QString::fromLocal8Bit(old_subject);
0092     oldCertificate.issuer = QString::fromLocal8Bit(old_issuer);
0093     oldCertificate.fingerprint = QString::fromLocal8Bit(old_fingerprint);
0094     oldCertificate.flags = flags;
0095 
0096     Certificate newCertificate;
0097     newCertificate.host = oldCertificate.host;
0098     newCertificate.port = oldCertificate.port;
0099     newCertificate.commonName = oldCertificate.commonName;
0100     newCertificate.subject = QString::fromLocal8Bit(subject);
0101     newCertificate.issuer = QString::fromLocal8Bit(issuer);
0102     newCertificate.fingerprint = QString::fromLocal8Bit(new_fingerprint);
0103     newCertificate.flags = flags;
0104 
0105     switch (session->onVerifyChangedCertificate(oldCertificate, newCertificate)) {
0106     case RdpSession::CertificateResult::DoNotAccept:
0107         return 0;
0108     case RdpSession::CertificateResult::AcceptTemporarily:
0109         return 2;
0110     case RdpSession::CertificateResult::AcceptPermanently:
0111         return 1;
0112     }
0113 
0114     return 0;
0115 }
0116 
0117 DWORD verifyCertificate(freerdp *rdp,
0118                         const char *host,
0119                         UINT16 port,
0120                         const char *common_name,
0121                         const char *subject,
0122                         const char *issuer,
0123                         const char *fingerprint,
0124                         DWORD flags)
0125 {
0126     auto session = reinterpret_cast<RdpContext *>(rdp->context)->session;
0127 
0128     Certificate certificate;
0129     certificate.host = QString::fromLocal8Bit(host);
0130     certificate.port = port;
0131     certificate.commonName = QString::fromLocal8Bit(common_name);
0132     certificate.subject = QString::fromLocal8Bit(subject);
0133     certificate.issuer = QString::fromLocal8Bit(issuer);
0134     certificate.fingerprint = QString::fromLocal8Bit(fingerprint);
0135     certificate.flags = flags;
0136 
0137     switch (session->onVerifyCertificate(certificate)) {
0138     case RdpSession::CertificateResult::DoNotAccept:
0139         return 0;
0140     case RdpSession::CertificateResult::AcceptTemporarily:
0141         return 2;
0142     case RdpSession::CertificateResult::AcceptPermanently:
0143         return 1;
0144     }
0145 
0146     return 0;
0147 }
0148 
0149 int logonErrorInfo(freerdp *rdp, UINT32 data, UINT32 type)
0150 {
0151     auto dataString = QString::fromLocal8Bit(freerdp_get_logon_error_info_data(data));
0152     auto typeString = QString::fromLocal8Bit(freerdp_get_logon_error_info_type(type));
0153 
0154     if (!rdp || !rdp->context)
0155         return -1;
0156 
0157     KMessageBox::error(nullptr, typeString + QStringLiteral(" ") + dataString, i18nc("@title:dialog", "Logon Error"));
0158 
0159     return 1;
0160 }
0161 
0162 BOOL endPaint(rdpContext *context)
0163 {
0164     auto session = reinterpret_cast<RdpContext *>(context)->session;
0165     if (session->onEndPaint()) {
0166         return TRUE;
0167     }
0168     return FALSE;
0169 }
0170 
0171 BOOL resizeDisplay(rdpContext *context)
0172 {
0173     auto session = reinterpret_cast<RdpContext *>(context)->session;
0174     if (session->onResizeDisplay()) {
0175         return TRUE;
0176     }
0177     return FALSE;
0178 }
0179 
0180 void channelConnected(void *context, ChannelConnectedEventArgs *e)
0181 {
0182     auto rdpC = reinterpret_cast<rdpContext *>(context);
0183     if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0) {
0184         gdi_graphics_pipeline_init(rdpC->gdi, (RdpgfxClientContext *)e->pInterface);
0185     } else if (strcmp(e->name, CLIPRDR_SVC_CHANNEL_NAME) == 0) {
0186         CliprdrClientContext *clip = (CliprdrClientContext *)e->pInterface;
0187         clip->custom = context;
0188     }
0189 }
0190 
0191 void channelDisconnected(void *context, ChannelDisconnectedEventArgs *e)
0192 {
0193     auto rdpC = reinterpret_cast<rdpContext *>(context);
0194     if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0) {
0195         gdi_graphics_pipeline_uninit(rdpC->gdi, (RdpgfxClientContext *)e->pInterface);
0196     } else if (strcmp(e->name, CLIPRDR_SVC_CHANNEL_NAME) == 0) {
0197         CliprdrClientContext *clip = (CliprdrClientContext *)e->pInterface;
0198         clip->custom = nullptr;
0199     }
0200 }
0201 
0202 QString Certificate::toString() const
0203 {
0204     return i18nc("@label", "Host: %1:%2\nCommon Name: %3\nSubject: %4\nIssuer: %5\nFingerprint: %6\n", host, port, commonName, subject, issuer, fingerprint);
0205 }
0206 
0207 RdpSession::RdpSession(RdpView *view)
0208     : QObject(nullptr)
0209     , m_view(view)
0210 {
0211 }
0212 
0213 RdpSession::~RdpSession()
0214 {
0215     stop();
0216 }
0217 
0218 RdpSession::State RdpSession::state() const
0219 {
0220     return m_state;
0221 }
0222 
0223 QString RdpSession::host() const
0224 {
0225     return m_host;
0226 }
0227 
0228 void RdpSession::setHost(const QString &newHost)
0229 {
0230     m_host = newHost;
0231 }
0232 
0233 QString RdpSession::user() const
0234 {
0235     return m_user;
0236 }
0237 
0238 void RdpSession::setUser(const QString &newUser)
0239 {
0240     m_user = newUser;
0241 }
0242 
0243 QString RdpSession::domain() const
0244 {
0245     return m_domain;
0246 }
0247 
0248 void RdpSession::setDomain(const QString &newDomain)
0249 {
0250     m_domain = newDomain;
0251 }
0252 
0253 QString RdpSession::password() const
0254 {
0255     return m_password;
0256 }
0257 
0258 void RdpSession::setPassword(const QString &newPassword)
0259 {
0260     m_password = newPassword;
0261 }
0262 
0263 int RdpSession::port() const
0264 {
0265     return m_port;
0266 }
0267 
0268 void RdpSession::setPort(int port)
0269 {
0270     m_port = port;
0271 }
0272 
0273 RdpHostPreferences *RdpSession::preferences() const
0274 {
0275     return m_preferences;
0276 }
0277 
0278 void RdpSession::setHostPreferences(RdpHostPreferences *preferences)
0279 {
0280     m_preferences = preferences;
0281 }
0282 
0283 QSize RdpSession::size() const
0284 {
0285     return m_size;
0286 }
0287 
0288 void RdpSession::setSize(QSize size)
0289 {
0290     m_size = size;
0291 }
0292 
0293 bool RdpSession::start()
0294 {
0295     setState(State::Starting);
0296 
0297     qCInfo(KRDC) << "Starting RDP session";
0298 
0299     m_freerdp = freerdp_new();
0300 
0301     m_freerdp->ContextSize = sizeof(RdpContext);
0302     m_freerdp->ContextNew = nullptr;
0303     m_freerdp->ContextFree = nullptr;
0304 
0305     m_freerdp->Authenticate = authenticate;
0306     m_freerdp->VerifyCertificateEx = verifyCertificate;
0307     m_freerdp->VerifyChangedCertificateEx = verifyChangedCertificate;
0308     m_freerdp->LogonErrorInfo = logonErrorInfo;
0309 
0310     m_freerdp->PreConnect = preConnect;
0311     m_freerdp->PostConnect = postConnect;
0312     m_freerdp->PostDisconnect = postDisconnect;
0313 
0314     freerdp_context_new(m_freerdp);
0315 
0316     m_context = reinterpret_cast<RdpContext *>(m_freerdp->context);
0317     m_context->session = this;
0318 
0319     if (freerdp_register_addin_provider(freerdp_channels_load_static_addin_entry, 0) != CHANNEL_RC_OK) {
0320         return false;
0321     }
0322 
0323     auto settings = m_freerdp->settings;
0324     settings->ServerHostname = qstrdup(m_host.toLocal8Bit().data());
0325     settings->ServerPort = m_port;
0326 
0327     settings->Username = qstrdup(m_user.toLocal8Bit().data());
0328     settings->Password = qstrdup(m_password.toLocal8Bit().data());
0329 
0330     if (m_size.width() > 0 && m_size.height() > 0) {
0331         settings->DesktopWidth = m_size.width();
0332         settings->DesktopHeight = m_size.height();
0333     }
0334 
0335     switch (m_preferences->acceleration()) {
0336     case RdpHostPreferences::Acceleration::ForceGraphicsPipeline:
0337         settings->SupportGraphicsPipeline = true;
0338         settings->GfxAVC444 = true;
0339         settings->GfxAVC444v2 = true;
0340         settings->GfxH264 = true;
0341         settings->RemoteFxCodec = false;
0342         settings->ColorDepth = 32;
0343         break;
0344     case RdpHostPreferences::Acceleration::ForceRemoteFx:
0345         settings->SupportGraphicsPipeline = false;
0346         settings->GfxAVC444 = false;
0347         settings->GfxAVC444v2 = false;
0348         settings->GfxH264 = false;
0349         settings->RemoteFxCodec = true;
0350         settings->ColorDepth = 32;
0351         break;
0352     case RdpHostPreferences::Acceleration::Disabled:
0353         settings->SupportGraphicsPipeline = false;
0354         settings->GfxAVC444 = false;
0355         settings->GfxAVC444v2 = false;
0356         settings->GfxH264 = false;
0357         settings->RemoteFxCodec = false;
0358         break;
0359     case RdpHostPreferences::Acceleration::Auto:
0360         settings->SupportGraphicsPipeline = true;
0361         settings->GfxAVC444 = true;
0362         settings->GfxAVC444v2 = true;
0363         settings->GfxH264 = true;
0364         settings->RemoteFxCodec = true;
0365         settings->ColorDepth = 32;
0366         break;
0367     }
0368 
0369     switch (m_preferences->colorDepth()) {
0370     case RdpHostPreferences::ColorDepth::Auto:
0371     case RdpHostPreferences::ColorDepth::Depth32:
0372         settings->ColorDepth = 32;
0373         break;
0374     case RdpHostPreferences::ColorDepth::Depth24:
0375         settings->ColorDepth = 24;
0376         break;
0377     case RdpHostPreferences::ColorDepth::Depth16:
0378         settings->ColorDepth = 16;
0379         break;
0380     case RdpHostPreferences::ColorDepth::Depth8:
0381         settings->ColorDepth = 8;
0382     }
0383 
0384     settings->FastPathOutput = true;
0385     settings->FastPathInput = true;
0386     settings->FrameMarkerCommandEnabled = true;
0387 
0388     settings->SupportDynamicChannels = true;
0389 
0390     switch (m_preferences->sound()) {
0391     case RdpHostPreferences::Sound::Local:
0392         settings->AudioPlayback = true;
0393         settings->AudioCapture = true;
0394         break;
0395     case RdpHostPreferences::Sound::Remote:
0396         settings->RemoteConsoleAudio = true;
0397         break;
0398     case RdpHostPreferences::Sound::Disabled:
0399         settings->AudioPlayback = false;
0400         settings->AudioCapture = false;
0401         break;
0402     }
0403 
0404     if (!m_preferences->shareMedia().isEmpty()) {
0405         char *params[2] = {strdup("drive"), m_preferences->shareMedia().toLocal8Bit().data()};
0406         freerdp_client_add_device_channel(settings, 1, params);
0407     }
0408 
0409     settings->KeyboardLayout = m_preferences->rdpKeyboardLayout();
0410 
0411     switch (m_preferences->tlsSecLevel()) {
0412     case RdpHostPreferences::TlsSecLevel::Bit80:
0413         settings->TlsSecLevel = 1;
0414         break;
0415     case RdpHostPreferences::TlsSecLevel::Bit112:
0416         settings->TlsSecLevel = 2;
0417         break;
0418     case RdpHostPreferences::TlsSecLevel::Bit128:
0419         settings->TlsSecLevel = 3;
0420         break;
0421     case RdpHostPreferences::TlsSecLevel::Bit192:
0422         settings->TlsSecLevel = 4;
0423         break;
0424     case RdpHostPreferences::TlsSecLevel::Bit256:
0425         settings->TlsSecLevel = 5;
0426         break;
0427     case RdpHostPreferences::TlsSecLevel::Any:
0428     default:
0429         settings->TlsSecLevel = 0;
0430         break;
0431     }
0432 
0433     if (!freerdp_connect(m_freerdp)) {
0434         qWarning(KRDC) << "Unable to connect";
0435         emitErrorMessage();
0436         return false;
0437     }
0438 
0439     m_thread = std::thread(std::bind(&RdpSession::run, this));
0440     pthread_setname_np(m_thread.native_handle(), "rdp_session");
0441 
0442     return true;
0443 }
0444 
0445 void RdpSession::stop()
0446 {
0447     freerdp_abort_connect(m_freerdp);
0448     if (m_thread.joinable()) {
0449         m_thread.join();
0450     }
0451 
0452     if (m_freerdp) {
0453         freerdp_context_free(m_freerdp);
0454         freerdp_free(m_freerdp);
0455 
0456         m_context = nullptr;
0457         m_freerdp = nullptr;
0458     }
0459 }
0460 
0461 const QImage *RdpSession::videoBuffer() const
0462 {
0463     return &m_videoBuffer;
0464 }
0465 
0466 bool RdpSession::sendEvent(QEvent *event, QWidget *source)
0467 {
0468     auto input = m_freerdp->context->input;
0469 
0470     switch (event->type()) {
0471     case QEvent::KeyPress:
0472     case QEvent::KeyRelease: {
0473         auto keyEvent = static_cast<QKeyEvent *>(event);
0474         auto code = freerdp_keyboard_get_rdp_scancode_from_x11_keycode(keyEvent->nativeScanCode());
0475         freerdp_input_send_keyboard_event_ex(input, keyEvent->type() == QEvent::KeyPress, code);
0476         return true;
0477     }
0478     case QEvent::MouseButtonPress:
0479     case QEvent::MouseButtonRelease:
0480     case QEvent::MouseButtonDblClick:
0481     case QEvent::MouseMove: {
0482         auto mouseEvent = static_cast<QMouseEvent *>(event);
0483         auto position = mouseEvent->localPos();
0484         auto sourceSize = QSizeF{source->size()};
0485 
0486         auto x = (position.x() / sourceSize.width()) * m_size.width();
0487         auto y = (position.y() / sourceSize.height()) * m_size.height();
0488 
0489         bool extendedEvent = false;
0490         UINT16 flags = 0;
0491 
0492         switch (mouseEvent->button()) {
0493         case Qt::LeftButton:
0494             flags |= PTR_FLAGS_BUTTON1;
0495             break;
0496         case Qt::RightButton:
0497             flags |= PTR_FLAGS_BUTTON2;
0498             break;
0499         case Qt::MiddleButton:
0500             flags |= PTR_FLAGS_BUTTON3;
0501             break;
0502         case Qt::BackButton:
0503             flags |= PTR_XFLAGS_BUTTON1;
0504             extendedEvent = true;
0505             break;
0506         case Qt::ForwardButton:
0507             flags |= PTR_XFLAGS_BUTTON2;
0508             extendedEvent = true;
0509             break;
0510         default:
0511             break;
0512         }
0513 
0514         if (mouseEvent->type() == QEvent::MouseButtonPress || mouseEvent->type() == QEvent::MouseButtonDblClick) {
0515             if (extendedEvent) {
0516                 flags |= PTR_XFLAGS_DOWN;
0517             } else {
0518                 flags |= PTR_FLAGS_DOWN;
0519             }
0520         } else if (mouseEvent->type() == QEvent::MouseMove) {
0521             flags |= PTR_FLAGS_MOVE;
0522         }
0523 
0524         if (extendedEvent) {
0525             freerdp_input_send_extended_mouse_event(input, flags, uint16_t(x), uint16_t(y));
0526         } else {
0527             freerdp_input_send_mouse_event(input, flags, uint16_t(x), uint16_t(y));
0528         }
0529 
0530         return true;
0531     }
0532     case QEvent::Wheel: {
0533         auto wheelEvent = static_cast<QWheelEvent *>(event);
0534         auto delta = wheelEvent->angleDelta();
0535 
0536         uint16_t flags = 0;
0537         uint16_t value = 0;
0538         if (delta.y() != 0) {
0539             value = std::clamp(std::abs(delta.y()), 0, 0xFF);
0540             flags |= PTR_FLAGS_WHEEL;
0541             if (wheelEvent->angleDelta().y() < 0) {
0542                 flags |= PTR_FLAGS_WHEEL_NEGATIVE;
0543                 flags = (flags & 0xFF00) | (0x100 - value);
0544             } else {
0545                 flags |= value;
0546             }
0547         } else if (wheelEvent->angleDelta().x() != 0) {
0548             value = std::clamp(std::abs(delta.x()), 0, 0xFF);
0549             flags |= PTR_FLAGS_HWHEEL;
0550             if (wheelEvent->angleDelta().x() < 0) {
0551                 flags |= PTR_FLAGS_WHEEL_NEGATIVE;
0552                 flags = (flags & 0xFF00) | (0x100 - value);
0553             } else {
0554                 flags |= value;
0555             }
0556         }
0557 
0558         auto position = wheelEvent->position();
0559         auto sourceSize = QSizeF{source->size()};
0560 
0561         auto x = (position.x() / sourceSize.width()) * m_size.width();
0562         auto y = (position.y() / sourceSize.height()) * m_size.height();
0563 
0564         freerdp_input_send_mouse_event(input, flags, uint16_t(x), uint16_t(y));
0565     }
0566     default:
0567         break;
0568     }
0569 
0570     return QObject::event(event);
0571 }
0572 
0573 void RdpSession::setState(RdpSession::State newState)
0574 {
0575     if (newState == m_state) {
0576         return;
0577     }
0578 
0579     m_state = newState;
0580     Q_EMIT stateChanged();
0581 }
0582 
0583 bool RdpSession::onPreConnect()
0584 {
0585     auto settings = m_freerdp->settings;
0586     settings->OsMajorType = OSMAJORTYPE_UNIX;
0587     settings->OsMinorType = OSMINORTYPE_UNSPECIFIED;
0588 
0589     PubSub_SubscribeChannelConnected(m_freerdp->context->pubSub, channelConnected);
0590     PubSub_SubscribeChannelDisconnected(m_freerdp->context->pubSub, channelDisconnected);
0591 
0592     if (!freerdp_client_load_addins(m_freerdp->context->channels, settings)) {
0593         return false;
0594     }
0595 
0596     return true;
0597 }
0598 
0599 bool RdpSession::onPostConnect()
0600 {
0601     setState(State::Connected);
0602 
0603     auto settings = m_freerdp->settings;
0604 
0605     m_videoBuffer = QImage(settings->DesktopWidth, settings->DesktopHeight, QImage::Format_RGBA8888);
0606 
0607     if (!gdi_init_ex(m_freerdp, PIXEL_FORMAT_RGBA32, m_videoBuffer.bytesPerLine(), m_videoBuffer.bits(), nullptr)) {
0608         qCWarning(KRDC) << "Could not initialize GDI subsystem";
0609         return false;
0610     }
0611 
0612     auto gdi = reinterpret_cast<rdpContext *>(m_context)->gdi;
0613     if (!gdi || gdi->width < 0 || gdi->height < 0) {
0614         return false;
0615     }
0616 
0617     m_size = QSize(gdi->width, gdi->height);
0618     Q_EMIT sizeChanged();
0619 
0620     m_freerdp->update->EndPaint = endPaint;
0621     m_freerdp->update->DesktopResize = resizeDisplay;
0622 
0623     freerdp_keyboard_init_ex(settings->KeyboardLayout, settings->KeyboardRemappingList);
0624 
0625     return true;
0626 }
0627 
0628 void RdpSession::onPostDisconnect()
0629 {
0630     setState(State::Closed);
0631     gdi_free(m_freerdp);
0632 }
0633 
0634 bool RdpSession::onAuthenticate(char **username, char **password, char **domain)
0635 {
0636     Q_UNUSED(domain);
0637 
0638     std::unique_ptr<KPasswordDialog> dialog;
0639     bool hasUsername = qstrlen(*username) != 0;
0640     if (hasUsername) {
0641         dialog = std::make_unique<KPasswordDialog>(nullptr, KPasswordDialog::ShowKeepPassword);
0642         dialog->setPrompt(i18nc("@label", "Access to this system requires a password."));
0643     } else {
0644         dialog = std::make_unique<KPasswordDialog>(nullptr, KPasswordDialog::ShowUsernameLine | KPasswordDialog::ShowKeepPassword);
0645         dialog->setPrompt(i18nc("@label", "Access to this system requires a username and password."));
0646     }
0647 
0648     if (!dialog->exec()) {
0649         return false;
0650     }
0651 
0652     *password = qstrdup(dialog->password().toLocal8Bit().data());
0653 
0654     if (!hasUsername) {
0655         *username = qstrdup(dialog->username().toLocal8Bit().data());
0656     }
0657 
0658     if (dialog->keepPassword()) {
0659         m_view->savePassword(dialog->password());
0660     }
0661 
0662     return true;
0663 }
0664 
0665 RdpSession::CertificateResult RdpSession::onVerifyCertificate(const Certificate &certificate)
0666 {
0667     KMessageDialog dialog{KMessageDialog::QuestionTwoActions, i18nc("@label", "The certificate for this system is unknown. Do you wish to continue?")};
0668     dialog.setCaption(i18nc("@title:dialog", "Verify Certificate"));
0669     dialog.setIcon(QIcon::fromTheme(QStringLiteral("view-certficate")));
0670 
0671     dialog.setDetails(certificate.toString());
0672 
0673     dialog.setDontAskAgainText(i18nc("@label", "Remember this certificate"));
0674 
0675     dialog.setButtons(KStandardGuiItem::cont(), KStandardGuiItem::cancel());
0676 
0677     if (!dialog.exec()) {
0678         return CertificateResult::DoNotAccept;
0679     }
0680 
0681     if (dialog.isDontAskAgainChecked()) {
0682         return CertificateResult::AcceptPermanently;
0683     } else {
0684         return CertificateResult::AcceptTemporarily;
0685     }
0686 }
0687 
0688 RdpSession::CertificateResult RdpSession::onVerifyChangedCertificate(const Certificate &oldCertificate, const Certificate &newCertificate)
0689 {
0690     KMessageDialog dialog{KMessageDialog::QuestionTwoActions, i18nc("@label", "The certificate for this system has changed. Do you wish to continue?")};
0691     dialog.setCaption(i18nc("@title:dialog", "Certificate has Changed"));
0692     dialog.setIcon(QIcon::fromTheme(QStringLiteral("view-certficate")));
0693 
0694     dialog.setDetails(i18nc("@label", "Previous certificate:\n%1\nNew Certificate:\n%2", oldCertificate.toString(), newCertificate.toString()));
0695 
0696     dialog.setDontAskAgainText(i18nc("@label", "Remember this certificate"));
0697 
0698     dialog.setButtons(KStandardGuiItem::cont(), KStandardGuiItem::cancel());
0699 
0700     if (!dialog.exec()) {
0701         return CertificateResult::DoNotAccept;
0702     }
0703 
0704     if (dialog.isDontAskAgainChecked()) {
0705         return CertificateResult::AcceptPermanently;
0706     } else {
0707         return CertificateResult::AcceptTemporarily;
0708     }
0709 }
0710 
0711 bool RdpSession::onEndPaint()
0712 {
0713     if (!m_context) {
0714         return false;
0715     }
0716 
0717     auto gdi = reinterpret_cast<rdpContext *>(m_context)->gdi;
0718     if (!gdi || !gdi->primary) {
0719         return false;
0720     }
0721 
0722     auto invalid = gdi->primary->hdc->hwnd->invalid;
0723     if (invalid->null) {
0724         return true;
0725     }
0726 
0727     auto rect = QRect{invalid->x, invalid->y, invalid->w, invalid->h};
0728     Q_EMIT rectangleUpdated(rect);
0729 
0730     return true;
0731 }
0732 
0733 bool RdpSession::onResizeDisplay()
0734 {
0735     auto gdi = reinterpret_cast<rdpContext *>(m_context)->gdi;
0736     auto settings = m_freerdp->settings;
0737 
0738     m_videoBuffer = QImage(settings->DesktopWidth, settings->DesktopHeight, QImage::Format_RGBA8888);
0739 
0740     if (!gdi_resize_ex(gdi,
0741                        settings->DesktopWidth,
0742                        settings->DesktopHeight,
0743                        m_videoBuffer.bytesPerLine(),
0744                        PIXEL_FORMAT_RGBA32,
0745                        m_videoBuffer.bits(),
0746                        nullptr)) {
0747         qCWarning(KRDC) << "Failed resizing GDI subsystem";
0748         return false;
0749     }
0750 
0751     m_size = QSize(settings->DesktopWidth, settings->DesktopHeight);
0752     Q_EMIT sizeChanged();
0753 
0754     return true;
0755 }
0756 
0757 void RdpSession::run()
0758 {
0759     auto rdpC = reinterpret_cast<rdpContext *>(m_context);
0760 
0761     auto timer = CreateWaitableTimerA(nullptr, FALSE, "rdp-session-timer");
0762     if (!timer) {
0763         return;
0764     }
0765 
0766     LARGE_INTEGER due;
0767     due.QuadPart = 0;
0768     if (!SetWaitableTimer(timer, &due, 1, nullptr, nullptr, false)) {
0769         return;
0770     }
0771 
0772     setState(State::Running);
0773 
0774     HANDLE handles[MAXIMUM_WAIT_OBJECTS] = {};
0775     while (!freerdp_shall_disconnect(m_freerdp)) {
0776         handles[0] = timer;
0777         auto count = freerdp_get_event_handles(rdpC, &handles[1], ARRAYSIZE(handles) - 1);
0778 
0779         auto status = WaitForMultipleObjects(count, handles, FALSE, INFINITE);
0780         if (status == WAIT_FAILED) {
0781             emitErrorMessage();
0782             break;
0783         }
0784 
0785         if (freerdp_check_event_handles(rdpC) != TRUE) {
0786             emitErrorMessage();
0787             break;
0788         }
0789     }
0790 
0791     freerdp_disconnect(m_freerdp);
0792 }
0793 
0794 void RdpSession::emitErrorMessage()
0795 {
0796     auto error = freerdp_get_last_error(m_freerdp->context);
0797 
0798     if (error == FREERDP_ERROR_CONNECT_CANCELLED) {
0799         return;
0800     }
0801 
0802     auto name = freerdp_get_last_error_name(error);
0803     auto description = freerdp_get_last_error_string(error);
0804 
0805     qCWarning(KRDC) << name << description;
0806 
0807     QString title;
0808     QString message;
0809 
0810     switch (error) {
0811     case FREERDP_ERROR_AUTHENTICATION_FAILED:
0812     case FREERDP_ERROR_CONNECT_LOGON_FAILURE:
0813     case FREERDP_ERROR_CONNECT_WRONG_PASSWORD:
0814         title = i18nc("@title:dialog", "Login Failure");
0815         message = i18nc("@label", "Unable to login with the provided credentials. Please double check the user and password.");
0816         break;
0817     case FREERDP_ERROR_CONNECT_ACCOUNT_LOCKED_OUT:
0818     case FREERDP_ERROR_CONNECT_ACCOUNT_EXPIRED:
0819     case FREERDP_ERROR_CONNECT_ACCOUNT_DISABLED:
0820     case FREERDP_ERROR_SERVER_INSUFFICIENT_PRIVILEGES:
0821         title = i18nc("@title:dialog", "Account Problems");
0822         message = i18nc("@label", "The provided account is not allowed to log in to this machine. Please contact your system administrator.");
0823         break;
0824     case FREERDP_ERROR_CONNECT_PASSWORD_EXPIRED:
0825     case FREERDP_ERROR_CONNECT_PASSWORD_CERTAINLY_EXPIRED:
0826     case FREERDP_ERROR_CONNECT_PASSWORD_MUST_CHANGE:
0827         title = i18nc("@title:dialog", "Password Problems");
0828         message = i18nc("@label", "Unable to login with the provided password. Please contact your system administrator to change it.");
0829         break;
0830     case FREERDP_ERROR_CONNECT_FAILED:
0831         title = i18nc("@title:dialog", "Connection Lost");
0832         message = i18nc("@label", "Lost connection to the server.");
0833         break;
0834     case FREERDP_ERROR_DNS_ERROR:
0835     case FREERDP_ERROR_DNS_NAME_NOT_FOUND:
0836         title = i18nc("@title:dialog", "Server not Found");
0837         message = i18nc("@label", "Could not find the server.");
0838         break;
0839     case FREERDP_ERROR_SERVER_DENIED_CONNECTION:
0840         title = i18nc("@title:dialog", "Connection Refused");
0841         message = i18nc("@label", "The server refused the connection request.");
0842         break;
0843     case FREERDP_ERROR_TLS_CONNECT_FAILED:
0844     case FREERDP_ERROR_CONNECT_TRANSPORT_FAILED:
0845         title = i18nc("@title:dialog", "Could not Connect");
0846         message = i18nc("@label", "Could not connect to the server.");
0847         break;
0848     case FREERDP_ERROR_LOGOFF_BY_USER:
0849     case FREERDP_ERROR_DISCONNECTED_BY_OTHER_CONNECTION:
0850     case FREERDP_ERROR_BASE:
0851         title = i18nc("@title:dialog", "Connection Closed");
0852         message = i18nc("@label", "The connection to the server was closed.");
0853         break;
0854     default:
0855         title = i18nc("@title:dialog", "Connection Failed");
0856         message = i18nc("@label", "An unknown error occurred");
0857         break;
0858     }
0859 
0860     Q_EMIT errorMessage(title, message);
0861 }