File indexing completed on 2024-05-12 09:01:21

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