#include "profile_dialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { int standardPortForProtocol(const QString& protocol) { if (protocol == QStringLiteral("RDP")) { return 3389; } if (protocol == QStringLiteral("VNC")) { return 5900; } return 22; // SSH default } QString normalizedProtocol(const QString& protocol) { if (protocol.compare(QStringLiteral("RDP"), Qt::CaseInsensitive) == 0) { return QStringLiteral("RDP"); } if (protocol.compare(QStringLiteral("VNC"), Qt::CaseInsensitive) == 0) { return QStringLiteral("VNC"); } return QStringLiteral("SSH"); } QString normalizedAuthMode(const QString& protocol, const QString& authMode) { if (protocol != QStringLiteral("SSH")) { return QStringLiteral("Password"); } if (authMode.compare(QStringLiteral("Private Key"), Qt::CaseInsensitive) == 0) { return QStringLiteral("Private Key"); } return QStringLiteral("Password"); } } ProfileDialog::ProfileDialog(QWidget* parent) : QDialog(parent), m_nameInput(new QLineEdit(this)), m_hostInput(new QLineEdit(this)), m_portInput(new QSpinBox(this)), m_usernameInput(new QLineEdit(this)), m_domainInput(new QLineEdit(this)), m_tagsInput(new QLineEdit(this)), m_protocolInput(new QComboBox(this)), m_authModeInput(new QComboBox(this)), m_privateKeyPathInput(new QLineEdit(this)), m_browsePrivateKeyButton(new QPushButton(QStringLiteral("Browse"), this)), m_knownHostsPolicyInput(new QComboBox(this)), m_rdpSecurityModeInput(new QComboBox(this)), m_rdpPerformanceProfileInput(new QComboBox(this)), m_protocolHint(new QLabel(this)), m_folderHint(new QLabel(this)) { resize(560, 360); auto* layout = new QVBoxLayout(this); auto* form = new QFormLayout(); m_nameInput->setPlaceholderText(QStringLiteral("Production Bastion")); m_hostInput->setPlaceholderText(QStringLiteral("example.internal")); m_portInput->setRange(1, 65535); m_portInput->setValue(22); m_usernameInput->setPlaceholderText(QStringLiteral("deploy")); m_domainInput->setPlaceholderText(QStringLiteral("CONTOSO")); m_tagsInput->setPlaceholderText(QStringLiteral("prod, linux, db")); m_protocolInput->addItems({QStringLiteral("SSH"), QStringLiteral("RDP"), QStringLiteral("VNC")}); m_authModeInput->addItems({QStringLiteral("Password"), QStringLiteral("Private Key")}); m_knownHostsPolicyInput->addItems( {QStringLiteral("Ask"), QStringLiteral("Strict"), QStringLiteral("Accept New"), QStringLiteral("Ignore")}); m_rdpSecurityModeInput->addItems( {QStringLiteral("Negotiate"), QStringLiteral("NLA"), QStringLiteral("TLS"), QStringLiteral("RDP")}); m_rdpPerformanceProfileInput->addItems({QStringLiteral("Balanced"), QStringLiteral("Best Quality"), QStringLiteral("Best Performance"), QStringLiteral("Auto Detect")}); m_privateKeyPathInput->setPlaceholderText(QStringLiteral("/home/user/.ssh/id_ed25519")); auto* privateKeyRow = new QWidget(this); auto* privateKeyLayout = new QHBoxLayout(privateKeyRow); privateKeyLayout->setContentsMargins(0, 0, 0, 0); privateKeyLayout->addWidget(m_privateKeyPathInput, 1); privateKeyLayout->addWidget(m_browsePrivateKeyButton); connect(m_browsePrivateKeyButton, &QPushButton::clicked, this, [this]() { const QString selected = QFileDialog::getOpenFileName(this, QStringLiteral("Select Private Key"), QString(), QStringLiteral("All Files (*)")); if (!selected.isEmpty()) { m_privateKeyPathInput->setText(selected); } }); connect(m_protocolInput, &QComboBox::currentTextChanged, this, [this](const QString& protocol) { m_portInput->setValue(standardPortForProtocol(protocol)); if (protocol != QStringLiteral("SSH")) { const QSignalBlocker blocker(m_authModeInput); m_authModeInput->setCurrentText(QStringLiteral("Password")); } refreshAuthFields(); }); connect(m_authModeInput, &QComboBox::currentTextChanged, this, [this](const QString&) { refreshAuthFields(); }); form->addRow(QStringLiteral("Name"), m_nameInput); form->addRow(QStringLiteral("Host"), m_hostInput); form->addRow(QStringLiteral("Port"), m_portInput); form->addRow(QStringLiteral("Username"), m_usernameInput); form->addRow(QStringLiteral("Domain"), m_domainInput); form->addRow(QStringLiteral("Tags"), m_tagsInput); form->addRow(QStringLiteral("Protocol"), m_protocolInput); form->addRow(QStringLiteral("Auth Mode"), m_authModeInput); form->addRow(QStringLiteral("Private Key"), privateKeyRow); form->addRow(QStringLiteral("Known Hosts"), m_knownHostsPolicyInput); form->addRow(QStringLiteral("RDP Security"), m_rdpSecurityModeInput); form->addRow(QStringLiteral("RDP Performance"), m_rdpPerformanceProfileInput); auto* note = new QLabel( QStringLiteral("Passwords are requested at connect time and are not stored."), this); note->setWordWrap(true); m_protocolHint->setWordWrap(true); m_folderHint->setWordWrap(true); auto* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); layout->addLayout(form); layout->addWidget(m_protocolHint); layout->addWidget(m_folderHint); layout->addWidget(note); layout->addWidget(buttons); refreshAuthFields(); } void ProfileDialog::setDialogTitle(const QString& title) { setWindowTitle(title); } void ProfileDialog::setDefaultFolderPath(const QString& folderPath) { m_defaultFolderPath = folderPath.trimmed(); refreshAuthFields(); } void ProfileDialog::setProfile(const Profile& profile) { m_nameInput->setText(profile.name); m_hostInput->setText(profile.host); m_portInput->setValue(profile.port > 0 ? profile.port : 22); m_usernameInput->setText(profile.username); m_domainInput->setText(profile.domain); m_defaultFolderPath = profile.folderPath.trimmed(); m_tagsInput->setText(profile.tags); m_privateKeyPathInput->setText(profile.privateKeyPath); const int protocolIndex = m_protocolInput->findText(profile.protocol); { // Keep stored custom port when loading an existing profile. const QSignalBlocker blocker(m_protocolInput); m_protocolInput->setCurrentIndex(protocolIndex >= 0 ? protocolIndex : 0); } const int authModeIndex = m_authModeInput->findText(profile.authMode); m_authModeInput->setCurrentIndex(authModeIndex >= 0 ? authModeIndex : 0); const int knownHostsIndex = m_knownHostsPolicyInput->findText(profile.knownHostsPolicy); m_knownHostsPolicyInput->setCurrentIndex(knownHostsIndex >= 0 ? knownHostsIndex : 0); const int securityModeIndex = m_rdpSecurityModeInput->findText(profile.rdpSecurityMode); m_rdpSecurityModeInput->setCurrentIndex(securityModeIndex >= 0 ? securityModeIndex : 0); const int performanceProfileIndex = m_rdpPerformanceProfileInput->findText(profile.rdpPerformanceProfile); m_rdpPerformanceProfileInput->setCurrentIndex(performanceProfileIndex >= 0 ? performanceProfileIndex : 0); refreshAuthFields(); } Profile ProfileDialog::profile() const { Profile profile; const QString protocol = normalizedProtocol(m_protocolInput->currentText()); const QString authMode = normalizedAuthMode(protocol, m_authModeInput->currentText()); profile.id = -1; profile.name = m_nameInput->text().trimmed(); profile.host = m_hostInput->text().trimmed(); profile.port = m_portInput->value(); profile.username = m_usernameInput->text().trimmed(); profile.domain = protocol == QStringLiteral("RDP") ? m_domainInput->text().trimmed() : QString(); profile.folderPath = m_defaultFolderPath.trimmed(); profile.tags = m_tagsInput->text().trimmed(); profile.protocol = protocol; profile.authMode = authMode; profile.privateKeyPath = (protocol == QStringLiteral("SSH") && authMode == QStringLiteral("Private Key")) ? m_privateKeyPathInput->text().trimmed() : QString(); profile.knownHostsPolicy = protocol == QStringLiteral("SSH") ? m_knownHostsPolicyInput->currentText() : QStringLiteral("Ask"); profile.rdpSecurityMode = protocol == QStringLiteral("RDP") ? m_rdpSecurityModeInput->currentText() : QStringLiteral("Negotiate"); profile.rdpPerformanceProfile = protocol == QStringLiteral("RDP") ? m_rdpPerformanceProfileInput->currentText() : QStringLiteral("Balanced"); return profile; } void ProfileDialog::accept() { if (m_nameInput->text().trimmed().isEmpty()) { QMessageBox::warning(this, QStringLiteral("Validation Error"), QStringLiteral("Profile name is required.")); return; } if (m_hostInput->text().trimmed().isEmpty()) { QMessageBox::warning(this, QStringLiteral("Validation Error"), QStringLiteral("Host is required.")); return; } const QString protocol = m_protocolInput->currentText(); if ((protocol == QStringLiteral("SSH") || protocol == QStringLiteral("RDP")) && m_usernameInput->text().trimmed().isEmpty()) { QMessageBox::warning(this, QStringLiteral("Validation Error"), QStringLiteral("Username is required for %1 profiles.").arg(protocol)); return; } if (protocol == QStringLiteral("SSH") && m_authModeInput->currentText() == QStringLiteral("Private Key")) { const QString privateKeyPath = m_privateKeyPathInput->text().trimmed(); if (privateKeyPath.isEmpty()) { QMessageBox::warning(this, QStringLiteral("Validation Error"), QStringLiteral("Private key path is required for SSH private key authentication.")); return; } if (!QFileInfo::exists(privateKeyPath)) { QMessageBox::warning(this, QStringLiteral("Validation Error"), QStringLiteral("Private key file does not exist: %1").arg(privateKeyPath)); return; } } QDialog::accept(); } void ProfileDialog::refreshAuthFields() { const QString protocol = normalizedProtocol(m_protocolInput->currentText()); const bool isSsh = protocol == QStringLiteral("SSH"); const bool isRdp = protocol == QStringLiteral("RDP"); const bool isVnc = protocol == QStringLiteral("VNC"); const QString normalizedMode = normalizedAuthMode(protocol, m_authModeInput->currentText()); if (normalizedMode != m_authModeInput->currentText()) { const QSignalBlocker blocker(m_authModeInput); m_authModeInput->setCurrentText(normalizedMode); } const bool isPrivateKey = normalizedMode == QStringLiteral("Private Key"); m_authModeInput->setEnabled(isSsh); m_privateKeyPathInput->setEnabled(isSsh && isPrivateKey); m_browsePrivateKeyButton->setEnabled(isSsh && isPrivateKey); m_knownHostsPolicyInput->setEnabled(isSsh); m_domainInput->setEnabled(isRdp); m_rdpSecurityModeInput->setEnabled(isRdp); m_rdpPerformanceProfileInput->setEnabled(isRdp); if (isSsh) { m_usernameInput->setPlaceholderText(QStringLiteral("deploy")); m_protocolHint->setText( QStringLiteral("SSH: username is required. Choose Password or Private Key auth.")); } else if (isRdp) { m_usernameInput->setPlaceholderText(QStringLiteral("Administrator")); m_protocolHint->setText( QStringLiteral("RDP: username and password are required. Domain is optional.")); } else if (isVnc) { m_usernameInput->setPlaceholderText(QStringLiteral("optional")); m_protocolHint->setText( QStringLiteral("VNC: host and port are required. Username/domain are optional and ignored by most servers.")); } m_folderHint->setText(m_defaultFolderPath.isEmpty() ? QStringLiteral("Target folder: root") : QStringLiteral("Target folder: %1").arg(m_defaultFolderPath)); }