Home

An SSL client example

This example demonstrates how to use QtSSLSocket to create a simple secure SSL client.


sslclient.h:

    /****************************************************************************
    **
    ** Copyright (C) 2003-2006 Trolltech ASA. All rights reserved.
    **
    ** This file is part of a Qt Solutions component.
    **
    ** Licensees holding valid Qt Solutions licenses may use this file in
    ** accordance with the Qt Solutions License Agreement provided with the
    ** Software.
    **
    ** See http://www.trolltech.com/products/qt/addon/solutions/
    ** or email [email protected] for information about Qt Solutions
    ** License Agreements.
    **
    ** Contact [email protected] if any conditions of this licensing are
    ** not clear to you.
    **
    ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
    ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
    **
    ****************************************************************************/
    #ifndef SSLCLIENT_H
    #define SSLCLIENT_H
    #include <qwidget.h>
    #include <qtsslsocket.h>

    class QPushButton;
    class QTextEdit;
    class QLabel;
    class QLineEdit;
    class QtSslSocket;
    class QSocket;

    class SSLClient : public QWidget
    {
        Q_OBJECT

    public:
        SSLClient(QWidget *parent = 0);

        QSize sizeHint() const;

    private slots:
        void doConnect();
        void readData();
        void sendData();
        void connectionClosed();
        void error(QAbstractSocket::SocketError err);
        void connectToHost(const QString &host, int port);
        void connectedToHost();
        void checkCertError(QtSslSocket::VerifyResult, bool, const QString &str);

    private:
        QString host;
        int port;
        QPushButton *quitButton;
        QTextEdit *terminal;
        QLabel *statusLabel;
        QLineEdit *userInput;
        //QtSslSocket *sslsocket;
        QtSslSocket *socket;
    };

    #endif

sslclient.cpp:

    /****************************************************************************
    **
    ** Copyright (C) 2003-2006 Trolltech ASA. All rights reserved.
    **
    ** This file is part of a Qt Solutions component.
    **
    ** Licensees holding valid Qt Solutions licenses may use this file in
    ** accordance with the Qt Solutions License Agreement provided with the
    ** Software.
    **
    ** See http://www.trolltech.com/products/qt/addon/solutions/
    ** or email [email protected] for information about Qt Solutions
    ** License Agreements.
    **
    ** Contact [email protected] if any conditions of this licensing are
    ** not clear to you.
    **
    ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
    ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
    **
    ****************************************************************************/
    #include <qtsslsocket.h>

    #include <qdialog.h>
    #include <qlabel.h>
    #include <qlayout.h>
    #include <qlineedit.h>
    #include <qmap.h>
    #include <qmessagebox.h>
    #include <qpushbutton.h>
    #include <qregexp.h>
    #include <qtabwidget.h>
    #include <qtextedit.h>
    #include <qtimer.h>
    #include <qtextcursor.h>
    #include <qscrollbar.h>

    #include "sslclient.h"

    /*
        A simple SSL client example.

        This example demostrates the use of QtSslSocket in a client
        application. The application connects to an SSL service on any
        given host and port. If the connection succeeds, the user is given
        a line edit for entering commands, and a text edit which displays
        the response from the server.

        Try this example by connection to your IMAP4 or POP3 server.
        Chances are that these services are running SSL. The SSL service
        IMAP4 runs on port 993, and POP3 is on port 995.

        When communicating with an IMAP4 server, the command "X LOGOUT"
        will close the connection. With POP3, use the command "QUIT".
    */

    /*
        This class provides a QDialog popup that is shown by SSLClient. It
        has line edits for hostname and port number.
    */
    class ConnectionDialog : public QDialog
    {
        Q_OBJECT
    public:
        ConnectionDialog(QWidget *parent = 0);

        QString host() const;
        int port() const;

    private slots:
        void abortDialog();
        void checkInputAndConnect();

    private:
        QLineEdit *hl;
        QLineEdit *pl;
        QPushButton *conn;
        QPushButton *abrt;
    };

    /*!
        Constructs a ConnectionDialog, which is the initial dialog
        that pops up when the user starts this program.

        Its \a parent and \a name arguments are passed to QDialog's
        constructor.
    */
    ConnectionDialog::ConnectionDialog(QWidget *parent)
        : QDialog(parent)
    {
        setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Maximum);

        setWindowTitle(tr("Connect to host"));
        QGridLayout *layout = new QGridLayout(this);

        // Create two labels
        layout->addWidget(new QLabel(tr("Hostname"), this), 0, 1);
        layout->addWidget(new QLabel(tr("Port"), this), 1, 1);

        // Create the hostname and port line edits. Find an appropriate
        // input mask for the port input.
        hl = new QLineEdit(this);
        pl = new QLineEdit(this);
        pl->setInputMask("99999");

        layout->addWidget(hl, 0, 2);
        layout->addWidget(pl, 1, 2);

        // Add two buttons: "Abort" and "Connect"
        abrt = new QPushButton(tr("&Abort"), this);
        conn = new QPushButton(tr("&Connect"), this);
        conn->setDefault(true);

        layout->addWidget(abrt, 2, 1);
        layout->addWidget(conn, 2, 2);

        connect(abrt, SIGNAL(clicked()), SLOT(abortDialog()));
        connect(conn, SIGNAL(clicked()), SLOT(checkInputAndConnect()));
    }

    /*!
        Checks that the hostname and port line edits are non-empty, then
        closes this dialog. Pops up a message box if the line edits are
        empty.
    */
    void ConnectionDialog::checkInputAndConnect()
    {
        if (hl->text().isEmpty()) {
            QMessageBox::information(this,
                                     tr("Hostname is empty"),
                                     tr("Please enter the name of the host you wish to connect to."),
                                     QMessageBox::Ok);

        } else if (pl->text().isEmpty()) {
            QMessageBox::information(this,
                                     tr("Port is empty"),
                                     tr("Please enter the port you wish to connect to."),
                                     QMessageBox::Ok);

        } else {
            // If all is ok, close the dialog. The client will use the
            // host and port values and start connecting.
            close();
        }
    }

    /*!
        Returns the value of the "Hostname" line edit.
    */
    QString ConnectionDialog::host() const
    {
        return hl->text();
    }

    /*!
        Returns the value of the "Port" line edit, as an integer.
    */
    int ConnectionDialog::port() const
    {
        return pl->text().toInt();
    }

    /*!
        Clears both line edits and closes the dialog.
    */
    void ConnectionDialog::abortDialog()
    {
        hl->clear();
        pl->clear();
        close();
    }

    /*!
        Constructs an SSLClient.
    */
    SSLClient::SSLClient(QWidget *parent)
        : QWidget(parent)
    {
        setWindowTitle(tr("A simple SSL client"));

        // Create an SSL socket. Do not assign a socket, but rather have
        // it created automatically on demand.
        QtSslSocket *sslsocket = new QtSslSocket(QtSslSocket::Client, this);

        // Notice the platform dependency here; the location of the CA
        // certificate bundle is specific to the OS.
        sslsocket->setPathToCACertDir("/etc/ssl/certs");

        // Build the GUI.
        quitButton = new QPushButton(tr("&Quit"), this);
        userInput = new QLineEdit(this);
        statusLabel = new QLabel(this);
        terminal = new QTextEdit(this);

        terminal->setReadOnly(true);
        quitButton->setDefault(false);
        quitButton->setAutoDefault(false);
        userInput->setReadOnly(true);
        userInput->setFocus();

        QVBoxLayout *vl = new QVBoxLayout(this);
        vl->addWidget(terminal);
        vl->addWidget(userInput);
        vl->addWidget(statusLabel);
        vl->addWidget(quitButton);

        // Connect signals with slots.
        connect(userInput, SIGNAL(returnPressed()), SLOT(sendData()));
        connect(quitButton, SIGNAL(clicked()), SLOT(close()));

        // QtSslSocket's own signals are connected.
        connect(sslsocket, SIGNAL(connected()), SLOT(connectedToHost()));
        connect(sslsocket, SIGNAL(readyRead()), SLOT(readData()));
        connect(sslsocket, SIGNAL(disconnected()), SLOT(connectionClosed()));
        connect(sslsocket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(error(QAbstractSocket::SocketError)));
        connect(sslsocket, SIGNAL(connectionVerificationDone(QtSslSocket::VerifyResult, bool, const QString &)),
                SLOT(checkCertError(QtSslSocket::VerifyResult, bool, const QString &)));

        QTimer::singleShot(0, this, SLOT(doConnect()));

        socket = sslsocket;
    }

    /*!
        Initiates connection to the host \a host on port \a port.
    */
    void SSLClient::connectToHost(const QString &host, int port)
    {
        this->host = host;
        statusLabel->setText(QString(tr("Connecting to %1:%2")).arg(host).arg(port));

        // Have the SSL socket initiate a connection to host:port. A
        // socket will be created implicitly. The SSL socket emits
        // connected() when the connection is established.
        socket->connectToHost(host, port);
    }

    /*! \reimp
    */
    QSize SSLClient::sizeHint() const
    {
        return QSize(400, 300);
    }

    /*!
        Pops up a dialog that lets the user type in a hostname and port.
        When the dialog is closed, a connection is initiated if the port
        and hostname fields are non-empty.
     */
    void SSLClient::doConnect()
    {
        ConnectionDialog dialog(this);
        dialog.exec();

        if (dialog.host().isEmpty() || dialog.port() == -1) {
            close();
        } else {
            connectToHost(dialog.host(), dialog.port());
        }
    }

    /*!
        This slot is called when a connection has been established. The
        line edit is enabled, and the status label is updated with a
        confirming message.
    */
    void SSLClient::connectedToHost()
    {
        statusLabel->setText(tr("Connected."));

        // Allow the user to communicate over the secure channel.
        userInput->setReadOnly(false);
    }

    /*!
        Sends the data from the line edit to the remote host.
    */
    void SSLClient::sendData()
    {
        // Get the text that the user wants to submit, and add a
        // linebreak. Then write the data to the SSL socket. The socket
        // will encrypt the data and write it to the internal socket.
        QString s = userInput->text().trimmed() + "\r\n";
        socket->write(s.toLatin1().constData(), s.length());

        // Append the user's text to the textedit display.
        QTextCursor cursor(terminal->textCursor());
        cursor.movePosition(QTextCursor::End);
        cursor.insertText(userInput->text());
        terminal->verticalScrollBar()->setValue(terminal->verticalScrollBar()->maximum());
        // Get ready for more user input.
        userInput->clear();

        // Update the status label.
        QString labelText(tr("Data sent."));
        statusLabel->setText(labelText);
    }

    /*!
        Reads data from the socket and appends it to the text edit.
    */
    void SSLClient::readData()
    {
        // Read all the data from the SSL socket. This is converted to a
        // QString, assuming that the server the user is communicating
        // with uses a text-based protocol.
        QString s(socket->readAll());
        s.replace('\r', "");

        // Append the server's text to the textedit display.
        terminal->append(s);
        terminal->verticalScrollBar()->setValue(terminal->verticalScrollBar()->maximum());
        // Update the status label.
        QString labelText(tr("Response received."));
        statusLabel->setText(labelText);
    }

    /*!
        This slot is called when the connection has been closed.
     */
    void SSLClient::connectionClosed()
    {
        // The connection has been closed. Update the status label and
        // disable user input. Set focus on the quit button, so pressing
        // enter exist the application.
        statusLabel->setText(tr("Connection closed."));
        userInput->setEnabled(false);
        terminal->setEnabled(false);
        quitButton->setFocus();
    }

    /*!
        Displays an error on the status label
    */
    void SSLClient::error(QAbstractSocket::SocketError err)
    {
        // Allow retries when the connection failed.
        if (err == QAbstractSocket::ConnectionRefusedError ||
                        err == QAbstractSocket::HostNotFoundError) {
            QTimer::singleShot(0, this, SLOT(doConnect()));
            return;
        }

        // Any errors reported by QtSslSocket have a human readable
        // representation, available through errorString():
        statusLabel->setText(tr("Error: ") + socket->errorString());
    }

    /*!
        This slot is called if there was an error checking a certificate
        during the SSL handshake phase. It pops up a warning box and asks
        the user to confirm whether or not to continue connecting.
    */
    void SSLClient::checkCertError(QtSslSocket::VerifyResult result,
                                   bool hostNameWrong, const QString &str)
    {
        if (result == QtSslSocket::VerifyOk && !hostNameWrong)
            return;

        QtSslSocket *sslsocket = socket;
        // Display some of the contents of the peer's SSL certificate.
        QStringList certStr = sslsocket->peerCertificate().split('/', QString::SkipEmptyParts);
        QMap<QString, QString> cert;
        QStringListIterator it(certStr);
        while (it.hasNext()) {
            QStringList pair = it.next().split('=', QString::SkipEmptyParts);
            cert.insert(pair[0], pair.size() > 1 ? pair[1] : QString());
        }

        QString sInfo = tr("<table border=1>"
                           "<tr><td>Organization</td><td>%1</td></tr>"
                           "<tr><td>Unit</td><td>%2</td></tr>"
                           "<tr><td>Common Name</td><td>%3</td></tr>"
                           "<tr><td>Email</td><td>%4</td></tr>"
                           "<tr><td>Country</td><td>%5</td></tr>"
                           "<tr><td>State</td><td>%6</td></tr>"
                           "<tr><td>Location</td><td>%7</td></tr>"
                           "<tr><td>Not valid before</td><td>%8</td></tr>"
                           "<tr><td>Not valid after</td><td>%9</td></tr>"
                           "</table>")
            .arg(cert["O"]).arg(cert["OU"]).arg(cert["CN"]).arg(cert["emailAddress"])
            .arg(cert["C"]).arg(cert["ST"]).arg(cert["L"]).arg(cert["notValidBefore"])
            .arg(cert["notValidAfter"]);

        QString warning = "<p>"
                          + tr("Could not confirm the identity of the host \"")
                          + host
                          + "\": " + str + "</p>" + sInfo
                          + tr("<p><center>Continue connecting to this server?</center></p>");

        // If the user does not wish to connect to the server, close the
        // socket.
        if (QMessageBox::warning(this, tr("Unknown server"), warning, "Abort", "Continue") == 0)
            socket->disconnectFromHost();
    }

    #include "sslclient.moc"

main.cpp:

    /****************************************************************************
    **
    ** Copyright (C) 2003-2006 Trolltech ASA. All rights reserved.
    **
    ** This file is part of a Qt Solutions component.
    **
    ** Licensees holding valid Qt Solutions licenses may use this file in
    ** accordance with the Qt Solutions License Agreement provided with the
    ** Software.
    **
    ** See http://www.trolltech.com/products/qt/addon/solutions/
    ** or email [email protected] for information about Qt Solutions
    ** License Agreements.
    **
    ** Contact [email protected] if any conditions of this licensing are
    ** not clear to you.
    **
    ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
    ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
    **
    ****************************************************************************/
    #include <qapplication.h>
    #include "sslclient.h"

    int main(int argc, char *argv[])
    {
        QApplication app(argc, argv);

        SSLClient client;

        client.show();
        return app.exec();
    }

client.pro:

    TEMPLATE = app
    INCLUDEPATH += .

    include(../../src/qtsslsocket.pri)

    HEADERS += sslclient.h
    SOURCES += main.cpp sslclient.cpp


Copyright © 2008 Nokia Corporation and/or its subsidiary(-ies) Trademarks
Qt Solutions