File indexing completed on 2024-04-28 11:41:11

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