File indexing completed on 2024-09-15 04:36:24

0001 /*
0002  * SPDX-FileCopyrightText: 2015 Daniel Vrátil <dvratil@redhat.com>
0003  *
0004  * SPDX-License-Identifier: LGPL-2.1-or-later
0005  *
0006  */
0007 
0008 #include "datastream_p_p.h"
0009 
0010 #ifdef Q_OS_WIN
0011 #include <QEventLoop>
0012 #include <QLocalSocket>
0013 #include <QTimer>
0014 #endif
0015 
0016 using namespace Akonadi;
0017 using namespace Akonadi::Protocol;
0018 
0019 DataStream::DataStream()
0020     : mDev(nullptr)
0021 {
0022 }
0023 
0024 DataStream::DataStream(QIODevice *device)
0025     : mDev(device)
0026 {
0027 }
0028 
0029 DataStream::~DataStream()
0030 {
0031     // No flush() here. Throwing an exception in a destructor would go badly. The caller MUST call flush after writing.
0032 }
0033 
0034 void DataStream::flush()
0035 {
0036     if (!mWriteBuffer.isEmpty()) {
0037         const int len = mWriteBuffer.size();
0038         int ret = mDev->write(mWriteBuffer);
0039         if (ret != len) {
0040             // TODO: Try to write data again unless ret is -1?
0041             throw ProtocolException("Failed to write all data");
0042         }
0043         mWriteBuffer.clear();
0044     }
0045 }
0046 
0047 void DataStream::waitForData(QIODevice *device, int timeoutMs)
0048 {
0049 #ifdef Q_OS_WIN
0050     // Apparently readyRead() gets emitted sometimes even if there are no data
0051     // so we will re-enter the wait again immediately
0052     while (device->bytesAvailable() == 0) {
0053         auto ls = qobject_cast<QLocalSocket *>(device);
0054         if (ls && ls->state() != QLocalSocket::ConnectedState) {
0055             throw ProtocolException("Socket not connected to server");
0056         }
0057 
0058         QEventLoop loop;
0059         QObject::connect(device, &QIODevice::readyRead, &loop, &QEventLoop::quit);
0060         if (ls) {
0061             QObject::connect(ls, &QLocalSocket::stateChanged, &loop, &QEventLoop::quit);
0062         }
0063         bool timeout = false;
0064         if (timeoutMs > 0) {
0065             QTimer::singleShot(timeoutMs, &loop, [&]() {
0066                 timeout = true;
0067                 loop.quit();
0068             });
0069         }
0070         loop.exec();
0071         if (timeout) {
0072             throw ProtocolException("Timeout while waiting for data");
0073         }
0074         if (ls && ls->state() != QLocalSocket::ConnectedState) {
0075             throw ProtocolException("Socket not connected to server");
0076         }
0077     }
0078 #else
0079     if (!device->waitForReadyRead(timeoutMs)) {
0080         throw ProtocolException("Timeout while waiting for data");
0081     }
0082 #endif
0083 }
0084 
0085 QIODevice *DataStream::device() const
0086 {
0087     return mDev;
0088 }
0089 
0090 void DataStream::setDevice(QIODevice *device)
0091 {
0092     mDev = device;
0093 }
0094 
0095 std::chrono::milliseconds DataStream::waitTimeout() const
0096 {
0097     return mWaitTimeout;
0098 }
0099 void DataStream::setWaitTimeout(std::chrono::milliseconds timeout)
0100 {
0101     mWaitTimeout = timeout;
0102 }
0103 
0104 void DataStream::waitForData(quint32 size)
0105 {
0106     checkDevice();
0107 
0108     while (mDev->bytesAvailable() < size) {
0109         waitForData(mDev, mWaitTimeout.count());
0110     }
0111 }
0112 
0113 void DataStream::writeRawData(const char *data, qsizetype len)
0114 {
0115     checkDevice();
0116 
0117     mWriteBuffer += QByteArray::fromRawData(data, len);
0118 }
0119 
0120 void DataStream::writeBytes(const char *bytes, qsizetype len)
0121 {
0122     *this << static_cast<quint32>(len);
0123     if (len) {
0124         writeRawData(bytes, len);
0125     }
0126 }
0127 
0128 qint64 DataStream::readRawData(char *buffer, qint64 len)
0129 {
0130     checkDevice();
0131 
0132     return mDev->read(buffer, len);
0133 }