1 /*
2     This file is part of the KDE games library
3     SPDX-FileCopyrightText: 2001 Burkhard Lehner <Burkhard.Lehner@gmx.de>
4 
5     SPDX-License-Identifier: LGPL-2.0-only
6 */
7 
8 /*
9      KMessageIO class and subclasses KMessageSocket and KMessageDirect
10 */
11 
12 #include "kmessageio.h"
13 
14 // Qt
15 #include <QTcpSocket>
16 #include <KProcess>
17 #include <QFile>
18 #include <QDataStream>
19 
20 // ----------------------- KMessageIO -------------------------
21 
KMessageIO(QObject * parent)22 KMessageIO::KMessageIO (QObject *parent)
23   : QObject (parent), m_id (0)
24 {
25 }
26 
~KMessageIO()27 KMessageIO::~KMessageIO ()
28 {}
29 
setId(quint32 id)30 void KMessageIO::setId (quint32 id)
31 {
32   m_id = id;
33 }
34 
id()35 quint32 KMessageIO::id ()
36 {
37   return m_id;
38 }
39 
40 // ----------------------KMessageSocket -----------------------
41 
KMessageSocket(const QString & host,quint16 port,QObject * parent)42 KMessageSocket::KMessageSocket (const QString& host, quint16 port, QObject *parent)
43   : KMessageIO (parent)
44 {
45   mSocket = new QTcpSocket ();
46   mSocket->connectToHost (host, port);
47   initSocket ();
48 }
49 
KMessageSocket(const QHostAddress & host,quint16 port,QObject * parent)50 KMessageSocket::KMessageSocket (const QHostAddress &host, quint16 port, QObject *parent)
51   : KMessageIO (parent)
52 {
53   mSocket = new QTcpSocket ();
54   mSocket->connectToHost (host.toString(), port);
55   initSocket ();
56 }
57 
KMessageSocket(QTcpSocket * socket,QObject * parent)58 KMessageSocket::KMessageSocket (QTcpSocket *socket, QObject *parent)
59   : KMessageIO (parent)
60 {
61   mSocket = socket;
62   initSocket ();
63 }
64 
KMessageSocket(int socketFD,QObject * parent)65 KMessageSocket::KMessageSocket (int socketFD, QObject *parent)
66   : KMessageIO (parent)
67 {
68   mSocket = new QTcpSocket ();
69   mSocket->setSocketDescriptor (socketFD);
70   initSocket ();
71 }
72 
~KMessageSocket()73 KMessageSocket::~KMessageSocket ()
74 {
75   delete mSocket;
76 }
77 
isConnected() const78 bool KMessageSocket::isConnected () const
79 {
80   return mSocket->state() == QAbstractSocket::ConnectedState;
81 }
82 
send(const QByteArray & msg)83 void KMessageSocket::send (const QByteArray &msg)
84 {
85   QDataStream str (mSocket);
86   str << quint8 ('M');  // magic number for begin of message
87   str.writeBytes (msg.data(), msg.size());  // writes the length (as quint32) and the data
88 }
89 
processNewData()90 void KMessageSocket::processNewData ()
91 {
92   if (isRecursive)
93     return;
94   isRecursive = true;
95 
96   QDataStream str (mSocket);
97   while (mSocket->bytesAvailable() > 0)
98   {
99     if (mAwaitingHeader)
100     {
101       // Header = magic number + packet length = 5 bytes
102       if (mSocket->bytesAvailable() < 5)
103       {
104         isRecursive = false;
105         return;
106       }
107 
108       // Read the magic number first. If something unexpected is found,
109       // start over again, ignoring the data that was read up to then.
110 
111       quint8 v;
112       str >> v;
113       if (v != 'M')
114       {
115         qCWarning(GAMES_PRIVATE_KGAME) << ": Received unexpected data, magic number wrong!";
116         continue;
117       }
118 
119       str >> mNextBlockLength;
120       mAwaitingHeader = false;
121     }
122     else
123     {
124       // Data not completely read => wait for more
125       if (mSocket->bytesAvailable() < (qint64) mNextBlockLength)
126       {
127         isRecursive = false;
128         return;
129       }
130 
131       QByteArray msg (mNextBlockLength, 0);
132       str.readRawData (msg.data(), mNextBlockLength);
133 
134       // send the received message
135       Q_EMIT received (msg);
136 
137       // Waiting for the header of the next message
138       mAwaitingHeader = true;
139     }
140   }
141 
142   isRecursive = false;
143 }
144 
initSocket()145 void KMessageSocket::initSocket ()
146 {
147   connect(mSocket, &QTcpSocket::errorOccurred,
148           this, &KMessageSocket::connectionBroken);
149   connect(mSocket, &QTcpSocket::disconnected, this, &KMessageSocket::connectionBroken);
150   connect(mSocket, &QTcpSocket::readyRead, this, &KMessageSocket::processNewData);
151   mAwaitingHeader = true;
152   mNextBlockLength = 0;
153   isRecursive = false;
154 }
155 
peerPort() const156 quint16 KMessageSocket::peerPort () const
157 {
158   return mSocket->peerPort();
159 }
160 
peerName() const161 QString KMessageSocket::peerName () const
162 {
163   return mSocket->peerName();
164 }
165 
166 // ----------------------KMessageDirect -----------------------
167 
KMessageDirect(KMessageDirect * partner,QObject * parent)168 KMessageDirect::KMessageDirect (KMessageDirect *partner, QObject *parent)
169   : KMessageIO (parent), mPartner (nullptr)
170 {
171   // 0 as first parameter leaves the object unconnected
172   if (!partner)
173     return;
174 
175   // Check if the other object is already connected
176   if (partner && partner->mPartner)
177   {
178     qCWarning(GAMES_PRIVATE_KGAME) << ": Object is already connected!";
179     return;
180   }
181 
182   // Connect from us to that object
183   mPartner = partner;
184 
185   // Connect the other object to us
186   partner->mPartner = this;
187 }
188 
~KMessageDirect()189 KMessageDirect::~KMessageDirect ()
190 {
191   if (mPartner)
192   {
193     mPartner->mPartner = nullptr;
194     Q_EMIT mPartner->connectionBroken();
195   }
196 }
197 
isConnected() const198 bool KMessageDirect::isConnected () const
199 {
200   return mPartner != nullptr;
201 }
202 
send(const QByteArray & msg)203 void KMessageDirect::send (const QByteArray &msg)
204 {
205   if (mPartner)
206     Q_EMIT mPartner->received (msg);
207   else
208     qCCritical(GAMES_PRIVATE_KGAME) << ": Not yet connected!";
209 }
210 
211 
212 // ----------------------- KMessageProcess ---------------------------
213 
~KMessageProcess()214 KMessageProcess::~KMessageProcess()
215 {
216   qCDebug(GAMES_PRIVATE_KGAME) << "@@@KMessageProcess::Delete process";
217   if (mProcess)
218   {
219     mProcess->kill();
220     mProcess->deleteLater();
221     mProcess=nullptr;
222     // Maybe todo: delete mSendBuffer
223   }
224 }
KMessageProcess(QObject * parent,const QString & file)225 KMessageProcess::KMessageProcess(QObject *parent, const QString& file) : KMessageIO(parent)
226 {
227   // Start process
228   qCDebug(GAMES_PRIVATE_KGAME) << "@@@KMessageProcess::Start process";
229   mProcessName=file;
230   mProcess=new KProcess;
231   // Need both stdout and stderr as separate channels in the communication
232   mProcess-> setOutputChannelMode(KProcess::SeparateChannels);
233   int id=0;
234   *mProcess << mProcessName << QStringLiteral( "%1").arg(id);
235   qCDebug(GAMES_PRIVATE_KGAME) << "@@@KMessageProcess::Init:Id=" << id;
236   qCDebug(GAMES_PRIVATE_KGAME) << "@@@KMessgeProcess::Init:Processname:" << mProcessName;
237   connect(mProcess, &KProcess::readyReadStandardOutput, this, &KMessageProcess::slotReceivedStdout);
238   connect(mProcess, &KProcess::readyReadStandardError, this, &KMessageProcess::slotReceivedStderr);
239   connect(mProcess, static_cast<void (KProcess::*)(int, QProcess::ExitStatus)>(&KProcess::finished), this, &KMessageProcess::slotProcessExited);
240   mProcess->start();
241   mSendBuffer=nullptr;
242   mReceiveCount=0;
243   mReceiveBuffer.resize(1024);
244 }
isConnected() const245 bool KMessageProcess::isConnected() const
246 {
247   qCDebug(GAMES_PRIVATE_KGAME) << "@@@KMessageProcess::Is connected";
248   if (!mProcess)
249      return false;
250   return (mProcess->state() == QProcess::Running);
251 }
252 
253 // Send to process
send(const QByteArray & msg)254 void KMessageProcess::send(const QByteArray &msg)
255 {
256   qCDebug(GAMES_PRIVATE_KGAME) << "@@@KMessageProcess:: SEND("<<msg.size()<<") to process";
257   unsigned int size=msg.size()+2*sizeof(long);
258 
259   if (mProcess == nullptr) {
260     qCDebug(GAMES_PRIVATE_KGAME) << "@@@KMessageProcess:: cannot write to stdin, no process available";
261     return;
262   }
263 
264   char *tmpbuffer=new char[size];
265   long *p1=(long *)tmpbuffer;
266   long *p2=p1+1;
267   qCDebug(GAMES_PRIVATE_KGAME)  << "p1="<<p1 << "p2="<< p2;
268   memcpy(tmpbuffer+2*sizeof(long),msg.data(),msg.size());
269   *p1=0x4242aeae;
270   *p2=size;
271 
272   // no need to add it to a queue -> qiodevice is buffered
273   mProcess->write(tmpbuffer,size);
274   delete [] tmpbuffer;
275 }
276 
slotReceivedStderr()277 void KMessageProcess::slotReceivedStderr()
278 {
279   QByteArray ba;
280   qCDebug(GAMES_PRIVATE_KGAME)<<"@@@ KMessageProcess::slotReceivedStderr";
281 
282   mProcess->setReadChannel(QProcess::StandardError);
283   while(mProcess->canReadLine())
284   {
285     ba = mProcess->readLine();
286     if( ba.isEmpty() )
287       return;
288     ba.chop( 1 );   // remove QLatin1Char( '\n' )
289 
290     qCDebug(GAMES_PRIVATE_KGAME) << "KProcess (" << ba.size() << "):" << ba.constData();
291     Q_EMIT signalReceivedStderr(QLatin1String( ba ));
292     ba.clear();
293   };
294 }
295 
296 
slotReceivedStdout()297 void KMessageProcess::slotReceivedStdout()
298 {
299   mProcess->setReadChannel(QProcess::StandardOutput);
300   QByteArray ba = mProcess->readAll();
301   qCDebug(GAMES_PRIVATE_KGAME) << "$$$$$$ " << ": Received" << ba.size() << "bytes over inter process communication";
302 
303   // Resize receive buffer
304   while (mReceiveCount+ba.size()>=mReceiveBuffer.size()) mReceiveBuffer.resize(mReceiveBuffer.size()+1024);
305   // was 08/2007: mReceiveBuffer += ba;
306   std::copy(ba.begin(), ba.begin()+ba.size(), mReceiveBuffer.begin()+mReceiveCount);
307   mReceiveCount += ba.size();
308 
309   // Possible message
310   while (mReceiveCount>int(2*sizeof(long)))
311   {
312     long *p1=(long *)mReceiveBuffer.data();
313     long *p2=p1+1;
314     int len;
315     if (*p1!=0x4242aeae)
316     {
317       qCDebug(GAMES_PRIVATE_KGAME) << ": Cookie error...transmission failure...serious problem...";
318     }
319     len=(int)(*p2);
320     if (len<int(2*sizeof(long)))
321     {
322       qCDebug(GAMES_PRIVATE_KGAME) << ": Message size error";
323       break;
324     }
325     if (len<=mReceiveCount)
326     {
327       qCDebug(GAMES_PRIVATE_KGAME) << ": Got message with len" << len;
328 
329       QByteArray msg ;
330       msg.resize(len);
331     //  msg.setRawData(mReceiveBuffer.data()+2*sizeof(long),len-2*sizeof(long));
332 
333       std::copy(mReceiveBuffer.begin()+2*sizeof(long),mReceiveBuffer.begin()+len, msg.begin());
334 //       msg.duplicate(mReceiveBuffer.data()+2*sizeof(long),len-2*sizeof(long));
335       Q_EMIT received(msg);
336      // msg.resetRawData(mReceiveBuffer.data()+2*sizeof(long),len-2*sizeof(long));
337       // Shift buffer
338       if (len<mReceiveCount)
339       {
340         memmove(mReceiveBuffer.data(),mReceiveBuffer.data()+len,mReceiveCount-len);
341       }
342       mReceiveCount-=len;
343     }
344     else break;
345   }
346 }
347 
slotProcessExited(int exitCode,QProcess::ExitStatus)348 void KMessageProcess::slotProcessExited(int exitCode, QProcess::ExitStatus)
349 {
350   qCDebug(GAMES_PRIVATE_KGAME) << "Process exited (slot) with code" << exitCode;
351   Q_EMIT connectionBroken();
352   delete mProcess;
353   mProcess=nullptr;
354 }
355 
356 
357 
358