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