Line data Source code
1 : /* Copyright (C) 2021 Wildfire Games.
2 : * This file is part of 0 A.D.
3 : *
4 : * 0 A.D. is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 2 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * 0 A.D. is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16 : */
17 :
18 : #ifndef INCLUDED_GLOOXWRAPPER_H
19 : #define INCLUDED_GLOOXWRAPPER_H
20 :
21 : /*
22 :
23 : The gloox API uses various STL types (std::string, std::list, etc), and
24 : it has functions that acquire/release ownership of objects and expect the
25 : library's user's 'new'/'delete' functions to be compatible with the library's.
26 :
27 : These assumptions are invalid when the game and library are built with
28 : different compiler versions (or the same version with different build flags):
29 : the STL types have different layouts, and new/delete can use different heaps.
30 :
31 : We want to let people build the game on Windows with any compiler version
32 : (any supported Visual Studio version, and debug vs release), without requiring
33 : them to rebuild the gloox library themselves. And we don't want to provide ~8
34 : different prebuilt versions of the library.
35 :
36 : glooxwrapper replaces the gloox API with a version that is safe to use across
37 : compiler versions. glooxwrapper and gloox must be compiled together with the
38 : same version, but the resulting library can be used with any other compiler.
39 :
40 : This is the small subset of the API that the game currently uses, with no
41 : attempt to be comprehensive.
42 :
43 : General design and rules:
44 :
45 : * There is a strict boundary between gloox+glooxwrapper.cpp, and the game
46 : code that includes glooxwrapper.h.
47 : Objects allocated with new/delete on one side of the boundary must be
48 : freed/allocated on the same side.
49 : Objects allocated with glooxwrapper_alloc()/glooxwrapper_delete() can be
50 : freely shared across the boundary.
51 :
52 : * glooxwrapper.h and users of glooxwrapper must not use any types from
53 : the gloox namespace, except for enums.
54 :
55 : * std::string is replaced with glooxwrapper::string,
56 : std::list with glooxwrapper::list
57 :
58 : * Most glooxwrapper classes are simple wrappers around gloox classes.
59 : Some always take ownership of their wrapped gloox object (i.e. their
60 : destructor will delete the wrapped object too); some never do; and some
61 : can be used either way (indicated by an m_Owned field).
62 :
63 : */
64 :
65 : #if OS_WIN
66 : # include "lib/sysdep/os/win/win.h"
67 : // Prevent gloox pulling in windows.h
68 : # define _WINDOWS_
69 : #endif
70 :
71 : #include <gloox/client.h>
72 : #include <gloox/mucroom.h>
73 : #include <gloox/registration.h>
74 : #include <gloox/message.h>
75 : #include <gloox/jinglecontent.h>
76 : #include <gloox/jingleiceudp.h>
77 : #include <gloox/jinglesessionhandler.h>
78 : #include <gloox/jinglesessionmanager.h>
79 :
80 : #include <cstring>
81 :
82 : // Gloox leaves some #define up, we need to undefine them.
83 : #undef lookup
84 : #undef lookup2
85 : #undef deflookup
86 : #undef deflookup2
87 :
88 : #if OS_WIN
89 : #define GLOOXWRAPPER_API __declspec(dllexport)
90 : #else
91 : #define GLOOXWRAPPER_API
92 : #endif
93 :
94 : namespace glooxwrapper
95 : {
96 : class Client;
97 : class DataForm;
98 : class DelayedDelivery;
99 : class Disco;
100 : class IQ;
101 : class JID;
102 : class MUCRoom;
103 : class MUCRoomConfigHandler;
104 : class Message;
105 : class MessageSession;
106 : class OOB;
107 : class Presence;
108 : class StanzaError;
109 : class StanzaExtension;
110 : class Tag;
111 :
112 : class ClientImpl;
113 : class MUCRoomHandlerWrapper;
114 : class SessionHandlerWrapper;
115 :
116 : GLOOXWRAPPER_API void* glooxwrapper_alloc(size_t size);
117 : GLOOXWRAPPER_API void glooxwrapper_free(void* p);
118 :
119 : class string
120 : {
121 : private:
122 : size_t m_Size;
123 : char* m_Data;
124 : public:
125 0 : string()
126 0 : {
127 0 : m_Size = 0;
128 0 : m_Data = (char*)glooxwrapper_alloc(1);
129 0 : m_Data[0] = '\0';
130 0 : }
131 :
132 0 : string(const string& str)
133 0 : {
134 0 : m_Size = str.m_Size;
135 0 : m_Data = (char*)glooxwrapper_alloc(m_Size + 1);
136 0 : memcpy(m_Data, str.m_Data, m_Size + 1);
137 0 : }
138 :
139 0 : string(const std::string& str) : m_Data(NULL)
140 : {
141 0 : m_Size = str.size();
142 0 : m_Data = (char*)glooxwrapper_alloc(m_Size + 1);
143 0 : memcpy(m_Data, str.c_str(), m_Size + 1);
144 0 : }
145 :
146 0 : string(const char* str)
147 0 : {
148 0 : m_Size = strlen(str);
149 0 : m_Data = (char*)glooxwrapper_alloc(m_Size + 1);
150 0 : memcpy(m_Data, str, m_Size + 1);
151 0 : }
152 :
153 0 : string& operator=(const string& str)
154 : {
155 0 : if (this != &str)
156 : {
157 0 : glooxwrapper_free(m_Data);
158 0 : m_Size = str.m_Size;
159 0 : m_Data = (char*)glooxwrapper_alloc(m_Size + 1);
160 0 : memcpy(m_Data, str.m_Data, m_Size + 1);
161 : }
162 0 : return *this;
163 : }
164 :
165 0 : ~string()
166 0 : {
167 0 : glooxwrapper_free(m_Data);
168 0 : }
169 :
170 : /**
171 : * Gloox strings are UTF encoded, so don't forget to decode it before passing it to the GUI!
172 : */
173 0 : std::string to_string() const
174 : {
175 0 : return std::string(m_Data, m_Size);
176 : }
177 :
178 0 : const char* c_str() const
179 : {
180 0 : return m_Data;
181 : }
182 :
183 0 : bool empty() const
184 : {
185 0 : return m_Size == 0;
186 : }
187 :
188 0 : bool operator==(const char* str) const
189 : {
190 0 : return strcmp(m_Data, str) == 0;
191 : }
192 :
193 0 : bool operator!=(const char* str) const
194 : {
195 0 : return strcmp(m_Data, str) != 0;
196 : }
197 :
198 0 : bool operator==(const string& str) const
199 : {
200 0 : return strcmp(m_Data, str.m_Data) == 0;
201 : }
202 :
203 0 : bool operator<(const string& str) const
204 : {
205 0 : return strcmp(m_Data, str.m_Data) < 0;
206 : }
207 : };
208 :
209 : static inline std::ostream& operator<<(std::ostream& stream, const string& string)
210 : {
211 : return stream << string.c_str();
212 : }
213 :
214 : template<typename T>
215 : class list
216 : {
217 : private:
218 : struct node
219 : {
220 0 : node(const T& item) : m_Item(item), m_Next(NULL) {}
221 : T m_Item;
222 : node* m_Next;
223 : };
224 : node* m_Head;
225 : node* m_Tail;
226 :
227 : public:
228 : struct const_iterator
229 : {
230 : const node* m_Node;
231 0 : const_iterator(const node* n) : m_Node(n) {}
232 0 : bool operator!=(const const_iterator& it) { return m_Node != it.m_Node; }
233 0 : const_iterator& operator++() { m_Node = m_Node->m_Next; return *this; }
234 0 : const T& operator*() { return m_Node->m_Item; }
235 : };
236 0 : const_iterator begin() const { return const_iterator(m_Head); }
237 0 : const_iterator end() const { return const_iterator(NULL); }
238 :
239 0 : list() : m_Head(NULL), m_Tail(NULL) {}
240 :
241 : list(const list& src) : m_Head(NULL), m_Tail(NULL)
242 : {
243 : *this = src;
244 : }
245 :
246 : list& operator=(const list& src)
247 : {
248 : if (this != &src)
249 : {
250 : clear();
251 : for (node* n = src.m_Head; n; n = n->m_Next)
252 : push_back(n->m_Item);
253 : }
254 : return *this;
255 : }
256 :
257 0 : ~list()
258 : {
259 0 : clear();
260 0 : }
261 :
262 0 : void push_back(const T& item)
263 : {
264 0 : node* n = new (glooxwrapper_alloc(sizeof(node))) node(item);
265 0 : if (m_Tail)
266 0 : m_Tail->m_Next = n;
267 0 : m_Tail = n;
268 0 : if (!m_Head)
269 0 : m_Head = n;
270 0 : }
271 :
272 0 : void clear()
273 : {
274 0 : node* n = m_Head;
275 0 : while (n)
276 : {
277 0 : node* next = n->m_Next;
278 0 : glooxwrapper_free(n);
279 0 : n = next;
280 : }
281 0 : m_Head = m_Tail = NULL;
282 0 : }
283 :
284 : const T& front() const
285 : {
286 : return *begin();
287 : }
288 : };
289 :
290 : typedef glooxwrapper::list<Tag*> TagList;
291 : typedef glooxwrapper::list<const Tag*> ConstTagList;
292 :
293 0 : struct CertInfo
294 : {
295 : int status;
296 : bool chain;
297 : string issuer;
298 : string server;
299 : int date_from;
300 : int date_to;
301 : string protocol;
302 : string cipher;
303 : string mac;
304 : string compression;
305 : };
306 :
307 0 : struct RegistrationFields
308 : {
309 : string username;
310 : string nick;
311 : string password;
312 : string name;
313 : string first;
314 : string last;
315 : string email;
316 : string address;
317 : string city;
318 : string state;
319 : string zip;
320 : string phone;
321 : string url;
322 : string date;
323 : string misc;
324 : string text;
325 : };
326 :
327 0 : struct MUCRoomParticipant
328 : {
329 : JID* nick;
330 : gloox::MUCRoomAffiliation affiliation;
331 : gloox::MUCRoomRole role;
332 : JID* jid;
333 : int flags;
334 : string reason;
335 : JID* actor;
336 : string newNick;
337 : string status;
338 : JID* alternate;
339 : };
340 :
341 :
342 0 : class GLOOXWRAPPER_API ConnectionListener
343 : {
344 : public:
345 0 : virtual ~ConnectionListener() {}
346 : virtual void onConnect() = 0;
347 : virtual void onDisconnect(gloox::ConnectionError e) = 0;
348 : virtual bool onTLSConnect(const CertInfo& info) = 0;
349 : };
350 :
351 0 : class GLOOXWRAPPER_API IqHandler
352 : {
353 : public:
354 0 : virtual ~IqHandler() {}
355 : virtual bool handleIq(const IQ& iq) = 0;
356 : virtual void handleIqID(const IQ& iq, int context) = 0;
357 : };
358 :
359 0 : class GLOOXWRAPPER_API MessageHandler
360 : {
361 : public:
362 0 : virtual ~MessageHandler() {}
363 : virtual void handleMessage(const Message& msg, MessageSession* session = 0) = 0; // MessageSession not supported
364 : };
365 :
366 0 : class GLOOXWRAPPER_API MUCRoomHandler
367 : {
368 : public:
369 0 : virtual ~MUCRoomHandler() {}
370 : virtual void handleMUCParticipantPresence(MUCRoom& room, const MUCRoomParticipant participant, const Presence& presence) = 0;
371 : virtual void handleMUCMessage(MUCRoom& room, const Message& msg, bool priv) = 0;
372 : virtual void handleMUCError(MUCRoom& room, gloox::StanzaError error) = 0;
373 : virtual void handleMUCSubject(MUCRoom& room, const string& nick, const string& subject) = 0;
374 : };
375 :
376 0 : class GLOOXWRAPPER_API RegistrationHandler
377 : {
378 : public:
379 0 : virtual ~RegistrationHandler() {}
380 : virtual void handleRegistrationFields(const JID& from, int fields, string instructions) = 0;
381 : virtual void handleAlreadyRegistered(const JID& from) = 0;
382 : virtual void handleRegistrationResult(const JID& from, gloox::RegistrationResult regResult) = 0;
383 : virtual void handleDataForm(const JID& from, const DataForm& form) = 0; // DataForm not supported
384 : virtual void handleOOB(const JID& from, const OOB& oob) = 0; // OOB not supported
385 : };
386 :
387 : class GLOOXWRAPPER_API StanzaExtension
388 : {
389 : public:
390 0 : StanzaExtension(int type) : m_extensionType(type) {}
391 0 : virtual ~StanzaExtension() {}
392 : virtual const string& filterString() const = 0;
393 : virtual StanzaExtension* newInstance(const Tag* tag) const = 0;
394 : virtual glooxwrapper::Tag* tag() const = 0;
395 : virtual StanzaExtension* clone() const = 0;
396 :
397 0 : int extensionType() const { return m_extensionType; }
398 : private:
399 : int m_extensionType;
400 : };
401 :
402 :
403 : class GLOOXWRAPPER_API Client
404 : {
405 : NONCOPYABLE(Client);
406 : private:
407 : gloox::Client* m_Wrapped;
408 : ClientImpl* m_Impl;
409 : Disco* m_DiscoWrapper;
410 :
411 : public:
412 0 : gloox::Client* getWrapped() { return m_Wrapped; }
413 :
414 : bool connect(bool block = true);
415 : gloox::ConnectionError recv(int timeout = -1);
416 : const string getID() const;
417 : const string getJID() const;
418 : void send(const IQ& iq);
419 :
420 : void setTls(gloox::TLSPolicy tls);
421 : void setCompression(bool compression);
422 :
423 : void setSASLMechanisms(int mechanisms);
424 : void registerStanzaExtension(StanzaExtension* ext);
425 : void registerConnectionListener(ConnectionListener* cl);
426 : void registerIqHandler(IqHandler* ih, int exttype);
427 : void registerMessageHandler(MessageHandler* mh);
428 :
429 : bool removePresenceExtension(int type);
430 :
431 0 : Disco* disco() const { return m_DiscoWrapper; }
432 :
433 : Client(const string& server);
434 : Client(const JID& jid, const string& password, int port = -1);
435 : ~Client();
436 :
437 : void setPresence(gloox::Presence::PresenceType pres, int priority, const string& status = "");
438 : void disconnect();
439 : };
440 :
441 : class GLOOXWRAPPER_API DelayedDelivery
442 : {
443 : NONCOPYABLE(DelayedDelivery);
444 : private:
445 : const gloox::DelayedDelivery* m_Wrapped;
446 : public:
447 : DelayedDelivery(const gloox::DelayedDelivery* wrapped);
448 : const string stamp() const;
449 : };
450 :
451 : class GLOOXWRAPPER_API Disco
452 : {
453 : NONCOPYABLE(Disco);
454 : private:
455 : gloox::Disco* m_Wrapped;
456 : public:
457 : Disco(gloox::Disco* wrapped);
458 : void setVersion(const string& name, const string& version, const string& os = "");
459 : void setIdentity(const string& category, const string& type, const string& name = "");
460 : };
461 :
462 : class GLOOXWRAPPER_API IQ
463 : {
464 : NONCOPYABLE(IQ);
465 : private:
466 : gloox::IQ* m_Wrapped;
467 : bool m_Owned;
468 : public:
469 0 : const gloox::IQ& getWrapped() const { return *m_Wrapped; }
470 0 : IQ(const gloox::IQ& iq) : m_Wrapped(const_cast<gloox::IQ*>(&iq)), m_Owned(false) { }
471 :
472 : IQ(gloox::IQ::IqType type, const JID& to, const string& id);
473 : ~IQ();
474 :
475 : void addExtension(const StanzaExtension* se);
476 : const StanzaExtension* findExtension(int type) const;
477 :
478 : template<class T>
479 0 : inline const T* findExtension(int type) const
480 : {
481 0 : return static_cast<const T*>(findExtension(type));
482 : }
483 :
484 : gloox::IQ::IqType subtype() const;
485 : const string id() const;
486 : const gloox::JID& from() const;
487 :
488 : gloox::StanzaError error_error() const; // wrapper for ->error()->error()
489 : Tag* tag() const;
490 : };
491 :
492 : class GLOOXWRAPPER_API JID
493 : {
494 : NONCOPYABLE(JID);
495 : private:
496 : gloox::JID* m_Wrapped;
497 : bool m_Owned;
498 : void init(const char* data, size_t len);
499 : public:
500 0 : const gloox::JID& getWrapped() const { return *m_Wrapped; }
501 0 : JID(const gloox::JID& jid) : m_Wrapped(const_cast<gloox::JID*>(&jid)), m_Owned(false) { }
502 :
503 : JID();
504 : JID(const string& jid);
505 0 : JID(const std::string& jid) { init(jid.c_str(), jid.size()); }
506 : ~JID();
507 :
508 : string username() const;
509 : string resource() const;
510 : };
511 :
512 : class GLOOXWRAPPER_API Message
513 : {
514 : NONCOPYABLE(Message);
515 : private:
516 : gloox::Message* m_Wrapped;
517 : bool m_Owned;
518 : glooxwrapper::JID m_From;
519 : glooxwrapper::DelayedDelivery* m_DelayedDelivery;
520 : public:
521 : Message(gloox::Message* wrapped, bool owned);
522 : ~Message();
523 : gloox::Message::MessageType subtype() const;
524 : const JID& from() const;
525 : string body() const;
526 : string subject(const string& lang = "default") const;
527 : string thread() const;
528 : const glooxwrapper::DelayedDelivery* when() const;
529 : };
530 :
531 : class GLOOXWRAPPER_API MUCRoom
532 : {
533 : NONCOPYABLE(MUCRoom);
534 : private:
535 : gloox::MUCRoom* m_Wrapped;
536 : MUCRoomHandlerWrapper* m_HandlerWrapper;
537 : bool m_Owned;
538 : public:
539 : MUCRoom(gloox::MUCRoom* room, bool owned);
540 : MUCRoom(Client* parent, const JID& nick, MUCRoomHandler* mrh, MUCRoomConfigHandler* mrch = 0);
541 : ~MUCRoom();
542 : const string nick() const;
543 : const string name() const;
544 : const string service() const;
545 : void join(gloox::Presence::PresenceType type = gloox::Presence::Available, const string& status = "", int priority = 0);
546 : void leave(const string& msg = "");
547 : void send(const string& message);
548 : void setNick(const string& nick);
549 : void setPresence(gloox::Presence::PresenceType presence, const string& msg = "");
550 : void setRequestHistory(int value, gloox::MUCRoom::HistoryRequestType type);
551 : void kick(const string& nick, const string& reason);
552 : void ban(const string& nick, const string& reason);
553 : };
554 :
555 : class GLOOXWRAPPER_API Presence
556 : {
557 : gloox::Presence::PresenceType m_Presence;
558 : public:
559 0 : Presence(gloox::Presence::PresenceType presence) : m_Presence(presence) {}
560 0 : gloox::Presence::PresenceType presence() const { return m_Presence; }
561 : };
562 :
563 : class GLOOXWRAPPER_API Registration
564 : {
565 : NONCOPYABLE(Registration);
566 : private:
567 : gloox::Registration* m_Wrapped;
568 : std::list<std::shared_ptr<gloox::RegistrationHandler> > m_RegistrationHandlers;
569 : public:
570 : Registration(Client* parent);
571 : ~Registration();
572 : void fetchRegistrationFields();
573 : bool createAccount(int fields, const RegistrationFields& values);
574 : void registerRegistrationHandler(RegistrationHandler* rh);
575 : };
576 :
577 : class GLOOXWRAPPER_API Tag
578 : {
579 : NONCOPYABLE(Tag);
580 : private:
581 : gloox::Tag* m_Wrapped;
582 : bool m_Owned;
583 :
584 : Tag(const string& name);
585 : Tag(const string& name, const string& cdata);
586 0 : Tag(gloox::Tag* wrapped, bool owned) : m_Wrapped(wrapped), m_Owned(owned) {}
587 : ~Tag();
588 :
589 : public:
590 : // Internal use:
591 : gloox::Tag* getWrapped() { return m_Wrapped; }
592 0 : gloox::Tag* stealWrapped() { m_Owned = false; return m_Wrapped; }
593 : static Tag* allocate(gloox::Tag* wrapped, bool owned);
594 :
595 : // Instead of using new/delete, Tags must be allocated/freed with these functions
596 : static Tag* allocate(const string& name);
597 : static Tag* allocate(const string& name, const string& cdata);
598 : static void free(const Tag* tag);
599 :
600 : bool addAttribute(const string& name, const string& value);
601 : string findAttribute(const string& name) const;
602 : Tag* clone() const;
603 : string xmlns() const;
604 : bool setXmlns(const string& xmlns);
605 : string xml() const;
606 : void addChild(Tag* child);
607 : string name() const;
608 : string cdata() const;
609 : const Tag* findTag_clone(const string& expression) const; // like findTag but must be Tag::free()d
610 : ConstTagList findTagList_clone(const string& expression) const; // like findTagList but each tag must be Tag::free()d
611 : };
612 :
613 : /**
614 : * See XEP-0166: Jingle and https://camaya.net/api/gloox/namespacegloox_1_1Jingle.html.
615 : */
616 : namespace Jingle
617 : {
618 :
619 : class GLOOXWRAPPER_API Plugin
620 : {
621 : protected:
622 : const gloox::Jingle::Plugin* m_Wrapped;
623 : bool m_Owned;
624 :
625 : public:
626 0 : Plugin(const gloox::Jingle::Plugin* wrapped, bool owned) : m_Wrapped(wrapped), m_Owned(owned) {}
627 :
628 : virtual ~Plugin();
629 :
630 : const Plugin findPlugin(int type) const;
631 : const gloox::Jingle::Plugin* getWrapped() const { return m_Wrapped; }
632 : };
633 :
634 : typedef list<const Plugin*> PluginList;
635 :
636 : /**
637 : * See XEP-0176: Jingle ICE-UDP Transport Method
638 : */
639 : class GLOOXWRAPPER_API ICEUDP
640 : {
641 : public:
642 0 : struct Candidate {
643 : string ip;
644 : int port;
645 : };
646 : private:
647 : // Class not implemented as it is not used.
648 : ICEUDP() = delete;
649 : };
650 :
651 : class GLOOXWRAPPER_API Session
652 : {
653 : protected:
654 : gloox::Jingle::Session* m_Wrapped;
655 : bool m_Owned;
656 :
657 : public:
658 : class GLOOXWRAPPER_API Jingle
659 : {
660 : private:
661 : const gloox::Jingle::Session::Jingle* m_Wrapped;
662 : bool m_Owned;
663 : public:
664 0 : Jingle(const gloox::Jingle::Session::Jingle* wrapped, bool owned) : m_Wrapped(wrapped), m_Owned(owned) {}
665 0 : ~Jingle()
666 0 : {
667 0 : if (m_Owned)
668 0 : delete m_Wrapped;
669 0 : }
670 : ICEUDP::Candidate getCandidate() const;
671 : };
672 :
673 : Session(gloox::Jingle::Session* wrapped, bool owned);
674 : ~Session();
675 :
676 : bool sessionInitiate(const char* ipStr, uint16_t port);
677 : };
678 :
679 0 : class GLOOXWRAPPER_API SessionHandler
680 : {
681 : public:
682 0 : virtual ~SessionHandler() {}
683 : virtual void handleSessionAction(gloox::Jingle::Action action, Session& session, const Session::Jingle& jingle) = 0;
684 : };
685 :
686 : }
687 :
688 : class GLOOXWRAPPER_API SessionManager
689 : {
690 : private:
691 : gloox::Jingle::SessionManager* m_Wrapped;
692 : SessionHandlerWrapper* m_HandlerWrapper;
693 :
694 : public:
695 : SessionManager(Client* parent, Jingle::SessionHandler* sh);
696 : ~SessionManager();
697 : void registerPlugins();
698 : Jingle::Session createSession(const JID& callee);
699 : };
700 :
701 : }
702 :
703 : #endif // INCLUDED_GLOOXWRAPPER_H
|