File indexing completed on 2024-12-22 03:52:57

0001 /*
0002     This file is part of the KDE games library
0003     SPDX-FileCopyrightText: 2001 Burkhard Lehner <Burkhard.Lehner@gmx.de>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-only
0006 */
0007 
0008 /*
0009      KMessageIO class and subclasses KMessageSocket and KMessageDirect
0010 */
0011 
0012 #include "kmessageio.h"
0013 
0014 // own
0015 #include <kdegamesprivate_kgame_logging.h>
0016 // Qt
0017 #include <KProcess>
0018 #include <QDataStream>
0019 #include <QTcpSocket>
0020 
0021 // ----------------------- KMessageIO -------------------------
0022 
0023 KMessageIO::KMessageIO(QObject *parent)
0024     : QObject(parent)
0025     , m_id(0)
0026 {
0027 }
0028 
0029 KMessageIO::~KMessageIO()
0030 {
0031 }
0032 
0033 void KMessageIO::setId(quint32 id)
0034 {
0035     m_id = id;
0036 }
0037 
0038 quint32 KMessageIO::id()
0039 {
0040     return m_id;
0041 }
0042 
0043 bool KMessageIO::isNetwork() const
0044 {
0045     qCCritical(KDEGAMESPRIVATE_KGAME_LOG) << "Calling PURE virtual isNetwork...BAD";
0046     return false;
0047 }
0048 
0049 bool KMessageIO::isConnected() const
0050 {
0051     qCCritical(KDEGAMESPRIVATE_KGAME_LOG) << "Calling PURE virtual isConnected...BAD";
0052     return false;
0053 }
0054 
0055 quint16 KMessageIO::peerPort() const
0056 {
0057     return 0;
0058 }
0059 
0060 QString KMessageIO::peerName() const
0061 {
0062     return QStringLiteral("localhost");
0063 }
0064 
0065 // ----------------------KMessageSocket -----------------------
0066 
0067 KMessageSocket::KMessageSocket(const QString &host, quint16 port, QObject *parent)
0068     : KMessageIO(parent)
0069 {
0070     mSocket = new QTcpSocket();
0071     mSocket->connectToHost(host, port);
0072     initSocket();
0073 }
0074 
0075 KMessageSocket::KMessageSocket(const QHostAddress &host, quint16 port, QObject *parent)
0076     : KMessageIO(parent)
0077 {
0078     mSocket = new QTcpSocket();
0079     mSocket->connectToHost(host.toString(), port);
0080     initSocket();
0081 }
0082 
0083 KMessageSocket::KMessageSocket(QTcpSocket *socket, QObject *parent)
0084     : KMessageIO(parent)
0085 {
0086     mSocket = socket;
0087     initSocket();
0088 }
0089 
0090 KMessageSocket::KMessageSocket(int socketFD, QObject *parent)
0091     : KMessageIO(parent)
0092 {
0093     mSocket = new QTcpSocket();
0094     mSocket->setSocketDescriptor(socketFD);
0095     initSocket();
0096 }
0097 
0098 KMessageSocket::~KMessageSocket()
0099 {
0100     delete mSocket;
0101 }
0102 
0103 bool KMessageSocket::isConnected() const
0104 {
0105     return mSocket->state() == QAbstractSocket::ConnectedState;
0106 }
0107 
0108 void KMessageSocket::send(const QByteArray &msg)
0109 {
0110     QDataStream str(mSocket);
0111     str << quint8('M'); // magic number for begin of message
0112     str.writeBytes(msg.data(), msg.size()); // writes the length (as quint32) and the data
0113 }
0114 
0115 void KMessageSocket::processNewData()
0116 {
0117     if (isRecursive)
0118         return;
0119     isRecursive = true;
0120 
0121     QDataStream str(mSocket);
0122     while (mSocket->bytesAvailable() > 0) {
0123         if (mAwaitingHeader) {
0124             // Header = magic number + packet length = 5 bytes
0125             if (mSocket->bytesAvailable() < 5) {
0126                 isRecursive = false;
0127                 return;
0128             }
0129 
0130             // Read the magic number first. If something unexpected is found,
0131             // start over again, ignoring the data that was read up to then.
0132 
0133             quint8 v;
0134             str >> v;
0135             if (v != 'M') {
0136                 qCWarning(KDEGAMESPRIVATE_KGAME_LOG) << ": Received unexpected data, magic number wrong!";
0137                 continue;
0138             }
0139 
0140             str >> mNextBlockLength;
0141             mAwaitingHeader = false;
0142         } else {
0143             // Data not completely read => wait for more
0144             if (mSocket->bytesAvailable() < (qint64)mNextBlockLength) {
0145                 isRecursive = false;
0146                 return;
0147             }
0148 
0149             QByteArray msg(mNextBlockLength, 0);
0150             str.readRawData(msg.data(), mNextBlockLength);
0151 
0152             // send the received message
0153             Q_EMIT received(msg);
0154 
0155             // Waiting for the header of the next message
0156             mAwaitingHeader = true;
0157         }
0158     }
0159 
0160     isRecursive = false;
0161 }
0162 
0163 void KMessageSocket::initSocket()
0164 {
0165     connect(mSocket, &QTcpSocket::errorOccurred, this, &KMessageSocket::connectionBroken);
0166     connect(mSocket, &QTcpSocket::disconnected, this, &KMessageSocket::connectionBroken);
0167     connect(mSocket, &QTcpSocket::readyRead, this, &KMessageSocket::processNewData);
0168     mAwaitingHeader = true;
0169     mNextBlockLength = 0;
0170     isRecursive = false;
0171 }
0172 
0173 quint16 KMessageSocket::peerPort() const
0174 {
0175     return mSocket->peerPort();
0176 }
0177 
0178 QString KMessageSocket::peerName() const
0179 {
0180     return mSocket->peerName();
0181 }
0182 
0183 // ----------------------KMessageDirect -----------------------
0184 
0185 KMessageDirect::KMessageDirect(KMessageDirect *partner, QObject *parent)
0186     : KMessageIO(parent)
0187     , mPartner(nullptr)
0188 {
0189     // 0 as first parameter leaves the object unconnected
0190     if (!partner)
0191         return;
0192 
0193     // Check if the other object is already connected
0194     if (partner && partner->mPartner) {
0195         qCWarning(KDEGAMESPRIVATE_KGAME_LOG) << ": Object is already connected!";
0196         return;
0197     }
0198 
0199     // Connect from us to that object
0200     mPartner = partner;
0201 
0202     // Connect the other object to us
0203     partner->mPartner = this;
0204 }
0205 
0206 KMessageDirect::~KMessageDirect()
0207 {
0208     if (mPartner) {
0209         mPartner->mPartner = nullptr;
0210         Q_EMIT mPartner->connectionBroken();
0211     }
0212 }
0213 
0214 bool KMessageDirect::isConnected() const
0215 {
0216     return mPartner != nullptr;
0217 }
0218 
0219 void KMessageDirect::send(const QByteArray &msg)
0220 {
0221     if (mPartner)
0222         Q_EMIT mPartner->received(msg);
0223     else
0224         qCCritical(KDEGAMESPRIVATE_KGAME_LOG) << ": Not yet connected!";
0225 }
0226 
0227 // ----------------------- KMessageProcess ---------------------------
0228 
0229 KMessageProcess::~KMessageProcess()
0230 {
0231     qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "@@@KMessageProcess::Delete process";
0232     if (mProcess) {
0233         mProcess->kill();
0234         mProcess->deleteLater();
0235         mProcess = nullptr;
0236         // Maybe todo: delete mSendBuffer
0237     }
0238 }
0239 KMessageProcess::KMessageProcess(QObject *parent, const QString &file)
0240     : KMessageIO(parent)
0241 {
0242     // Start process
0243     qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "@@@KMessageProcess::Start process";
0244     mProcessName = file;
0245     mProcess = new KProcess;
0246     // Need both stdout and stderr as separate channels in the communication
0247     mProcess->setOutputChannelMode(KProcess::SeparateChannels);
0248     int id = 0;
0249     *mProcess << mProcessName << QStringLiteral("%1").arg(id);
0250     qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "@@@KMessageProcess::Init:Id=" << id;
0251     qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "@@@KMessgeProcess::Init:Processname:" << mProcessName;
0252     connect(mProcess, &KProcess::readyReadStandardOutput, this, &KMessageProcess::slotReceivedStdout);
0253     connect(mProcess, &KProcess::readyReadStandardError, this, &KMessageProcess::slotReceivedStderr);
0254     connect(mProcess, static_cast<void (KProcess::*)(int, QProcess::ExitStatus)>(&KProcess::finished), this, &KMessageProcess::slotProcessExited);
0255     mProcess->start();
0256     mSendBuffer = nullptr;
0257     mReceiveCount = 0;
0258     mReceiveBuffer.resize(1024);
0259 }
0260 bool KMessageProcess::isConnected() const
0261 {
0262     qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "@@@KMessageProcess::Is connected";
0263     if (!mProcess)
0264         return false;
0265     return (mProcess->state() == QProcess::Running);
0266 }
0267 
0268 // Send to process
0269 void KMessageProcess::send(const QByteArray &msg)
0270 {
0271     qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "@@@KMessageProcess:: SEND(" << msg.size() << ") to process";
0272     unsigned int size = msg.size() + 2 * sizeof(long);
0273 
0274     if (mProcess == nullptr) {
0275         qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "@@@KMessageProcess:: cannot write to stdin, no process available";
0276         return;
0277     }
0278 
0279     char *tmpbuffer = new char[size];
0280     long *p1 = (long *)tmpbuffer;
0281     long *p2 = p1 + 1;
0282     qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "p1=" << p1 << "p2=" << p2;
0283     memcpy(tmpbuffer + 2 * sizeof(long), msg.data(), msg.size());
0284     *p1 = 0x4242aeae;
0285     *p2 = size;
0286 
0287     // no need to add it to a queue -> qiodevice is buffered
0288     mProcess->write(tmpbuffer, size);
0289     delete[] tmpbuffer;
0290 }
0291 
0292 void KMessageProcess::slotReceivedStderr()
0293 {
0294     QByteArray ba;
0295     qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "@@@ KMessageProcess::slotReceivedStderr";
0296 
0297     mProcess->setReadChannel(QProcess::StandardError);
0298     while (mProcess->canReadLine()) {
0299         ba = mProcess->readLine();
0300         if (ba.isEmpty())
0301             return;
0302         ba.chop(1); // remove QLatin1Char( '\n' )
0303 
0304         qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "KProcess (" << ba.size() << "):" << ba.constData();
0305         Q_EMIT signalReceivedStderr(QLatin1String(ba));
0306         ba.clear();
0307     };
0308 }
0309 
0310 void KMessageProcess::slotReceivedStdout()
0311 {
0312     mProcess->setReadChannel(QProcess::StandardOutput);
0313     QByteArray ba = mProcess->readAll();
0314     qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "$$$$$$ "
0315                                        << ": Received" << ba.size() << "bytes over inter process communication";
0316 
0317     // Resize receive buffer
0318     while (mReceiveCount + ba.size() >= mReceiveBuffer.size())
0319         mReceiveBuffer.resize(mReceiveBuffer.size() + 1024);
0320     // was 08/2007: mReceiveBuffer += ba;
0321     std::copy(ba.begin(), ba.begin() + ba.size(), mReceiveBuffer.begin() + mReceiveCount);
0322     mReceiveCount += ba.size();
0323 
0324     // Possible message
0325     while (mReceiveCount > int(2 * sizeof(long))) {
0326         long *p1 = (long *)mReceiveBuffer.data();
0327         long *p2 = p1 + 1;
0328         int len;
0329         if (*p1 != 0x4242aeae) {
0330             qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << ": Cookie error...transmission failure...serious problem...";
0331         }
0332         len = (int)(*p2);
0333         if (len < int(2 * sizeof(long))) {
0334             qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << ": Message size error";
0335             break;
0336         }
0337         if (len <= mReceiveCount) {
0338             qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << ": Got message with len" << len;
0339 
0340             QByteArray msg;
0341             msg.resize(len);
0342             //  msg.setRawData(mReceiveBuffer.data()+2*sizeof(long),len-2*sizeof(long));
0343 
0344             std::copy(mReceiveBuffer.begin() + 2 * sizeof(long), mReceiveBuffer.begin() + len, msg.begin());
0345             //       msg.duplicate(mReceiveBuffer.data()+2*sizeof(long),len-2*sizeof(long));
0346             Q_EMIT received(msg);
0347             // msg.resetRawData(mReceiveBuffer.data()+2*sizeof(long),len-2*sizeof(long));
0348             // Shift buffer
0349             if (len < mReceiveCount) {
0350                 memmove(mReceiveBuffer.data(), mReceiveBuffer.data() + len, mReceiveCount - len);
0351             }
0352             mReceiveCount -= len;
0353         } else
0354             break;
0355     }
0356 }
0357 
0358 void KMessageProcess::slotProcessExited(int exitCode, QProcess::ExitStatus)
0359 {
0360     qCDebug(KDEGAMESPRIVATE_KGAME_LOG) << "Process exited (slot) with code" << exitCode;
0361     Q_EMIT connectionBroken();
0362     delete mProcess;
0363     mProcess = nullptr;
0364 }
0365 
0366 #include "moc_kmessageio.cpp"