diff options
36 files changed, 450 insertions, 36 deletions
diff --git a/apidocs/c/profapi.h b/apidocs/c/profapi.h index a1578739..4d61ca7d 100644 --- a/apidocs/c/profapi.h +++ b/apidocs/c/profapi.h @@ -139,6 +139,18 @@ Retrieve the users nickname in a chat room, when in a chat room window. char* prof_get_current_nick(void); /** +Retrieve the nickname for a given barejid if it is in the roster. +@return the users nickname e.g. "eddie", or the input barejid if it is not in the roster. +*/ +char* prof_get_name_from_roster(const char *barejid); + +/** +Retrieve the barejid for a given nickname if it is in the roster. +@return the users barejid e.g. "eddie@server.tld", or NULLL if the nickname is not in the roster. +*/ +char* prof_get_barejid_from_roster(const char *name); + +/** Retrieve nicknames of all occupants in a chat room, when in a chat room window. @return nicknames of all occupants in the current room or an empty list if not in a chat room window. */ diff --git a/apidocs/python/src/prof.py b/apidocs/python/src/prof.py index 6c80e2f2..1b926b36 100644 --- a/apidocs/python/src/prof.py +++ b/apidocs/python/src/prof.py @@ -253,6 +253,24 @@ def get_current_nick(): pass +def get_name_from_roster(barejid): + """Retrieve a nickname from a barejid if it is in the roster. + + :return: the users nickname e.g. "eddie", or the input barejid if it is not in the roster. + :rtype: str + """ + pass + + +def get_barejid_from_roster(name): + """Retrieve the barejid for a given nickname if it is in the roster. + + :return: the users barejid e.g. "eddie@server.tld", or ``None`` if the nickname is not in the roster. + :rtype: str + """ + pass + + def get_current_occupants(): """Retrieve nicknames of all occupants in a chat room, when in a chat room window. 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); diff --git a/tests/unittests/config/stub_accounts.c b/tests/unittests/config/stub_accounts.c index 19a0bdcb..653b2897 100644 --- a/tests/unittests/config/stub_accounts.c +++ b/tests/unittests/config/stub_accounts.c @@ -213,6 +213,12 @@ accounts_get_last_presence(const char* const account_name) return mock_type(resource_presence_t); } +char* +accounts_get_login_status(const char* const account_name) +{ + return NULL; +} + void accounts_set_priority_online(const char* const account_name, const gint value) { diff --git a/tests/unittests/test_roster_list.c b/tests/unittests/test_roster_list.c index b46e7b57..fc10d1a7 100644 --- a/tests/unittests/test_roster_list.c +++ b/tests/unittests/test_roster_list.c @@ -689,3 +689,35 @@ remove_contact_with_remaining_in_group(void** state) g_list_free_full(groups_res, free); roster_destroy(); } + +void +get_contact_display_name(void** state) +{ + roster_create(); + roster_add("person@server.org", "nickname", NULL, NULL, FALSE); + + assert_string_equal("nickname", roster_get_display_name("person@server.org")); + + roster_destroy(); +} + +void +get_contact_display_name_is_barejid_if_name_is_empty(void** state) +{ + roster_create(); + roster_add("person@server.org", NULL, NULL, NULL, FALSE); + + assert_string_equal("person@server.org", roster_get_display_name("person@server.org")); + + roster_destroy(); +} + +void +get_contact_display_name_is_passed_barejid_if_contact_does_not_exist(void** state) +{ + roster_create(); + + assert_string_equal("person@server.org", roster_get_display_name("person@server.org")); + + roster_destroy(); +} diff --git a/tests/unittests/test_roster_list.h b/tests/unittests/test_roster_list.h index b9874fec..81983d46 100644 --- a/tests/unittests/test_roster_list.h +++ b/tests/unittests/test_roster_list.h @@ -30,3 +30,6 @@ void add_contacts_with_different_groups(void** state); void add_contacts_with_same_groups(void** state); void add_contacts_with_overlapping_groups(void** state); void remove_contact_with_remaining_in_group(void** state); +void get_contact_display_name(void** state); +void get_contact_display_name_is_barejid_if_name_is_empty(void** state); +void get_contact_display_name_is_passed_barejid_if_contact_does_not_exist(void** state); diff --git a/tests/unittests/ui/stub_ui.c b/tests/unittests/ui/stub_ui.c index 1cae8bde..edacc80a 100644 --- a/tests/unittests/ui/stub_ui.c +++ b/tests/unittests/ui/stub_ui.c @@ -1125,6 +1125,12 @@ void cons_mam_setting(void) { } + +void +cons_silence_setting(void) +{ +} + void cons_show_bookmarks_ignore(gchar** list, gsize len) { diff --git a/tests/unittests/unittests.c b/tests/unittests/unittests.c index 7fd3b192..e2293004 100644 --- a/tests/unittests/unittests.c +++ b/tests/unittests/unittests.c @@ -219,6 +219,9 @@ main(int argc, char* argv[]) unit_test(add_contacts_with_same_groups), unit_test(add_contacts_with_overlapping_groups), unit_test(remove_contact_with_remaining_in_group), + unit_test(get_contact_display_name), + unit_test(get_contact_display_name_is_barejid_if_name_is_empty), + unit_test(get_contact_display_name_is_passed_barejid_if_contact_does_not_exist), unit_test_setup_teardown(returns_false_when_chat_session_does_not_exist, init_chat_sessions, diff --git a/tests/unittests/xmpp/stub_xmpp.c b/tests/unittests/xmpp/stub_xmpp.c index ba72024c..4ecb8f79 100644 --- a/tests/unittests/xmpp/stub_xmpp.c +++ b/tests/unittests/xmpp/stub_xmpp.c @@ -547,7 +547,7 @@ blocked_list(void) } gboolean -blocked_add(char* jid) +blocked_add(char* jid, blocked_report reportkind, const char* const message) { return TRUE; } |