1/****************************************************************************
2**
3** Copyright (C) 2017 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the examples of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:BSD$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** BSD License Usage
18** Alternatively, you may use this file under the terms of the BSD license
19** as follows:
20**
21** "Redistribution and use in source and binary forms, with or without
22** modification, are permitted provided that the following conditions are
23** met:
24** * Redistributions of source code must retain the above copyright
25** notice, this list of conditions and the following disclaimer.
26** * Redistributions in binary form must reproduce the above copyright
27** notice, this list of conditions and the following disclaimer in
28** the documentation and/or other materials provided with the
29** distribution.
30** * Neither the name of The Qt Company Ltd nor the names of its
31** contributors may be used to endorse or promote products derived
32** from this software without specific prior written permission.
33**
34**
35** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46**
47** $QT_END_LICENSE$
48**
49****************************************************************************/
50
51#include "certificateinfo.h"
52#include "sslclient.h"
53
54#include "ui_sslclient.h"
55#include "ui_sslerrors.h"
56
57SslClient::SslClient(QWidget *parent)
58 : QWidget(parent)
59{
60 setupUi();
61 setupSecureSocket();
62}
63
64SslClient::~SslClient()
65{
66 delete socket;
67 delete form;
68}
69
70void SslClient::updateEnabledState()
71{
72 const bool unconnected = socket->state() == QAbstractSocket::UnconnectedState;
73 form->hostNameEdit->setReadOnly(!unconnected);
74 form->hostNameEdit->setFocusPolicy(unconnected ? Qt::StrongFocus : Qt::NoFocus);
75 form->hostNameLabel->setEnabled(unconnected);
76 form->portBox->setEnabled(unconnected);
77 form->portLabel->setEnabled(unconnected);
78 form->connectButton->setEnabled(unconnected && !form->hostNameEdit->text().isEmpty());
79
80 const bool connected = socket->state() == QAbstractSocket::ConnectedState;
81 form->sessionOutput->setEnabled(connected);
82 form->sessionInput->setEnabled(connected);
83 form->sessionInputLabel->setEnabled(connected);
84 form->sendButton->setEnabled(connected);
85}
86
87void SslClient::secureConnect()
88{
89 socket->connectToHostEncrypted(hostName: form->hostNameEdit->text(), port: form->portBox->value());
90 updateEnabledState();
91}
92
93void SslClient::socketStateChanged(QAbstractSocket::SocketState state)
94{
95 if (executingDialog)
96 return;
97
98 updateEnabledState();
99
100 if (state == QAbstractSocket::UnconnectedState) {
101 form->sessionInput->clear();
102 form->hostNameEdit->setPalette(QPalette());
103 form->hostNameEdit->setFocus();
104 form->cipherLabel->setText(tr(s: "<none>"));
105 padLock->hide();
106 }
107}
108
109void SslClient::socketEncrypted()
110{
111 form->sessionOutput->clear();
112 form->sessionInput->setFocus();
113
114 QPalette palette;
115 palette.setColor(acr: QPalette::Base, acolor: QColor(255, 255, 192));
116 form->hostNameEdit->setPalette(palette);
117
118 const QSslCipher cipher = socket->sessionCipher();
119 const QString cipherInfo = QString("%1, %2 (%3/%4)").arg(a: cipher.authenticationMethod())
120 .arg(a: cipher.name()).arg(a: cipher.usedBits())
121 .arg(a: cipher.supportedBits());;
122 form->cipherLabel->setText(cipherInfo);
123 padLock->show();
124}
125
126void SslClient::socketReadyRead()
127{
128 appendString(line: QString::fromUtf8(str: socket->readAll()));
129}
130
131void SslClient::sendData()
132{
133 const QString input = form->sessionInput->text();
134 appendString(line: input + '\n');
135 socket->write(data: input.toUtf8() + "\r\n");
136 form->sessionInput->clear();
137}
138
139void SslClient::socketError(QAbstractSocket::SocketError)
140{
141 if (handlingSocketError)
142 return;
143
144 handlingSocketError = true;
145 QMessageBox::critical(parent: this, title: tr(s: "Connection error"), text: socket->errorString());
146 handlingSocketError = false;
147}
148
149void SslClient::sslErrors(const QList<QSslError> &errors)
150{
151 QDialog errorDialog(this);
152 Ui_SslErrors ui;
153 ui.setupUi(&errorDialog);
154 connect(sender: ui.certificateChainButton, signal: &QPushButton::clicked,
155 receiver: this, slot: &SslClient::displayCertificateInfo);
156
157 for (const auto &error : errors)
158 ui.sslErrorList->addItem(label: error.errorString());
159
160 executingDialog = true;
161 if (errorDialog.exec() == QDialog::Accepted)
162 socket->ignoreSslErrors();
163 executingDialog = false;
164
165 // did the socket state change?
166 if (socket->state() != QAbstractSocket::ConnectedState)
167 socketStateChanged(state: socket->state());
168}
169
170void SslClient::displayCertificateInfo()
171{
172 CertificateInfo info;
173 info.setCertificateChain(socket->peerCertificateChain());
174 info.exec();
175}
176
177void SslClient::setupUi()
178{
179 if (form)
180 return;
181
182 form = new Ui_Form;
183 form->setupUi(this);
184 form->hostNameEdit->setSelection(0, form->hostNameEdit->text().size());
185 form->sessionOutput->setHtml(tr(s: "&lt;not connected&gt;"));
186
187 connect(sender: form->hostNameEdit, signal: &QLineEdit::textChanged,
188 receiver: this, slot: &SslClient::updateEnabledState);
189 connect(sender: form->connectButton, signal: &QPushButton::clicked,
190 receiver: this, slot: &SslClient::secureConnect);
191 connect(sender: form->sendButton, signal: &QPushButton::clicked,
192 receiver: this, slot: &SslClient::sendData);
193
194 padLock = new QToolButton;
195 padLock->setIcon(QIcon(":/encrypted.png"));
196 connect(sender: padLock, signal: &QToolButton::clicked,
197 receiver: this, slot: &SslClient::displayCertificateInfo);
198
199#if QT_CONFIG(cursor)
200 padLock->setCursor(Qt::ArrowCursor);
201#endif
202 padLock->setToolTip(tr(s: "Display encryption details."));
203
204 const int extent = form->hostNameEdit->height() - 2;
205 padLock->resize(w: extent, h: extent);
206 padLock->setSizePolicy(hor: QSizePolicy::Fixed, ver: QSizePolicy::Ignored);
207
208 QHBoxLayout *layout = new QHBoxLayout(form->hostNameEdit);
209 const int margin = form->hostNameEdit->style()->pixelMetric(metric: QStyle::PM_DefaultFrameWidth);
210 layout->setContentsMargins(left: margin, top: margin, right: margin, bottom: margin);
211 layout->setSpacing(0);
212 layout->addStretch();
213 layout->addWidget(padLock);
214
215 form->hostNameEdit->setLayout(layout);
216 padLock->hide();
217}
218
219void SslClient::setupSecureSocket()
220{
221 if (socket)
222 return;
223
224 socket = new QSslSocket(this);
225
226 connect(sender: socket, signal: &QSslSocket::stateChanged,
227 receiver: this, slot: &SslClient::socketStateChanged);
228 connect(sender: socket, signal: &QSslSocket::encrypted,
229 receiver: this, slot: &SslClient::socketEncrypted);
230 connect(sender: socket, signal: &QSslSocket::errorOccurred,
231 receiver: this, slot: &SslClient::socketError);
232 connect(sender: socket, signal: QOverload<const QList<QSslError> &>::of(ptr: &QSslSocket::sslErrors),
233 receiver: this, slot: &SslClient::sslErrors);
234 connect(sender: socket, signal: &QSslSocket::readyRead,
235 receiver: this, slot: &SslClient::socketReadyRead);
236
237}
238
239void SslClient::appendString(const QString &line)
240{
241 QTextCursor cursor(form->sessionOutput->textCursor());
242 cursor.movePosition(op: QTextCursor::End);
243 cursor.insertText(text: line);
244 form->sessionOutput->verticalScrollBar()->setValue(form->sessionOutput->verticalScrollBar()->maximum());
245}
246

source code of qtbase/examples/network/securesocketclient/sslclient.cpp