diff options
Diffstat (limited to 'src/xmpp')
-rw-r--r-- | src/xmpp/adhoc_commands_handler.cpp | 5 | ||||
-rw-r--r-- | src/xmpp/biboumi_adhoc_commands.cpp | 104 | ||||
-rw-r--r-- | src/xmpp/biboumi_component.cpp | 63 | ||||
-rw-r--r-- | src/xmpp/biboumi_component.hpp | 3 | ||||
-rw-r--r-- | src/xmpp/jid.cpp | 2 | ||||
-rw-r--r-- | src/xmpp/xmpp_component.cpp | 16 | ||||
-rw-r--r-- | src/xmpp/xmpp_component.hpp | 1 | ||||
-rw-r--r-- | src/xmpp/xmpp_parser.cpp | 2 |
8 files changed, 152 insertions, 44 deletions
diff --git a/src/xmpp/adhoc_commands_handler.cpp b/src/xmpp/adhoc_commands_handler.cpp index bc4c108..ff4c1e5 100644 --- a/src/xmpp/adhoc_commands_handler.cpp +++ b/src/xmpp/adhoc_commands_handler.cpp @@ -80,7 +80,10 @@ XmlNode AdhocCommandsHandler::handle_request(const std::string& executor_jid, co { command_node["status"] = "executing"; XmlSubNode actions(command_node, "actions"); - XmlSubNode next(actions, "next"); + if (session.remaining_steps() == 1) + XmlSubNode next(actions, "complete"); + else + XmlSubNode next(actions, "next"); } } else if (session_it != this->sessions.end() && action == "cancel") diff --git a/src/xmpp/biboumi_adhoc_commands.cpp b/src/xmpp/biboumi_adhoc_commands.cpp index 3bd2e5a..113943c 100644 --- a/src/xmpp/biboumi_adhoc_commands.cpp +++ b/src/xmpp/biboumi_adhoc_commands.cpp @@ -15,10 +15,17 @@ #ifdef USE_DATABASE #include <database/database.hpp> #include <database/save.hpp> + +static void set_desc(XmlSubNode& field, const char* text) +{ + XmlSubNode desc(field, "desc"); + desc.set_inner(text); +} + #endif #ifndef HAS_PUT_TIME -#include <ctime> +# include <time.h> #endif using namespace std::string_literals; @@ -116,6 +123,7 @@ void ConfigureGlobalStep1(XmppComponent&, AdhocSession& session, XmlNode& comman auto options = Database::get_global_options(owner.bare()); + command_node.delete_all_children(); XmlSubNode x(command_node, "jabber:x:data:x"); x["type"] = "form"; XmlSubNode title(x, "title"); @@ -128,7 +136,7 @@ void ConfigureGlobalStep1(XmppComponent&, AdhocSession& session, XmlNode& comman max_histo_length["var"] = "max_history_length"; max_histo_length["type"] = "text-single"; max_histo_length["label"] = "Max history length"; - max_histo_length["desc"] = "The maximum number of lines in the history that the server sends when joining a channel"; + set_desc(max_histo_length, "The maximum number of lines in the history that the server sends when joining a channel"); { XmlSubNode value(max_histo_length, "value"); value.set_inner(std::to_string(options.col<Database::MaxHistoryLength>())); @@ -140,7 +148,7 @@ void ConfigureGlobalStep1(XmppComponent&, AdhocSession& session, XmlNode& comman record_history["var"] = "record_history"; record_history["type"] = "boolean"; record_history["label"] = "Record history"; - record_history["desc"] = "Whether to save the messages into the database, or not"; + set_desc(record_history, "Whether to save the messages into the database, or not"); { XmlSubNode value(record_history, "value"); value.set_name("value"); @@ -156,7 +164,7 @@ void ConfigureGlobalStep1(XmppComponent&, AdhocSession& session, XmlNode& comman persistent["var"] = "persistent"; persistent["type"] = "boolean"; persistent["label"] = "Make all channels persistent"; - persistent["desc"] = "If true, all channels will be persistent"; + set_desc(persistent, "If true, all channels will be persistent"); { XmlSubNode value(persistent, "value"); value.set_name("value"); @@ -184,7 +192,13 @@ void ConfigureGlobalStep2(XmppComponent& xmpp_component, AdhocSession& session, if (field->get_tag("var") == "max_history_length" && value && !value->get_inner().empty()) - options.col<Database::MaxHistoryLength>() = atoi(value->get_inner().data()); + { + try { + options.col<Database::MaxHistoryLength>() = std::stol(value->get_inner().data()); + } catch (const std::logic_error&) { + options.col<Database::MaxHistoryLength>() = 20; + } + } else if (field->get_tag("var") == "record_history" && value && !value->get_inner().empty()) { @@ -223,6 +237,7 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com server_domain); auto commands = Database::get_after_connection_commands(options); + command_node.delete_all_children(); XmlSubNode x(command_node, "jabber:x:data:x"); x["type"] = "form"; XmlSubNode title(x, "title"); @@ -236,7 +251,7 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com field["var"] = "address"; field["type"] = "text-single"; field["label"] = "Address"; - field["desc"] = "The address (hostname or IP) to connect to."; + set_desc(field, "The address (hostname or IP) to connect to."); XmlSubNode value(field, "value"); if (options.col<Database::Address>().empty()) value.set_inner(server_domain); @@ -249,7 +264,7 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com ports["var"] = "ports"; ports["type"] = "text-multi"; ports["label"] = "Ports"; - ports["desc"] = "List of ports to try, without TLS. Defaults: 6667."; + set_desc(ports, "List of ports to try, without TLS. Defaults: 6667."); for (const auto& val: utils::split(options.col<Database::Ports>(), ';', false)) { XmlSubNode ports_value(ports, "value"); @@ -263,7 +278,7 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com tls_ports["var"] = "tls_ports"; tls_ports["type"] = "text-multi"; tls_ports["label"] = "TLS ports"; - tls_ports["desc"] = "List of ports to try, with TLS. Defaults: 6697, 6670."; + set_desc(tls_ports, "List of ports to try, with TLS. Defaults: 6697, 6670."); for (const auto& val: utils::split(options.col<Database::TlsPorts>(), ';', false)) { XmlSubNode tls_ports_value(tls_ports, "value"); @@ -276,7 +291,7 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com verify_cert["var"] = "verify_cert"; verify_cert["type"] = "boolean"; verify_cert["label"] = "Verify certificate"; - verify_cert["desc"] = "Whether or not to abort the connection if the server’s TLS certificate is invalid"; + set_desc(verify_cert, "Whether or not to abort the connection if the server’s TLS certificate is invalid"); XmlSubNode verify_cert_value(verify_cert, "value"); if (options.col<Database::VerifyCert>()) verify_cert_value.set_inner("true"); @@ -302,7 +317,7 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com field["var"] = "nick"; field["type"] = "text-single"; field["label"] = "Nickname"; - field["desc"] = "If set, will override the nickname provided in the initial presence sent to join the first server channel"; + set_desc(field, "If set, will override the nickname provided in the initial presence sent to join the first server channel"); if (!options.col<Database::Nick>().empty()) { XmlSubNode value(field, "value"); @@ -315,7 +330,7 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com pass["var"] = "pass"; pass["type"] = "text-private"; pass["label"] = "Server password"; - pass["desc"] = "Will be used in a PASS command when connecting"; + set_desc(pass, "Will be used in a PASS command when connecting"); if (!options.col<Database::Pass>().empty()) { XmlSubNode pass_value(pass, "value"); @@ -327,7 +342,7 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com XmlSubNode after_cnt_cmd(x, "field"); after_cnt_cmd["var"] = "after_connect_commands"; after_cnt_cmd["type"] = "text-multi"; - after_cnt_cmd["desc"] = "Custom IRC commands sent after the connection is established with the server."; + set_desc(after_cnt_cmd, "Custom IRC commands sent after the connection is established with the server."); after_cnt_cmd["label"] = "After-connection IRC commands"; for (const auto& command: commands) { @@ -364,10 +379,28 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com } { + XmlSubNode throttle_limit(x, "field"); + throttle_limit["var"] = "throttle_limit"; + throttle_limit["type"] = "text-single"; + throttle_limit["label"] = "Throttle limit"; + XmlSubNode value(throttle_limit, "value"); + value.set_inner(std::to_string(options.col<Database::ThrottleLimit>())); + } + + { + XmlSubNode max_history_length(x, "field"); + max_history_length["var"] = "max_history_length"; + max_history_length["type"] = "text-single"; + max_history_length["label"] = "Throttle limit"; + XmlSubNode value(max_history_length, "value"); + value.set_inner(std::to_string(options.col<Database::MaxHistoryLength>())); + } + + { XmlSubNode encoding_out(x, "field"); encoding_out["var"] = "encoding_out"; encoding_out["type"] = "text-single"; - encoding_out["desc"] = "The encoding used when sending messages to the IRC server."; + set_desc(encoding_out, "The encoding used when sending messages to the IRC server."); encoding_out["label"] = "Out encoding"; if (!options.col<Database::EncodingOut>().empty()) { @@ -380,7 +413,7 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com XmlSubNode encoding_in(x, "field"); encoding_in["var"] = "encoding_in"; encoding_in["type"] = "text-single"; - encoding_in["desc"] = "The encoding used to decode message received from the IRC server."; + set_desc(encoding_in, "The encoding used to decode message received from the IRC server."); encoding_in["label"] = "In encoding"; if (!options.col<Database::EncodingIn>().empty()) { @@ -390,8 +423,10 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com } } -void ConfigureIrcServerStep2(XmppComponent&, AdhocSession& session, XmlNode& command_node) +void ConfigureIrcServerStep2(XmppComponent& xmpp_component, AdhocSession& session, XmlNode& command_node) { + auto& biboumi_component = dynamic_cast<BiboumiComponent&>(xmpp_component); + const XmlNode* x = command_node.get_child("x", "jabber:x:data"); if (x) { @@ -472,6 +507,31 @@ void ConfigureIrcServerStep2(XmppComponent&, AdhocSession& session, XmlNode& com else if (field->get_tag("var") == "realname" && value) options.col<Database::Realname>() = value->get_inner(); + else if (field->get_tag("var") == "throttle_limit" && value) + { + try { + options.col<Database::ThrottleLimit>() = std::stol(value->get_inner()); + } catch (const std::logic_error&) { + options.col<Database::ThrottleLimit>() = 10; + } + Bridge* bridge = biboumi_component.find_user_bridge(session.get_owner_jid()); + if (bridge) + { + IrcClient* client = bridge->find_irc_client(server_domain); + if (client) + client->set_throttle_limit(options.col<Database::ThrottleLimit>()); + } + } + + else if (field->get_tag("var") == "max_history_length" && value) + { + try { + options.col<Database::MaxHistoryLength>() = std::stol(value->get_inner()); + } catch (const std::logic_error&) { + options.col<Database::MaxHistoryLength>() = 20; + } + } + else if (field->get_tag("var") == "encoding_out" && value) options.col<Database::EncodingOut>() = value->get_inner(); @@ -509,6 +569,7 @@ void insert_irc_channel_configuration_form(XmlNode& node, const Jid& requester, auto options = Database::get_irc_channel_options_with_server_default(requester.local + "@" + requester.domain, iid.get_server(), iid.get_local()); + node.delete_all_children(); XmlSubNode x(node, "jabber:x:data:x"); x["type"] = "form"; XmlSubNode title(x, "title"); @@ -521,7 +582,7 @@ void insert_irc_channel_configuration_form(XmlNode& node, const Jid& requester, record_history["var"] = "record_history"; record_history["type"] = "list-single"; record_history["label"] = "Record history for this channel"; - record_history["desc"] = "If unset, the value is the one configured globally"; + set_desc(record_history, "If unset, the value is the one configured globally"); { // Value selected by default XmlSubNode value(record_history, "value"); @@ -541,7 +602,7 @@ void insert_irc_channel_configuration_form(XmlNode& node, const Jid& requester, XmlSubNode encoding_out(x, "field"); encoding_out["var"] = "encoding_out"; encoding_out["type"] = "text-single"; - encoding_out["desc"] = "The encoding used when sending messages to the IRC server. Defaults to the server's “out encoding” if unset for the channel"; + set_desc(encoding_out, "The encoding used when sending messages to the IRC server. Defaults to the server's “out encoding” if unset for the channel"); encoding_out["label"] = "Out encoding"; if (!options.col<Database::EncodingOut>().empty()) { @@ -554,7 +615,7 @@ void insert_irc_channel_configuration_form(XmlNode& node, const Jid& requester, XmlSubNode encoding_in(x, "field"); encoding_in["var"] = "encoding_in"; encoding_in["type"] = "text-single"; - encoding_in["desc"] = "The encoding used to decode message received from the IRC server. Defaults to the server's “in encoding” if unset for the channel"; + set_desc(encoding_in, "The encoding used to decode message received from the IRC server. Defaults to the server's “in encoding” if unset for the channel"); encoding_in["label"] = "In encoding"; if (!options.col<Database::EncodingIn>().empty()) { @@ -567,7 +628,7 @@ void insert_irc_channel_configuration_form(XmlNode& node, const Jid& requester, XmlSubNode persistent(x, "field"); persistent["var"] = "persistent"; persistent["type"] = "boolean"; - persistent["desc"] = "If set to true, when all XMPP clients have left this channel, biboumi will stay idle in it, without sending a PART command."; + set_desc(persistent, "If set to true, when all XMPP clients have left this channel, biboumi will stay idle in it, without sending a PART command."); persistent["label"] = "Persistent"; { XmlSubNode value(persistent, "value"); @@ -847,12 +908,13 @@ void GetIrcConnectionInfoStep1(XmppComponent& component, AdhocSession& session, if (irc->is_using_tls()) ss << " (using TLS)"; const std::time_t now_c = std::chrono::system_clock::to_time_t(irc->connection_date); + struct tm tm; #ifdef HAS_PUT_TIME - ss << " since " << std::put_time(std::localtime(&now_c), "%F %T"); + ss << " since " << std::put_time(localtime_r(&now_c, &tm), "%F %T"); #else constexpr std::size_t timestamp_size{10 + 1 + 8 + 1}; char buf[timestamp_size] = {}; - const auto res = std::strftime(buf, timestamp_size, "%F %T", std::localtime(&now_c)); + const auto res = std::strftime(buf, timestamp_size, "%F %T", localtime(&now_c, &tm)); if (res > 0) ss << " since " << buf; #endif diff --git a/src/xmpp/biboumi_component.cpp b/src/xmpp/biboumi_component.cpp index be34873..6fe6972 100644 --- a/src/xmpp/biboumi_component.cpp +++ b/src/xmpp/biboumi_component.cpp @@ -102,8 +102,8 @@ void BiboumiComponent::shutdown() void BiboumiComponent::clean() { - auto it = this->bridges.begin(); - while (it != this->bridges.end()) + auto it = std::begin(this->bridges); + while (it != std::end(this->bridges)) { it->second->clean(); if (it->second->active_clients() == 0) @@ -185,8 +185,13 @@ void BiboumiComponent::handle_presence(const Stanza& stanza) } bridge->join_irc_channel(iid, to.resource, password ? password->get_inner(): "", from.resource, history_limit, x != nullptr); - if (!own_nick.empty() && own_nick != to.resource) - bridge->send_irc_nick_change(iid, to.resource, from.resource); + const IrcClient* irc = bridge->find_irc_client(iid.get_server()); + if (irc) + { + const auto chan = irc->find_channel(iid.get_local()); + if (chan->joined) + bridge->send_irc_nick_change(iid, to.resource, from.resource); + } } else if (type == "unavailable") { @@ -273,9 +278,10 @@ void BiboumiComponent::handle_message(const Stanza& stanza) std::string error_type("cancel"); std::string error_name("internal-server-error"); - utils::ScopeGuard stanza_error([this, &from_str, &to_str, &id, &error_type, &error_name](){ + std::string error_text{}; + utils::ScopeGuard stanza_error([this, &from_str, &to_str, &id, &error_type, &error_name, &error_text](){ this->send_stanza_error("message", from_str, to_str, id, - error_type, error_name, ""); + error_type, error_name, error_text); }); const XmlNode* body = stanza.get_child("body", COMPONENT_NS); @@ -284,7 +290,15 @@ void BiboumiComponent::handle_message(const Stanza& stanza) { if (body && !body->get_inner().empty()) { - bridge->send_channel_message(iid, body->get_inner(), id); + if (bridge->is_resource_in_chan(iid.to_tuple(), from.resource)) + bridge->send_channel_message(iid, body->get_inner(), id); + else + { + error_type = "modify"; + error_name = "not-acceptable"; + error_text = "You are not a participant in this room."; + return; + } } const XmlNode* subject = stanza.get_child("subject", COMPONENT_NS); if (subject) @@ -350,7 +364,6 @@ void BiboumiComponent::handle_message(const Stanza& stanza) this->send_invitation_from_fulljid(std::to_string(iid), invite_to, from_str); } } - } } catch (const IRCNotConnected& ex) { @@ -514,7 +527,11 @@ void BiboumiComponent::handle_iq(const Stanza& stanza) { if (node.empty()) { - this->send_irc_channel_disco_info(id, from, to_str); + const IrcClient* irc_client = bridge->find_irc_client(iid.get_server()); + const IrcChannel* irc_channel{}; + if (irc_client) + irc_channel = irc_client->find_channel(iid.get_local()); + this->send_irc_channel_disco_info(id, from, to_str, irc_channel); stanza_error.disable(); } else if (node == MUC_TRAFFIC_NS) @@ -592,7 +609,6 @@ void BiboumiComponent::handle_iq(const Stanza& stanza) const XmlNode* max = set_node->get_child("max", RSM_NS); if (max) rs_info.max = std::atoi(max->get_inner().data()); - } if (rs_info.max == -1) rs_info.max = 100; @@ -751,7 +767,7 @@ bool BiboumiComponent::handle_mam_request(const Stanza& stanza) if (limit < 0 || limit > 100) limit = 100; auto result = Database::get_muc_logs(from.bare(), iid.get_local(), iid.get_server(), - limit, + static_cast<std::size_t>(limit), start, end, reference_record_id, paging_order); bool complete = std::get<bool>(result); @@ -964,7 +980,8 @@ void BiboumiComponent::send_irc_channel_muc_traffic_info(const std::string& id, this->send_stanza(iq); } -void BiboumiComponent::send_irc_channel_disco_info(const std::string& id, const std::string& jid_to, const std::string& jid_from) +void BiboumiComponent::send_irc_channel_disco_info(const std::string& id, const std::string& jid_to, + const std::string& jid_from, const IrcChannel* irc_channel) { Jid from(jid_from); Iid iid(from.local, {}); @@ -980,11 +997,31 @@ void BiboumiComponent::send_irc_channel_disco_info(const std::string& id, const identity["category"] = "conference"; identity["type"] = "irc"; identity["name"] = ""s + iid.get_local() + " on " + iid.get_server(); - for (const char *ns: {DISCO_INFO_NS, MUC_NS, ADHOC_NS, PING_NS, MAM_NS, VERSION_NS, STABLE_MUC_ID_NS}) + for (const char *ns: {DISCO_INFO_NS, MUC_NS, ADHOC_NS, PING_NS, MAM_NS, VERSION_NS, STABLE_MUC_ID_NS, SELF_PING_FLAG, "muc_nonanonymous"}) { XmlSubNode feature(query, "feature"); feature["var"] = ns; } + + XmlSubNode x(query, "x"); + x["xmlns"] = DATAFORM_NS; + x["type"] = "result"; + { + XmlSubNode field(x, "field"); + field["var"] = "FORM_TYPE"; + field["type"] = "hidden"; + XmlSubNode value(field, "value"); + value.set_inner("http://jabber.org/protocol/muc#roominfo"); + } + + if (irc_channel && irc_channel->joined) + { + XmlSubNode field(x, "field"); + field["var"] = "muc#roominfo_occupants"; + field["label"] = "Number of occupants"; + XmlSubNode value(field, "value"); + value.set_inner(std::to_string(irc_channel->get_users().size())); + } } this->send_stanza(iq); } diff --git a/src/xmpp/biboumi_component.hpp b/src/xmpp/biboumi_component.hpp index caf990e..f59ed9b 100644 --- a/src/xmpp/biboumi_component.hpp +++ b/src/xmpp/biboumi_component.hpp @@ -73,7 +73,8 @@ public: * http://xmpp.org/extensions/xep-0045.html#impl-service-traffic */ void send_irc_channel_muc_traffic_info(const std::string& id, const std::string& jid_to, const std::string& jid_from); - void send_irc_channel_disco_info(const std::string& id, const std::string& jid_to, const std::string& jid_from); + void send_irc_channel_disco_info(const std::string& id, const std::string& jid_to, const std::string& jid_from, + const IrcChannel* irc_channel); /** * Send a ping request */ diff --git a/src/xmpp/jid.cpp b/src/xmpp/jid.cpp index 19d1b55..3c54fd4 100644 --- a/src/xmpp/jid.cpp +++ b/src/xmpp/jid.cpp @@ -106,7 +106,7 @@ std::string jidprep(const std::string& original) --domain_end; if (domain_end != domain && special_chars.count(domain[0])) { - std::memmove(domain, domain + 1, domain_end - domain + 1); + std::memmove(domain, domain + 1, static_cast<std::size_t>(domain_end - domain) + 1); --domain_end; } // And if the final result is an empty string, return a dummy hostname diff --git a/src/xmpp/xmpp_component.cpp b/src/xmpp/xmpp_component.cpp index b3d925e..f82f9ce 100644 --- a/src/xmpp/xmpp_component.cpp +++ b/src/xmpp/xmpp_component.cpp @@ -298,8 +298,8 @@ void XmppComponent::send_message(const std::string& from, Xmpp::body&& body, con { XmlSubNode private_node(message, "private"); private_node["xmlns"] = "urn:xmpp:carbons:2"; - XmlSubNode nocopy(message, "no-copy"); - nocopy["xmlns"] = "urn:xmpp:hints"; + XmlSubNode nocopy_node(message, "no-copy"); + nocopy_node["xmlns"] = "urn:xmpp:hints"; } if (muc_private) { @@ -340,8 +340,12 @@ void XmppComponent::send_user_join(const std::string& from, if (self) { - XmlSubNode status(x, "status"); - status["code"] = "110"; + XmlSubNode status_self(x, "status"); + status_self["code"] = "110"; + XmlSubNode status_nick_modified(x, "status"); + status_nick_modified["code"] = "210"; + XmlSubNode status_nonanonymous(x, "status"); + status_nonanonymous["code"] = "100"; } } this->send_stanza(presence); @@ -588,8 +592,8 @@ void XmppComponent::send_version(const std::string& id, const std::string& jid_t name.set_inner("biboumi"); } { - XmlSubNode version(query, "version"); - version.set_inner(SOFTWARE_VERSION); + XmlSubNode version_node(query, "version"); + version_node.set_inner(SOFTWARE_VERSION); } { XmlSubNode os(query, "os"); diff --git a/src/xmpp/xmpp_component.hpp b/src/xmpp/xmpp_component.hpp index e18da40..156e286 100644 --- a/src/xmpp/xmpp_component.hpp +++ b/src/xmpp/xmpp_component.hpp @@ -38,6 +38,7 @@ #define MUC_TRAFFIC_NS "http://jabber.org/protocol/muc#traffic" #define STABLE_ID_NS "urn:xmpp:sid:0" #define STABLE_MUC_ID_NS "http://jabber.org/protocol/muc#stable_id" +#define SELF_PING_FLAG MUC_NS"#self-ping-optimization" /** * An XMPP component, communicating with an XMPP server using the protocole diff --git a/src/xmpp/xmpp_parser.cpp b/src/xmpp/xmpp_parser.cpp index 0488be9..781fe4c 100644 --- a/src/xmpp/xmpp_parser.cpp +++ b/src/xmpp/xmpp_parser.cpp @@ -20,7 +20,7 @@ static void end_element_handler(void* user_data, const XML_Char* name) static void character_data_handler(void *user_data, const XML_Char *s, int len) { - static_cast<XmppParser*>(user_data)->char_data(s, len); + static_cast<XmppParser*>(user_data)->char_data(s, static_cast<std::size_t>(len)); } /** |