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 #ifndef _KMESSAGEIO_H_
13 #define _KMESSAGEIO_H_
14 
15 // own
16 #include "libkdegamesprivate_export.h"
17 // Qt
18 #include <QObject>
19 #include <QProcess>
20 #include <QString>
21 #include <QHostAddress>
22 #include <QLoggingCategory>
23 
24 /*
25     This macro shouldn't be here ideally. Already declared in kgame.h, but throws error if not placed here.
26 */
27 Q_DECLARE_LOGGING_CATEGORY(GAMES_PRIVATE_KGAME)
28 
29 class QTcpSocket;
30 class KProcess;
31 
32 
33 /**
34   \class KMessageIO kmessageio.h <KGame/KMessageIO>
35 
36   This abstract base class represents one end of a message connections
37   between two clients. Each client has one object of a subclass of
38   KMessageIO. Calling /e send() on one object will emit the signal
39   /e received() on the other object, and vice versa.
40 
41   For each type of connection (TCP/IP socket, COM port, direct connection
42   within the same class) a subclass of KMessageIO must be defined that
43   implements the pure virtual methods /e send() and /e isConnected(),
44   and sends the signals. (See /e KMessageSocket for an example implementation.)
45 
46   Two subclasses are already included: /e KMessageSocket (connection using
47   TCP/IP sockets) and /e KMessageDirect (connection using method calls, both
48   sides must be within the same process).
49 */
50 
51 class KDEGAMESPRIVATE_EXPORT KMessageIO : public QObject
52 {
53   Q_OBJECT
54 
55 public:
56   /**
57    * The usual QObject constructor, does nothing else.
58    **/
59   explicit KMessageIO (QObject *parent = nullptr);
60 
61   /**
62    * The usual destructor, does nothing special.
63    **/
64   ~KMessageIO () override;
65 
66   /**
67   * The runtime identification
68   */
rtti()69   virtual int rtti() const {return 0;}
70 
71   /**
72    * @return Whether this KMessageIO is a network IO or not.
73    **/
74   //virtual bool isNetwork () const = 0;
isNetwork()75   virtual bool isNetwork () const
76   {
77    qCCritical(GAMES_PRIVATE_KGAME) << "Calling PURE virtual isNetwork...BAD";
78    return false;
79   }
80 
81   /**
82     This method returns the status of the object, whether it is already
83     (or still) connected to another KMessageIO object or not.
84 
85     This is a pure virtual method, so it has to be implemented in a subclass
86     of KMessageIO.
87   */
88   //virtual bool isConnected () const = 0;
isConnected()89   virtual bool isConnected () const
90   {
91    qCCritical(GAMES_PRIVATE_KGAME) << "Calling PURE virtual isConnected...BAD";
92    return false;
93   }
94 
95   /**
96     Sets the ID number of this object. This number can for example be used to
97     distinguish several objects in a server.
98 
99     NOTE: Sometimes it is useful to let two connected KMessageIO objects
100     have the same ID number. You have to do so yourself, KMessageIO doesn't
101     change this value on its own!
102   */
103   void setId (quint32 id);
104 
105   /**
106     Queries the ID of this object.
107   */
108   quint32 id ();
109 
110   /**
111     @return 0 in the default implementation. Reimplemented in @ref KMessageSocket.
112   */
peerPort()113   virtual quint16 peerPort () const { return 0; }
114 
115   /**
116     @return "localhost" in the default implementation. Reimplemented in @ref KMessageSocket
117   */
peerName()118   virtual QString peerName () const { return QStringLiteral("localhost"); }
119 
120 
121 Q_SIGNALS:
122   /**
123     This signal is emitted when /e send() on the connected KMessageIO
124     object is called. The parameter contains the same data array in /e msg
125     as was used in /e send().
126   */
127   void received (const QByteArray &msg);
128 
129   /**
130     This signal is emitted when the connection is closed. This can be caused
131     by a hardware error (e.g. broken internet connection) or because the other
132     object was killed.
133 
134     Note: Sometimes a broken connection can be undetected for a long time,
135     or may never be detected at all. So don't rely on this signal!
136   */
137   void connectionBroken ();
138 
139 public Q_SLOTS:
140 
141   /**
142     This slot sends the data block in /e msg to the connected object, that will
143     emit /e received().
144 
145     For a concrete class, you have to subclass /e KMessageIO and overwrite this
146     method. In the subclass, implement this method as an ordinary method, not
147     as a slot! (Otherwise another slot would be defined. It would work, but uses
148     more memory and time.) See /e KMessageSocket for an example implementation.
149   */
150   virtual void send (const QByteArray &msg) = 0;
151 
152 protected:
153   quint32 m_id;
154 };
155 
156 
157 /**
158   \class KMessageSocket kmessageio.h <KGame/KMessageIO>
159 
160   This class implements the message communication using a TCP/IP socket. The
161   object can connect to a server socket, or can use an already connected socket.
162 */
163 
164 class KMessageSocket : public KMessageIO
165 {
166   Q_OBJECT
167 
168 public:
169   /**
170     Connects to a server socket on /e host with /e port. host can be an
171     numerical (e.g. "192.168.0.212") or symbolic (e.g. "wave.peter.org")
172     IP address. You can immediately use the /e sendSystem() and
173     /e sendBroadcast() methods. The messages are stored and sent to the
174     receiver after the connection is established.
175 
176     If the connection could not be established (e.g. unknown host or no server
177     socket at this port), the signal /e connectionBroken is emitted.
178   */
179   KMessageSocket (const QString& host, quint16 port, QObject *parent = nullptr );
180 
181   /**
182     Connects to a server socket on /e host with /e port. You can immediately
183     use the /e sendSystem() and /e sendBroadcast() methods. The messages are
184     stored and sent to the receiver after the connection is established.
185 
186     If the connection could not be established (e.g. unknown host or no server
187     socket at this port), the signal /e connectionBroken is emitted.
188   */
189   KMessageSocket (const QHostAddress &host, quint16 port, QObject *parent = nullptr);
190 
191   /**
192     Uses /e socket to do the communication.
193 
194     The socket should already be connected, or at least be in /e connecting
195     state.
196 
197     Note: The /e socket object is then owned by the /e KMessageSocket object.
198     So don't use it otherwise any more and don't delete it. It is deleted
199     together with this KMessageSocket object. (Use 0 as parent for the QSocket
200     object t ensure it is not deleted.)
201   */
202   explicit KMessageSocket (QTcpSocket *socket, QObject *parent = nullptr);
203 
204   /**
205     Uses the socket specified by the socket descriptor socketFD to do the
206     communication. The socket must already be connected.
207 
208     This constructor can be used with a QServerSocket within the (pure
209     virtual) method /e newConnection.
210 
211     Note: The socket is then owned by the /e KMessageSocket object. So don't
212     manipulate the socket afterwards, especially don't close it. The socket is
213     automatically closed when KMessageSocket is deleted.
214   */
215   explicit KMessageSocket (int socketFD, QObject *parent = nullptr);
216 
217   /**
218     Destructor, closes the socket.
219   */
220   ~KMessageSocket () override;
221 
222   /**
223   * The runtime identification
224   */
rtti()225   int rtti() const override {return 1;}
226 
227   /**
228     @return The port that this object is connected to. See QSocket::peerPort
229   */
230   quint16 peerPort () const override;
231 
232   /**
233     @return The hostname this object is connected to. See QSocket::peerName.
234   */
235   QString peerName () const override;
236 
237   /**
238     @return TRUE as this is a network IO.
239   */
isNetwork()240   bool isNetwork() const override { return true; }
241 
242   /**
243     Returns true if the socket is in state /e connected.
244   */
245   bool isConnected () const override;
246 
247   /**
248     Overwritten slot method from KMessageIO.
249 
250     Note: It is not declared as a slot method, since the slot is already
251     defined in KMessageIO as a virtual method.
252   */
253   void send (const QByteArray &msg) override;
254 
255 protected Q_SLOTS:
256   virtual void processNewData ();
257 
258 protected:
259   void initSocket ();
260   QTcpSocket *mSocket;
261   bool mAwaitingHeader;
262   quint32 mNextBlockLength;
263 
264   bool isRecursive;  // workaround for "bug" in QSocket, Qt 2.2.3 or older
265 };
266 
267 
268 /**
269   \class KMessageDirect kmessageio.h <KGame/KMessageIO>
270 
271   This class implements the message communication using function calls
272   directly. It can only be used when both sides of the message pipe are
273   within the same process. The communication is very fast.
274 
275   To establish a communication, you have to create two instances of
276   KMessageDirect, the first one with no parameters in the constructor,
277   the second one with the first as parameter:
278 
279   /code
280     KMessageDirect *peer1, *peer2;
281     peer1 = new KMessageDirect ();       // unconnected
282     peer2 = new KMessageDirect (peer1);  // connect with peer1
283   /endcode
284 
285   The connection is only closed when one of the instances is deleted.
286 */
287 
288 class KMessageDirect : public KMessageIO
289 {
290   Q_OBJECT
291 
292 public:
293   /**
294     Creates an object and connects it to the object given in the first
295     parameter. Use 0 as first parameter to create an unconnected object,
296     that is later connected.
297 
298     If that object is already connected, the object remains unconnected.
299   */
300   explicit KMessageDirect (KMessageDirect *partner = nullptr, QObject *parent = nullptr);
301 
302   /**
303     Destructor, closes the connection.
304   */
305   ~KMessageDirect () override;
306 
307   /**
308   * The runtime identification
309   */
rtti()310   int rtti() const override {return 2;}
311 
312 
313   /**
314     @return FALSE as this is no network IO.
315   */
isNetwork()316   bool isNetwork() const override { return false; }
317 
318   /**
319     Returns true, if the object is connected to another instance.
320 
321     If you use the first constructor, the object is unconnected unless another
322     object is created with this one as parameter.
323 
324     The connection can only be closed by deleting one of the objects.
325   */
326   bool isConnected () const override;
327 
328   /**
329     Overwritten slot method from KMessageIO.
330 
331     Note: It is not declared as a slot method, since the slot is already
332     defined in KMessageIO as a virtual method.
333   */
334   void send (const QByteArray &msg) override;
335 
336 protected:
337   KMessageDirect *mPartner;
338 };
339 
340 /**
341  * \class KMessageProcess kmessageio.h <KGame/KMessageIO>
342  */
343 class KMessageProcess : public KMessageIO
344 {
345   Q_OBJECT
346 
347   public:
348     KMessageProcess(QObject *parent, const QString& file);
349     ~KMessageProcess() override;
350     bool isConnected() const override;
351     void send (const QByteArray &msg) override;
352 
353     /**
354       @return FALSE as this is no network IO.
355     */
isNetwork()356     bool isNetwork() const override { return false; }
357 
358   /**
359   * The runtime identification
360   */
rtti()361   int rtti() const override {return 3;}
362 
363 
364 
365   public Q_SLOTS:
366   void  slotReceivedStdout();
367   void  slotReceivedStderr();
368   void  slotProcessExited(int, QProcess::ExitStatus);
369 
370   Q_SIGNALS:
371     void signalReceivedStderr(const QString &msg);
372 
373   private:
374     QString mProcessName;
375     KProcess *mProcess;
376     QByteArray* mSendBuffer;
377     QByteArray mReceiveBuffer;
378     int mReceiveCount;
379 };
380 
381 #endif
382 
383