diff options
-rw-r--r-- | Dockerfile.arch | 4 | ||||
-rw-r--r-- | Dockerfile.debian | 4 | ||||
-rw-r--r-- | Dockerfile.fedora | 4 | ||||
-rw-r--r-- | Dockerfile.tumbleweed | 4 | ||||
-rw-r--r-- | Dockerfile.ubuntu | 4 | ||||
-rwxr-xr-x | ci-build.sh | 5 | ||||
-rw-r--r-- | configure.ac | 21 | ||||
-rw-r--r-- | src/command/cmd_ac.c | 7 | ||||
-rw-r--r-- | src/command/cmd_defs.c | 13 | ||||
-rw-r--r-- | src/command/cmd_funcs.c | 35 | ||||
-rw-r--r-- | src/command/cmd_funcs.h | 1 | ||||
-rw-r--r-- | src/main.c | 6 | ||||
-rw-r--r-- | src/omemo/omemo.c | 20 | ||||
-rw-r--r-- | src/omemo/omemo.h | 2 | ||||
-rw-r--r-- | src/ui/console.c | 47 | ||||
-rw-r--r-- | src/ui/ui.h | 4 | ||||
-rw-r--r-- | src/xmpp/avatar.c | 58 | ||||
-rw-r--r-- | src/xmpp/avatar.h | 3 | ||||
-rw-r--r-- | src/xmpp/iq.c | 7 | ||||
-rw-r--r-- | src/xmpp/message.c | 45 | ||||
-rw-r--r-- | src/xmpp/stanza.c | 101 | ||||
-rw-r--r-- | src/xmpp/stanza.h | 4 | ||||
-rw-r--r-- | tests/unittests/omemo/stub_omemo.c | 6 | ||||
-rw-r--r-- | tests/unittests/ui/stub_ui.c | 11 | ||||
-rw-r--r-- | tests/unittests/xmpp/stub_avatar.c | 6 |
25 files changed, 403 insertions, 19 deletions
diff --git a/Dockerfile.arch b/Dockerfile.arch index f7e64576..d4c4e0a1 100644 --- a/Dockerfile.arch +++ b/Dockerfile.arch @@ -26,7 +26,9 @@ RUN pacman -Syu --noconfirm && pacman -S --needed --noconfirm \ pkg-config \ python \ wget \ - sqlite + sqlite \ + gdk-pixbuf2 \ + qrencode RUN mkdir -p /usr/src/{stabber,profanity} diff --git a/Dockerfile.debian b/Dockerfile.debian index c7e0300b..6da1f414 100644 --- a/Dockerfile.debian +++ b/Dockerfile.debian @@ -27,7 +27,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ pkg-config \ python3-dev \ python-dev-is-python3 \ - libsqlite3-dev + libsqlite3-dev \ + libgdk-pixbuf-2.0-dev \ + libqrencode-dev RUN mkdir -p /usr/src/{stabber,libstrophe,profanity} WORKDIR /usr/src diff --git a/Dockerfile.fedora b/Dockerfile.fedora index 684794dd..b885bebb 100644 --- a/Dockerfile.fedora +++ b/Dockerfile.fedora @@ -34,7 +34,9 @@ RUN dnf install -y \ python3-devel \ readline-devel \ openssl-devel \ - sqlite-devel + sqlite-devel \ + gdk-pixbuf2-devel \ + qrencode-devel # https://github.com/openSUSE/docker-containers-build/issues/26 ENV LANG en_US.UTF-8 diff --git a/Dockerfile.tumbleweed b/Dockerfile.tumbleweed index 3820dc84..5fc134dc 100644 --- a/Dockerfile.tumbleweed +++ b/Dockerfile.tumbleweed @@ -34,7 +34,9 @@ RUN zypper --non-interactive in --no-recommends \ python38 \ python38-devel \ readline-devel \ - sqlite3-devel + sqlite3-devel \ + gdk-pixbuf-devel \ + qrencode-devel # https://github.com/openSUSE/docker-containers-build/issues/26 ENV LANG en_US.UTF-8 diff --git a/Dockerfile.ubuntu b/Dockerfile.ubuntu index a9ff84b0..853544c0 100644 --- a/Dockerfile.ubuntu +++ b/Dockerfile.ubuntu @@ -28,7 +28,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ pkg-config \ python3-dev \ python-dev-is-python3 \ - libsqlite3-dev + libsqlite3-dev \ + libgdk-pixbuf-2.0-dev \ + libqrencode-dev RUN mkdir -p /usr/src/{stabber,libstrophe,profanity} WORKDIR /usr/src diff --git a/ci-build.sh b/ci-build.sh index b1a02fc0..0e84cae9 100755 --- a/ci-build.sh +++ b/ci-build.sh @@ -44,7 +44,7 @@ case $(uname | tr '[:upper:]' '[:lower:]') in tests=( "--enable-notifications --enable-icons-and-clipboard --enable-otr --enable-pgp --enable-omemo --enable-plugins --enable-c-plugins - --enable-python-plugins --with-xscreensaver" + --enable-python-plugins --with-xscreensaver --enable-omemo-qrcode --enable-gdk-pixbuf" "--disable-notifications --disable-icons-and-clipboard --disable-otr --disable-pgp --disable-omemo --disable-plugins --disable-c-plugins --disable-python-plugins --without-xscreensaver" @@ -52,7 +52,7 @@ case $(uname | tr '[:upper:]' '[:lower:]') in "--disable-icons-and-clipboard" "--disable-otr" "--disable-pgp" - "--disable-omemo" + "--disable-omemo --disable-omemo-qrcode" "--disable-pgp --disable-otr" "--disable-pgp --disable-otr --disable-omemo" "--disable-plugins" @@ -60,6 +60,7 @@ case $(uname | tr '[:upper:]' '[:lower:]') in "--disable-c-plugins" "--disable-c-plugins --disable-python-plugins" "--without-xscreensaver" + "--disable-gdk-pixbuf" "") ;; darwin*) diff --git a/configure.ac b/configure.ac index 158878b0..96495386 100644 --- a/configure.ac +++ b/configure.ac @@ -65,6 +65,10 @@ AC_ARG_WITH([themes], [AS_HELP_STRING([--with-themes[[=PATH]]], [install themes (default yes)])]) AC_ARG_ENABLE([icons-and-clipboard], [AS_HELP_STRING([--enable-icons-and-clipboard], [enable GTK tray icons and clipboard paste support])]) +AC_ARG_ENABLE([gdk-pixbuf], + [AS_HELP_STRING([--enable-gdk-pixbuf], [enable GDK Pixbuf support to scale avatars before uploading])]) +AC_ARG_ENABLE([omemo-qrcode], + [AS_HELP_STRING([--enable-omemo-qrcode], [enable ability to display omemo qr code])]) # Required dependencies @@ -306,6 +310,15 @@ if test "x$enable_otr" != xno; then AM_COND_IF([BUILD_OTR], [AC_DEFINE([HAVE_LIBOTR], [1], [Have libotr])]) fi +dnl feature: pixbuf / used for scaling avatars before uploading via `/avatar set` +AS_IF([test "x$enable_pixbuf" != xno], + [PKG_CHECK_MODULES([gdk_pixbuf], [gdk-pixbuf-2.0 >= 2.4], + [AC_DEFINE([HAVE_PIXBUF], [1], [gdk-pixbuf module]) + LIBS="$gdk_pixbuf_LIBS $LIBS" CFLAGS="$gdk_pixbuf_CFLAGS $CFLAGS"], + [AS_IF([test "x$enable_pixbuf" = xyes], + [AC_MSG_ERROR([gdk-pixbuf-2.0 >= 2.4 is required to scale avatars before uploading])], + [AC_MSG_NOTICE([gdk-pixbuf-2.0 >= 2.4 not found, GDK Pixbuf support not enabled])])])]) + dnl feature: omemo AM_CONDITIONAL([BUILD_OMEMO], [false]) if test "x$enable_omemo" != xno; then @@ -334,6 +347,14 @@ AS_IF([test "x$with_themes" = xno -o "x$with_themes" = xyes -o "x$with_themes" = AC_SUBST(THEMES_PATH) AM_CONDITIONAL([THEMES_INSTALL], "$THEMES_INSTALL") +if test "x$enable_omemo_qrcode" != xno; then + PKG_CHECK_MODULES([libqrencode], [libqrencode], + [AC_DEFINE([HAVE_QRENCODE], [1], [Have QRencode]) LIBS="$libqrencode_LIBS $LIBS" CFLAGS="$libqrencode_CFLAGS $CFLAGS"], + [AS_IF([test "x$enable_qrcode" = xyes], + [AC_MSG_ERROR([libqrencode not found])], + [AC_MSG_NOTICE([librencode not found])])]) +fi + ## Tests # cmocka is required only for tests, profanity shouldn't be linked with it diff --git a/src/command/cmd_ac.c b/src/command/cmd_ac.c index ec0e9c30..531d189b 100644 --- a/src/command/cmd_ac.c +++ b/src/command/cmd_ac.c @@ -700,6 +700,7 @@ cmd_ac_init(void) autocomplete_add(omemo_ac, "policy"); autocomplete_add(omemo_ac, "trustmode"); autocomplete_add(omemo_ac, "char"); + autocomplete_add(omemo_ac, "qrcode"); omemo_log_ac = autocomplete_new(); autocomplete_add(omemo_log_ac, "on"); @@ -1057,6 +1058,7 @@ cmd_ac_init(void) autocomplete_add(correction_ac, "char"); avatar_ac = autocomplete_new(); + autocomplete_add(avatar_ac, "set"); autocomplete_add(avatar_ac, "get"); autocomplete_add(avatar_ac, "open"); @@ -4101,6 +4103,11 @@ _avatar_autocomplete(ProfWin* window, const char* const input, gboolean previous jabber_conn_status_t conn_status = connection_get_status(); if (conn_status == JABBER_CONNECTED) { + result = cmd_ac_complete_filepath(input, "/avatar set", previous); + if (result) { + return result; + } + result = autocomplete_param_with_func(input, "/avatar get", roster_barejid_autocomplete, previous, NULL); if (result) { return result; diff --git a/src/command/cmd_defs.c b/src/command/cmd_defs.c index b6053e12..411de396 100644 --- a/src/command/cmd_defs.c +++ b/src/command/cmd_defs.c @@ -2312,7 +2312,8 @@ static struct cmd_t command_defs[] = { { "fingerprint", cmd_omemo_fingerprint }, { "char", cmd_omemo_char }, { "policy", cmd_omemo_policy }, - { "clear_device_list", cmd_omemo_clear_device_list }) + { "clear_device_list", cmd_omemo_clear_device_list }, + { "qrcode", cmd_omemo_qrcode }) CMD_NOMAINFUNC CMD_TAGS( CMD_TAG_CHAT, @@ -2327,7 +2328,8 @@ static struct cmd_t command_defs[] = { "/omemo char <char>", "/omemo trustmode manual|firstusage|blind", "/omemo policy manual|automatic|always", - "/omemo clear_device_list") + "/omemo clear_device_list", + "/omemo qrcode") CMD_DESC( "OMEMO commands to manage keys, and perform encryption during chat sessions.") CMD_ARGS( @@ -2344,7 +2346,8 @@ static struct cmd_t command_defs[] = { { "policy manual", "Set the global OMEMO policy to manual, OMEMO sessions must be started manually." }, { "policy automatic", "Set the global OMEMO policy to opportunistic, an OMEMO session will be attempted upon starting a conversation." }, { "policy always", "Set the global OMEMO policy to always, an error will be displayed if an OMEMO session cannot be initiated upon starting a conversation." }, - { "clear_device_list", "Clear your own device list on server side. Each client will reannounce itself when connected back."}) + { "clear_device_list", "Clear your own device list on server side. Each client will reannounce itself when connected back."}, + { "qrcode", "Display QR code of your OMEMO fingerprint"}) CMD_EXAMPLES( "/omemo gen", "/omemo start odin@valhalla.edda", @@ -2443,16 +2446,20 @@ static struct cmd_t command_defs[] = { CMD_TAGS( CMD_TAG_CHAT) CMD_SYN( + "/avatar set <path>", "/avatar get <barejid>", "/avatar open <barejid>") CMD_DESC( + "Upload avatar for oneself (XEP-0084). " "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( + { "set <path>", "Set avatar to the image at <path>." }, { "get <barejid>", "Download the avatar. barejid is the JID to download avatar from." }, { "open <barejid>", "Download avatar and open it with command." }) CMD_EXAMPLES( + "/avatar set ~/images/avatar.png", "/avatar get thor@valhalla.edda", "/avatar open freyja@vanaheimr.edda") }, diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 9e821ede..57381831 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -9036,6 +9036,31 @@ cmd_omemo_policy(ProfWin* window, const char* const command, gchar** args) } gboolean +cmd_omemo_qrcode(ProfWin* window, const char* const command, gchar** args) +{ +#ifdef HAVE_OMEMO + if (connection_get_status() != JABBER_CONNECTED) { + cons_show("You must be connected with an account to load OMEMO information."); + return TRUE; + } + + if (!omemo_loaded()) { + win_println(window, THEME_DEFAULT, "!", "You have not generated or loaded a cryptographic materials, use '/omemo gen'"); + return TRUE; + } + + char* qrstr = omemo_qrcode_str(); + cons_show_qrcode(qrstr); + free(qrstr); + + return TRUE; +#else + cons_show("This version of Profanity has not been built with OMEMO support enabled"); + return TRUE; +#endif +} + +gboolean cmd_save(ProfWin* window, const char* const command, gchar** args) { log_info("Saving preferences to configuration file"); @@ -9195,7 +9220,15 @@ cmd_avatar(ProfWin* window, const char* const command, gchar** args) return TRUE; } - if (g_strcmp0(args[0], "get") == 0) { + if (g_strcmp0(args[0], "set") == 0) { +#ifdef HAVE_PIXBUF + if (avatar_set(args[1])) { + cons_show("Avatar updated successfully"); + } +#else + cons_show("Profanity has not been built with GDK Pixbuf support enabled which is needed to scale the avatar when uploading."); +#endif + } else if (g_strcmp0(args[0], "get") == 0) { avatar_get_by_nick(args[1], false); } else if (g_strcmp0(args[0], "open") == 0) { avatar_get_by_nick(args[1], true); diff --git a/src/command/cmd_funcs.h b/src/command/cmd_funcs.h index 621dd1c6..adc6793d 100644 --- a/src/command/cmd_funcs.h +++ b/src/command/cmd_funcs.h @@ -227,6 +227,7 @@ gboolean cmd_omemo_untrust(ProfWin* window, const char* const command, gchar** a gboolean cmd_omemo_trust_mode(ProfWin* window, const char* const command, gchar** args); gboolean cmd_omemo_policy(ProfWin* window, const char* const command, gchar** args); gboolean cmd_omemo_clear_device_list(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_omemo_qrcode(ProfWin* window, const char* const command, gchar** args); gboolean cmd_save(ProfWin* window, const char* const command, gchar** args); gboolean cmd_reload(ProfWin* window, const char* const command, gchar** args); diff --git a/src/main.c b/src/main.c index 15e4948a..42629370 100644 --- a/src/main.c +++ b/src/main.c @@ -173,6 +173,12 @@ main(int argc, char** argv) g_print("GTK icons/clipboard: Disabled\n"); #endif +#ifdef HAVE_PIXBUF + g_print("GDK Pixbuf: Enabled\n"); +#else + g_print("GDK Pixbuf: Disabled\n"); +#endif + return 0; } diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index e6d9da42..db2c4217 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -856,7 +856,7 @@ omemo_on_message_send(ProfWin* win, const char* const message, gboolean request_ // (Since none of the recipients would be able to read the message.) if (keys == NULL) { win_println(win, THEME_ERROR, "!", "This message cannot be decrypted for any recipient.\n" - "You should trust your recipients' device fingerprint(s) using \"/omemo fingerprint trust FINGERPRINT\".\n" + "You should trust your recipients' device fingerprint(s) using \"/omemo trust FINGERPRINT\".\n" "It could also be that the key bundle of the recipient(s) have not been received. " "In this case, you could try \"omemo end\", \"omemo start\", and send the message again."); goto out; @@ -1896,3 +1896,21 @@ out: curl_url_cleanup(url); return ret; } + +/* returns a string in the format `xmpp:<user@server>?omemo-sid-<numerical-sid>=<omemo-fingerprint-hex-string>` + * used for verification over QR codes + */ +char* +omemo_qrcode_str() +{ + char* mybarejid = connection_get_barejid(); + char* fingerprint = omemo_own_fingerprint(TRUE); + uint32_t sid = omemo_device_id(); + + char* qrstr = g_strdup_printf("xmpp:%s?omemo-sid-%d=%s", mybarejid, sid, fingerprint); + + free(mybarejid); + free(fingerprint); + + return qrstr; +} diff --git a/src/omemo/omemo.h b/src/omemo/omemo.h index 7e7c1f14..624bec51 100644 --- a/src/omemo/omemo.h +++ b/src/omemo/omemo.h @@ -105,3 +105,5 @@ char* omemo_encrypt_file(FILE* in, FILE* out, off_t file_size, int* gcry_res); gcry_error_t omemo_decrypt_file(FILE* in, FILE* out, off_t file_size, const char* fragment); void omemo_free(void* a); int omemo_parse_aesgcm_url(const char* aesgcm_url, char** https_url, char** fragment); + +char* omemo_qrcode_str(); diff --git a/src/ui/console.c b/src/ui/console.c index 3e7a0844..37b2d377 100644 --- a/src/ui/console.c +++ b/src/ui/console.c @@ -48,6 +48,10 @@ #include <curses.h> #endif +#ifdef HAVE_QRENCODE +#include <qrencode.h> +#endif + #include "common.h" #include "log.h" #include "config/preferences.h" @@ -863,6 +867,49 @@ cons_show_disco_contact_information(GHashTable* addresses) } void +cons_show_qrcode(const char* const text) +{ +#ifdef HAVE_QRENCODE + static const size_t ZOOM_SIZE = 10; + QRcode* qrcode = QRcode_encodeString(text, 0, QR_ECLEVEL_L, QR_MODE_8, 1); + + int width = (qrcode->width * ZOOM_SIZE); + unsigned char* data = qrcode->data; + + ProfWin* console = wins_get_console(); + + char buf[(width * 4) + 1]; + memset(buf, 0, sizeof buf); + + char tmp[(width * 4) + 5]; + memset(tmp, 0, sizeof tmp); + + for (int i = 0; i < width + 2 * ZOOM_SIZE; i += ZOOM_SIZE) { + strcat(tmp, "\u2588\u2588"); + } + + win_println(console, THEME_DEFAULT, "", tmp); + for (size_t y = 0; y < width; y += ZOOM_SIZE) { + for (size_t x = 0; x < width; x += ZOOM_SIZE) { + strcat(buf, !(*data & 1) ? "\u2588\u2588" : "\u2800\u2800"); + + data++; + } + + // The extra squares are for padding, so that the QR code doesn't + // "blend in" with the rest of the terminal window. + win_println(console, THEME_DEFAULT, "", "\u2588\u2588%s\u2588\u2588", buf); + memset(buf, 0, sizeof buf); + } + win_println(console, THEME_DEFAULT, "", "%s", tmp); + + QRcode_free(qrcode); +#else + cons_show("This version of Profanity has not been built with libqrencode"); +#endif +} + +void cons_show_status(const char* const barejid) { ProfWin* console = wins_get_console(); diff --git a/src/ui/ui.h b/src/ui/ui.h index 5f31354f..a7886ab3 100644 --- a/src/ui/ui.h +++ b/src/ui/ui.h @@ -277,7 +277,11 @@ void cons_show_bookmarks(const GList* list); void cons_show_bookmarks_ignore(gchar** list, gsize len); void cons_show_disco_items(GSList* items, const char* const jid); void cons_show_disco_info(const char* from, GSList* identities, GSList* features); + void cons_show_disco_contact_information(GHashTable* addresses); + +void cons_show_qrcode(); + void cons_show_room_invite(const char* const invitor, const char* const room, const char* const reason); void cons_check_version(gboolean not_available_msg); void cons_show_typing(const char* const barejid); diff --git a/src/xmpp/avatar.c b/src/xmpp/avatar.c index b962fcef..9345ba3a 100644 --- a/src/xmpp/avatar.c +++ b/src/xmpp/avatar.c @@ -36,6 +36,9 @@ #include "config.h" #include <glib.h> +#ifdef HAVE_PIXBUF +#include <gdk-pixbuf-2.0/gdk-pixbuf/gdk-pixbuf.h> +#endif #include <string.h> #include <stdio.h> #include <stdlib.h> @@ -59,6 +62,7 @@ typedef struct avatar_metadata static GHashTable* looking_for = NULL; // contains nicks/barejids from who we want to get the avatar static GHashTable* shall_open = NULL; // contains a list of nicks that shall not just downloaded but also opened +const int MAX_PIXEL = 192; // max pixel width/height for an avatar static void _avatar_request_item_by_id(const char* jid, avatar_metadata* data); static int _avatar_metadata_handler(xmpp_stanza_t* const stanza, void* const userdata); @@ -92,6 +96,60 @@ avatar_pep_subscribe(void) shall_open = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); } +#ifdef HAVE_PIXBUF +gboolean +avatar_set(const char* path) +{ + char* expanded_path = get_expanded_path(path); + + GError* err = NULL; + GdkPixbuf* pixbuf = gdk_pixbuf_new_from_file(expanded_path, &err); + + if (pixbuf == NULL) { + cons_show_error("An error occurred while opening %s: %s.", expanded_path, err ? err->message : "No error message given"); + return FALSE; + } + free(expanded_path); + + // Scale img + int w = gdk_pixbuf_get_width(pixbuf); + int h = gdk_pixbuf_get_height(pixbuf); + + if (w >= h && w > MAX_PIXEL) { + int dest_height = (int)((float)MAX_PIXEL / w * h); + GdkPixbuf* new_pixbuf = gdk_pixbuf_scale_simple(pixbuf, MAX_PIXEL, dest_height, GDK_INTERP_BILINEAR); + g_object_unref(pixbuf); + pixbuf = new_pixbuf; + } else if (h > w && w > MAX_PIXEL) { + int dest_width = (int)((float)MAX_PIXEL / h * w); + GdkPixbuf* new_pixbuf = gdk_pixbuf_scale_simple(pixbuf, dest_width, MAX_PIXEL, GDK_INTERP_BILINEAR); + g_object_unref(pixbuf); + pixbuf = new_pixbuf; + } + + gchar* img_data; + gsize len = -1; + + if (!gdk_pixbuf_save_to_buffer(pixbuf, &img_data, &len, "png", &err, NULL)) { + cons_show_error("Unable to scale and convert avatar."); + return FALSE; + } + + xmpp_ctx_t* const ctx = connection_get_ctx(); + xmpp_stanza_t* iq = stanza_create_avatar_data_publish_iq(ctx, img_data, len); + iq_send_stanza(iq); + xmpp_stanza_release(iq); + + iq = stanza_create_avatar_metadata_publish_iq(ctx, img_data, len, gdk_pixbuf_get_height(pixbuf), gdk_pixbuf_get_width(pixbuf)); + free(img_data); + g_object_unref(pixbuf); + iq_send_stanza(iq); + xmpp_stanza_release(iq); + + return TRUE; +} +#endif + gboolean avatar_get_by_nick(const char* nick, gboolean open) { diff --git a/src/xmpp/avatar.h b/src/xmpp/avatar.h index 98532917..c65328ad 100644 --- a/src/xmpp/avatar.h +++ b/src/xmpp/avatar.h @@ -40,5 +40,8 @@ void avatar_pep_subscribe(void); gboolean avatar_get_by_nick(const char* nick, gboolean open); +#ifdef HAVE_PIXBUF +gboolean avatar_set(const char* path); +#endif #endif diff --git a/src/xmpp/iq.c b/src/xmpp/iq.c index c97fcbad..8d40a86f 100644 --- a/src/xmpp/iq.c +++ b/src/xmpp/iq.c @@ -892,13 +892,6 @@ _caps_response_id_handler(xmpp_stanza_t* const stanza, void* const userdata) log_debug("Capabilities not cached: %s, storing", given_sha1); EntityCapabilities* capabilities = stanza_create_caps_from_query_element(query); - // Update window name - ProfMucWin* win = wins_get_muc(from); - if (win != NULL) { - free(win->room_name); - win->room_name = strdup(capabilities->identity->name); - } - caps_add_by_ver(given_sha1, capabilities); caps_destroy(capabilities); } diff --git a/src/xmpp/message.c b/src/xmpp/message.c index 97a1132e..6b29777f 100644 --- a/src/xmpp/message.c +++ b/src/xmpp/message.c @@ -59,6 +59,7 @@ #include "xmpp/connection.h" #include "xmpp/xmpp.h" #include "xmpp/form.h" +#include "xmpp/iq.h" #ifdef HAVE_OMEMO #include "xmpp/omemo.h" @@ -1006,6 +1007,28 @@ _handle_captcha(xmpp_stanza_t* const stanza) xmpp_free(ctx, message); } +// Handle changes to muc configuration +static int +_room_config_handler(xmpp_stanza_t* const stanza, void* const userdata) +{ + const char* from = xmpp_stanza_get_from(stanza); + xmpp_stanza_t* query = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_QUERY); + EntityCapabilities* capabilities = stanza_create_caps_from_query_element(query); + + // Update window name + ProfMucWin* win = wins_get_muc(from); + if (win != NULL) { + free(win->room_name); + win->room_name = strdup(capabilities->identity->name); + + // Update features + muc_set_features(from, capabilities->features); + } + caps_destroy(capabilities); + + return 0; +} + static void _handle_groupchat(xmpp_stanza_t* const stanza) { @@ -1037,6 +1060,28 @@ _handle_groupchat(xmpp_stanza_t* const stanza) char* broadcast; broadcast = xmpp_message_get_body(stanza); if (!broadcast) { + xmpp_stanza_t* x = xmpp_stanza_get_child_by_name(stanza, "x"); + + if (x) { + xmpp_stanza_t* status = xmpp_stanza_get_child_by_name(x, "status"); + + if (status) { + const char* code = xmpp_stanza_get_attribute(status, "code"); + + if (code) { + // If configuration change notification send disco info to get updated info of the muc + char* disqo_info_id = connection_create_stanza_id(); + xmpp_stanza_t* iq = stanza_create_disco_info_iq(ctx, disqo_info_id, room_jid, NULL); + iq_id_handler_add(disqo_info_id, _room_config_handler, NULL, NULL); + free(disqo_info_id); + iq_send_stanza(iq); + xmpp_stanza_release(iq); + + return; + } + } + } + jid_destroy(from_jid); return; } diff --git a/src/xmpp/stanza.c b/src/xmpp/stanza.c index bfb782da..8dcad982 100644 --- a/src/xmpp/stanza.c +++ b/src/xmpp/stanza.c @@ -2587,6 +2587,107 @@ stanza_create_avatar_retrieve_data_request(xmpp_ctx_t* ctx, const char* stanza_i } xmpp_stanza_t* +stanza_create_avatar_data_publish_iq(xmpp_ctx_t* ctx, const char* img_data, gsize len) +{ + char* id = connection_create_stanza_id(); + xmpp_stanza_t* iq = xmpp_iq_new(ctx, STANZA_TYPE_SET, id); + free(id); + xmpp_stanza_set_attribute(iq, STANZA_ATTR_FROM, connection_get_fulljid()); + + 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* publish = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(publish, STANZA_NAME_PUBLISH); + xmpp_stanza_set_attribute(publish, STANZA_ATTR_NODE, STANZA_NS_USER_AVATAR_DATA); + + xmpp_stanza_t* item = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(item, STANZA_NAME_ITEM); + char* sha1 = xmpp_sha1(ctx, (guchar*)img_data, len); + xmpp_stanza_set_attribute(item, "id", sha1); + xmpp_free(ctx, sha1); + + xmpp_stanza_t* data = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(data, STANZA_NAME_DATA); + xmpp_stanza_set_ns(data, STANZA_NS_USER_AVATAR_DATA); + + xmpp_stanza_t* text = xmpp_stanza_new(ctx); + gchar* base64 = g_base64_encode((guchar*)img_data, len); + xmpp_stanza_set_text(text, base64); + free(base64); + + xmpp_stanza_add_child(data, text); + xmpp_stanza_add_child(item, data); + xmpp_stanza_add_child(publish, item); + xmpp_stanza_add_child(pubsub, publish); + xmpp_stanza_add_child(iq, pubsub); + + xmpp_stanza_release(text); + xmpp_stanza_release(data); + xmpp_stanza_release(item); + xmpp_stanza_release(publish); + xmpp_stanza_release(pubsub); + + return iq; +} + +xmpp_stanza_t* +stanza_create_avatar_metadata_publish_iq(xmpp_ctx_t* ctx, const char* img_data, gsize len, int height, int width) +{ + char* id = id = connection_create_stanza_id(); + xmpp_stanza_t* iq = xmpp_iq_new(ctx, STANZA_TYPE_SET, id); + free(id); + xmpp_stanza_set_attribute(iq, STANZA_ATTR_FROM, connection_get_fulljid()); + + 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* publish = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(publish, STANZA_NAME_PUBLISH); + xmpp_stanza_set_attribute(publish, STANZA_ATTR_NODE, STANZA_NS_USER_AVATAR_METADATA); + + xmpp_stanza_t* item = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(item, STANZA_NAME_ITEM); + char* sha1 = xmpp_sha1(ctx, (guchar*)img_data, len); + xmpp_stanza_set_attribute(item, "id", sha1); + + xmpp_stanza_t* metadata = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(metadata, STANZA_NAME_METADATA); + xmpp_stanza_set_ns(metadata, STANZA_NS_USER_AVATAR_METADATA); + + xmpp_stanza_t* info = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(info, STANZA_NAME_INFO); + xmpp_stanza_set_attribute(info, "id", sha1); + xmpp_free(ctx, sha1); + char* bytes = g_strdup_printf("%lu", len); + char* h = g_strdup_printf("%d", height); + char* w = g_strdup_printf("%d", width); + xmpp_stanza_set_attribute(info, "bytes", bytes); + xmpp_stanza_set_attribute(info, "type", "img/png"); + xmpp_stanza_set_attribute(info, "height", h); + xmpp_stanza_set_attribute(info, "width", w); + g_free(bytes); + g_free(h); + g_free(w); + + xmpp_stanza_add_child(metadata, info); + xmpp_stanza_add_child(item, metadata); + xmpp_stanza_add_child(publish, item); + xmpp_stanza_add_child(pubsub, publish); + xmpp_stanza_add_child(iq, pubsub); + + xmpp_stanza_release(info); + xmpp_stanza_release(metadata); + xmpp_stanza_release(item); + xmpp_stanza_release(publish); + xmpp_stanza_release(pubsub); + + return iq; +} + +xmpp_stanza_t* stanza_attach_correction(xmpp_ctx_t* ctx, xmpp_stanza_t* stanza, const char* const replace_id) { xmpp_stanza_t* replace_stanza = xmpp_stanza_new(ctx); diff --git a/src/xmpp/stanza.h b/src/xmpp/stanza.h index 6e201497..12c9a5ee 100644 --- a/src/xmpp/stanza.h +++ b/src/xmpp/stanza.h @@ -62,6 +62,8 @@ #define STANZA_NAME_PUBLIC_KEYS_LIST "public-keys-list" #define STANZA_NAME_PUBKEY_METADATA "pubkey-metadata" #define STANZA_NAME_DATA "data" +#define STANZA_NAME_METADATA "metadata" +#define STANZA_NAME_INFO "info" #define STANZA_NAME_SHOW "show" #define STANZA_NAME_STATUS "status" #define STANZA_NAME_IQ "iq" @@ -409,6 +411,8 @@ XMPPCaps* stanza_parse_caps(xmpp_stanza_t* const stanza); void stanza_free_caps(XMPPCaps* caps); xmpp_stanza_t* stanza_create_avatar_retrieve_data_request(xmpp_ctx_t* ctx, const char* stanza_id, const char* const item_id, const char* const jid); +xmpp_stanza_t* stanza_create_avatar_data_publish_iq(xmpp_ctx_t* ctx, const char* img_data, gsize len); +xmpp_stanza_t* stanza_create_avatar_metadata_publish_iq(xmpp_ctx_t* ctx, const char* img_data, gsize len, int height, int width); xmpp_stanza_t* stanza_create_mam_iq(xmpp_ctx_t* ctx, const char* const jid, const char* const startdate, const char* const lastid); xmpp_stanza_t* stanza_change_password(xmpp_ctx_t* ctx, const char* const user, const char* const password); xmpp_stanza_t* stanza_register_new_account(xmpp_ctx_t* ctx, const char* const user, const char* const password); diff --git a/tests/unittests/omemo/stub_omemo.c b/tests/unittests/omemo/stub_omemo.c index f7c60190..0336e1a3 100644 --- a/tests/unittests/omemo/stub_omemo.c +++ b/tests/unittests/omemo/stub_omemo.c @@ -120,3 +120,9 @@ omemo_device_id() { return 123; } + +char* +omemo_qrcode_str() +{ + return NULL; +} diff --git a/tests/unittests/ui/stub_ui.c b/tests/unittests/ui/stub_ui.c index 768e654b..37323ba7 100644 --- a/tests/unittests/ui/stub_ui.c +++ b/tests/unittests/ui/stub_ui.c @@ -923,27 +923,38 @@ void cons_show_disco_items(GSList* items, const char* const jid) { } + void cons_show_disco_info(const char* from, GSList* identities, GSList* features) { } + +void +cons_show_qrcode(const char* const text) +{ +} + void cons_show_room_invite(const char* const invitor, const char* const room, const char* const reason) { } + void cons_check_version(gboolean not_available_msg) { } + void cons_show_typing(const char* const barejid) { } + void cons_show_incoming_room_message(const char* const nick, const char* const room, const int win_index, gboolean mention, GList* triggers, int unread, ProfWin* const window) { } + void cons_show_incoming_message(const char* const short_from, const int win_index, int unread, ProfWin* const window) { diff --git a/tests/unittests/xmpp/stub_avatar.c b/tests/unittests/xmpp/stub_avatar.c index ccd9cf4d..fd63293e 100644 --- a/tests/unittests/xmpp/stub_avatar.c +++ b/tests/unittests/xmpp/stub_avatar.c @@ -8,3 +8,9 @@ avatar_get_by_nick(const char* nick) { return TRUE; } + +gboolean +avatar_set(const char* path) +{ + return TRUE; +} |