File indexing completed on 2024-09-08 03:38:44
0001 /* 0002 This file is part of the KDE libraries 0003 SPDX-FileCopyrightText: 2000 Stephan Kulow <coolo@kde.org> 0004 SPDX-FileCopyrightText: 2000 David Faure <faure@kde.org> 0005 SPDX-FileCopyrightText: 2007 Thiago Macieira <thiago@kde.org> 0006 0007 SPDX-License-Identifier: LGPL-2.0-or-later 0008 */ 0009 0010 #include "connection_p.h" 0011 #include "connectionbackend_p.h" 0012 #include "kiocoredebug.h" 0013 #include <QDebug> 0014 0015 #include <cerrno> 0016 0017 using namespace KIO; 0018 0019 void ConnectionPrivate::dequeue() 0020 { 0021 if (!backend || suspended) { 0022 return; 0023 } 0024 0025 for (const Task &task : std::as_const(outgoingTasks)) { 0026 q->sendnow(task.cmd, task.data); 0027 } 0028 outgoingTasks.clear(); 0029 0030 if (!incomingTasks.isEmpty()) { 0031 Q_EMIT q->readyRead(); 0032 } 0033 } 0034 0035 void ConnectionPrivate::commandReceived(const Task &task) 0036 { 0037 // qDebug() << this << "Command" << task.cmd << "added to the queue"; 0038 if (!suspended && incomingTasks.isEmpty() && readMode == Connection::ReadMode::EventDriven) { 0039 auto dequeueFunc = [this]() { 0040 dequeue(); 0041 }; 0042 QMetaObject::invokeMethod(q, dequeueFunc, Qt::QueuedConnection); 0043 } 0044 incomingTasks.append(task); 0045 } 0046 0047 void ConnectionPrivate::disconnected() 0048 { 0049 q->close(); 0050 if (readMode == Connection::ReadMode::EventDriven) { 0051 QMetaObject::invokeMethod(q, &Connection::readyRead, Qt::QueuedConnection); 0052 } 0053 } 0054 0055 void ConnectionPrivate::setBackend(ConnectionBackend *b) 0056 { 0057 delete backend; 0058 backend = b; 0059 if (backend) { 0060 q->connect(backend, &ConnectionBackend::commandReceived, q, [this](const Task &task) { 0061 commandReceived(task); 0062 }); 0063 q->connect(backend, &ConnectionBackend::disconnected, q, [this]() { 0064 disconnected(); 0065 }); 0066 backend->setSuspended(suspended); 0067 } 0068 } 0069 0070 Connection::Connection(QObject *parent) 0071 : QObject(parent) 0072 , d(new ConnectionPrivate) 0073 { 0074 d->q = this; 0075 } 0076 0077 Connection::~Connection() 0078 { 0079 close(); 0080 } 0081 0082 void Connection::suspend() 0083 { 0084 // qDebug() << this << "Suspended"; 0085 d->suspended = true; 0086 if (d->backend) { 0087 d->backend->setSuspended(true); 0088 } 0089 } 0090 0091 void Connection::resume() 0092 { 0093 // send any outgoing or incoming commands that may be in queue 0094 if (d->readMode == Connection::ReadMode::EventDriven) { 0095 auto dequeueFunc = [this]() { 0096 d->dequeue(); 0097 }; 0098 QMetaObject::invokeMethod(this, dequeueFunc, Qt::QueuedConnection); 0099 } 0100 0101 // qDebug() << this << "Resumed"; 0102 d->suspended = false; 0103 if (d->backend) { 0104 d->backend->setSuspended(false); 0105 } 0106 } 0107 0108 void Connection::close() 0109 { 0110 if (d->backend) { 0111 d->backend->disconnect(this); 0112 d->backend->deleteLater(); 0113 d->backend = nullptr; 0114 } 0115 d->outgoingTasks.clear(); 0116 d->incomingTasks.clear(); 0117 } 0118 0119 bool Connection::isConnected() const 0120 { 0121 return d->backend && d->backend->state == ConnectionBackend::Connected; 0122 } 0123 0124 bool Connection::inited() const 0125 { 0126 return d->backend; 0127 } 0128 0129 bool Connection::suspended() const 0130 { 0131 return d->suspended; 0132 } 0133 0134 void Connection::connectToRemote(const QUrl &address) 0135 { 0136 // qDebug() << "Connection requested to" << address; 0137 const QString scheme = address.scheme(); 0138 0139 if (scheme == QLatin1String("local")) { 0140 d->setBackend(new ConnectionBackend(this)); 0141 } else { 0142 qCWarning(KIO_CORE) << "Unknown protocol requested:" << scheme << "(" << address << ")"; 0143 Q_ASSERT(0); 0144 return; 0145 } 0146 0147 // connection succeeded 0148 if (!d->backend->connectToRemote(address)) { 0149 // qCWarning(KIO_CORE) << "could not connect to" << address << "using scheme" << scheme; 0150 delete d->backend; 0151 d->backend = nullptr; 0152 return; 0153 } 0154 0155 d->dequeue(); 0156 } 0157 0158 QString Connection::errorString() const 0159 { 0160 if (d->backend) { 0161 return d->backend->errorString; 0162 } 0163 return QString(); 0164 } 0165 0166 bool Connection::send(int cmd, const QByteArray &data) 0167 { 0168 if (!inited() || !d->outgoingTasks.isEmpty()) { 0169 Task task; 0170 task.cmd = cmd; 0171 task.data = data; 0172 d->outgoingTasks.append(std::move(task)); 0173 return true; 0174 } else { 0175 return sendnow(cmd, data); 0176 } 0177 } 0178 0179 bool Connection::sendnow(int cmd, const QByteArray &data) 0180 { 0181 if (!d->backend || data.size() > 0xffffff || !isConnected()) { 0182 return false; 0183 } 0184 0185 // qDebug() << this << "Sending command" << cmd << "of size" << data.size(); 0186 return d->backend->sendCommand(cmd, data); 0187 } 0188 0189 bool Connection::hasTaskAvailable() const 0190 { 0191 return !d->incomingTasks.isEmpty(); 0192 } 0193 0194 bool Connection::waitForIncomingTask(int ms) 0195 { 0196 if (!isConnected()) { 0197 return false; 0198 } 0199 0200 if (d->backend) { 0201 return d->backend->waitForIncomingTask(ms); 0202 } 0203 return false; 0204 } 0205 0206 int Connection::read(int *_cmd, QByteArray &data) 0207 { 0208 // if it's still empty, then it's an error 0209 if (d->incomingTasks.isEmpty()) { 0210 // qCWarning(KIO_CORE) << this << "Task list is empty!"; 0211 return -1; 0212 } 0213 const Task &task = d->incomingTasks.constFirst(); 0214 // qDebug() << this << "Command" << task.cmd << "removed from the queue (size" << task.data.size() << ")"; 0215 *_cmd = task.cmd; 0216 data = task.data; 0217 0218 d->incomingTasks.removeFirst(); 0219 0220 // if we didn't empty our reading queue, emit again 0221 if (!d->suspended && !d->incomingTasks.isEmpty() && d->readMode == Connection::ReadMode::EventDriven) { 0222 auto dequeueFunc = [this]() { 0223 d->dequeue(); 0224 }; 0225 QMetaObject::invokeMethod(this, dequeueFunc, Qt::QueuedConnection); 0226 } 0227 0228 return data.size(); 0229 } 0230 0231 void Connection::setReadMode(ReadMode readMode) 0232 { 0233 d->readMode = readMode; 0234 } 0235 0236 #include "moc_connection_p.cpp"