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