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 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 }