From 771dfab12354bc509af9163fca0c599ec9e58433 Mon Sep 17 00:00:00 2001 From: Michael Vetter Date: Wed, 18 Dec 2019 10:14:46 +0100 Subject: [PATCH 01/15] Start implementing XEP-0084 So far we just subscribe and get the IDs. --- Makefile.am | 1 + src/event/server_events.c | 3 ++ src/xmpp/avatar.c | 94 +++++++++++++++++++++++++++++++++++++++ src/xmpp/avatar.h | 38 ++++++++++++++++ src/xmpp/stanza.h | 3 ++ src/xmpp/xmpp.h | 2 + 6 files changed, 141 insertions(+) create mode 100644 src/xmpp/avatar.c create mode 100644 src/xmpp/avatar.h diff --git a/Makefile.am b/Makefile.am index c49b6ba6..6fe31446 100644 --- a/Makefile.am +++ b/Makefile.am @@ -15,6 +15,7 @@ core_sources = \ src/xmpp/bookmark.c src/xmpp/bookmark.h \ src/xmpp/blocking.c src/xmpp/blocking.h \ src/xmpp/form.c src/xmpp/form.h \ + src/xmpp/avatar.c src/xmpp/avatar.h \ src/event/common.c src/event/common.h \ src/event/server_events.c src/event/server_events.h \ src/event/client_events.c src/event/client_events.h \ diff --git a/src/event/server_events.c b/src/event/server_events.c index 40cc1240..62d64869 100644 --- a/src/event/server_events.c +++ b/src/event/server_events.c @@ -54,6 +54,7 @@ #include "xmpp/muc.h" #include "xmpp/chat_session.h" #include "xmpp/roster_list.h" +#include "xmpp/avatar.h" #ifdef HAVE_LIBOTR #include "otr/otr.h" @@ -90,6 +91,8 @@ sv_ev_login_account_success(char *account_name, gboolean secured) omemo_on_connect(account); #endif + avatar_pep_subscribe(); + ui_handle_login_account_success(account, secured); // attempt to rejoin all rooms diff --git a/src/xmpp/avatar.c b/src/xmpp/avatar.c new file mode 100644 index 00000000..29ec453b --- /dev/null +++ b/src/xmpp/avatar.c @@ -0,0 +1,94 @@ +/* + * omemo.c + * vim: expandtab:ts=4:sts=4:sw=4 + * + * Copyright (C) 2019 Michael Vetter + * + * This file is part of Profanity. + * + * Profanity is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Profanity is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Profanity. If not, see . + * + * In addition, as a special exception, the copyright holders give permission to + * link the code of portions of this program with the OpenSSL library under + * certain conditions as described in each individual source file, and + * distribute linked combinations including the two. + * + * You must obey the GNU General Public License in all respects for all of the + * code used other than OpenSSL. If you modify file(s) with this exception, you + * may extend this exception to your version of the file(s), but you are not + * obligated to do so. If you do not wish to do so, delete this exception + * statement from your version. If you delete this exception statement from all + * source files in the program, then also delete it here. + * + */ + +#include + +#include "log.h" +#include "xmpp/connection.h" +#include "xmpp/form.h" +#include "xmpp/iq.h" +#include "xmpp/message.h" +#include "xmpp/stanza.h" + +static int _avatar_metadata_nofication(xmpp_stanza_t *const stanza, void *const userdata); + +void +avatar_pep_subscribe(void) +{ + message_pubsub_event_handler_add(STANZA_NS_USER_AVATAR_METADATA, _avatar_metadata_nofication, NULL, NULL); + message_pubsub_event_handler_add(STANZA_NS_USER_AVATAR_DATA, _avatar_metadata_nofication, NULL, NULL); + + caps_add_feature(XMPP_FEATURE_USER_AVATAR_METADATA_NOTIFY); +} + +static int +_avatar_metadata_nofication(xmpp_stanza_t *const stanza, void *const userdata) +{ + const char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM); + from = from; + + xmpp_stanza_t *root = NULL; + xmpp_stanza_t *event = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_PUBSUB_EVENT); + if (event) { + root = event; + } + + xmpp_stanza_t *pubsub = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_PUBSUB); + if (pubsub) { + root = pubsub; + } + + if (!root) { + return 1; + } + + xmpp_stanza_t *items = xmpp_stanza_get_child_by_name(root, "items"); + if (!items) { + return 1; + } + + xmpp_stanza_t *item = xmpp_stanza_get_child_by_name(items, "item"); + if (item) { + xmpp_stanza_t *metadata = xmpp_stanza_get_child_by_name(item, "metadata"); + if (!metadata) + return 1; + + xmpp_stanza_t *info = xmpp_stanza_get_child_by_name(metadata, "info"); + const char *id = xmpp_stanza_get_id(info); + id = id; + } + + return 1; +} diff --git a/src/xmpp/avatar.h b/src/xmpp/avatar.h new file mode 100644 index 00000000..dcaaf24a --- /dev/null +++ b/src/xmpp/avatar.h @@ -0,0 +1,38 @@ +/* + * omemo.h + * vim: expandtab:ts=4:sts=4:sw=4 + * + * Copyright (C) 2019 Michael Vetter + * + * This file is part of Profanity. + * + * Profanity is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Profanity is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Profanity. If not, see . + * + * In addition, as a special exception, the copyright holders give permission to + * link the code of portions of this program with the OpenSSL library under + * certain conditions as described in each individual source file, and + * distribute linked combinations including the two. + * + * You must obey the GNU General Public License in all respects for all of the + * code used other than OpenSSL. If you modify file(s) with this exception, you + * may extend this exception to your version of the file(s), but you are not + * obligated to do so. If you do not wish to do so, delete this exception + * statement from your version. If you delete this exception statement from all + * source files in the program, then also delete it here. + * + */ + +#include + +void avatar_pep_subscribe(void); diff --git a/src/xmpp/stanza.h b/src/xmpp/stanza.h index 31078ae8..c7e04703 100644 --- a/src/xmpp/stanza.h +++ b/src/xmpp/stanza.h @@ -2,6 +2,7 @@ * stanza.h * * Copyright (C) 2012 - 2019 James Booth + * Copyright (C) 2019 Michael Vetter * * This file is part of Profanity. * @@ -198,6 +199,8 @@ #define STANZA_NS_OMEMO_DEVICELIST "eu.siacs.conversations.axolotl.devicelist" #define STANZA_NS_OMEMO_BUNDLES "eu.siacs.conversations.axolotl.bundles" #define STANZA_NS_STABLE_ID "urn:xmpp:sid:0" +#define STANZA_NS_USER_AVATAR_DATA "urn:xmpp:avatar:data" +#define STANZA_NS_USER_AVATAR_METADATA "urn:xmpp:avatar:metadata" #define STANZA_DATAFORM_SOFTWARE "urn:xmpp:dataforms:softwareinfo" diff --git a/src/xmpp/xmpp.h b/src/xmpp/xmpp.h index cb5ae9d6..c48da210 100644 --- a/src/xmpp/xmpp.h +++ b/src/xmpp/xmpp.h @@ -2,6 +2,7 @@ * xmpp.h * * Copyright (C) 2012 - 2019 James Booth + * Copyright (C) 2019 Michael Vetter * * This file is part of Profanity. * @@ -66,6 +67,7 @@ #define XMPP_FEATURE_OMEMO_DEVICELIST_NOTIFY "eu.siacs.conversations.axolotl.devicelist+notify" #define XMPP_FEATURE_PUBSUB "http://jabber.org/protocol/pubsub" #define XMPP_FEATURE_PUBSUB_PUBLISH_OPTIONS "http://jabber.org/protocol/pubsub#publish-options" +#define XMPP_FEATURE_USER_AVATAR_METADATA_NOTIFY "urn:xmpp:avatar:metadata+notify" typedef enum { JABBER_CONNECTING, From e3538cf73976738c190777d055f8766e4247a906 Mon Sep 17 00:00:00 2001 From: Michael Vetter Date: Wed, 18 Dec 2019 11:24:50 +0100 Subject: [PATCH 02/15] xep-0084: Look for specific user and print ID Add a `/avatar` command. --- src/command/cmd_defs.c | 15 +++++++++++++++ src/command/cmd_funcs.c | 9 +++++++++ src/command/cmd_funcs.h | 2 +- src/xmpp/avatar.c | 22 +++++++++++++++++++++- src/xmpp/avatar.h | 1 + 5 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/command/cmd_defs.c b/src/command/cmd_defs.c index 7e0001c6..509b78e2 100644 --- a/src/command/cmd_defs.c +++ b/src/command/cmd_defs.c @@ -2312,6 +2312,21 @@ static struct cmd_t command_defs[] = "/color on", "/color blue") }, + + { "/avatar", + parse_args, 1, 1, NULL, + CMD_NOSUBFUNCS + CMD_MAINFUNC(cmd_avatar) + CMD_TAGS( + CMD_TAG_CHAT) + CMD_SYN( + "/color nick") + CMD_DESC( + "Not ready") + CMD_ARGS( + { "nick", "Not ready"}) + CMD_NOEXAMPLES + }, }; static GHashTable *search_index; diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 425b50fc..2b9acb02 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -78,6 +78,7 @@ #include "xmpp/jid.h" #include "xmpp/muc.h" #include "xmpp/chat_session.h" +#include "xmpp/avatar.h" #ifdef HAVE_LIBOTR #include "otr/otr.h" @@ -8660,3 +8661,11 @@ cmd_color(ProfWin *window, const char *const command, gchar **args) return TRUE; } + +gboolean +cmd_avatar(ProfWin *window, const char *const command, gchar **args) +{ + avatar_get_by_nick(args[0]); + + return TRUE; +} diff --git a/src/command/cmd_funcs.h b/src/command/cmd_funcs.h index cd37192e..4ce1e284 100644 --- a/src/command/cmd_funcs.h +++ b/src/command/cmd_funcs.h @@ -223,6 +223,6 @@ gboolean cmd_save(ProfWin *window, const char *const command, gchar **args); gboolean cmd_reload(ProfWin *window, const char *const command, gchar **args); gboolean cmd_paste(ProfWin *window, const char *const command, gchar **args); - gboolean cmd_color(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_avatar(ProfWin *window, const char *const command, gchar **args); #endif diff --git a/src/xmpp/avatar.c b/src/xmpp/avatar.c index 29ec453b..2685472a 100644 --- a/src/xmpp/avatar.c +++ b/src/xmpp/avatar.c @@ -41,6 +41,9 @@ #include "xmpp/iq.h" #include "xmpp/message.h" #include "xmpp/stanza.h" +#include "ui/ui.h" + +char *looking_for = NULL; static int _avatar_metadata_nofication(xmpp_stanza_t *const stanza, void *const userdata); @@ -50,7 +53,15 @@ avatar_pep_subscribe(void) message_pubsub_event_handler_add(STANZA_NS_USER_AVATAR_METADATA, _avatar_metadata_nofication, NULL, NULL); message_pubsub_event_handler_add(STANZA_NS_USER_AVATAR_DATA, _avatar_metadata_nofication, NULL, NULL); + //caps_add_feature(XMPP_FEATURE_USER_AVATAR_METADATA_NOTIFY); +} + +bool +avatar_get_by_nick(const char* nick) +{ + looking_for = strdup(nick); caps_add_feature(XMPP_FEATURE_USER_AVATAR_METADATA_NOTIFY); + return TRUE; } static int @@ -59,6 +70,14 @@ _avatar_metadata_nofication(xmpp_stanza_t *const stanza, void *const userdata) const char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM); from = from; + if (!(looking_for && + (g_strcmp0(looking_for, from) == 0))) { + return 1; + } + + free(looking_for); + looking_for = NULL; + xmpp_stanza_t *root = NULL; xmpp_stanza_t *event = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_PUBSUB_EVENT); if (event) { @@ -87,7 +106,8 @@ _avatar_metadata_nofication(xmpp_stanza_t *const stanza, void *const userdata) xmpp_stanza_t *info = xmpp_stanza_get_child_by_name(metadata, "info"); const char *id = xmpp_stanza_get_id(info); - id = id; + + cons_show("Id for %s is: %s", from, id); } return 1; diff --git a/src/xmpp/avatar.h b/src/xmpp/avatar.h index dcaaf24a..6a8ce94b 100644 --- a/src/xmpp/avatar.h +++ b/src/xmpp/avatar.h @@ -36,3 +36,4 @@ #include void avatar_pep_subscribe(void); +bool avatar_get_by_nick(const char* nick); From c2aa585615d35186fbf8ada94f8974bcb386ce8d Mon Sep 17 00:00:00 2001 From: Michael Vetter Date: Wed, 18 Dec 2019 12:46:03 +0100 Subject: [PATCH 03/15] xep-0084: Download avatars Proof of concept. Needs error checking, a nice interface, general cleanup.. --- src/xmpp/avatar.c | 83 +++++++++++++++++++++++++++++++++++++++++++++-- src/xmpp/stanza.c | 31 ++++++++++++++++++ src/xmpp/stanza.h | 2 ++ 3 files changed, 113 insertions(+), 3 deletions(-) diff --git a/src/xmpp/avatar.c b/src/xmpp/avatar.c index 2685472a..0a09f43e 100644 --- a/src/xmpp/avatar.c +++ b/src/xmpp/avatar.c @@ -34,6 +34,7 @@ */ #include +#include #include "log.h" #include "xmpp/connection.h" @@ -42,10 +43,13 @@ #include "xmpp/message.h" #include "xmpp/stanza.h" #include "ui/ui.h" +#include "config/files.h" char *looking_for = NULL; static int _avatar_metadata_nofication(xmpp_stanza_t *const stanza, void *const userdata); +void avatar_request_item_by_id(const char *jid, const char *id); +int avatar_request_item_handler(xmpp_stanza_t *const stanza, void *const userdata); void avatar_pep_subscribe(void) @@ -68,15 +72,14 @@ static int _avatar_metadata_nofication(xmpp_stanza_t *const stanza, void *const userdata) { const char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM); - from = from; if (!(looking_for && (g_strcmp0(looking_for, from) == 0))) { return 1; } - free(looking_for); - looking_for = NULL; +// free(looking_for); +// looking_for = NULL; xmpp_stanza_t *root = NULL; xmpp_stanza_t *event = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_PUBSUB_EVENT); @@ -108,7 +111,81 @@ _avatar_metadata_nofication(xmpp_stanza_t *const stanza, void *const userdata) const char *id = xmpp_stanza_get_id(info); cons_show("Id for %s is: %s", from, id); + avatar_request_item_by_id(from, id); } return 1; } + +void +avatar_request_item_by_id(const char *jid, const char *id) +{ + xmpp_ctx_t * const ctx = connection_get_ctx(); + //char *id = connection_create_stanza_id(); + + xmpp_stanza_t *iq = stanza_create_avatar_retrieve_data_request(ctx, id, jid); + iq_id_handler_add("retrieve1", avatar_request_item_handler, free, NULL); + + iq_send_stanza(iq); + + //free(id); + xmpp_stanza_release(iq); +} + +int +avatar_request_item_handler(xmpp_stanza_t *const stanza, void *const userdata) +{ + const char *from_attr = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM); + + if (!from_attr) { + return 1; + } + + if (g_strcmp0(from_attr, looking_for) != 0) { + return 1; + } + free(looking_for); + looking_for = NULL; + + xmpp_stanza_t *pubsub = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_PUBSUB); + if (!pubsub) { + return 1; + } + + xmpp_stanza_t *items = xmpp_stanza_get_child_by_name(pubsub, "items"); + if (!items) { + return 1; + } + + xmpp_stanza_t *item = xmpp_stanza_get_child_by_name(items, "item"); + if (!item) { + return 1; + } + + xmpp_stanza_t *data = stanza_get_child_by_name_and_ns(item, "data", STANZA_NS_USER_AVATAR_DATA); + if (!data) { + return 1; + } + + char *buf = xmpp_stanza_get_text(data); + gsize size; + gchar *de = (gchar*)g_base64_decode(buf, &size); + free(buf); + GError *err = NULL; + char *path = files_get_data_path(""); + GString *filename = g_string_new(path); + g_string_append(filename, from_attr); + g_string_append(filename, ".png"); + free(path); + + if (g_file_set_contents (filename->str, de, size, &err) == FALSE) { + log_error("Unable to save picture: %s", err->message); + cons_show("Unable to save picture %s", err->message); + g_error_free(err); + } + + g_string_free(filename, TRUE); + free(de); + + return 1; +} diff --git a/src/xmpp/stanza.c b/src/xmpp/stanza.c index 7a744fad..dc0da68b 100644 --- a/src/xmpp/stanza.c +++ b/src/xmpp/stanza.c @@ -2523,3 +2523,34 @@ stanza_get_child_by_name_and_ns(xmpp_stanza_t * const stanza, const char * const return child; } + +xmpp_stanza_t* +stanza_create_avatar_retrieve_data_request(xmpp_ctx_t *ctx, const char *const id, const char *const jid) +{ + xmpp_stanza_t *iq = xmpp_iq_new(ctx, STANZA_TYPE_GET, "retrieve1"); + xmpp_stanza_set_to(iq, jid); + + xmpp_stanza_t *pubsub = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(pubsub, STANZA_NAME_PUBSUB); + xmpp_stanza_set_ns(pubsub, STANZA_NS_PUBSUB); + + xmpp_stanza_t *items = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(items, "items"); + char *node = g_strdup_printf("%s", STANZA_NS_USER_AVATAR_DATA); + xmpp_stanza_set_attribute(items, STANZA_ATTR_NODE, node); + g_free(node); + + xmpp_stanza_t *item = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(item, STANZA_NAME_ITEM); + xmpp_stanza_set_attribute(item, "id", id); + + xmpp_stanza_add_child(items, item); + xmpp_stanza_add_child(pubsub, items); + xmpp_stanza_add_child(iq, pubsub); + + xmpp_stanza_release(item); + xmpp_stanza_release(items); + xmpp_stanza_release(pubsub); + + return iq; +} diff --git a/src/xmpp/stanza.h b/src/xmpp/stanza.h index c7e04703..6e41b81d 100644 --- a/src/xmpp/stanza.h +++ b/src/xmpp/stanza.h @@ -354,4 +354,6 @@ void stanza_free_caps(XMPPCaps *caps); xmpp_stanza_t* stanza_get_child_by_name_and_ns(xmpp_stanza_t * const stanza, const char * const name, const char * const ns); +xmpp_stanza_t* stanza_create_avatar_retrieve_data_request(xmpp_ctx_t *ctx, const char *const id, const char *const jid); + #endif From f692d6d0e0620051902a1f398e41da38c6af20f1 Mon Sep 17 00:00:00 2001 From: Michael Vetter Date: Wed, 18 Dec 2019 13:14:17 +0100 Subject: [PATCH 04/15] xep-0084: Make it work multiple times --- src/xmpp/avatar.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/xmpp/avatar.c b/src/xmpp/avatar.c index 0a09f43e..9d8013f7 100644 --- a/src/xmpp/avatar.c +++ b/src/xmpp/avatar.c @@ -120,6 +120,8 @@ _avatar_metadata_nofication(xmpp_stanza_t *const stanza, void *const userdata) void avatar_request_item_by_id(const char *jid, const char *id) { + caps_remove_feature(XMPP_FEATURE_USER_AVATAR_METADATA_NOTIFY); + xmpp_ctx_t * const ctx = connection_get_ctx(); //char *id = connection_create_stanza_id(); @@ -182,6 +184,8 @@ avatar_request_item_handler(xmpp_stanza_t *const stanza, void *const userdata) log_error("Unable to save picture: %s", err->message); cons_show("Unable to save picture %s", err->message); g_error_free(err); + } else { + cons_show("Avatar saved as %s", filename->str); } g_string_free(filename, TRUE); From 3f58aba2bd373b011f104c499ff42f1bf4b45226 Mon Sep 17 00:00:00 2001 From: Michael Vetter Date: Wed, 18 Dec 2019 13:29:28 +0100 Subject: [PATCH 05/15] xep-0084: save avatars in own directory --- src/xmpp/avatar.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/xmpp/avatar.c b/src/xmpp/avatar.c index 9d8013f7..79aebe49 100644 --- a/src/xmpp/avatar.c +++ b/src/xmpp/avatar.c @@ -35,6 +35,8 @@ #include #include +#include +#include #include "log.h" #include "xmpp/connection.h" @@ -63,6 +65,7 @@ avatar_pep_subscribe(void) bool avatar_get_by_nick(const char* nick) { + free(looking_for); looking_for = strdup(nick); caps_add_feature(XMPP_FEATURE_USER_AVATAR_METADATA_NOTIFY); return TRUE; @@ -123,14 +126,12 @@ avatar_request_item_by_id(const char *jid, const char *id) caps_remove_feature(XMPP_FEATURE_USER_AVATAR_METADATA_NOTIFY); xmpp_ctx_t * const ctx = connection_get_ctx(); - //char *id = connection_create_stanza_id(); xmpp_stanza_t *iq = stanza_create_avatar_retrieve_data_request(ctx, id, jid); iq_id_handler_add("retrieve1", avatar_request_item_handler, free, NULL); iq_send_stanza(iq); - //free(id); xmpp_stanza_release(iq); } @@ -173,13 +174,28 @@ avatar_request_item_handler(xmpp_stanza_t *const stanza, void *const userdata) gsize size; gchar *de = (gchar*)g_base64_decode(buf, &size); free(buf); - GError *err = NULL; + char *path = files_get_data_path(""); GString *filename = g_string_new(path); - g_string_append(filename, from_attr); - g_string_append(filename, ".png"); free(path); + g_string_append(filename, "avatars/"); + + errno = 0; + int res = g_mkdir_with_parents(filename->str, S_IRWXU); + if (res == -1) { + char *errmsg = strerror(errno); + if (errmsg) { + log_error("Avatar: error creating directory: %s, %s", filename->str, errmsg); + } else { + log_error("Avatar: creating directory: %s", filename->str); + } + } + + g_string_append(filename, from_attr); + g_string_append(filename, ".png"); + + GError *err = NULL; if (g_file_set_contents (filename->str, de, size, &err) == FALSE) { log_error("Unable to save picture: %s", err->message); cons_show("Unable to save picture %s", err->message); From 3fdeb52f1c481982c68e9cbcb7206ff145f942b6 Mon Sep 17 00:00:00 2001 From: Michael Vetter Date: Wed, 18 Dec 2019 13:37:42 +0100 Subject: [PATCH 06/15] xep-0084: add /avatar help and autocompletion --- src/command/cmd_ac.c | 18 ++++++++++++++++++ src/command/cmd_defs.c | 6 +++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/command/cmd_ac.c b/src/command/cmd_ac.c index 8908777a..9399aa8d 100644 --- a/src/command/cmd_ac.c +++ b/src/command/cmd_ac.c @@ -112,6 +112,7 @@ static char* _invite_autocomplete(ProfWin *window, const char *const input, gboo static char* _status_autocomplete(ProfWin *window, const char *const input, gboolean previous); static char* _logging_autocomplete(ProfWin *window, const char *const input, gboolean previous); static char* _color_autocomplete(ProfWin *window, const char *const input, gboolean previous); +static char* _avatar_autocomplete(ProfWin *window, const char *const input, gboolean previous); static char* _script_autocomplete_func(const char *const prefix, gboolean previous); @@ -1608,6 +1609,7 @@ _cmd_ac_complete_params(ProfWin *window, const char *const input, gboolean previ g_hash_table_insert(ac_funcs, "/status", _status_autocomplete); g_hash_table_insert(ac_funcs, "/logging", _logging_autocomplete); g_hash_table_insert(ac_funcs, "/color", _color_autocomplete); + g_hash_table_insert(ac_funcs, "/avatar", _avatar_autocomplete); int len = strlen(input); char parsed[len+1]; @@ -3619,3 +3621,19 @@ _color_autocomplete(ProfWin *window, const char *const input, gboolean previous) return NULL; } + +static char* +_avatar_autocomplete(ProfWin *window, const char *const input, gboolean previous) +{ + char *result = NULL; + + jabber_conn_status_t conn_status = connection_get_status(); + if (conn_status == JABBER_CONNECTED) { + result = autocomplete_param_with_func(input, "/avatar", roster_barejid_autocomplete, previous); + if (result) { + return result; + } + } + + return NULL; +} diff --git a/src/command/cmd_defs.c b/src/command/cmd_defs.c index 509b78e2..9b7c6b37 100644 --- a/src/command/cmd_defs.c +++ b/src/command/cmd_defs.c @@ -2320,11 +2320,11 @@ static struct cmd_t command_defs[] = CMD_TAGS( CMD_TAG_CHAT) CMD_SYN( - "/color nick") + "/avatar ") CMD_DESC( - "Not ready") + "Download avatar for a certain contact.") CMD_ARGS( - { "nick", "Not ready"}) + { "", "JID to download avatar from."}) CMD_NOEXAMPLES }, }; From 64a86a17855dd0b12cea73e3b0aa3efca561f783 Mon Sep 17 00:00:00 2001 From: Michael Vetter Date: Wed, 18 Dec 2019 13:42:21 +0100 Subject: [PATCH 07/15] Fix header --- src/xmpp/avatar.c | 2 +- src/xmpp/avatar.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/xmpp/avatar.c b/src/xmpp/avatar.c index 79aebe49..a1eca839 100644 --- a/src/xmpp/avatar.c +++ b/src/xmpp/avatar.c @@ -1,5 +1,5 @@ /* - * omemo.c + * avatar.c * vim: expandtab:ts=4:sts=4:sw=4 * * Copyright (C) 2019 Michael Vetter diff --git a/src/xmpp/avatar.h b/src/xmpp/avatar.h index 6a8ce94b..142f0178 100644 --- a/src/xmpp/avatar.h +++ b/src/xmpp/avatar.h @@ -1,5 +1,5 @@ /* - * omemo.h + * avatar.h * vim: expandtab:ts=4:sts=4:sw=4 * * Copyright (C) 2019 Michael Vetter From 795ae00acfde831e5baa344f310ea7bc14c28e64 Mon Sep 17 00:00:00 2001 From: Michael Vetter Date: Wed, 18 Dec 2019 13:52:48 +0100 Subject: [PATCH 08/15] xep-0084: So far we removed the avatar feature only after a succesful retrive in avatar_request_item_by_id() before we are going to retrieve the actual image. We should remove it at every `/avatar barejid` call too so in case one retrieval was unsucessful that we can call it again. So far it seems like there is no other way to trigger getting the nodes except announcing that we support the avatar feature. --- src/xmpp/avatar.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/xmpp/avatar.c b/src/xmpp/avatar.c index a1eca839..a968780c 100644 --- a/src/xmpp/avatar.c +++ b/src/xmpp/avatar.c @@ -65,9 +65,13 @@ avatar_pep_subscribe(void) bool avatar_get_by_nick(const char* nick) { + caps_remove_feature(XMPP_FEATURE_USER_AVATAR_METADATA_NOTIFY); free(looking_for); + looking_for = strdup(nick); + caps_add_feature(XMPP_FEATURE_USER_AVATAR_METADATA_NOTIFY); + return TRUE; } From 2b6b165ed2aed54073da3abf30a6f13f73f243ab Mon Sep 17 00:00:00 2001 From: Michael Vetter Date: Wed, 18 Dec 2019 14:35:47 +0100 Subject: [PATCH 09/15] xep-0084: Improve help --- src/command/cmd_defs.c | 4 +++- src/xmpp/avatar.c | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/command/cmd_defs.c b/src/command/cmd_defs.c index 9b7c6b37..a1dde57b 100644 --- a/src/command/cmd_defs.c +++ b/src/command/cmd_defs.c @@ -2322,7 +2322,9 @@ static struct cmd_t command_defs[] = CMD_SYN( "/avatar ") CMD_DESC( - "Download avatar for a certain contact.") + "Download avatar (XEP-0084) for a certain contact. " + "If nothing happens after using this command the user either doesn't have an avatar set at all " + "or doesn't use XEP-0084 to publish it.") CMD_ARGS( { "", "JID to download avatar from."}) CMD_NOEXAMPLES diff --git a/src/xmpp/avatar.c b/src/xmpp/avatar.c index a968780c..7ca10b56 100644 --- a/src/xmpp/avatar.c +++ b/src/xmpp/avatar.c @@ -117,7 +117,7 @@ _avatar_metadata_nofication(xmpp_stanza_t *const stanza, void *const userdata) xmpp_stanza_t *info = xmpp_stanza_get_child_by_name(metadata, "info"); const char *id = xmpp_stanza_get_id(info); - cons_show("Id for %s is: %s", from, id); + log_debug("Avatar ID for %s is: %s", from, id); avatar_request_item_by_id(from, id); } From 0ea1d3f85d276f6c3f28be8b1ae831a96138d5f7 Mon Sep 17 00:00:00 2001 From: Michael Vetter Date: Wed, 18 Dec 2019 14:36:18 +0100 Subject: [PATCH 10/15] xep-0084: Normalize at out of image filename --- src/xmpp/avatar.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/xmpp/avatar.c b/src/xmpp/avatar.c index 7ca10b56..364d0143 100644 --- a/src/xmpp/avatar.c +++ b/src/xmpp/avatar.c @@ -196,8 +196,10 @@ avatar_request_item_handler(xmpp_stanza_t *const stanza, void *const userdata) } } - g_string_append(filename, from_attr); + gchar *from = str_replace(from_attr, "@", "_at_"); + g_string_append(filename, from); g_string_append(filename, ".png"); + free(from); GError *err = NULL; if (g_file_set_contents (filename->str, de, size, &err) == FALSE) { From fe41459d86c41b497b5d1f520616781d0e06eab8 Mon Sep 17 00:00:00 2001 From: Michael Vetter Date: Wed, 18 Dec 2019 15:20:07 +0100 Subject: [PATCH 11/15] xep-0084: Add correct file extension --- src/xmpp/avatar.c | 50 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/src/xmpp/avatar.c b/src/xmpp/avatar.c index 364d0143..5e0ffd2f 100644 --- a/src/xmpp/avatar.c +++ b/src/xmpp/avatar.c @@ -49,10 +49,23 @@ char *looking_for = NULL; +typedef struct avatar_metadata { + char *type; +} avatar_metadata; + static int _avatar_metadata_nofication(xmpp_stanza_t *const stanza, void *const userdata); -void avatar_request_item_by_id(const char *jid, const char *id); +void avatar_request_item_by_id(const char *jid, const char *id, const char *type); int avatar_request_item_handler(xmpp_stanza_t *const stanza, void *const userdata); +static void +_free_avatar_data(avatar_metadata *data) +{ + if (data) { + free(data->type); + free(data); + } +} + void avatar_pep_subscribe(void) { @@ -81,7 +94,7 @@ _avatar_metadata_nofication(xmpp_stanza_t *const stanza, void *const userdata) const char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM); if (!(looking_for && - (g_strcmp0(looking_for, from) == 0))) { + (g_strcmp0(looking_for, from) == 0))) { return 1; } @@ -115,24 +128,29 @@ _avatar_metadata_nofication(xmpp_stanza_t *const stanza, void *const userdata) return 1; xmpp_stanza_t *info = xmpp_stanza_get_child_by_name(metadata, "info"); + const char *id = xmpp_stanza_get_id(info); + const char *type = xmpp_stanza_get_attribute(info, "type"); log_debug("Avatar ID for %s is: %s", from, id); - avatar_request_item_by_id(from, id); + avatar_request_item_by_id(from, id, type); } return 1; } void -avatar_request_item_by_id(const char *jid, const char *id) +avatar_request_item_by_id(const char *jid, const char *id, const char *type) { caps_remove_feature(XMPP_FEATURE_USER_AVATAR_METADATA_NOTIFY); xmpp_ctx_t * const ctx = connection_get_ctx(); + avatar_metadata *data = malloc(sizeof(avatar_metadata)); + data->type = strdup(type); + xmpp_stanza_t *iq = stanza_create_avatar_retrieve_data_request(ctx, id, jid); - iq_id_handler_add("retrieve1", avatar_request_item_handler, free, NULL); + iq_id_handler_add("retrieve1", avatar_request_item_handler, (ProfIqFreeCallback)_free_avatar_data, data); iq_send_stanza(iq); @@ -169,12 +187,12 @@ avatar_request_item_handler(xmpp_stanza_t *const stanza, void *const userdata) return 1; } - xmpp_stanza_t *data = stanza_get_child_by_name_and_ns(item, "data", STANZA_NS_USER_AVATAR_DATA); - if (!data) { + xmpp_stanza_t *st_data = stanza_get_child_by_name_and_ns(item, "data", STANZA_NS_USER_AVATAR_DATA); + if (!st_data) { return 1; } - char *buf = xmpp_stanza_get_text(data); + char *buf = xmpp_stanza_get_text(st_data); gsize size; gchar *de = (gchar*)g_base64_decode(buf, &size); free(buf); @@ -198,7 +216,21 @@ avatar_request_item_handler(xmpp_stanza_t *const stanza, void *const userdata) gchar *from = str_replace(from_attr, "@", "_at_"); g_string_append(filename, from); - g_string_append(filename, ".png"); + + avatar_metadata *data = (avatar_metadata*)userdata; + + // check a few image types ourselves + // if none matches we won't add an extension but linux will + // be able to open it anyways + // TODO: we could use /etc/mime-types + if (g_strcmp0(data->type, "image/png") == 0) { + g_string_append(filename, ".png"); + } else if (g_strcmp0(data->type, "image/jpeg") == 0) { + g_string_append(filename, ".jpeg"); + } else if (g_strcmp0(data->type, "image/webp") == 0) { + g_string_append(filename, ".webp"); + } + free(from); GError *err = NULL; From da3ee9539debc61af1f0778ab2904b4aae1fa86a Mon Sep 17 00:00:00 2001 From: Michael Vetter Date: Wed, 18 Dec 2019 15:29:08 +0100 Subject: [PATCH 12/15] xep-0084: move id to avatar_metadata --- src/xmpp/avatar.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/xmpp/avatar.c b/src/xmpp/avatar.c index 5e0ffd2f..58a0a171 100644 --- a/src/xmpp/avatar.c +++ b/src/xmpp/avatar.c @@ -47,14 +47,15 @@ #include "ui/ui.h" #include "config/files.h" -char *looking_for = NULL; - typedef struct avatar_metadata { char *type; + char *id; } avatar_metadata; +char *looking_for = NULL; + static int _avatar_metadata_nofication(xmpp_stanza_t *const stanza, void *const userdata); -void avatar_request_item_by_id(const char *jid, const char *id, const char *type); +void avatar_request_item_by_id(const char *jid, avatar_metadata *data); int avatar_request_item_handler(xmpp_stanza_t *const stanza, void *const userdata); static void @@ -98,9 +99,6 @@ _avatar_metadata_nofication(xmpp_stanza_t *const stanza, void *const userdata) return 1; } -// free(looking_for); -// looking_for = NULL; - xmpp_stanza_t *root = NULL; xmpp_stanza_t *event = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_PUBSUB_EVENT); if (event) { @@ -133,23 +131,25 @@ _avatar_metadata_nofication(xmpp_stanza_t *const stanza, void *const userdata) const char *type = xmpp_stanza_get_attribute(info, "type"); log_debug("Avatar ID for %s is: %s", from, id); - avatar_request_item_by_id(from, id, type); + + avatar_metadata *data = malloc(sizeof(avatar_metadata)); + data->type = strdup(type); + data->id = strdup(id); + + avatar_request_item_by_id(from, data); } return 1; } void -avatar_request_item_by_id(const char *jid, const char *id, const char *type) +avatar_request_item_by_id(const char *jid, avatar_metadata *data) { caps_remove_feature(XMPP_FEATURE_USER_AVATAR_METADATA_NOTIFY); xmpp_ctx_t * const ctx = connection_get_ctx(); - avatar_metadata *data = malloc(sizeof(avatar_metadata)); - data->type = strdup(type); - - xmpp_stanza_t *iq = stanza_create_avatar_retrieve_data_request(ctx, id, jid); + xmpp_stanza_t *iq = stanza_create_avatar_retrieve_data_request(ctx, data->id, jid); iq_id_handler_add("retrieve1", avatar_request_item_handler, (ProfIqFreeCallback)_free_avatar_data, data); iq_send_stanza(iq); From 6978c395383d77e6c71d2cb0955d6b9a6016f536 Mon Sep 17 00:00:00 2001 From: Michael Vetter Date: Wed, 18 Dec 2019 15:31:34 +0100 Subject: [PATCH 13/15] xep-0084: make internal functions static --- src/xmpp/avatar.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/xmpp/avatar.c b/src/xmpp/avatar.c index 58a0a171..4d250462 100644 --- a/src/xmpp/avatar.c +++ b/src/xmpp/avatar.c @@ -55,8 +55,8 @@ typedef struct avatar_metadata { char *looking_for = NULL; static int _avatar_metadata_nofication(xmpp_stanza_t *const stanza, void *const userdata); -void avatar_request_item_by_id(const char *jid, avatar_metadata *data); -int avatar_request_item_handler(xmpp_stanza_t *const stanza, void *const userdata); +static void _avatar_request_item_by_id(const char *jid, avatar_metadata *data); +static int _avatar_request_item_handler(xmpp_stanza_t *const stanza, void *const userdata); static void _free_avatar_data(avatar_metadata *data) @@ -136,29 +136,29 @@ _avatar_metadata_nofication(xmpp_stanza_t *const stanza, void *const userdata) data->type = strdup(type); data->id = strdup(id); - avatar_request_item_by_id(from, data); + _avatar_request_item_by_id(from, data); } return 1; } -void -avatar_request_item_by_id(const char *jid, avatar_metadata *data) +static void +_avatar_request_item_by_id(const char *jid, avatar_metadata *data) { caps_remove_feature(XMPP_FEATURE_USER_AVATAR_METADATA_NOTIFY); xmpp_ctx_t * const ctx = connection_get_ctx(); xmpp_stanza_t *iq = stanza_create_avatar_retrieve_data_request(ctx, data->id, jid); - iq_id_handler_add("retrieve1", avatar_request_item_handler, (ProfIqFreeCallback)_free_avatar_data, data); + iq_id_handler_add("retrieve1", _avatar_request_item_handler, (ProfIqFreeCallback)_free_avatar_data, data); iq_send_stanza(iq); xmpp_stanza_release(iq); } -int -avatar_request_item_handler(xmpp_stanza_t *const stanza, void *const userdata) +static int +_avatar_request_item_handler(xmpp_stanza_t *const stanza, void *const userdata) { const char *from_attr = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM); From 53b92561cb1a815ca2f3622ad7ec4e27cf2c069d Mon Sep 17 00:00:00 2001 From: Michael Vetter Date: Wed, 18 Dec 2019 15:33:16 +0100 Subject: [PATCH 14/15] Add header file guard to avatar.h --- src/xmpp/avatar.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/xmpp/avatar.h b/src/xmpp/avatar.h index 142f0178..8f1415b6 100644 --- a/src/xmpp/avatar.h +++ b/src/xmpp/avatar.h @@ -33,7 +33,12 @@ * */ +#ifndef XMPP_AVATAR_H +#define XMPP_AVATAR_H + #include void avatar_pep_subscribe(void); bool avatar_get_by_nick(const char* nick); + +#endif From 9ac72980d192b716cfb549e0501b70a152811e62 Mon Sep 17 00:00:00 2001 From: Michael Vetter Date: Wed, 18 Dec 2019 15:44:59 +0100 Subject: [PATCH 15/15] xep-0084: add test stub file --- Makefile.am | 1 + src/xmpp/avatar.c | 2 +- src/xmpp/avatar.h | 2 +- tests/unittests/xmpp/stub_avatar.c | 7 +++++++ 4 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 tests/unittests/xmpp/stub_avatar.c diff --git a/Makefile.am b/Makefile.am index 6fe31446..95b2401d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -105,6 +105,7 @@ unittest_sources = \ src/event/server_events.c src/event/server_events.h \ src/event/client_events.c src/event/client_events.h \ src/ui/tray.h src/ui/tray.c \ + tests/unittests/xmpp/stub_avatar.c \ tests/unittests/xmpp/stub_xmpp.c \ tests/unittests/xmpp/stub_message.c \ tests/unittests/ui/stub_ui.c tests/unittests/ui/stub_ui.h \ diff --git a/src/xmpp/avatar.c b/src/xmpp/avatar.c index 4d250462..fb69424c 100644 --- a/src/xmpp/avatar.c +++ b/src/xmpp/avatar.c @@ -76,7 +76,7 @@ avatar_pep_subscribe(void) //caps_add_feature(XMPP_FEATURE_USER_AVATAR_METADATA_NOTIFY); } -bool +gboolean avatar_get_by_nick(const char* nick) { caps_remove_feature(XMPP_FEATURE_USER_AVATAR_METADATA_NOTIFY); diff --git a/src/xmpp/avatar.h b/src/xmpp/avatar.h index 8f1415b6..37026542 100644 --- a/src/xmpp/avatar.h +++ b/src/xmpp/avatar.h @@ -39,6 +39,6 @@ #include void avatar_pep_subscribe(void); -bool avatar_get_by_nick(const char* nick); +gboolean avatar_get_by_nick(const char* nick); #endif diff --git a/tests/unittests/xmpp/stub_avatar.c b/tests/unittests/xmpp/stub_avatar.c new file mode 100644 index 00000000..64b8a95f --- /dev/null +++ b/tests/unittests/xmpp/stub_avatar.c @@ -0,0 +1,7 @@ +#include +#include +#include + +void avatar_pep_subscribe(void) {}; +gboolean avatar_get_by_nick(const char* nick) {return TRUE;} +