File indexing completed on 2023-10-03 03:20:07
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