From 04a152cc1ff1551a8ed27873bd5c5000b8415cc0 Mon Sep 17 00:00:00 2001 From: bqv Date: Sat, 26 Mar 2022 18:15:47 +0000 Subject: [PATCH] roster fetch and smart oob-ing --- .gitignore | 9 +++ channel.cpp | 166 +++++++++++++++++++++++++++++++++++++--------- connection.cpp | 10 +++ sexp/lexer.l | 10 +-- xmpp/node.hh | 4 +- xmpp/rfc-6121.inl | 34 ++++++++++ 6 files changed, 194 insertions(+), 39 deletions(-) create mode 100644 xmpp/rfc-6121.inl diff --git a/.gitignore b/.gitignore index 391ddff..e635207 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,12 @@ cscope* # Symbolic links mdb.c + +# Generated +sexp/lexer.yy.cc +sexp/location.hh +sexp/parser.output +sexp/parser.tab.cc +sexp/parser.tab.hh +sexp/position.hh +sexp/stack.hh diff --git a/channel.cpp b/channel.cpp index 30099b5..e6c8d2e 100644 --- a/channel.cpp +++ b/channel.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -1051,6 +1052,77 @@ struct t_channel_member *channel__remove_member(struct t_account *account, return member; } +int channel__send_message(struct t_account *account, struct t_channel *channel, + std::string to, std::string body, + std::optional oob = {}) +{ + xmpp_stanza_t *message = xmpp_message_new(account->context, + channel->type == CHANNEL_TYPE_MUC + ? "groupchat" : "chat", + to.data(), NULL); + + char *id = xmpp_uuid_gen(account->context); + xmpp_stanza_set_id(message, id); + xmpp_free(account->context, id); + xmpp_message_set_body(message, body.data()); + + if (oob) + { + xmpp_stanza_t *message__x = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(message__x, "x"); + xmpp_stanza_set_ns(message__x, "jabber:x:oob"); + + xmpp_stanza_t *message__x__url = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(message__x__url, "url"); + + xmpp_stanza_t *message__x__url__text = xmpp_stanza_new(account->context); + xmpp_stanza_set_text(message__x__url__text, oob->data()); + xmpp_stanza_add_child(message__x__url, message__x__url__text); + xmpp_stanza_release(message__x__url__text); + + xmpp_stanza_add_child(message__x, message__x__url); + xmpp_stanza_release(message__x__url); + + xmpp_stanza_add_child(message, message__x); + xmpp_stanza_release(message__x); + } + + xmpp_stanza_t *message__active = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(message__active, "active"); + xmpp_stanza_set_ns(message__active, "http://jabber.org/protocol/chatstates"); + xmpp_stanza_add_child(message, message__active); + xmpp_stanza_release(message__active); + + xmpp_stanza_t *message__request = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(message__request, "request"); + xmpp_stanza_set_ns(message__request, "urn:xmpp:receipts"); + xmpp_stanza_add_child(message, message__request); + xmpp_stanza_release(message__request); + + xmpp_stanza_t *message__markable = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(message__markable, "markable"); + xmpp_stanza_set_ns(message__markable, "urn:xmpp:chat-markers:0"); + xmpp_stanza_add_child(message, message__markable); + xmpp_stanza_release(message__markable); + + xmpp_stanza_t *message__store = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(message__store, "store"); + xmpp_stanza_set_ns(message__store, "urn:xmpp:hints"); + xmpp_stanza_add_child(message, message__store); + xmpp_stanza_release(message__store); + + xmpp_send(account->connection, message); + xmpp_stanza_release(message); + if (channel->type != CHANNEL_TYPE_MUC) + weechat_printf_date_tags(channel->buffer, 0, + "xmpp_message,message,private,notify_none,self_msg,log1", + "%s\t%s", + user__as_prefix_raw(account, account_jid(account)), + body); + + return WEECHAT_RC_OK; +} + int channel__send_message(struct t_account *account, struct t_channel *channel, const char *to, const char *body) { @@ -1145,40 +1217,68 @@ int channel__send_message(struct t_account *account, struct t_channel *channel, { std::string url { &*match[0].first, static_cast(match[0].length()) }; - //struct t_hashtable *options = weechat_hashtable_new (8, - // WEECHAT_HASHTABLE_STRING, - // WEECHAT_HASHTABLE_STRING, - // NULL, - // NULL); - //if (!options) { return; } - //weechat_hashtable_set(options, "nobody", "1"); - //auto command = "url:" + url; - //const int timeout = 30000; - //struct t_hook *process_hook = - // weechat_hook_process_hashtable(command, options, timeout, - // int (*callback)(const void *pointer, void *data, - // const char *command, - // int return_code, const char *out, const char *err), - // const void *callback_pointer, void *callback_data); - //weechat_hashtable_free(options); - - xmpp_stanza_t *message__x = xmpp_stanza_new(account->context); - xmpp_stanza_set_name(message__x, "x"); - xmpp_stanza_set_ns(message__x, "jabber:x:oob"); - - xmpp_stanza_t *message__x__url = xmpp_stanza_new(account->context); - xmpp_stanza_set_name(message__x__url, "url"); - - xmpp_stanza_t *message__x__url__text = xmpp_stanza_new(account->context); - xmpp_stanza_set_text(message__x__url__text, url.data()); - xmpp_stanza_add_child(message__x__url, message__x__url__text); - xmpp_stanza_release(message__x__url__text); - - xmpp_stanza_add_child(message__x, message__x__url); - xmpp_stanza_release(message__x__url); + do { + struct t_hashtable *options = weechat_hashtable_new(8, + WEECHAT_HASHTABLE_STRING, WEECHAT_HASHTABLE_STRING, + NULL, NULL); + if (!options) { return WEECHAT_RC_ERROR; }; + weechat_hashtable_set(options, "header", "1"); + weechat_hashtable_set(options, "nobody", "1"); + auto command = "url:" + url; + const int timeout = 30000; + struct message_task { + struct t_account *account; + struct t_channel *channel; + std::string to; + std::string body; + std::string url; + }; + auto *task = new message_task { account, channel, to, body, url }; + auto callback = [](const void *pointer, void *, + const char *, int ret, const char *out, const char *err) { + auto task = static_cast(pointer); + if (!task) return WEECHAT_RC_ERROR; + + if (ret == 0) + { + const std::string_view prefix = "content-type: "; + std::istringstream ss(out ? out : ""); + std::string line; + while (std::getline(ss, line)) { + std::transform(line.begin(), line.end(), line.begin(), + [](char c) -> char { return std::tolower(c); }); + if (line.starts_with(prefix)) break; + } + std::string mime = line.substr(prefix.size()); + if (mime.starts_with("image") || mime.starts_with("video")) + { + weechat_printf_date_tags(task->channel->buffer, 0, + "notify_none,no_log", "[oob]\t%s%s", + weechat_color("gray"), mime.data()); + channel__send_message(task->account, task->channel, + task->to, task->body, { task->url }); + } + else + channel__send_message(task->account, task->channel, + task->to, task->body); + } + else + { + auto result = fmt::format("[nohttp] {}", err ? err : "NULL"); + channel__send_message(task->account, task->channel, + task->to.data(), result.data()); + } - xmpp_stanza_add_child(message, message__x); - xmpp_stanza_release(message__x); + delete task; + return WEECHAT_RC_OK; + }; + struct t_hook *process_hook = + weechat_hook_process_hashtable(command.data(), options, timeout, + callback, task, nullptr); + weechat_hashtable_free(options); + (void) process_hook; + return WEECHAT_RC_OK; + } while(0); } xmpp_stanza_t *message__active = xmpp_stanza_new(account->context); diff --git a/connection.cpp b/connection.cpp index 5bf4c43..686dc78 100644 --- a/connection.cpp +++ b/connection.cpp @@ -1332,6 +1332,16 @@ void connection__handler(xmpp_conn_t *conn, xmpp_conn_event_t status, .build(account->context) .get()); + xmpp_send(conn, stanza::iq() + .from(account_jid(account)) + .to(account_jid(account)) + .type("get") + .id(stanza::uuid(account->context)) + .rfc6121() + .query(stanza::rfc6121::query()) + .build(account->context) + .get()); + xmpp_send(conn, stanza::iq() .from(account_jid(account)) .to(account_jid(account)) diff --git a/sexp/lexer.l b/sexp/lexer.l index 69f59c3..6ddb624 100644 --- a/sexp/lexer.l +++ b/sexp/lexer.l @@ -29,11 +29,11 @@ "(" { return token::LPAREN; } - + ")" { return token::RPAREN; } - + [\n \t\r]+ { // Update line number loc->lines(); @@ -44,13 +44,13 @@ yylval->build(yytext); return token::STRING; } - + [^ "(:@)]+ { yylval->build(yytext); return token::NAME; } - + . { return *yytext; } -%% +%% diff --git a/xmpp/node.hh b/xmpp/node.hh index fd785ab..4f5990f 100644 --- a/xmpp/node.hh +++ b/xmpp/node.hh @@ -169,6 +169,7 @@ namespace stanza { #include "xep-0115.inl" #include "xep-0280.inl" #include "xep-0319.inl" +#include "rfc-6121.inl" namespace stanza { struct body : virtual public spec { @@ -202,7 +203,8 @@ namespace stanza { struct iq : virtual public spec, public xep0030::iq, public xep0049::iq, - public xep0280::iq { + public xep0280::iq, + public rfc6121::iq { iq() : spec("iq") {} iq& id(std::string_view s) { attr("id", s); return *this; } diff --git a/xmpp/rfc-6121.inl b/xmpp/rfc-6121.inl new file mode 100644 index 0000000..abd812c --- /dev/null +++ b/xmpp/rfc-6121.inl @@ -0,0 +1,34 @@ +// 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 "node.hh" +#pragma GCC visibility push(default) +#include "ns.hh" +#pragma GCC visibility pop + +namespace stanza { + + /* Instant Messaging and Presence */ + struct rfc6121 { + struct query : virtual public spec { + query() : spec("query") { + xmlns(); + } + }; + + struct iq : virtual public spec { + iq() : spec("iq") {} + + iq& rfc6121() { return *this; } + + iq& query(rfc6121::query q = {}) { child(q); return *this; } + }; + }; + +}