diff --git a/connection.cpp b/connection.cpp index 066df66..abc30d8 100644 --- a/connection.cpp +++ b/connection.cpp @@ -123,6 +123,16 @@ int connection__presence_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void channel = channel__new(account, CHANNEL_TYPE_PM, binding.from->bare.data(), binding.from->bare.data()); } + if (binding.type && *binding.type == "error" && binding.muc() && channel) + { + if (auto error = binding.error()) + { + weechat_printf(channel->buffer, "%sError joining MUC: %s", + weechat_prefix("network"), error->reason()); + } + return 1; + } + if (auto x = binding.muc_user()) { for (int& status : x->statuses) diff --git a/xmpp/xep-0045.inl b/xmpp/xep-0045.inl index ef8484e..c80daca 100644 --- a/xmpp/xep-0045.inl +++ b/xmpp/xep-0045.inl @@ -217,9 +217,132 @@ namespace xml { std::vector statuses; }; + class error { // THIS IS RFC 6120 :( + private: + enum condition : int { + not_authorized, + registration_required, + forbidden, + not_allowed, + not_acceptable, + conflict, + service_unavailable, + item_not_found + }; + + enum class action { + auth, + cancel, + continue_, + modify, + wait + }; + + static action parse_action(std::string_view s) { + if (s == "auth") + return action::auth; + else if (s == "cancel") + return action::cancel; + else if (s == "continue") + return action::continue_; + else if (s == "modify") + return action::modify; + else if (s == "wait") + return action::wait; + throw std::invalid_argument( + fmt::format("Bad action: {}", s)); + } + + static std::string_view format_action(action e) { + switch (e) { + case action::auth: + return "auth"; + case action::cancel: + return "cancel"; + case action::continue_: + return "continue"; + case action::modify: + return "modify"; + case action::wait: + return "wait"; + default: + return ""; + } + } + + public: + error(node& node) { + if (auto attr = node.get_attr("by")) + by = jid(node.context, *attr); + if (auto attr = node.get_attr("type")) + type = parse_action(*attr); + + using xmpp_stanzas = urn::ietf::params::xml::ns::xmpp_stanzas; + + for (auto& child : node.get_children("not-authorized")) + condition = not_authorized; + for (auto& child : node.get_children("forbidden")) + condition = forbidden; + for (auto& child : node.get_children("item-not-found")) + condition = item_not_found; + for (auto& child : node.get_children("not-allowed")) + condition = not_allowed; + for (auto& child : node.get_children("not-acceptable")) + condition = not_acceptable; + for (auto& child : node.get_children("registration-required")) + condition = registration_required; + for (auto& child : node.get_children("conflict")) + condition = conflict; + for (auto& child : node.get_children("service-unavailable")) + condition = service_unavailable; + + for (auto& child : node.get_children("text")) + description = child.get().text; + } + + std::optional by; + std::optional type; + std::optional condition; + std::optional description; + + const char* reason() { + switch (condition) + { + case not_authorized: + return "Password Required"; + case forbidden: + return "Banned"; + case item_not_found: + return "No such MUC"; + case not_allowed: + return "MUC Creation Failed"; + case not_acceptable: + return "Unacceptable Nickname"; + case registration_required: + return "Not on Member List"; + case conflict: + return "Nickname Conflict"; + case service_unavailable: + return "Service Unavailable (MUC Full?)"; + } + return "Unspecified"; + } + }; + private: + std::optional _muc; std::optional> _muc_user; + std::optional> _error; public: + bool muc() { + if (!_muc) + { + auto child = get_children("x"); + _muc = child.size() > 0; + } + return *_muc; + } + std::optional& muc_user() { if (!_muc_user) { @@ -231,6 +354,18 @@ namespace xml { } return *_muc_user; } + + std::optional& error() { + if (!_error) + { + auto child = get_children("error"); + if (child.size() > 0) + _error = child.front().get(); + else + _error.emplace(std::nullopt); + } + return *_error; + } }; } diff --git a/xmpp/xep-0115.inl b/xmpp/xep-0115.inl index 2bf5299..17cdc3f 100644 --- a/xmpp/xep-0115.inl +++ b/xmpp/xep-0115.inl @@ -44,7 +44,8 @@ namespace xml{ auto child = get_children("c"); if (child.size() > 0) _capabilities = caps(child.front().get()); - _capabilities.emplace(std::nullopt); + else + _capabilities.emplace(std::nullopt); } return *_capabilities; } diff --git a/xmpp/xep-0319.inl b/xmpp/xep-0319.inl index cd19577..f466666 100644 --- a/xmpp/xep-0319.inl +++ b/xmpp/xep-0319.inl @@ -28,17 +28,17 @@ namespace xml { if (children.size() <= 0) _idle_since.emplace(std::nullopt); else { - auto since = children.front().get().get_attr("since"); - if (!since) - _idle_since.emplace(std::nullopt); - else { - try { - _idle_since = get_time(*since); - } - catch (const std::invalid_argument& ex) { - _idle_since.emplace(std::nullopt); - } - } + auto since = children.front().get().get_attr("since"); + if (!since) + _idle_since.emplace(std::nullopt); + else { + try { + _idle_since = get_time(*since); + } + catch (const std::invalid_argument& ex) { + _idle_since.emplace(std::nullopt); + } + } } } return *_idle_since;