// This Source Code Form is subject to the terms of the Mozilla Public // License, version 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include std::string get_name(xmpp_stanza_t *stanza); std::optional get_attribute(xmpp_stanza_t *stanza, const char *name); std::string get_text(xmpp_stanza_t *stanza); std::chrono::system_clock::time_point get_time(const std::string& text); class jid { private: static const std::regex pattern; public: jid(xmpp_ctx_t *context, std::string s); operator std::string&() { return full; } std::string full; std::string bare; std::string local; std::string domain; std::string resource; bool is_bare() const; }; class xmlns; namespace xml { class node { protected: explicit node(); public: inline node(xmpp_ctx_t *context, xmpp_stanza_t *stanza) : context(context) { bind(context, stanza); } xmpp_ctx_t *context; std::optional name; std::optional id; std::optional ns; std::map attributes; std::vector children; std::string text; virtual void bind(xmpp_ctx_t *context, xmpp_stanza_t *stanza); inline std::optional get_attr(const std::string& name) { auto attribute = attributes.find(name); if (attribute != attributes.end()) return attribute->second; return {}; } template::value, int> = 0> inline std::vector> get_children(std::string_view name) { std::vector> list; std::copy_if(children.begin(), children.end(), std::back_inserter(list), [&](node& x) { return x.name == name && x.ns == std::string_view(X()); }); return list; } inline std::vector> get_children(std::string_view name) { std::vector> list; std::copy_if(children.begin(), children.end(), std::back_inserter(list), [&](node& x) { return x.name == name; }); return list; } }; } namespace stanza { template struct overloaded : Fs... { overloaded(const Fs& ...ts) : Fs{ts}... {} overloaded(Fs&& ...ts) : Fs{ts}... {} using Fs::operator()...; }; template overloaded(Ts&&...) -> overloaded...>; extern std::string uuid(xmpp_ctx_t *context); class spec { protected: explicit spec(std::string_view tag): tag(tag) {} void child(spec& ch) { children.push_back(ch); } void attr(std::string k, std::string_view v) { attributes[k] = v; } void text(std::string_view s) { children.push_back(std::string(s)); } template::value, int> = 0> void xmlns() { attr("xmlns", X().ns()); } private: const std::string tag; std::map attributes; std::vector> children; public: std::shared_ptr build(xmpp_ctx_t *context) { auto stanza = xmpp_stanza_new(context); xmpp_stanza_set_name(stanza, tag.data()); for (auto& at : attributes) { xmpp_stanza_set_attribute(stanza, at.first.data(), at.second.data()); } for (auto& ch : children) { std::visit(overloaded { [&](spec& child) { xmpp_stanza_add_child(stanza, child.build(context).get()); }, [&](std::string& s) { auto child = xmpp_stanza_new(context); xmpp_stanza_set_text(child, s.data()); xmpp_stanza_add_child(stanza, child); xmpp_stanza_release(child); } }, ch); } return { stanza, &xmpp_stanza_release }; } }; }; #include "xep-0027.inl" #include "xep-0030.inl" #include "xep-0045.inl" #include "xep-0049.inl" #include "xep-0115.inl" #include "xep-0280.inl" #include "xep-0319.inl" #include "rfc-6121.inl" namespace stanza { struct body : virtual public spec { body() : body("") {} body(std::string_view s) : spec("body") { text(s); } }; struct message : virtual public spec { message() : spec("message") {} message& id(std::string_view s) { attr("id", s); return *this; } message& from(std::string_view s) { attr("from", s); return *this; } message& to(std::string_view s) { attr("to", s); return *this; } message& type(std::string_view s) { attr("type", s); return *this; } message& body(stanza::body b) { child(b); return *this; } message& body(std::string_view s) { return body(stanza::body(s)); } }; struct presence : virtual public spec { presence() : spec("presence") {} presence& id(std::string_view s) { attr("id", s); return *this; } presence& from(std::string_view s) { attr("from", s); return *this; } presence& to(std::string_view s) { attr("to", s); return *this; } presence& lang(std::string_view s) { attr("lang", s); return *this; } }; struct iq : virtual public spec, public xep0030::iq, public xep0049::iq, public xep0280::iq, public rfc6121::iq { iq() : spec("iq") {} iq& id(std::string_view s) { attr("id", s); return *this; } iq& from(std::string_view s) { attr("from", s); return *this; } iq& to(std::string_view s) { attr("to", s); return *this; } iq& type(std::string_view s) { attr("type", s); return *this; } }; struct error : virtual public spec { error() : spec("error") {} }; } namespace xml { class message : virtual public node, public xep0027 { public: inline message(xmpp_ctx_t *context, xmpp_stanza_t *stanza) : node(context, stanza) { bind(context, stanza); } std::optional from; std::optional to; std::optional type; void bind(xmpp_ctx_t *context, xmpp_stanza_t *stanza) override; }; class presence : virtual public node, public xep0027, public xep0045, public xep0115, public xep0319 { public: inline presence(xmpp_ctx_t *context, xmpp_stanza_t *stanza) : node(context, stanza) { bind(context, stanza); } std::optional from; std::optional to; std::optional type; std::optional show(); std::optional status(); void bind(xmpp_ctx_t *context, xmpp_stanza_t *stanza) override; }; class iq : virtual public node { public: inline iq(xmpp_ctx_t *context, xmpp_stanza_t *stanza) : node(context, stanza) { bind(context, stanza); } std::optional from; std::optional to; std::optional type; void bind(xmpp_ctx_t *context, xmpp_stanza_t *stanza) override; }; class error : virtual public node { public: inline error(xmpp_ctx_t *context, xmpp_stanza_t *stanza) : node(context, stanza) { bind(context, stanza); } std::optional from; std::optional to; void bind(xmpp_ctx_t *context, xmpp_stanza_t *stanza) override; }; }