about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorMichael Vetter <jubalh@iodoru.org>2021-06-30 11:48:50 +0200
committerGitHub <noreply@github.com>2021-06-30 11:48:50 +0200
commitf30a9e125649b4a7623845d520bdb40ef39dfd8f (patch)
treebb3bb408aa10edb2bf1d5970f36d907b35a2f28d
parent3c648ee2f5715998e440c316ea9eadfd8205463c (diff)
parent3d5e59895b05ed611860aa55569ecfa893b73d64 (diff)
downloadprofani-tty-f30a9e125649b4a7623845d520bdb40ef39dfd8f.tar.gz
Merge pull request #1567 from profanity-im/feature/1524-contact
Add support for XEP-0157 server contact information discovery
-rw-r--r--src/command/cmd_defs.c3
-rw-r--r--src/ui/console.c20
-rw-r--r--src/ui/ui.h1
-rw-r--r--src/xmpp/iq.c13
-rw-r--r--src/xmpp/stanza.c51
-rw-r--r--src/xmpp/stanza.h3
6 files changed, 86 insertions, 5 deletions
diff --git a/src/command/cmd_defs.c b/src/command/cmd_defs.c
index f5b111ac..e04ba2a1 100644
--- a/src/command/cmd_defs.c
+++ b/src/command/cmd_defs.c
@@ -869,7 +869,8 @@ static struct cmd_t command_defs[] = {
               "/disco items [<jid>]")
       CMD_DESC(
               "Find out information about an entities supported services. "
-              "Calling with no arguments will query the server you are currently connected to.")
+              "Calling with no arguments will query the server you are currently connected to. "
+              "This includes discovering contact addresses for XMPP services (XEP-0157).")
       CMD_ARGS(
               { "info [<jid>]", "List protocols and features supported by an entity." },
               { "items [<jid>]", "List items associated with an entity." })
diff --git a/src/ui/console.c b/src/ui/console.c
index ad13f0cc..a99769b1 100644
--- a/src/ui/console.c
+++ b/src/ui/console.c
@@ -837,6 +837,26 @@ cons_show_disco_items(GSList* items, const char* const jid)
     cons_alert(NULL);
 }
 
+static void _cons_print_contact_information_item(gpointer data, gpointer user_data)
+{
+    cons_show("    %s", (char*)data);
+}
+
+static void _cons_print_contact_information_hashlist_item(gpointer key, gpointer value, gpointer userdata)
+{
+    cons_show("  %s:", (char*)key);
+    g_slist_foreach((GSList*)value, _cons_print_contact_information_item, NULL);
+}
+
+void
+cons_show_disco_contact_information(GHashTable* addresses)
+{
+    cons_show("");
+    cons_show("Server contact information:");
+
+    g_hash_table_foreach(addresses, _cons_print_contact_information_hashlist_item, NULL);
+}
+
 void
 cons_show_status(const char* const barejid)
 {
diff --git a/src/ui/ui.h b/src/ui/ui.h
index 0c58b09c..441c9adf 100644
--- a/src/ui/ui.h
+++ b/src/ui/ui.h
@@ -276,6 +276,7 @@ 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_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/iq.c b/src/xmpp/iq.c
index beecb97e..19b620e2 100644
--- a/src/xmpp/iq.c
+++ b/src/xmpp/iq.c
@@ -2308,6 +2308,8 @@ _disco_info_response_id_handler(xmpp_stanza_t* const stanza, void* const userdat
         GSList* features = NULL;
         while (child) {
             const char* stanza_name = xmpp_stanza_get_name(child);
+            const char* child_type = xmpp_stanza_get_type(child);
+
             if (g_strcmp0(stanza_name, STANZA_NAME_FEATURE) == 0) {
                 const char* var = xmpp_stanza_get_attribute(child, STANZA_ATTR_VAR);
                 if (var) {
@@ -2315,10 +2317,9 @@ _disco_info_response_id_handler(xmpp_stanza_t* const stanza, void* const userdat
                 }
             } else if (g_strcmp0(stanza_name, STANZA_NAME_IDENTITY) == 0) {
                 const char* name = xmpp_stanza_get_attribute(child, STANZA_ATTR_NAME);
-                const char* type = xmpp_stanza_get_type(child);
                 const char* category = xmpp_stanza_get_attribute(child, STANZA_ATTR_CATEGORY);
 
-                if (name || category || type) {
+                if (name || category || child_type) {
                     DiscoIdentity* identity = malloc(sizeof(struct disco_identity_t));
 
                     if (identity) {
@@ -2332,8 +2333,8 @@ _disco_info_response_id_handler(xmpp_stanza_t* const stanza, void* const userdat
                         } else {
                             identity->category = NULL;
                         }
-                        if (type) {
-                            identity->type = strdup(type);
+                        if (child_type) {
+                            identity->type = strdup(child_type);
                         } else {
                             identity->type = NULL;
                         }
@@ -2341,6 +2342,10 @@ _disco_info_response_id_handler(xmpp_stanza_t* const stanza, void* const userdat
                         identities = g_slist_append(identities, identity);
                     }
                 }
+            } else if (g_strcmp0(child_type, STANZA_TYPE_RESULT) == 0) {
+                GHashTable *adr = stanza_get_service_contact_addresses(connection_get_ctx(), child);
+                cons_show_disco_contact_information(adr);
+                g_hash_table_destroy(adr);
             }
 
             child = xmpp_stanza_get_next(child);
diff --git a/src/xmpp/stanza.c b/src/xmpp/stanza.c
index 235a7dee..604d4003 100644
--- a/src/xmpp/stanza.c
+++ b/src/xmpp/stanza.c
@@ -2838,3 +2838,54 @@ stanza_create_muc_register_nick(xmpp_ctx_t* ctx, const char* const id, const cha
 
     return iq;
 }
+
+static void
+_contact_addresses_list_free(GSList* list)
+{
+    if (list) {
+        g_slist_free_full(list, g_free);
+    }
+}
+
+GHashTable*
+stanza_get_service_contact_addresses(xmpp_ctx_t* ctx, xmpp_stanza_t* stanza)
+{
+    GHashTable* addresses = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)_contact_addresses_list_free);
+
+    xmpp_stanza_t* fields = xmpp_stanza_get_children(stanza);
+    while (fields) {
+        const char* child_name = xmpp_stanza_get_name(fields);
+        const char* child_type = xmpp_stanza_get_type(fields);
+
+        if (g_strcmp0(child_name, STANZA_NAME_FIELD) == 0 && g_strcmp0(child_type, STANZA_TYPE_LIST_MULTI) == 0) {
+            // extract key (eg 'admin-addresses')
+            const char* var = xmpp_stanza_get_attribute(fields, STANZA_ATTR_VAR );
+
+            // extract values (a list of contact addresses eg mailto:xmpp@shakespeare.lit, xmpp:admins@shakespeare.lit)
+            xmpp_stanza_t* values = xmpp_stanza_get_children(fields);
+            GSList* val_list = NULL;
+            while (values) {
+                const char* value_name = xmpp_stanza_get_name(values);
+                if (value_name && (g_strcmp0(value_name, STANZA_NAME_VALUE) == 0)) {
+                    char* value_text = xmpp_stanza_get_text(values);
+                    if (value_text) {
+                        val_list = g_slist_append(val_list, g_strdup(value_text));
+
+                        xmpp_free(ctx, value_text);
+                    }
+                }
+
+                values = xmpp_stanza_get_next(values);
+            }
+
+            // add to list
+            if (g_slist_length(val_list) > 0) {
+                g_hash_table_insert(addresses, g_strdup(var), val_list);
+            }
+        }
+
+        fields = xmpp_stanza_get_next(fields);
+    }
+
+    return addresses;
+}
diff --git a/src/xmpp/stanza.h b/src/xmpp/stanza.h
index 5effaf3e..aeddf6a2 100644
--- a/src/xmpp/stanza.h
+++ b/src/xmpp/stanza.h
@@ -162,6 +162,7 @@
 #define STANZA_TYPE_SUBMIT       "submit"
 #define STANZA_TYPE_CANCEL       "cancel"
 #define STANZA_TYPE_MODIFY       "modify"
+#define STANZA_TYPE_LIST_MULTI   "list-multi"
 
 #define STANZA_ATTR_TO             "to"
 #define STANZA_ATTR_FROM           "from"
@@ -388,6 +389,8 @@ char* stanza_get_muc_destroy_reason(xmpp_stanza_t* stanza);
 const char* stanza_get_actor(xmpp_stanza_t* stanza);
 char* stanza_get_reason(xmpp_stanza_t* stanza);
 
+GHashTable* stanza_get_service_contact_addresses(xmpp_ctx_t* ctx, xmpp_stanza_t* stanza);
+
 Resource* stanza_resource_from_presence(XMPPPresence* presence);
 XMPPPresence* stanza_parse_presence(xmpp_stanza_t* stanza, int* err);
 void stanza_free_presence(XMPPPresence* presence);