// Copyright (C) 2017 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include "certificateinfo.h" #include "sslclient.h" #include "ui_sslclient.h" #include "ui_sslerrors.h" SslClient::SslClient(QWidget *parent) : QWidget(parent) { setupUi(); setupSecureSocket(); } SslClient::~SslClient() { delete socket; delete form; } void SslClient::updateEnabledState() { const bool unconnected = socket->state() == QAbstractSocket::UnconnectedState; form->hostNameEdit->setReadOnly(!unconnected); form->hostNameEdit->setFocusPolicy(unconnected ? Qt::StrongFocus : Qt::NoFocus); form->hostNameLabel->setEnabled(unconnected); form->portBox->setEnabled(unconnected); form->portLabel->setEnabled(unconnected); form->connectButton->setEnabled(unconnected && !form->hostNameEdit->text().isEmpty()); const bool connected = socket->state() == QAbstractSocket::ConnectedState; form->sessionOutput->setEnabled(connected); form->sessionInput->setEnabled(connected); form->sessionInputLabel->setEnabled(connected); form->sendButton->setEnabled(connected); } void SslClient::secureConnect() { socket->connectToHostEncrypted(form->hostNameEdit->text(), form->portBox->value()); updateEnabledState(); } void SslClient::socketStateChanged(QAbstractSocket::SocketState state) { if (executingDialog) return; updateEnabledState(); if (state == QAbstractSocket::UnconnectedState) { form->sessionInput->clear(); form->hostNameEdit->setPalette(QPalette()); form->hostNameEdit->setFocus(); form->cipherLabel->setText(tr("")); padLock->hide(); } } void SslClient::socketEncrypted() { form->sessionOutput->clear(); form->sessionInput->setFocus(); QPalette palette; palette.setColor(QPalette::Base, QColor(255, 255, 192)); form->hostNameEdit->setPalette(palette); const QSslCipher cipher = socket->sessionCipher(); const QString cipherInfo = QString("%1, %2 (%3/%4)").arg(cipher.authenticationMethod()) .arg(cipher.name()).arg(cipher.usedBits()) .arg(cipher.supportedBits());; form->cipherLabel->setText(cipherInfo); padLock->show(); } void SslClient::socketReadyRead() { appendString(QString::fromUtf8(socket->readAll())); } void SslClient::sendData() { const QString input = form->sessionInput->text(); appendString(input + '\n'); socket->write(input.toUtf8() + "\r\n"); form->sessionInput->clear(); } void SslClient::socketError(QAbstractSocket::SocketError) { if (handlingSocketError) return; handlingSocketError = true; QMessageBox::critical(this, tr("Connection error"), socket->errorString()); handlingSocketError = false; } void SslClient::sslErrors(const QList &errors) { QDialog errorDialog(this); Ui_SslErrors ui; ui.setupUi(&errorDialog); connect(ui.certificateChainButton, &QPushButton::clicked, this, &SslClient::displayCertificateInfo); for (const auto &error : errors) ui.sslErrorList->addItem(error.errorString()); executingDialog = true; if (errorDialog.exec() == QDialog::Accepted) socket->ignoreSslErrors(); executingDialog = false; // did the socket state change? if (socket->state() != QAbstractSocket::ConnectedState) socketStateChanged(socket->state()); } void SslClient::displayCertificateInfo() { CertificateInfo info; info.setCertificateChain(socket->peerCertificateChain()); info.exec(); } void SslClient::setupUi() { if (form) return; form = new Ui_Form; form->setupUi(this); form->hostNameEdit->setSelection(0, form->hostNameEdit->text().size()); form->sessionOutput->setHtml(tr("<not connected>")); connect(form->hostNameEdit, &QLineEdit::textChanged, this, &SslClient::updateEnabledState); connect(form->connectButton, &QPushButton::clicked, this, &SslClient::secureConnect); connect(form->sendButton, &QPushButton::clicked, this, &SslClient::sendData); padLock = new QToolButton; padLock->setIcon(QIcon(":/encrypted.png")); connect(padLock, &QToolButton::clicked, this, &SslClient::displayCertificateInfo); #if QT_CONFIG(cursor) padLock->setCursor(Qt::ArrowCursor); #endif padLock->setToolTip(tr("Display encryption details.")); const int extent = form->hostNameEdit->height() - 2; padLock->resize(extent, extent); padLock->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Ignored); QHBoxLayout *layout = new QHBoxLayout(form->hostNameEdit); const int margin = form->hostNameEdit->style()->pixelMetric(QStyle::PM_DefaultFrameWidth); layout->setContentsMargins(margin, margin, margin, margin); layout->setSpacing(0); layout->addStretch(); layout->addWidget(padLock); form->hostNameEdit->setLayout(layout); padLock->hide(); } void SslClient::setupSecureSocket() { if (socket) return; socket = new QSslSocket(this); connect(socket, &QSslSocket::stateChanged, this, &SslClient::socketStateChanged); connect(socket, &QSslSocket::encrypted, this, &SslClient::socketEncrypted); connect(socket, &QSslSocket::errorOccurred, this, &SslClient::socketError); connect(socket, QOverload &>::of(&QSslSocket::sslErrors), this, &SslClient::sslErrors); connect(socket, &QSslSocket::readyRead, this, &SslClient::socketReadyRead); } void SslClient::appendString(const QString &line) { QTextCursor cursor(form->sessionOutput->textCursor()); cursor.movePosition(QTextCursor::End); cursor.insertText(line); form->sessionOutput->verticalScrollBar()->setValue(form->sessionOutput->verticalScrollBar()->maximum()); }