File indexing completed on 2024-04-28 15:26:35

0001 /*
0002     This file is part of the KDE project
0003     SPDX-FileCopyrightText: 2000 Alex Zepeda <zipzippy@sonic.net>
0004     SPDX-FileCopyrightText: 2001-2003 George Staikos <staikos@kde.org>
0005     SPDX-FileCopyrightText: 2001 Dawit Alemayehu <adawit@kde.org>
0006     SPDX-FileCopyrightText: 2007, 2008 Andreas Hartmetz <ahartmetz@gmail.com>
0007     SPDX-FileCopyrightText: 2008 Roland Harnau <tau@gmx.eu>
0008     SPDX-FileCopyrightText: 2010 Richard Moore <rich@kde.org>
0009     SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org>
0010 
0011     SPDX-License-Identifier: LGPL-2.0-or-later
0012 */
0013 
0014 #include "tcpworkerbase.h"
0015 #include "kiocoredebug.h"
0016 
0017 #include <KConfigGroup>
0018 #include <KLocalizedString>
0019 #include <ksslcertificatemanager.h>
0020 #include <ksslsettings.h>
0021 
0022 #include <QSslCipher>
0023 #include <QSslSocket>
0024 
0025 #include <QDBusConnection>
0026 
0027 using namespace KIO;
0028 // using namespace KNetwork;
0029 
0030 namespace KIO
0031 {
0032 Q_DECLARE_OPERATORS_FOR_FLAGS(TCPWorkerBase::SslResult)
0033 }
0034 
0035 // TODO Proxy support whichever way works; KPAC reportedly does *not* work.
0036 // NOTE kded_proxyscout may or may not be interesting
0037 
0038 // TODO resurrect SSL session recycling; this means save the session on disconnect and look
0039 // for a reusable session on connect. Consider how HTTP persistent connections interact with that.
0040 
0041 // TODO in case we support SSL-lessness we need static KTcpSocket::sslAvailable() and check it
0042 // in most places we ATM check for d->isSSL.
0043 
0044 // TODO check if d->isBlocking is honored everywhere it makes sense
0045 
0046 // TODO fold KSSLSetting and KSSLCertificateHome into KSslSettings and use that everywhere.
0047 
0048 // TODO recognize partially encrypted websites as "somewhat safe"
0049 
0050 /* List of dialogs/messageboxes we need to use (current code location in parentheses)
0051    - Can the "dontAskAgainName" thing be improved?
0052 
0053    - "SSLCertDialog" [select client cert] (SlaveInterface)
0054    - Enter password for client certificate (inline)
0055    - Password for client cert was wrong. Please reenter. (inline)
0056    - Setting client cert failed. [doesn't give reason] (inline)
0057    - "SSLInfoDialog" [mostly server cert info] (SlaveInterface)
0058    - You are about to enter secure mode. Security information/Display SSL information/Connect (inline)
0059    - You are about to leave secure mode. Security information/Continue loading/Abort (inline)
0060    - Hostname mismatch: Continue/Details/Cancel (inline)
0061    - IP address mismatch: Continue/Details/Cancel (inline)
0062    - Certificate failed authenticity check: Continue/Details/Cancel (inline)
0063    - Would you like to accept this certificate forever: Yes/No/Current sessions only (inline)
0064  */
0065 
0066 /** @internal */
0067 class Q_DECL_HIDDEN TCPWorkerBase::TCPWorkerBasePrivate
0068 {
0069 public:
0070     explicit TCPWorkerBasePrivate(TCPWorkerBase *qq)
0071         : q(qq)
0072     {
0073     }
0074 
0075     void setSslMetaData()
0076     {
0077         sslMetaData.insert(QStringLiteral("ssl_in_use"), QStringLiteral("TRUE"));
0078         QSslCipher cipher = socket.sessionCipher();
0079         sslMetaData.insert(QStringLiteral("ssl_protocol_version"), cipher.protocolString());
0080         sslMetaData.insert(QStringLiteral("ssl_cipher"), cipher.name());
0081         sslMetaData.insert(QStringLiteral("ssl_cipher_used_bits"), QString::number(cipher.usedBits()));
0082         sslMetaData.insert(QStringLiteral("ssl_cipher_bits"), QString::number(cipher.supportedBits()));
0083         sslMetaData.insert(QStringLiteral("ssl_peer_ip"), ip);
0084 
0085         const QList<QSslCertificate> peerCertificateChain = socket.peerCertificateChain();
0086         // try to fill in the blanks, i.e. missing certificates, and just assume that
0087         // those belong to the peer (==website or similar) certificate.
0088         for (int i = 0; i < sslErrors.count(); i++) {
0089             if (sslErrors[i].certificate().isNull()) {
0090                 sslErrors[i] = QSslError(sslErrors[i].error(), peerCertificateChain[0]);
0091             }
0092         }
0093 
0094         QString errorStr;
0095         // encode the two-dimensional numeric error list using '\n' and '\t' as outer and inner separators
0096         for (const QSslCertificate &cert : peerCertificateChain) {
0097             for (const QSslError &error : std::as_const(sslErrors)) {
0098                 if (error.certificate() == cert) {
0099                     errorStr += QString::number(static_cast<int>(error.error())) + QLatin1Char('\t');
0100                 }
0101             }
0102             if (errorStr.endsWith(QLatin1Char('\t'))) {
0103                 errorStr.chop(1);
0104             }
0105             errorStr += QLatin1Char('\n');
0106         }
0107         errorStr.chop(1);
0108         sslMetaData.insert(QStringLiteral("ssl_cert_errors"), errorStr);
0109 
0110         QString peerCertChain;
0111         for (const QSslCertificate &cert : peerCertificateChain) {
0112             peerCertChain += QString::fromUtf8(cert.toPem()) + QLatin1Char('\x01');
0113         }
0114         peerCertChain.chop(1);
0115         sslMetaData.insert(QStringLiteral("ssl_peer_chain"), peerCertChain);
0116         sendSslMetaData();
0117     }
0118 
0119     void clearSslMetaData()
0120     {
0121         sslMetaData.clear();
0122         sslMetaData.insert(QStringLiteral("ssl_in_use"), QStringLiteral("FALSE"));
0123         sendSslMetaData();
0124     }
0125 
0126     void sendSslMetaData()
0127     {
0128         MetaData::ConstIterator it = sslMetaData.constBegin();
0129         for (; it != sslMetaData.constEnd(); ++it) {
0130             q->setMetaData(it.key(), it.value());
0131         }
0132     }
0133 
0134     SslResult startTLSInternal(QSsl::SslProtocol sslVersion, int waitForEncryptedTimeout = -1);
0135 
0136     TCPWorkerBase *const q;
0137 
0138     bool isBlocking = true;
0139 
0140     QSslSocket socket;
0141 
0142     QString host;
0143     QString ip;
0144     quint16 port = 0;
0145     QByteArray serviceName;
0146 
0147     KSSLSettings sslSettings;
0148     bool usingSSL = false;
0149     bool autoSSL = false;
0150     bool sslNoUi = false; // If true, we just drop the connection silently
0151     // if SSL certificate check fails in some way.
0152     QList<QSslError> sslErrors;
0153 
0154     MetaData sslMetaData;
0155 };
0156 
0157 QAbstractSocket *TCPWorkerBase::tcpSocket() const
0158 {
0159     return &d->socket;
0160 }
0161 
0162 TCPWorkerBase::TCPWorkerBase(const QByteArray &protocol, const QByteArray &poolSocket, const QByteArray &appSocket, bool autoSSL)
0163     : WorkerBase(protocol, poolSocket, appSocket)
0164     , d(new TCPWorkerBasePrivate(this))
0165 {
0166     d->isBlocking = true;
0167     d->port = 0;
0168     d->serviceName = protocol;
0169     d->usingSSL = false;
0170     d->autoSSL = autoSSL;
0171     d->sslNoUi = false;
0172     // Limit the read buffer size to 14 MB (14*1024*1024) (based on the upload limit
0173     // in TransferJob::slotDataReq). See the docs for QAbstractSocket::setReadBufferSize
0174     // and the BR# 187876 to understand why setting this limit is necessary.
0175     d->socket.setReadBufferSize(14680064);
0176 }
0177 
0178 TCPWorkerBase::~TCPWorkerBase() = default;
0179 
0180 ssize_t TCPWorkerBase::write(const char *data, ssize_t len)
0181 {
0182     ssize_t written = d->socket.write(data, len);
0183     if (written == -1) {
0184         /*qDebug() << "d->socket.write() returned -1! Socket error is"
0185           << d->socket.error() << ", Socket state is" << d->socket.state();*/
0186     }
0187 
0188     bool success = false;
0189     if (d->isBlocking) {
0190         // Drain the tx buffer
0191         success = d->socket.waitForBytesWritten(-1);
0192     } else {
0193         // ### I don't know how to make sure that all data does get written at some point
0194         // without doing it now. There is no event loop to do it behind the scenes.
0195         // Polling in the dispatch() loop? Something timeout based?
0196         success = d->socket.waitForBytesWritten(0);
0197     }
0198 
0199     d->socket.flush(); // this is supposed to get the data on the wire faster
0200 
0201     if (d->socket.state() != QAbstractSocket::ConnectedState || !success) {
0202         /*qDebug() << "Write failed, will return -1! Socket error is"
0203           << d->socket.error() << ", Socket state is" << d->socket.state()
0204           << "Return value of waitForBytesWritten() is" << success;*/
0205         return -1;
0206     }
0207 
0208     return written;
0209 }
0210 
0211 ssize_t TCPWorkerBase::read(char *data, ssize_t len)
0212 {
0213     if (d->usingSSL && (d->socket.mode() != QSslSocket::SslClientMode)) {
0214         d->clearSslMetaData();
0215         // qDebug() << "lost SSL connection.";
0216         return -1;
0217     }
0218 
0219     if (!d->socket.bytesAvailable()) {
0220         const int timeout = d->isBlocking ? -1 : (readTimeout() * 1000);
0221         d->socket.waitForReadyRead(timeout);
0222     }
0223     return d->socket.read(data, len);
0224 }
0225 
0226 ssize_t TCPWorkerBase::readLine(char *data, ssize_t len)
0227 {
0228     if (d->usingSSL && (d->socket.mode() != QSslSocket::SslClientMode)) {
0229         d->clearSslMetaData();
0230         // qDebug() << "lost SSL connection.";
0231         return -1;
0232     }
0233 
0234     const int timeout = (d->isBlocking ? -1 : (readTimeout() * 1000));
0235     ssize_t readTotal = 0;
0236     do {
0237         if (!d->socket.bytesAvailable()) {
0238             d->socket.waitForReadyRead(timeout);
0239         }
0240         ssize_t readStep = d->socket.readLine(&data[readTotal], len - readTotal);
0241         if (readStep == -1 || (readStep == 0 && d->socket.state() != QAbstractSocket::ConnectedState)) {
0242             return -1;
0243         }
0244         readTotal += readStep;
0245     } while (readTotal == 0 || data[readTotal - 1] != '\n');
0246 
0247     return readTotal;
0248 }
0249 
0250 KIO::WorkerResult TCPWorkerBase::connectToHost(const QString & /*protocol*/, const QString &host, quint16 port)
0251 {
0252     QString errorString;
0253     const int errCode = connectToHost(host, port, &errorString);
0254     if (errCode == 0) {
0255         return WorkerResult::pass();
0256     }
0257 
0258     return WorkerResult::fail(errCode, errorString);
0259 }
0260 
0261 int TCPWorkerBase::connectToHost(const QString &host, quint16 port, QString *errorString)
0262 {
0263     d->clearSslMetaData(); // We have separate connection and SSL setup phases
0264 
0265     if (errorString) {
0266         errorString->clear(); // clear prior error messages.
0267     }
0268 
0269     d->socket.setPeerVerifyName(host); // Used for ssl certificate verification (SNI)
0270 
0271     //  - leaving SSL - warn before we even connect
0272     // ### see if it makes sense to move this into the HTTP KIO worker which is the only
0273     //    user.
0274     if (metaData(QStringLiteral("main_frame_request")) == QLatin1String("TRUE") // ### this looks *really* unreliable
0275         && metaData(QStringLiteral("ssl_activate_warnings")) == QLatin1String("TRUE") && metaData(QStringLiteral("ssl_was_in_use")) == QLatin1String("TRUE")
0276         && !d->autoSSL) {
0277         if (d->sslSettings.warnOnLeave()) {
0278             int result = messageBox(i18n("You are about to leave secure "
0279                                          "mode. Transmissions will no "
0280                                          "longer be encrypted.\nThis "
0281                                          "means that a third party could "
0282                                          "observe your data in transit."),
0283                                     WarningContinueCancel,
0284                                     i18n("Security Information"),
0285                                     i18n("C&ontinue Loading"),
0286                                     QString(),
0287                                     QStringLiteral("WarnOnLeaveSSLMode"));
0288 
0289             if (result == WorkerBase::Cancel) {
0290                 if (errorString) {
0291                     *errorString = host;
0292                 }
0293                 return ERR_USER_CANCELED;
0294             }
0295         }
0296     }
0297 
0298     const int timeout = (connectTimeout() * 1000); // 20 sec timeout value
0299 
0300     disconnectFromHost(); // Reset some state, even if we are already disconnected
0301     d->host = host;
0302 
0303     d->socket.connectToHost(host, port);
0304     /*const bool connectOk = */ d->socket.waitForConnected(timeout > -1 ? timeout : -1);
0305 
0306     /*qDebug() << "Socket: state=" << d->socket.state()
0307         << ", error=" << d->socket.error()
0308         << ", connected?" << connectOk;*/
0309 
0310     if (d->socket.state() != QAbstractSocket::ConnectedState) {
0311         if (errorString) {
0312             *errorString = host + QLatin1String(": ") + d->socket.errorString();
0313         }
0314         switch (d->socket.error()) {
0315         case QAbstractSocket::UnsupportedSocketOperationError:
0316             return ERR_UNSUPPORTED_ACTION;
0317         case QAbstractSocket::RemoteHostClosedError:
0318             return ERR_CONNECTION_BROKEN;
0319         case QAbstractSocket::SocketTimeoutError:
0320             return ERR_SERVER_TIMEOUT;
0321         case QAbstractSocket::HostNotFoundError:
0322             return ERR_UNKNOWN_HOST;
0323         default:
0324             return ERR_CANNOT_CONNECT;
0325         }
0326     }
0327 
0328     // ### check for proxyAuthenticationRequiredError
0329 
0330     d->ip = d->socket.peerAddress().toString();
0331     d->port = d->socket.peerPort();
0332 
0333     if (d->autoSSL) {
0334         const SslResult res = d->startTLSInternal(QSsl::SecureProtocols, timeout);
0335 
0336         if (res & ResultFailed) {
0337             if (errorString) {
0338                 *errorString = i18nc("%1 is a host name", "%1: SSL negotiation failed", host);
0339             }
0340             return ERR_CANNOT_CONNECT;
0341         }
0342     }
0343     return 0;
0344 }
0345 
0346 void TCPWorkerBase::disconnectFromHost()
0347 {
0348     // qDebug();
0349     d->host.clear();
0350     d->ip.clear();
0351     d->usingSSL = false;
0352 
0353     if (d->socket.state() == QAbstractSocket::UnconnectedState) {
0354         // discard incoming data - the remote host might have disconnected us in the meantime
0355         // but the visible effect of disconnectFromHost() should stay the same.
0356         d->socket.close();
0357         return;
0358     }
0359 
0360     // ### maybe save a session for reuse on SSL shutdown if and when QSslSocket
0361     //     does that. QCA::TLS can do it apparently but that is not enough if
0362     //     we want to present that as KDE API. Not a big loss in any case.
0363     d->socket.disconnectFromHost();
0364     if (d->socket.state() != QAbstractSocket::UnconnectedState) {
0365         d->socket.waitForDisconnected(-1); // wait for unsent data to be sent
0366     }
0367     d->socket.close(); // whatever that means on a socket
0368 }
0369 
0370 bool TCPWorkerBase::isAutoSsl() const
0371 {
0372     return d->autoSSL;
0373 }
0374 
0375 bool TCPWorkerBase::isUsingSsl() const
0376 {
0377     return d->usingSSL;
0378 }
0379 
0380 quint16 TCPWorkerBase::port() const
0381 {
0382     return d->port;
0383 }
0384 
0385 bool TCPWorkerBase::atEnd() const
0386 {
0387     return d->socket.atEnd();
0388 }
0389 
0390 bool TCPWorkerBase::startSsl()
0391 {
0392     if (d->usingSSL) {
0393         return false;
0394     }
0395     return d->startTLSInternal(QSsl::SecureProtocols) & ResultOk;
0396 }
0397 
0398 TCPWorkerBase::SslResult TCPWorkerBase::TCPWorkerBasePrivate::startTLSInternal(QSsl::SslProtocol sslVersion, int waitForEncryptedTimeout)
0399 {
0400     // setMetaData("ssl_session_id", d->kssl->session()->toString());
0401     // ### we don't support session reuse for now...
0402     usingSSL = true;
0403 
0404     // Set the SSL protocol version to use...
0405     socket.setProtocol(sslVersion);
0406 
0407     /* Usually ignoreSslErrors() would be called in the slot invoked by the sslErrors()
0408        signal but that would mess up the flow of control. We will check for errors
0409        anyway to decide if we want to continue connecting. Otherwise ignoreSslErrors()
0410        before connecting would be very insecure. */
0411     socket.ignoreSslErrors();
0412     socket.startClientEncryption();
0413     const bool encryptionStarted = socket.waitForEncrypted(waitForEncryptedTimeout);
0414 
0415     // Set metadata, among other things for the "SSL Details" dialog
0416     QSslCipher cipher = socket.sessionCipher();
0417 
0418     if (!encryptionStarted || socket.mode() != QSslSocket::SslClientMode || cipher.isNull() || cipher.usedBits() == 0
0419         || socket.peerCertificateChain().isEmpty()) {
0420         usingSSL = false;
0421         clearSslMetaData();
0422         /*qDebug() << "Initial SSL handshake failed. encryptionStarted is"
0423           << encryptionStarted << ", cipher.isNull() is" << cipher.isNull()
0424           << ", cipher.usedBits() is" << cipher.usedBits()
0425           << ", length of certificate chain is" << socket.peerCertificateChain().count()
0426           << ", the socket says:" << socket.errorString()
0427           << "and the list of SSL errors contains"
0428           << socket.sslErrors().count() << "items.";*/
0429         /*for (const QSslError &sslError : socket.sslErrors()) {
0430           qDebug() << "SSL ERROR: (" << sslError.error() << ")" << sslError.errorString();
0431           }*/
0432         return ResultFailed | ResultFailedEarly;
0433     }
0434 
0435     /*qDebug() << "Cipher info - "
0436       << " advertised SSL protocol version" << socket.protocol()
0437       << " negotiated SSL protocol version" << socket.sessionProtocol()
0438       << " authenticationMethod:" << cipher.authenticationMethod()
0439       << " encryptionMethod:" << cipher.encryptionMethod()
0440       << " keyExchangeMethod:" << cipher.keyExchangeMethod()
0441       << " name:" << cipher.name()
0442       << " supportedBits:" << cipher.supportedBits()
0443       << " usedBits:" << cipher.usedBits();*/
0444 
0445     sslErrors = socket.sslHandshakeErrors();
0446 
0447     // TODO: review / rewrite / remove the comment
0448     // The app side needs the metadata now for the SSL error dialog (if any) but
0449     // the same metadata will be needed later, too. When "later" arrives the worker
0450     // may actually be connected to a different application that doesn't know
0451     // the metadata the worker sent to the previous application.
0452     // The quite important SSL indicator icon in Konqi's URL bar relies on metadata
0453     // from here, for example. And Konqi will be the second application to connect
0454     // to the worker.
0455     // Therefore we choose to have our metadata and send it, too :)
0456     setSslMetaData();
0457     q->sendAndKeepMetaData();
0458 
0459     SslResult rc = q->verifyServerCertificate();
0460     if (rc & ResultFailed) {
0461         usingSSL = false;
0462         clearSslMetaData();
0463         // qDebug() << "server certificate verification failed.";
0464         socket.disconnectFromHost(); // Make the connection fail (cf. ignoreSslErrors())
0465         return ResultFailed;
0466     }
0467     if (rc & ResultOverridden) {
0468         // qDebug() << "server certificate verification failed but continuing at user's request.";
0469     }
0470 
0471     //"warn" when starting SSL/TLS
0472     if (q->metaData(QStringLiteral("ssl_activate_warnings")) == QLatin1String("TRUE") && q->metaData(QStringLiteral("ssl_was_in_use")) == QLatin1String("FALSE")
0473         && sslSettings.warnOnEnter()) {
0474         int msgResult = q->messageBox(i18n("You are about to enter secure mode. "
0475                                            "All transmissions will be encrypted "
0476                                            "unless otherwise noted.\nThis means "
0477                                            "that no third party will be able to "
0478                                            "easily observe your data in transit."),
0479                                       WarningTwoActions,
0480                                       i18n("Security Information"),
0481                                       i18n("Display SSL &Information"),
0482                                       i18n("C&onnect"),
0483                                       QStringLiteral("WarnOnEnterSSLMode"));
0484         if (msgResult == WorkerBase::PrimaryAction) {
0485             q->messageBox(SSLMessageBox /*==the SSL info dialog*/, host);
0486         }
0487     }
0488 
0489     return rc;
0490 }
0491 
0492 TCPWorkerBase::SslResult TCPWorkerBase::verifyServerCertificate()
0493 {
0494     d->sslNoUi = hasMetaData(QStringLiteral("ssl_no_ui")) && (metaData(QStringLiteral("ssl_no_ui")) != QLatin1String("FALSE"));
0495 
0496     if (d->sslErrors.isEmpty()) {
0497         return ResultOk;
0498     }
0499     if (d->sslNoUi) {
0500         return ResultFailed;
0501     }
0502 
0503     const QList<QSslError> fatalErrors = KSslCertificateManager::nonIgnorableErrors(d->sslErrors);
0504     if (!fatalErrors.isEmpty()) {
0505         // TODO message "sorry, fatal error, you can't override it"
0506         return ResultFailed;
0507     }
0508     QList<QSslCertificate> peerCertificationChain = d->socket.peerCertificateChain();
0509     KSslCertificateManager *const cm = KSslCertificateManager::self();
0510     KSslCertificateRule rule = cm->rule(peerCertificationChain.first(), d->host);
0511 
0512     // remove previously seen and acknowledged errors
0513     const QList<QSslError> remainingErrors = rule.filterErrors(d->sslErrors);
0514     if (remainingErrors.isEmpty()) {
0515         // qDebug() << "Error list empty after removing errors to be ignored. Continuing.";
0516         return ResultOk | ResultOverridden;
0517     }
0518 
0519     // ### We don't ask to permanently reject the certificate
0520 
0521     QString message = i18n("The server failed the authenticity check (%1).\n\n", d->host);
0522     for (const QSslError &err : std::as_const(d->sslErrors)) {
0523         message += err.errorString() + QLatin1Char('\n');
0524     }
0525     message = message.trimmed();
0526 
0527     int msgResult = -1;
0528     QDateTime ruleExpiry = QDateTime::currentDateTime();
0529     do {
0530         msgResult = messageBox(WarningTwoActionsCancel, message, i18n("Server Authentication"), i18n("&Details"), i18n("Co&ntinue"));
0531         switch (msgResult) {
0532         case WorkerBase::PrimaryAction:
0533             // Details was chosen- show the certificate and error details
0534             messageBox(SSLMessageBox /*the SSL info dialog*/, d->host);
0535             break;
0536         case WorkerBase::SecondaryAction: {
0537             // fall through on WorkerBase::SecondaryAction
0538             const int result = messageBox(WarningTwoActionsCancel,
0539                                           i18n("Would you like to accept this "
0540                                                "certificate forever without "
0541                                                "being prompted?"),
0542                                           i18n("Server Authentication"),
0543                                           i18n("&Forever"),
0544                                           i18n("&Current Session only"));
0545             if (result == WorkerBase::PrimaryAction) {
0546                 // accept forever ("for a very long time")
0547                 ruleExpiry = ruleExpiry.addYears(1000);
0548             } else if (result == WorkerBase::SecondaryAction) {
0549                 // accept "for a short time", half an hour.
0550                 ruleExpiry = ruleExpiry.addSecs(30 * 60);
0551             } else {
0552                 msgResult = WorkerBase::PrimaryAction;
0553             }
0554             break;
0555         }
0556         case WorkerBase::Cancel:
0557             return ResultFailed;
0558         default:
0559             qCWarning(KIO_CORE) << "Unexpected MessageBox response received:" << msgResult;
0560             return ResultFailed;
0561         }
0562     } while (msgResult == WorkerBase::PrimaryAction);
0563 
0564     // TODO special cases for wildcard domain name in the certificate!
0565     // rule = KSslCertificateRule(d->socket.peerCertificateChain().first(), whatever);
0566 
0567     rule.setExpiryDateTime(ruleExpiry);
0568     rule.setIgnoredErrors(d->sslErrors);
0569     cm->setRule(rule);
0570 
0571     return ResultOk | ResultOverridden;
0572 }
0573 
0574 bool TCPWorkerBase::isConnected() const
0575 {
0576     // QSslSocket::isValid() is shady...
0577     return d->socket.state() == QAbstractSocket::ConnectedState;
0578 }
0579 
0580 bool TCPWorkerBase::waitForResponse(int t)
0581 {
0582     if (d->socket.bytesAvailable()) {
0583         return true;
0584     }
0585     return d->socket.waitForReadyRead(t * 1000);
0586 }
0587 
0588 void TCPWorkerBase::setBlocking(bool b)
0589 {
0590     if (!b) {
0591         qCWarning(KIO_CORE) << "Caller requested non-blocking mode, but that doesn't work";
0592         return;
0593     }
0594     d->isBlocking = b;
0595 }
0596 
0597 void TCPWorkerBase::appConnectionMade()
0598 {
0599     d->sendSslMetaData();
0600 }