1 /* Copyright (C) 2018 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 (VC2008, 2010, 2012, 2013, and debug vs release), without requiring them to
33 rebuild the gloox library themselves. And we don't want to provide ~8 different
34 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 #if OS_WIN
83 #define GLOOXWRAPPER_API __declspec(dllexport)
84 #else
85 #define GLOOXWRAPPER_API
86 #endif
87 
88 namespace glooxwrapper
89 {
90 	class Client;
91 	class DataForm;
92 	class DelayedDelivery;
93 	class Disco;
94 	class IQ;
95 	class JID;
96 	class MUCRoom;
97 	class MUCRoomConfigHandler;
98 	class Message;
99 	class MessageSession;
100 	class OOB;
101 	class Presence;
102 	class StanzaError;
103 	class StanzaExtension;
104 	class Tag;
105 
106 	class ClientImpl;
107 	class MUCRoomHandlerWrapper;
108 	class SessionHandlerWrapper;
109 
110 	GLOOXWRAPPER_API void* glooxwrapper_alloc(size_t size);
111 	GLOOXWRAPPER_API void glooxwrapper_free(void* p);
112 
113 	class string
114 	{
115 	private:
116 		size_t m_Size;
117 		char* m_Data;
118 	public:
string()119 		string()
120 		{
121 			m_Size = 0;
122 			m_Data = (char*)glooxwrapper_alloc(1);
123 			m_Data[0] = '\0';
124 		}
125 
string(const string & str)126 		string(const string& str)
127 		{
128 			m_Size = str.m_Size;
129 			m_Data = (char*)glooxwrapper_alloc(m_Size + 1);
130 			memcpy(m_Data, str.m_Data, m_Size + 1);
131 		}
132 
string(const std::string & str)133 		string(const std::string& str) : m_Data(NULL)
134 		{
135 			m_Size = str.size();
136 			m_Data = (char*)glooxwrapper_alloc(m_Size + 1);
137 			memcpy(m_Data, str.c_str(), m_Size + 1);
138 		}
139 
string(const char * str)140 		string(const char* str)
141 		{
142 			m_Size = strlen(str);
143 			m_Data = (char*)glooxwrapper_alloc(m_Size + 1);
144 			memcpy(m_Data, str, m_Size + 1);
145 		}
146 
147 		string& operator=(const string& str)
148 		{
149 			if (this != &str)
150 			{
151 				glooxwrapper_free(m_Data);
152 				m_Size = str.m_Size;
153 				m_Data = (char*)glooxwrapper_alloc(m_Size + 1);
154 				memcpy(m_Data, str.m_Data, m_Size + 1);
155 			}
156 			return *this;
157 		}
158 
~string()159 		~string()
160 		{
161 			glooxwrapper_free(m_Data);
162 		}
163 
to_string()164 		std::string to_string() const
165 		{
166 			return std::string(m_Data, m_Size);
167 		}
168 
c_str()169 		const char* c_str() const
170 		{
171 			return m_Data;
172 		}
173 
empty()174 		bool empty() const
175 		{
176 			return m_Size == 0;
177 		}
178 
179 		bool operator==(const char* str) const
180 		{
181 			return strcmp(m_Data, str) == 0;
182 		}
183 
184 		bool operator!=(const char* str) const
185 		{
186 			return strcmp(m_Data, str) != 0;
187 		}
188 	};
189 
190 	static inline std::ostream& operator<<(std::ostream& stream, const string& string)
191 	{
192 		return stream << string.c_str();
193 	}
194 
195 	template<typename T>
196 	class list
197 	{
198 	private:
199 		struct node
200 		{
nodenode201 			node(const T& item) : m_Item(item), m_Next(NULL) {}
202 			T m_Item;
203 			node* m_Next;
204 		};
205 		node* m_Head;
206 		node* m_Tail;
207 
208 	public:
209 		struct const_iterator
210 		{
211 			const node* m_Node;
const_iteratorconst_iterator212 			const_iterator(const node* n) : m_Node(n) {}
213 			bool operator!=(const const_iterator& it) { return m_Node != it.m_Node; }
214 			const_iterator& operator++() { m_Node = m_Node->m_Next; return *this; }
215 			const T& operator*() { return m_Node->m_Item; }
216 		};
begin()217 		const_iterator begin() const { return const_iterator(m_Head); }
end()218 		const_iterator end() const { return const_iterator(NULL); }
219 
list()220 		list() : m_Head(NULL), m_Tail(NULL) {}
221 
list(const list & src)222 		list(const list& src) : m_Head(NULL), m_Tail(NULL)
223 		{
224 			*this = src;
225 		}
226 
227 		list& operator=(const list& src)
228 		{
229 			if (this != &src)
230 			{
231 				clear();
232 				for (node* n = src.m_Head; n; n = n->m_Next)
233 					push_back(n->m_Item);
234 			}
235 			return *this;
236 		}
237 
~list()238 		~list()
239 		{
240 			clear();
241 		}
242 
push_back(const T & item)243 		void push_back(const T& item)
244 		{
245 			node* n = new (glooxwrapper_alloc(sizeof(node))) node(item);
246 			if (m_Tail)
247 				m_Tail->m_Next = n;
248 			m_Tail = n;
249 			if (!m_Head)
250 				m_Head = n;
251 		}
252 
clear()253 		void clear()
254 		{
255 			node* n = m_Head;
256 			while (n)
257 			{
258 				node* next = n->m_Next;
259 				glooxwrapper_free(n);
260 				n = next;
261 			}
262 			m_Head = m_Tail = NULL;
263 		}
264 
front()265 		const T& front() const
266 		{
267 			return *begin();
268 		}
269 	};
270 
271 	typedef glooxwrapper::list<Tag*> TagList;
272 	typedef glooxwrapper::list<const Tag*> ConstTagList;
273 
274 	struct CertInfo
275 	{
276 		int status;
277 		bool chain;
278 		string issuer;
279 		string server;
280 		int date_from;
281 		int date_to;
282 		string protocol;
283 		string cipher;
284 		string mac;
285 		string compression;
286 	};
287 
288 	struct RegistrationFields
289 	{
290 		string username;
291 		string nick;
292 		string password;
293 		string name;
294 		string first;
295 		string last;
296 		string email;
297 		string address;
298 		string city;
299 		string state;
300 		string zip;
301 		string phone;
302 		string url;
303 		string date;
304 		string misc;
305 		string text;
306 	};
307 
308 	struct MUCRoomParticipant
309 	{
310 		JID* nick;
311 		gloox::MUCRoomAffiliation affiliation;
312 		gloox::MUCRoomRole role;
313 		JID* jid;
314 		int flags;
315 		string reason;
316 		JID* actor;
317 		string newNick;
318 		string status;
319 		JID* alternate;
320 	};
321 
322 
323 	class GLOOXWRAPPER_API ConnectionListener
324 	{
325 	public:
~ConnectionListener()326 		virtual ~ConnectionListener() {}
327 		virtual void onConnect() = 0;
328 		virtual void onDisconnect(gloox::ConnectionError e) = 0;
329 		virtual bool onTLSConnect(const CertInfo& info) = 0;
330 	};
331 
332 	class GLOOXWRAPPER_API IqHandler
333 	{
334 	public:
~IqHandler()335 		virtual ~IqHandler() {}
336 		virtual bool handleIq(const IQ& iq) = 0;
337 		virtual void handleIqID(const IQ& iq, int context) = 0;
338 	};
339 
340 	class GLOOXWRAPPER_API MessageHandler
341 	{
342 	public:
~MessageHandler()343 		virtual ~MessageHandler() {}
344 		virtual void handleMessage(const Message& msg, MessageSession* session = 0) = 0; // MessageSession not supported
345 	};
346 
347 	class GLOOXWRAPPER_API MUCRoomHandler
348 	{
349 	public:
~MUCRoomHandler()350 		virtual ~MUCRoomHandler() {}
351 		virtual void handleMUCParticipantPresence(MUCRoom* room, const MUCRoomParticipant participant, const Presence& presence) = 0; // MUCRoom not supported
352 		virtual void handleMUCMessage(MUCRoom* room, const Message& msg, bool priv) = 0; // MUCRoom not supported
353 		virtual void handleMUCError(MUCRoom* room, gloox::StanzaError error) = 0; // MUCRoom not supported
354 		virtual void handleMUCSubject(MUCRoom* room, const string& nick, const string& subject) = 0; // MUCRoom not supported
355 	};
356 
357 	class GLOOXWRAPPER_API RegistrationHandler
358 	{
359 	public:
~RegistrationHandler()360 		virtual ~RegistrationHandler() {}
361 		virtual void handleRegistrationFields(const JID& from, int fields, string instructions) = 0;
362 		virtual void handleAlreadyRegistered(const JID& from) = 0;
363 		virtual void handleRegistrationResult(const JID& from, gloox::RegistrationResult regResult) = 0;
364 		virtual void handleDataForm(const JID& from, const DataForm& form) = 0; // DataForm not supported
365 		virtual void handleOOB(const JID& from, const OOB& oob) = 0; // OOB not supported
366 	};
367 
368 	class GLOOXWRAPPER_API StanzaExtension
369 	{
370 	public:
StanzaExtension(int type)371 		StanzaExtension(int type) : m_extensionType(type) {}
~StanzaExtension()372 		virtual ~StanzaExtension() {}
373 		virtual const string& filterString() const = 0;
374 		virtual StanzaExtension* newInstance(const Tag* tag) const = 0;
375 		virtual glooxwrapper::Tag* tag() const = 0;
376 		virtual StanzaExtension* clone() const = 0;
377 
extensionType()378 		int extensionType() const { return m_extensionType; }
379 	private:
380 		int m_extensionType;
381 	};
382 
383 
384 	class GLOOXWRAPPER_API Client
385 	{
386 		NONCOPYABLE(Client);
387 	private:
388 		gloox::Client* m_Wrapped;
389 		ClientImpl* m_Impl;
390 		Disco* m_DiscoWrapper;
391 
392 	public:
getWrapped()393 		gloox::Client* getWrapped() { return m_Wrapped; }
394 
395 		bool connect(bool block = true);
396 		gloox::ConnectionError recv(int timeout = -1);
397 		const string getID() const;
398 		void send(const IQ& iq);
399 
400 		void setTls(gloox::TLSPolicy tls);
401 		void setCompression(bool compression);
402 
403 		void setSASLMechanisms(int mechanisms);
404 		void registerStanzaExtension(StanzaExtension* ext);
405 		void registerConnectionListener(ConnectionListener* cl);
406 		void registerIqHandler(IqHandler* ih, int exttype);
407 		void registerMessageHandler(MessageHandler* mh);
408 
409 		bool removePresenceExtension(int type);
410 
disco()411 		Disco* disco() const { return m_DiscoWrapper; }
412 
413 		Client(const string& server);
414 		Client(const JID& jid, const string& password, int port = -1);
415 		~Client();
416 
417 		void setPresence(gloox::Presence::PresenceType pres, int priority, const string& status = "");
418 		void disconnect();
419 	};
420 
421 	class GLOOXWRAPPER_API DelayedDelivery
422 	{
423 		NONCOPYABLE(DelayedDelivery);
424 	private:
425 		const gloox::DelayedDelivery* m_Wrapped;
426 	public:
427 		DelayedDelivery(const gloox::DelayedDelivery* wrapped);
428 		const string stamp() const;
429 	};
430 
431 	class GLOOXWRAPPER_API Disco
432 	{
433 		NONCOPYABLE(Disco);
434 	private:
435 		gloox::Disco* m_Wrapped;
436 	public:
437 		Disco(gloox::Disco* wrapped);
438 		void setVersion(const string& name, const string& version, const string& os = "");
439 		void setIdentity(const string& category, const string& type, const string& name = "");
440 	};
441 
442 	class GLOOXWRAPPER_API IQ
443 	{
444 		NONCOPYABLE(IQ);
445 	private:
446 		gloox::IQ* m_Wrapped;
447 		bool m_Owned;
448 	public:
getWrapped()449 		const gloox::IQ& getWrapped() const { return *m_Wrapped; }
IQ(const gloox::IQ & iq)450 		IQ(const gloox::IQ& iq) : m_Wrapped(const_cast<gloox::IQ*>(&iq)), m_Owned(false) { }
451 
452 		IQ(gloox::IQ::IqType type, const JID& to, const string& id);
453 		~IQ();
454 
455 		void addExtension(const StanzaExtension* se);
456 		const StanzaExtension* findExtension(int type) const;
457 
458 		template<class T>
findExtension(int type)459 		inline const T* findExtension(int type) const
460 		{
461 			return static_cast<const T*>(findExtension(type));
462 		}
463 
464 		gloox::IQ::IqType subtype() const;
465 		const string id() const;
466 		const gloox::JID& from() const;
467 
468 		gloox::StanzaError error_error() const; // wrapper for ->error()->error()
469 		Tag* tag() const;
470 	};
471 
472 	class GLOOXWRAPPER_API JID
473 	{
474 		NONCOPYABLE(JID);
475 	private:
476 		gloox::JID* m_Wrapped;
477 		bool m_Owned;
478 		void init(const char* data, size_t len);
479 	public:
getWrapped()480 		const gloox::JID& getWrapped() const { return *m_Wrapped; }
JID(const gloox::JID & jid)481 		JID(const gloox::JID& jid) : m_Wrapped(const_cast<gloox::JID*>(&jid)), m_Owned(false) { }
482 
483 		JID();
484 		JID(const string& jid);
JID(const std::string & jid)485 		JID(const std::string& jid) { init(jid.c_str(), jid.size()); }
486 		~JID();
487 
488 		string username() const;
489 		string resource() const;
490 	};
491 
492 	class GLOOXWRAPPER_API Message
493 	{
494 		NONCOPYABLE(Message);
495 	private:
496 		gloox::Message* m_Wrapped;
497 		bool m_Owned;
498 		glooxwrapper::JID* m_From;
499 	public:
500 		Message(gloox::Message* wrapped, bool owned);
501 		~Message();
502 		gloox::Message::MessageType subtype() const;
503 		const JID& from() const;
504 		string body() const;
505 		string subject(const string& lang = "default") const;
506 		string thread() const;
507 		const glooxwrapper::DelayedDelivery* when() const;
508 	};
509 
510 	class GLOOXWRAPPER_API MUCRoom
511 	{
512 		NONCOPYABLE(MUCRoom);
513 	private:
514 		gloox::MUCRoom* m_Wrapped;
515 		MUCRoomHandlerWrapper* m_HandlerWrapper;
516 	public:
517 		MUCRoom(Client* parent, const JID& nick, MUCRoomHandler* mrh, MUCRoomConfigHandler* mrch = 0);
518 		~MUCRoom();
519 		const string nick() const;
520 		void join(gloox::Presence::PresenceType type = gloox::Presence::Available, const string& status = "", int priority = 0);
521 		void leave(const string& msg = "");
522 		void send(const string& message);
523 		void setNick(const string& nick);
524 		void setPresence(gloox::Presence::PresenceType presence, const string& msg = "");
525 		void setRequestHistory(int value, gloox::MUCRoom::HistoryRequestType type);
526 		void kick(const string& nick, const string& reason);
527 		void ban(const string& nick, const string& reason);
528 	};
529 
530 	class GLOOXWRAPPER_API Presence
531 	{
532 		gloox::Presence::PresenceType m_Presence;
533 	public:
Presence(gloox::Presence::PresenceType presence)534 		Presence(gloox::Presence::PresenceType presence) : m_Presence(presence) {}
presence()535 		gloox::Presence::PresenceType presence() const { return m_Presence; }
536 	};
537 
538 	class GLOOXWRAPPER_API Registration
539 	{
540 		NONCOPYABLE(Registration);
541 	private:
542 		gloox::Registration* m_Wrapped;
543 		std::list<shared_ptr<gloox::RegistrationHandler> > m_RegistrationHandlers;
544 	public:
545 		Registration(Client* parent);
546 		~Registration();
547 		void fetchRegistrationFields();
548 		bool createAccount(int fields, const RegistrationFields& values);
549 		void registerRegistrationHandler(RegistrationHandler* rh);
550 	};
551 
552 	class GLOOXWRAPPER_API Tag
553 	{
554 		NONCOPYABLE(Tag);
555 	private:
556 		gloox::Tag* m_Wrapped;
557 		bool m_Owned;
558 
559 		Tag(const string& name);
560 		Tag(const string& name, const string& cdata);
Tag(gloox::Tag * wrapped,bool owned)561 		Tag(gloox::Tag* wrapped, bool owned) : m_Wrapped(wrapped), m_Owned(owned) {}
562 		~Tag();
563 
564 	public:
565 		// Internal use:
getWrapped()566 		gloox::Tag* getWrapped() { return m_Wrapped; }
stealWrapped()567 		gloox::Tag* stealWrapped() { m_Owned = false; return m_Wrapped; }
568 		static Tag* allocate(gloox::Tag* wrapped, bool owned);
569 
570 		// Instead of using new/delete, Tags must be allocated/freed with these functions
571 		static Tag* allocate(const string& name);
572 		static Tag* allocate(const string& name, const string& cdata);
573 		static void free(const Tag* tag);
574 
575 		bool addAttribute(const string& name, const string& value);
576 		string findAttribute(const string& name) const;
577 		Tag* clone() const;
578 		string xmlns() const;
579 		bool setXmlns(const string& xmlns);
580 		string xml() const;
581 		void addChild(Tag* child);
582 		string name() const;
583 		string cdata() const;
584 		const Tag* findTag_clone(const string& expression) const; // like findTag but must be Tag::free()d
585 		ConstTagList findTagList_clone(const string& expression) const; // like findTagList but each tag must be Tag::free()d
586 	};
587 
588 	namespace Jingle
589 	{
590 
591 		class GLOOXWRAPPER_API Plugin
592 		{
593 		protected:
594 			const gloox::Jingle::Plugin* m_Wrapped;
595 			bool m_Owned;
596 
597 		public:
Plugin(const gloox::Jingle::Plugin * wrapped,bool owned)598 			Plugin(const gloox::Jingle::Plugin* wrapped, bool owned) : m_Wrapped(wrapped), m_Owned(owned) {}
599 
600 			virtual ~Plugin();
601 
602 			const Plugin findPlugin(int type) const;
getWrapped()603 			const gloox::Jingle::Plugin* getWrapped() const { return m_Wrapped; }
604 		};
605 
606 		typedef list<const Plugin*> PluginList;
607 
608 		class GLOOXWRAPPER_API Content : public Plugin
609 		{
610 		public:
611 			Content(const string& name, const PluginList& plugins);
612 			Content();
613 		};
614 
615 		class GLOOXWRAPPER_API ICEUDP : public Plugin
616 		{
617 		public:
618 			struct Candidate {
619 				string ip;
620 				int port;
621 			};
622 
623 			typedef std::list<Candidate> CandidateList;
624 
625 			ICEUDP(CandidateList& candidates);
626 			ICEUDP();
627 
628 			const CandidateList candidates() const;
629 		};
630 
631 		class GLOOXWRAPPER_API Session
632 		{
633 		protected:
634 			gloox::Jingle::Session* m_Wrapped;
635 			bool m_Owned;
636 
637 		public:
638 			class GLOOXWRAPPER_API Jingle
639 			{
640 			private:
641 				const gloox::Jingle::Session::Jingle* m_Wrapped;
642 				bool m_Owned;
643 			public:
Jingle(const gloox::Jingle::Session::Jingle * wrapped,bool owned)644 				Jingle(const gloox::Jingle::Session::Jingle* wrapped, bool owned) : m_Wrapped(wrapped), m_Owned(owned) {}
645 
646 				const PluginList plugins() const;
647 
648 				ICEUDP::Candidate getCandidate() const;
649 			};
650 
Session(gloox::Jingle::Session * wrapped,bool owned)651 			Session(gloox::Jingle::Session* wrapped, bool owned) : m_Wrapped(wrapped), m_Owned(owned) {}
652 
653 			bool sessionInitiate(char* ipStr, uint16_t port);
654 		};
655 
656 		class GLOOXWRAPPER_API SessionHandler
657 		{
658 		public:
~SessionHandler()659 			virtual ~SessionHandler() {}
660 			virtual void handleSessionAction(gloox::Jingle::Action action, Session* session, const Session::Jingle* jingle) = 0;
661 		};
662 
663 	}
664 
665 	class GLOOXWRAPPER_API SessionManager
666 	{
667 	private:
668 		gloox::Jingle::SessionManager* m_Wrapped;
669 		SessionHandlerWrapper* m_HandlerWrapper;
670 
671 	public:
672 		SessionManager(Client* parent, Jingle::SessionHandler* sh);
673 		~SessionManager();
674 		void registerPlugins();
675 		Jingle::Session createSession(const JID& callee);
676 	};
677 
678 }
679 
680 #endif // INCLUDED_GLOOXWRAPPER_H
681