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 }