about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/command/cmd_ac.c5
-rw-r--r--src/command/cmd_defs.c29
-rw-r--r--src/command/cmd_funcs.c60
-rw-r--r--src/command/cmd_funcs.h1
-rw-r--r--src/config/accounts.c13
-rw-r--r--src/config/accounts.h1
-rw-r--r--src/config/preferences.c3
-rw-r--r--src/config/preferences.h1
-rw-r--r--src/event/server_events.c8
-rw-r--r--src/omemo/omemo.c10
-rw-r--r--src/omemo/store.c2
-rw-r--r--src/plugins/api.c13
-rw-r--r--src/plugins/api.h2
-rw-r--r--src/plugins/c_api.c14
-rw-r--r--src/plugins/profapi.c2
-rw-r--r--src/plugins/profapi.h2
-rw-r--r--src/plugins/python_api.c45
-rw-r--r--src/tools/aesgcm_download.c1
-rw-r--r--src/ui/console.c32
-rw-r--r--src/ui/ui.h2
-rw-r--r--src/xmpp/blocking.c27
-rw-r--r--src/xmpp/iq.c17
-rw-r--r--src/xmpp/message.c22
-rw-r--r--src/xmpp/roster_list.c24
-rw-r--r--src/xmpp/roster_list.h1
-rw-r--r--src/xmpp/stanza.c51
-rw-r--r--src/xmpp/stanza.h7
-rw-r--r--src/xmpp/xmpp.h9
28 files changed, 369 insertions, 35 deletions
diff --git a/src/command/cmd_ac.c b/src/command/cmd_ac.c
index 80daf3db..389a31d6 100644
--- a/src/command/cmd_ac.c
+++ b/src/command/cmd_ac.c
@@ -951,6 +951,8 @@ cmd_ac_init(void)
     blocked_ac = autocomplete_new();
     autocomplete_add(blocked_ac, "add");
     autocomplete_add(blocked_ac, "remove");
+    autocomplete_add(blocked_ac, "report-abuse");
+    autocomplete_add(blocked_ac, "report-spam");
 
     clear_ac = autocomplete_new();
     autocomplete_add(clear_ac, "persist_history");
@@ -1670,7 +1672,8 @@ _cmd_ac_complete_params(ProfWin* window, const char* const input, gboolean previ
 
     // autocomplete boolean settings
     gchar* boolean_choices[] = { "/beep", "/states", "/outtype", "/flash", "/splash",
-                                 "/history", "/vercheck", "/privileges", "/wrap", "/carbons", "/os", "/slashguard", "/mam" };
+                                 "/history", "/vercheck", "/privileges", "/wrap",
+                                 "/carbons", "/os", "/slashguard", "/mam", "/silence" };
 
     for (int i = 0; i < ARRAY_SIZE(boolean_choices); i++) {
         result = autocomplete_param_with_func(input, boolean_choices[i], prefs_autocomplete_boolean_choice, previous, NULL);
diff --git a/src/command/cmd_defs.c b/src/command/cmd_defs.c
index 3223ff40..f2fac9cc 100644
--- a/src/command/cmd_defs.c
+++ b/src/command/cmd_defs.c
@@ -420,7 +420,7 @@ static struct cmd_t command_defs[] = {
     },
 
     { "/blocked",
-      parse_args, 0, 2, NULL,
+      parse_args_with_freetext, 0, 3, NULL,
       CMD_NOSUBFUNCS
       CMD_MAINFUNC(cmd_blocked)
       CMD_TAGS(
@@ -429,15 +429,21 @@ static struct cmd_t command_defs[] = {
       CMD_SYN(
               "/blocked",
               "/blocked add [<jid>]",
+              "/blocked report-abuse [<jid>] [<message>]",
+              "/blocked report-spam [<jid>] [<message>]",
               "/blocked remove <jid>")
       CMD_DESC(
               "Manage blocked users (XEP-0191), calling with no arguments shows the current list of blocked users. "
-              "To blog a certain user in a MUC use the following as jid: room@conference.example.org/spammy-user")
+              "To blog a certain user in a MUC use the following as jid: room@conference.example.org/spammy-user"
+              "It is also possible to block and report (XEP-0377) a user with the report-abuse and report-spam commands.")
       CMD_ARGS(
               { "add [<jid>]", "Block the specified Jabber ID. If in a chat window and no jid is specified, the current recipient will be blocked." },
-              { "remove <jid>", "Remove the specified Jabber ID from the blocked list." })
+              { "remove <jid>", "Remove the specified Jabber ID from the blocked list." },
+              { "report-abuse <jid> [<message>]", "Report the jid as abuse with an optional message to the service operator." },
+              { "report-spam <jid> [<message>]", "Report the jid as spam with an optional message to the service operator." })
       CMD_EXAMPLES(
               "/blocked add hel@helheim.edda",
+              "/blocked report-spam hel@helheim.edda Very annoying guy",
               "/blocked add profanity@rooms.dismail.de/spammy-user")
     },
 
@@ -869,7 +875,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." })
@@ -2617,6 +2624,20 @@ static struct cmd_t command_defs[] = {
       CMD_NOEXAMPLES
     },
 
+    { "/silence",
+      parse_args, 1, 1, &cons_silence_setting,
+      CMD_NOSUBFUNCS
+      CMD_MAINFUNC(cmd_silence)
+      CMD_TAGS(
+              CMD_TAG_CHAT)
+      CMD_SYN(
+              "/silence on|off")
+      CMD_DESC(
+              "Let's you silence all message attempts from people who are not in your roster.")
+      CMD_NOARGS
+      CMD_NOEXAMPLES
+    },
+
     // NEXT-COMMAND (search helper)
 };
 
diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c
index 6f3068ee..dc9b1fb0 100644
--- a/src/command/cmd_funcs.c
+++ b/src/command/cmd_funcs.c
@@ -3008,23 +3008,29 @@ cmd_blocked(ProfWin* window, const char* const command, gchar** args)
     }
 
     if (!connection_supports(XMPP_FEATURE_BLOCKING)) {
-        cons_show("Blocking not supported by server.");
+        cons_show("Blocking (%s) not supported by server.", XMPP_FEATURE_BLOCKING);
         return TRUE;
     }
 
+    blocked_report br = BLOCKED_NO_REPORT;
+
     if (g_strcmp0(args[0], "add") == 0) {
         char* jid = args[1];
-        if (jid == NULL && (window->type == WIN_CHAT)) {
-            ProfChatWin* chatwin = (ProfChatWin*)window;
-            jid = chatwin->barejid;
-        }
 
-        if (jid == NULL) {
-            cons_bad_cmd_usage(command);
-            return TRUE;
+        // /blocked add jid or /blocked add (in window)
+        if (g_strv_length(args) < 3) {
+            if (jid == NULL && (window->type == WIN_CHAT)) {
+                ProfChatWin* chatwin = (ProfChatWin*)window;
+                jid = chatwin->barejid;
+            }
+
+            if (jid == NULL) {
+                cons_bad_cmd_usage(command);
+                return TRUE;
+            }
         }
 
-        gboolean res = blocked_add(jid);
+        gboolean res = blocked_add(jid, br, NULL);
         if (!res) {
             cons_show("User %s already blocked.", jid);
         }
@@ -3046,6 +3052,28 @@ cmd_blocked(ProfWin* window, const char* const command, gchar** args)
         return TRUE;
     }
 
+    if (strncmp(args[0], "report-", 7) == 0) {
+        if (args[1] && g_strcmp0(args[0], "report-abuse") == 0) {
+            br = BLOCKED_REPORT_ABUSE;
+        } else if (args[1] && g_strcmp0(args[0], "report-spam") == 0) {
+            br = BLOCKED_REPORT_SPAM;
+        } else {
+            cons_bad_cmd_usage(command);
+            return TRUE;
+        }
+
+        if (!connection_supports(XMPP_FEATURE_SPAM_REPORTING)) {
+            cons_show("Spam reporting (%s) not supported by server.", XMPP_FEATURE_SPAM_REPORTING);
+            return TRUE;
+        }
+
+        // args[3] is an optional message
+        gboolean res = blocked_add(args[1], br, args[3]);
+        if (!res) {
+            cons_show("User %s already blocked.", args[1]);
+        }
+    }
+
     GList* blocked = blocked_list();
     GList* curr = blocked;
     if (curr) {
@@ -6497,7 +6525,7 @@ cmd_ping(ProfWin* window, const char* const command, gchar** args)
     }
 
     if (args[0] == NULL && connection_supports(XMPP_FEATURE_PING) == FALSE) {
-        cons_show("Server does not support ping requests.");
+        cons_show("Server does not support ping requests (%s).", XMPP_FEATURE_PING);
         return TRUE;
     }
 
@@ -8118,7 +8146,7 @@ cmd_command_list(ProfWin* window, const char* const command, gchar** args)
     }
 
     if (connection_supports(XMPP_FEATURE_COMMANDS) == FALSE) {
-        cons_show("Server does not support ad hoc commands.");
+        cons_show("Server does not support ad hoc commands (%s).", XMPP_FEATURE_COMMANDS);
         return TRUE;
     }
 
@@ -8174,7 +8202,7 @@ cmd_command_exec(ProfWin* window, const char* const command, gchar** args)
     }
 
     if (connection_supports(XMPP_FEATURE_COMMANDS) == FALSE) {
-        cons_show("Server does not support ad hoc commands.");
+        cons_show("Server does not support ad hoc commands (%s).", XMPP_FEATURE_COMMANDS);
         return TRUE;
     }
 
@@ -9493,3 +9521,11 @@ cmd_editor(ProfWin* window, const char* const command, gchar** args)
     }
     return TRUE;
 }
+
+gboolean
+cmd_silence(ProfWin* window, const char* const command, gchar** args)
+{
+    _cmd_set_boolean_preference(args[0], command, "Block all messages from JIDs that are not in the roster", PREF_SILENCE_NON_ROSTER);
+
+    return TRUE;
+}
diff --git a/src/command/cmd_funcs.h b/src/command/cmd_funcs.h
index aadcb55f..54cc6e78 100644
--- a/src/command/cmd_funcs.h
+++ b/src/command/cmd_funcs.h
@@ -246,5 +246,6 @@ gboolean cmd_executable_urlsave(ProfWin* window, const char* const command, gcha
 gboolean cmd_executable_editor(ProfWin* window, const char* const command, gchar** args);
 gboolean cmd_mam(ProfWin* window, const char* const command, gchar** args);
 gboolean cmd_editor(ProfWin* window, const char* const command, gchar** args);
+gboolean cmd_silence(ProfWin* window, const char* const command, gchar** args);
 
 #endif
diff --git a/src/config/accounts.c b/src/config/accounts.c
index 6c876055..774adc0d 100644
--- a/src/config/accounts.c
+++ b/src/config/accounts.c
@@ -844,8 +844,6 @@ accounts_set_last_status(const char* const account_name, const char* const value
     if (accounts_account_exists(account_name)) {
         if (value) {
             g_key_file_set_string(accounts, account_name, "presence.laststatus", value);
-        } else {
-            g_key_file_remove_key(accounts, account_name, "presence.laststatus", NULL);
         }
         _save_accounts();
     }
@@ -952,6 +950,17 @@ accounts_get_login_presence(const char* const account_name)
     return result;
 }
 
+char*
+accounts_get_login_status(const char* const account_name)
+{
+    gchar* setting = g_key_file_get_string(accounts, account_name, "presence.login", NULL);
+    gchar* status = NULL;
+    if (g_strcmp0(setting, "last") == 0) {
+        status = accounts_get_last_status(account_name);
+    }
+    return status;
+}
+
 static void
 _save_accounts(void)
 {
diff --git a/src/config/accounts.h b/src/config/accounts.h
index 4195a82e..d279bbd0 100644
--- a/src/config/accounts.h
+++ b/src/config/accounts.h
@@ -76,6 +76,7 @@ void accounts_set_login_presence(const char* const account_name, const char* con
 resource_presence_t accounts_get_login_presence(const char* const account_name);
 char* accounts_get_last_status(const char* const account_name);
 resource_presence_t accounts_get_last_presence(const char* const account_name);
+char* accounts_get_login_status(const char* const account_name);
 void accounts_set_priority_online(const char* const account_name, const gint value);
 void accounts_set_priority_chat(const char* const account_name, const gint value);
 void accounts_set_priority_away(const char* const account_name, const gint value);
diff --git a/src/config/preferences.c b/src/config/preferences.c
index 3e1ba585..4b24d46b 100644
--- a/src/config/preferences.c
+++ b/src/config/preferences.c
@@ -1916,6 +1916,7 @@ _get_group(preference_t pref)
     case PREF_TLS_CERTPATH:
     case PREF_CORRECTION_ALLOW:
     case PREF_MAM:
+    case PREF_SILENCE_NON_ROSTER:
         return PREF_GROUP_CONNECTION;
     case PREF_OTR_LOG:
     case PREF_OTR_POLICY:
@@ -2198,6 +2199,8 @@ _get_key(preference_t pref)
         return "url.save.cmd";
     case PREF_COMPOSE_EDITOR:
         return "compose.editor";
+    case PREF_SILENCE_NON_ROSTER:
+        return "silence.incoming.nonroster";
     default:
         return NULL;
     }
diff --git a/src/config/preferences.h b/src/config/preferences.h
index d8573349..f7474c1f 100644
--- a/src/config/preferences.h
+++ b/src/config/preferences.h
@@ -176,6 +176,7 @@ typedef enum {
     PREF_URL_OPEN_CMD,
     PREF_URL_SAVE_CMD,
     PREF_COMPOSE_EDITOR,
+    PREF_SILENCE_NON_ROSTER,
 } preference_t;
 
 typedef struct prof_alias_t
diff --git a/src/event/server_events.c b/src/event/server_events.c
index c1fe381c..7e5e3d7d 100644
--- a/src/event/server_events.c
+++ b/src/event/server_events.c
@@ -40,6 +40,7 @@
 #include <stdlib.h>
 #include <assert.h>
 
+#include "config/accounts.h"
 #include "profanity.h"
 #include "log.h"
 #include "database.h"
@@ -166,6 +167,7 @@ sv_ev_roster_received(void)
     // send initial presence
     resource_presence_t conn_presence = accounts_get_login_presence(account_name);
     char* last_activity_str = accounts_get_last_activity(account_name);
+    char* status_message = accounts_get_login_status(account_name);
     if (prefs_get_boolean(PREF_LASTACTIVITY) && last_activity_str) {
 
         GTimeVal lasttv;
@@ -177,17 +179,17 @@ sv_ev_roster_received(void)
             GTimeSpan diff_micros = g_date_time_difference(nowdt, lastdt);
             int diff_secs = (diff_micros / 1000) / 1000;
 
-            connection_set_presence_msg(NULL);
+            connection_set_presence_msg(status_message);
             cl_ev_presence_send(conn_presence, diff_secs);
 
             g_date_time_unref(lastdt);
         } else {
-            connection_set_presence_msg(NULL);
+            connection_set_presence_msg(status_message);
             cl_ev_presence_send(conn_presence, 0);
         }
         g_date_time_unref(nowdt);
     } else {
-        connection_set_presence_msg(NULL);
+        connection_set_presence_msg(status_message);
         cl_ev_presence_send(conn_presence, 0);
     }
 
diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c
index efc3dbfe..560a3473 100644
--- a/src/omemo/omemo.c
+++ b/src/omemo/omemo.c
@@ -825,7 +825,7 @@ omemo_on_message_send(ProfWin* win, const char* const message, gboolean request_
             res = session_cipher_encrypt(cipher, key_tag, AES128_GCM_KEY_LENGTH + AES128_GCM_TAG_LENGTH, &ciphertext);
             session_cipher_free(cipher);
             if (res != SG_SUCCESS ) {
-                log_error("[OMEMO][SEND] cannot encrypt key for %s device id %d - code: %d", address.name, address.device_id,res);
+                log_info("[OMEMO][SEND] cannot encrypt key for %s device id %d - code: %d", address.name, address.device_id,res);
                 continue;
             }
             signal_buffer* buffer = ciphertext_message_get_serialized(ciphertext);
@@ -855,6 +855,7 @@ omemo_on_message_send(ProfWin* win, const char* const message, gboolean request_
     // Encrypt keys for the sender
     if (!muc) {
         GList* sender_device_id = g_hash_table_lookup(omemo_ctx.device_list, jid->barejid);
+
         for (device_ids_iter = sender_device_id; device_ids_iter != NULL; device_ids_iter = device_ids_iter->next) {
             int res;
             ciphertext_message* ciphertext;
@@ -864,7 +865,7 @@ omemo_on_message_send(ProfWin* win, const char* const message, gboolean request_
                 .name_len = strlen(jid->barejid),
                 .device_id = GPOINTER_TO_INT(device_ids_iter->data)
             };
-            log_debug("[OMEMO][SEND] Sending to device %d for %s ", address.device_id, address.name);
+            log_debug("[OMEMO][SEND][Sender] Sending to device %d for %s ", address.device_id, address.name);
             // Don't encrypt for this device (according to
             // <https://xmpp.org/extensions/xep-0384.html#encrypt>).
             if (address.device_id == omemo_ctx.device_id) {
@@ -873,14 +874,14 @@ omemo_on_message_send(ProfWin* win, const char* const message, gboolean request_
 
             res = session_cipher_create(&cipher, omemo_ctx.store, &address, omemo_ctx.signal);
             if (res != 0) {
-                log_error("[OMEMO][SEND] cannot create cipher for %s device id %d", address.name, address.device_id);
+                log_info("[OMEMO][SEND][Sender] cannot create cipher for %s device id %d", address.name, address.device_id);
                 continue;
             }
 
             res = session_cipher_encrypt(cipher, key_tag, AES128_GCM_KEY_LENGTH + AES128_GCM_TAG_LENGTH, &ciphertext);
             session_cipher_free(cipher);
             if (res != 0) {
-                log_error("[OMEMO][SEND] cannot encrypt key for %s device id %d", address.name, address.device_id);
+                log_info("[OMEMO][SEND][Sender] cannot encrypt key for %s device id %d", address.name, address.device_id);
                 continue;
             }
             signal_buffer* buffer = ciphertext_message_get_serialized(ciphertext);
@@ -1861,6 +1862,7 @@ omemo_parse_aesgcm_url(const char* aesgcm_url,
     }
 
     if (strlen(*fragment) != AESGCM_URL_NONCE_LEN + AESGCM_URL_KEY_LEN) {
+        ret = 1;
         goto out;
     }
 
diff --git a/src/omemo/store.c b/src/omemo/store.c
index d5ee521c..70fd91d3 100644
--- a/src/omemo/store.c
+++ b/src/omemo/store.c
@@ -85,7 +85,7 @@ load_session(signal_buffer** record, signal_buffer** user_record,
     device_store = g_hash_table_lookup(session_store, address->name);
     if (!device_store) {
         *record = NULL;
-        log_warning("[OMEMO][STORE] No device store for %s found", address->name);
+        log_info("[OMEMO][STORE] No device store for %s found", address->name);
         return 0;
     }
 
diff --git a/src/plugins/api.c b/src/plugins/api.c
index 2c8983aa..de286d96 100644
--- a/src/plugins/api.c
+++ b/src/plugins/api.c
@@ -55,6 +55,7 @@
 #include "plugins/disco.h"
 #include "ui/ui.h"
 #include "ui/window_list.h"
+#include "xmpp/roster_list.h"
 
 void
 api_cons_alert(void)
@@ -239,6 +240,18 @@ api_get_current_nick(void)
     }
 }
 
+char*
+api_get_name_from_roster(const char* barejid)
+{
+    return roster_get_display_name(barejid);
+}
+
+char*
+api_get_barejid_from_roster(const char* name)
+{
+    return roster_barejid_from_name(name);
+}
+
 char**
 api_get_current_occupants(void)
 {
diff --git a/src/plugins/api.h b/src/plugins/api.h
index f0842c4e..fcd068e2 100644
--- a/src/plugins/api.h
+++ b/src/plugins/api.h
@@ -49,6 +49,8 @@ char* api_get_current_recipient(void);
 char* api_get_current_muc(void);
 gboolean api_current_win_is_console(void);
 char* api_get_current_nick(void);
+char* api_get_name_from_roster(const char* barejid);
+char* api_get_barejid_from_roster(const char* name);
 char** api_get_current_occupants(void);
 
 char* api_get_room_nick(const char* barejid);
diff --git a/src/plugins/c_api.c b/src/plugins/c_api.c
index 0f236074..9f54d94d 100644
--- a/src/plugins/c_api.c
+++ b/src/plugins/c_api.c
@@ -195,6 +195,18 @@ c_api_get_current_nick(void)
     return api_get_current_nick();
 }
 
+static char*
+c_api_get_name_from_roster(const char* barejid)
+{
+    return api_get_name_from_roster(barejid);
+}
+
+static char*
+c_api_get_barejid_from_roster(const char* name)
+{
+    return api_get_barejid_from_roster(name);
+}
+
 static char**
 c_api_get_current_occupants(void)
 {
@@ -483,6 +495,8 @@ c_api_init(void)
     prof_get_current_muc = c_api_get_current_muc;
     prof_current_win_is_console = c_api_current_win_is_console;
     prof_get_current_nick = c_api_get_current_nick;
+    prof_get_name_from_roster = c_api_get_name_from_roster;
+    prof_get_barejid_from_roster = c_api_get_barejid_from_roster;
     prof_get_current_occupants = c_api_get_current_occupants;
     prof_get_room_nick = c_api_get_room_nick;
     prof_log_debug = c_api_log_debug;
diff --git a/src/plugins/profapi.c b/src/plugins/profapi.c
index 75475631..9011f1a2 100644
--- a/src/plugins/profapi.c
+++ b/src/plugins/profapi.c
@@ -64,6 +64,8 @@ char* (*prof_get_current_recipient)(void) = NULL;
 char* (*prof_get_current_muc)(void) = NULL;
 int (*prof_current_win_is_console)(void) = NULL;
 char* (*prof_get_current_nick)(void) = NULL;
+char* (*prof_get_name_from_roster)(const char *barejid) = NULL;
+char* (*prof_get_barejid_from_roster)(const char *name) = NULL;
 char** (*prof_get_current_occupants)(void) = NULL;
 
 char* (*prof_get_room_nick)(const char *barejid) = NULL;
diff --git a/src/plugins/profapi.h b/src/plugins/profapi.h
index cfca45c3..7e607f09 100644
--- a/src/plugins/profapi.h
+++ b/src/plugins/profapi.h
@@ -74,6 +74,8 @@ char* (*prof_get_current_recipient)(void);
 char* (*prof_get_current_muc)(void);
 int (*prof_current_win_is_console)(void);
 char* (*prof_get_current_nick)(void);
+char* (*prof_get_name_from_roster)(const char *barejid);
+char* (*prof_get_barejid_from_roster)(const char *name);
 char** (*prof_get_current_occupants)(void);
 
 char* (*prof_get_room_nick)(const char *barejid);
diff --git a/src/plugins/python_api.c b/src/plugins/python_api.c
index fe66d99e..8ea54514 100644
--- a/src/plugins/python_api.c
+++ b/src/plugins/python_api.c
@@ -46,6 +46,7 @@
 #include "plugins/python_plugins.h"
 #include "plugins/callbacks.h"
 #include "plugins/autocompleters.h"
+#include "xmpp/roster_list.h"
 
 static char* _python_plugin_name(void);
 
@@ -441,6 +442,48 @@ python_api_get_current_nick(PyObject* self, PyObject* args)
 }
 
 static PyObject*
+python_api_get_name_from_roster(PyObject* self, PyObject* args)
+{
+    PyObject* barejid = NULL;
+    if (!PyArg_ParseTuple(args, "O", &barejid)) {
+        Py_RETURN_NONE;
+    }
+
+    char* barejid_str = python_str_or_unicode_to_string(barejid);
+
+    allow_python_threads();
+    char* name = roster_get_display_name(barejid_str);
+    free(barejid_str);
+    disable_python_threads();
+    if (name) {
+        return Py_BuildValue("s", name);
+    } else {
+        Py_RETURN_NONE;
+    }
+}
+
+static PyObject*
+python_api_get_barejid_from_roster(PyObject* self, PyObject* args)
+{
+    PyObject* name = NULL;
+    if (!PyArg_ParseTuple(args, "O", &name)) {
+        Py_RETURN_NONE;
+    }
+
+    char* name_str = python_str_or_unicode_to_string(name);
+
+    allow_python_threads();
+    char* barejid = roster_barejid_from_name(name_str);
+    free(name_str);
+    disable_python_threads();
+    if (barejid) {
+        return Py_BuildValue("s", barejid);
+    } else {
+        Py_RETURN_NONE;
+    }
+}
+
+static PyObject*
 python_api_get_current_occupants(PyObject* self, PyObject* args)
 {
     allow_python_threads();
@@ -1487,6 +1530,8 @@ static PyMethodDef apiMethods[] = {
     { "get_current_recipient", python_api_get_current_recipient, METH_VARARGS, "Return the jid of the recipient of the current window." },
     { "get_current_muc", python_api_get_current_muc, METH_VARARGS, "Return the jid of the room of the current window." },
     { "get_current_nick", python_api_get_current_nick, METH_VARARGS, "Return nickname in current room." },
+    { "get_name_from_roster", python_api_get_name_from_roster, METH_VARARGS, "Return nickname in roster of barejid." },
+    { "get_barejid_from_roster", python_api_get_barejid_from_roster, METH_VARARGS, "Return nickname in roster of barejid." },
     { "get_current_occupants", python_api_get_current_occupants, METH_VARARGS, "Return list of occupants in current room." },
     { "current_win_is_console", python_api_current_win_is_console, METH_VARARGS, "Returns whether the current window is the console." },
     { "get_room_nick", python_api_get_room_nick, METH_VARARGS, "Return the nickname used in the specified room, or None if not in the room." },
diff --git a/src/tools/aesgcm_download.c b/src/tools/aesgcm_download.c
index 864da6b7..45aa7b66 100644
--- a/src/tools/aesgcm_download.c
+++ b/src/tools/aesgcm_download.c
@@ -70,6 +70,7 @@ aesgcm_file_get(void* userdata)
     // Convert the aesgcm:// URL to a https:// URL and extract the encoded key
     // and tag stored in the URL fragment.
     if (omemo_parse_aesgcm_url(aesgcm_dl->url, &https_url, &fragment) != 0) {
+        cons_show_error("Download failed: Cannot parse URL '%s'.", aesgcm_dl->url);
         http_print_transfer_update(aesgcm_dl->window, aesgcm_dl->url,
                                    "Download failed: Cannot parse URL '%s'.",
                                    aesgcm_dl->url);
diff --git a/src/ui/console.c b/src/ui/console.c
index ad13f0cc..f7963a27 100644
--- a/src/ui/console.c
+++ b/src/ui/console.c
@@ -837,6 +837,28 @@ 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)
+{
+    if (addresses && g_hash_table_size(addresses) > 0) {
+        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)
 {
@@ -2157,6 +2179,16 @@ cons_mam_setting(void)
 }
 
 void
+cons_silence_setting(void)
+{
+    if (prefs_get_boolean(PREF_SILENCE_NON_ROSTER)) {
+        cons_show("Block all messages from JIDs that are not in the roster (/silence)    : ON");
+    } else {
+        cons_show("Block all messages from JIDs that are not in the roster (/silence)    : OFF");
+    }
+}
+
+void
 cons_show_connection_prefs(void)
 {
     cons_show("Connection preferences:");
diff --git a/src/ui/ui.h b/src/ui/ui.h
index 0c58b09c..dc2fc497 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);
@@ -327,6 +328,7 @@ void cons_correction_setting(void);
 void cons_executable_setting(void);
 void cons_slashguard_setting(void);
 void cons_mam_setting(void);
+void cons_silence_setting(void);
 void cons_show_contact_online(PContact contact, Resource* resource, GDateTime* last_activity);
 void cons_show_contact_offline(PContact contact, char* resource, char* status);
 void cons_theme_properties(void);
diff --git a/src/xmpp/blocking.c b/src/xmpp/blocking.c
index 34614679..b16f95cb 100644
--- a/src/xmpp/blocking.c
+++ b/src/xmpp/blocking.c
@@ -109,7 +109,7 @@ blocked_ac_reset(void)
 }
 
 gboolean
-blocked_add(char* jid)
+blocked_add(char* jid, blocked_report reportkind, const char* const message)
 {
     GList* found = g_list_find_custom(blocked, jid, (GCompareFunc)g_strcmp0);
     if (found) {
@@ -129,6 +129,31 @@ blocked_add(char* jid)
     xmpp_stanza_set_name(item, STANZA_NAME_ITEM);
     xmpp_stanza_set_attribute(item, STANZA_ATTR_JID, jid);
 
+    if (reportkind != BLOCKED_NO_REPORT) {
+        xmpp_stanza_t* report = xmpp_stanza_new(ctx);
+        xmpp_stanza_set_name(report, STANZA_NAME_REPORT);
+        if (reportkind == BLOCKED_REPORT_ABUSE) {
+            xmpp_stanza_set_attribute(report, STANZA_ATTR_REASON, STANZA_REPORTING_ABUSE);
+        } else {
+            xmpp_stanza_set_attribute(report, STANZA_ATTR_REASON, STANZA_REPORTING_SPAM);
+        }
+
+        if (message) {
+            xmpp_stanza_t* text = xmpp_stanza_new(ctx);
+            xmpp_stanza_set_name(text, STANZA_NAME_TEXT);
+
+            xmpp_stanza_t* txt = xmpp_stanza_new(ctx);
+            xmpp_stanza_set_text(txt, message);
+
+            xmpp_stanza_add_child(text, txt);
+            xmpp_stanza_add_child(report, text);
+            xmpp_stanza_release(txt);
+        }
+
+        xmpp_stanza_add_child(item, report);
+        xmpp_stanza_release(report);
+    }
+
     xmpp_stanza_add_child(block, item);
     xmpp_stanza_release(item);
 
diff --git a/src/xmpp/iq.c b/src/xmpp/iq.c
index beecb97e..42571b9c 100644
--- a/src/xmpp/iq.c
+++ b/src/xmpp/iq.c
@@ -1385,7 +1385,7 @@ _autoping_timed_send(xmpp_conn_t* const conn, void* const userdata)
     if (connection_supports(XMPP_FEATURE_PING) == FALSE) {
         log_warning("Server doesn't advertise %s feature, disabling autoping.", XMPP_FEATURE_PING);
         prefs_set_autoping(0);
-        cons_show_error("Server ping not supported, autoping disabled.");
+        cons_show_error("Server ping not supported (%s), autoping disabled.", XMPP_FEATURE_PING);
         xmpp_conn_t* conn = connection_get_conn();
         xmpp_timed_handler_delete(conn, _autoping_timed_send);
         return 1;
@@ -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);
@@ -2581,7 +2586,7 @@ iq_mam_request(ProfChatWin* win)
 {
     if (connection_supports(XMPP_FEATURE_MAM2) == FALSE) {
         log_warning("Server doesn't advertise %s feature.", XMPP_FEATURE_MAM2);
-        cons_show_error("Server doesn't support MAM.");
+        cons_show_error("Server doesn't support MAM (%s).", XMPP_FEATURE_MAM2);
         return;
     }
 
diff --git a/src/xmpp/message.c b/src/xmpp/message.c
index 38d3ad49..051d1ece 100644
--- a/src/xmpp/message.c
+++ b/src/xmpp/message.c
@@ -94,6 +94,7 @@ static gboolean _handle_mam(xmpp_stanza_t* const stanza);
 static void _handle_pubsub(xmpp_stanza_t* const stanza, xmpp_stanza_t* const event);
 static gboolean _handle_form(xmpp_stanza_t* const stanza);
 static gboolean _handle_jingle_message(xmpp_stanza_t* const stanza);
+static gboolean _should_ignore_based_on_silence(xmpp_stanza_t* const stanza);
 
 #ifdef HAVE_LIBGPGME
 static xmpp_stanza_t* _openpgp_signcrypt(xmpp_ctx_t* ctx, const char* const to, const char* const text);
@@ -171,6 +172,11 @@ _message_handler(xmpp_conn_t* const conn, xmpp_stanza_t* const stanza, void* con
     } else if (type == NULL || g_strcmp0(type, STANZA_TYPE_CHAT) == 0 || g_strcmp0(type, STANZA_TYPE_NORMAL) == 0) {
         // type: chat, normal (==NULL)
 
+        // ignore all messages from JIDs that are not in roster, if 'silence' is set
+        if (_should_ignore_based_on_silence(stanza)) {
+            return 1;
+        }
+
         // XEP-0353: Jingle Message Initiation
         if (_handle_jingle_message(stanza)) {
             return 1;
@@ -1689,3 +1695,19 @@ _handle_jingle_message(xmpp_stanza_t* const stanza)
     }
     return FALSE;
 }
+
+static gboolean
+_should_ignore_based_on_silence(xmpp_stanza_t* const stanza)
+{
+    if (prefs_get_boolean(PREF_SILENCE_NON_ROSTER)) {
+        const char* const from = xmpp_stanza_get_from(stanza);
+        Jid* from_jid = jid_create(from);
+        PContact contact = roster_get_contact(from_jid->barejid);
+        jid_destroy(from_jid);
+        if (!contact) {
+            log_debug("[Silence] Ignoring message from: %s", from);
+            return TRUE;
+        }
+    }
+    return FALSE;
+}
diff --git a/src/xmpp/roster_list.c b/src/xmpp/roster_list.c
index 04d16a17..9d7ac427 100644
--- a/src/xmpp/roster_list.c
+++ b/src/xmpp/roster_list.c
@@ -171,6 +171,30 @@ roster_get_contact(const char* const barejid)
 }
 
 char*
+roster_get_display_name(const char* const barejid)
+{
+    assert(roster != NULL);
+
+    GString* result = g_string_new("");
+
+    PContact contact = roster_get_contact(barejid);
+    if (contact) {
+        if (p_contact_name(contact)) {
+            g_string_append(result, p_contact_name(contact));
+        } else {
+            g_string_append(result, barejid);
+        }
+    } else {
+        g_string_append(result, barejid);
+    }
+
+    char* result_str = result->str;
+    g_string_free(result, FALSE);
+
+    return result_str;
+}
+
+char*
 roster_get_msg_display_name(const char* const barejid, const char* const resource)
 {
     assert(roster != NULL);
diff --git a/src/xmpp/roster_list.h b/src/xmpp/roster_list.h
index e47a29cb..f9548d97 100644
--- a/src/xmpp/roster_list.h
+++ b/src/xmpp/roster_list.h
@@ -70,6 +70,7 @@ GList* roster_get_groups(void);
 char* roster_group_autocomplete(const char* const search_str, gboolean previous, void* context);
 char* roster_barejid_autocomplete(const char* const search_str, gboolean previous, void* context);
 GSList* roster_get_contacts_by_presence(const char* const presence);
+char* roster_get_display_name(const char* const barejid);
 char* roster_get_msg_display_name(const char* const barejid, const char* const resource);
 gint roster_compare_name(PContact a, PContact b);
 gint roster_compare_presence(PContact a, PContact b);
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..47560ce0 100644
--- a/src/xmpp/stanza.h
+++ b/src/xmpp/stanza.h
@@ -122,6 +122,7 @@
 #define STANZA_NAME_AFTER            "after"
 #define STANZA_NAME_USERNAME         "username"
 #define STANZA_NAME_PROPOSE          "propose"
+#define STANZA_NAME_REPORT           "report"
 
 // error conditions
 #define STANZA_NAME_BAD_REQUEST             "bad-request"
@@ -162,6 +163,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"
@@ -244,6 +246,9 @@
 
 #define STANZA_DATAFORM_SOFTWARE "urn:xmpp:dataforms:softwareinfo"
 
+#define STANZA_REPORTING_ABUSE "urn:xmpp:reporting:abuse"
+#define STANZA_REPORTING_SPAM  "urn:xmpp:reporting:spam"
+
 typedef struct caps_stanza_t
 {
     char* hash;
@@ -388,6 +393,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);
diff --git a/src/xmpp/xmpp.h b/src/xmpp/xmpp.h
index 003c3e07..61c7a642 100644
--- a/src/xmpp/xmpp.h
+++ b/src/xmpp/xmpp.h
@@ -70,6 +70,7 @@
 #define XMPP_FEATURE_USER_AVATAR_METADATA_NOTIFY "urn:xmpp:avatar:metadata+notify"
 #define XMPP_FEATURE_LAST_MESSAGE_CORRECTION     "urn:xmpp:message-correct:0"
 #define XMPP_FEATURE_MAM2                        "urn:xmpp:mam:2"
+#define XMPP_FEATURE_SPAM_REPORTING              "urn:xmpp:reporting:1"
 
 typedef enum {
     JABBER_CONNECTING,
@@ -89,6 +90,12 @@ typedef enum {
     INVITE_MEDIATED
 } jabber_invite_t;
 
+typedef enum {
+    BLOCKED_NO_REPORT,
+    BLOCKED_REPORT_ABUSE,
+    BLOCKED_REPORT_SPAM
+} blocked_report;
+
 typedef struct bookmark_t
 {
     char* barejid;
@@ -286,7 +293,7 @@ void roster_send_add_new(const char* const barejid, const char* const name);
 void roster_send_remove(const char* const barejid);
 
 GList* blocked_list(void);
-gboolean blocked_add(char* jid);
+gboolean blocked_add(char* jid, blocked_report reportkind, const char* const message);
 gboolean blocked_remove(char* jid);
 char* blocked_ac_find(const char* const search_str, gboolean previous, void* context);
 void blocked_ac_reset(void);