From b8c94376aad2d5edf4390e47b8dc89db8ab1a517 Mon Sep 17 00:00:00 2001 From: James Booth Date: Sun, 1 May 2016 19:39:39 +0100 Subject: Add /blocked command --- src/command/command.c | 51 +++++++- src/command/commands.c | 57 +++++++++ src/command/commands.h | 1 + src/xmpp/blocking.c | 323 +++++++++++++++++++++++++++++++++++++++++++++++++ src/xmpp/blocking.h | 41 +++++++ src/xmpp/connection.c | 17 +++ src/xmpp/iq.c | 6 + src/xmpp/stanza.c | 20 +++ src/xmpp/stanza.h | 6 + src/xmpp/xmpp.h | 9 ++ 10 files changed, 530 insertions(+), 1 deletion(-) create mode 100644 src/xmpp/blocking.c create mode 100644 src/xmpp/blocking.h (limited to 'src') diff --git a/src/command/command.c b/src/command/command.c index 5895ad9d..fd357668 100644 --- a/src/command/command.c +++ b/src/command/command.c @@ -125,6 +125,7 @@ static char* _win_autocomplete(ProfWin *window, const char *const input); static char* _close_autocomplete(ProfWin *window, const char *const input); static char* _plugins_autocomplete(ProfWin *window, const char *const input); static char* _sendfile_autocomplete(ProfWin *window, const char *const input); +static char* _blocked_autocomplete(ProfWin *window, const char *const input); GHashTable *commands = NULL; @@ -423,6 +424,26 @@ static struct cmd_t command_defs[] = "/roster size 15") }, + { "/blocked", + parse_args, 0, 2, NULL, + CMD_NOSUBFUNCS + CMD_MAINFUNC(cmd_blocked) + CMD_TAGS( + CMD_TAG_ROSTER, + CMD_TAG_CHAT) + CMD_SYN( + "/blocked", + "/blocked add []", + "/blocked remove ") + CMD_DESC( + "Manage blocked users, calling with no arguments shows the current list of blocked users.") + CMD_ARGS( + { "add []", "Block the specified Jabber ID, if called in a chat window, the current recipient will be used." }, + { "remove ", "Remove the specified Jabber ID from the blocked list." }) + CMD_EXAMPLES( + "/blocked add spammer@spam.org") + }, + { "/group", parse_args_with_freetext, 0, 3, NULL, CMD_NOSUBFUNCS @@ -2279,6 +2300,7 @@ static Autocomplete autoping_ac; static Autocomplete plugins_ac; static Autocomplete plugins_load_ac; static Autocomplete sendfile_ac; +static Autocomplete blocked_ac; /* * Initialise command autocompleter and history @@ -2835,6 +2857,10 @@ cmd_init(void) autocomplete_add(plugins_ac, "load"); sendfile_ac = autocomplete_new(); + + blocked_ac = autocomplete_new(); + autocomplete_add(blocked_ac, "add"); + autocomplete_add(blocked_ac, "remove"); } void @@ -2925,6 +2951,7 @@ cmd_uninit(void) autocomplete_free(plugins_ac); autocomplete_free(plugins_load_ac); autocomplete_free(sendfile_ac); + autocomplete_free(blocked_ac); } gboolean @@ -3154,6 +3181,7 @@ cmd_reset_autocomplete(ProfWin *window) autocomplete_reset(console_msg_ac); autocomplete_reset(autoping_ac); autocomplete_reset(plugins_ac); + autocomplete_reset(blocked_ac); autocomplete_reset(script_ac); if (script_show_ac) { autocomplete_free(script_show_ac); @@ -3176,6 +3204,7 @@ cmd_reset_autocomplete(ProfWin *window) } bookmark_autocomplete_reset(); + blocked_ac_reset(); prefs_reset_room_trigger_ac(); win_reset_search_attempts(); win_close_reset_search_attempts(); @@ -3445,8 +3474,9 @@ _cmd_complete_parameters(ProfWin *window, const char *const input) g_hash_table_insert(ac_funcs, "/console", _console_autocomplete); g_hash_table_insert(ac_funcs, "/win", _win_autocomplete); g_hash_table_insert(ac_funcs, "/close", _close_autocomplete); - g_hash_table_insert(ac_funcs, "/plugins", _plugins_autocomplete); + g_hash_table_insert(ac_funcs, "/plugins", _plugins_autocomplete); g_hash_table_insert(ac_funcs, "/sendfile", _sendfile_autocomplete); + g_hash_table_insert(ac_funcs, "/blocked", _blocked_autocomplete); int len = strlen(input); char parsed[len+1]; @@ -3716,6 +3746,25 @@ _group_autocomplete(ProfWin *window, const char *const input) return NULL; } +static char* +_blocked_autocomplete(ProfWin *window, const char *const input) +{ + char *result = NULL; + + result = autocomplete_param_with_func(input, "/blocked remove", blocked_ac_find); + if (result) { + return result; + } + + result = autocomplete_param_with_ac(input, "/blocked", blocked_ac, FALSE); + if (result) { + return result; + } + + return NULL; +} + + static char* _bookmark_autocomplete(ProfWin *window, const char *const input) { diff --git a/src/command/commands.c b/src/command/commands.c index 58777a2d..a717054b 100644 --- a/src/command/commands.c +++ b/src/command/commands.c @@ -2984,6 +2984,63 @@ cmd_roster(ProfWin *window, const char *const command, gchar **args) } } +gboolean +cmd_blocked(ProfWin *window, const char *const command, gchar **args) +{ + if (jabber_get_connection_status() != JABBER_CONNECTED) { + cons_show("You are not currently connected."); + return TRUE; + } + + if (!jabber_service_supports(XMPP_FEATURE_BLOCKING)) { + cons_show("Blocking not supported by server."); + return TRUE; + } + + if (g_strcmp0(args[0], "add") == 0) { + if (args[1] == NULL) { + cons_bad_cmd_usage(command); + return TRUE; + } + + gboolean res = blocked_add(args[1]); + if (!res) { + cons_show("User %s already blocked.", args[1]); + } + + return TRUE; + } + + if (g_strcmp0(args[0], "remove") == 0) { + if (args[1] == NULL) { + cons_bad_cmd_usage(command); + return TRUE; + } + + gboolean res = blocked_remove(args[1]); + if (!res) { + cons_show("User %s is not currently blocked.", args[1]); + } + + return TRUE; + } + + GList *blocked = blocked_list(); + GList *curr = blocked; + if (curr) { + cons_show("Blocked users:"); + while (curr) { + cons_show(" %s", curr->data); + curr = g_list_next(curr); + } + } else { + cons_show("No blocked users."); + } + + return TRUE; +} + + gboolean cmd_resource(ProfWin *window, const char *const command, gchar **args) { diff --git a/src/command/commands.h b/src/command/commands.h index c5463d88..24828989 100644 --- a/src/command/commands.h +++ b/src/command/commands.h @@ -199,6 +199,7 @@ gboolean cmd_export(ProfWin *window, const char *const command, gchar **args); gboolean cmd_charset(ProfWin *window, const char *const command, gchar **args); gboolean cmd_console(ProfWin *window, const char *const command, gchar **args); gboolean cmd_plugins(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_blocked(ProfWin *window, const char *const command, gchar **args); gboolean cmd_form_field(ProfWin *window, char *tag, gchar **args); diff --git a/src/xmpp/blocking.c b/src/xmpp/blocking.c new file mode 100644 index 00000000..06bbadf6 --- /dev/null +++ b/src/xmpp/blocking.c @@ -0,0 +1,323 @@ +/* + * blocking.c + * + * Copyright (C) 2012 - 2016 James Booth + * + * This file is part of Profanity. + * + * Profanity is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Profanity is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Profanity. If not, see . + * + * In addition, as a special exception, the copyright holders give permission to + * link the code of portions of this program with the OpenSSL library under + * certain conditions as described in each individual source file, and + * distribute linked combinations including the two. + * + * You must obey the GNU General Public License in all respects for all of the + * code used other than OpenSSL. If you modify file(s) with this exception, you + * may extend this exception to your version of the file(s), but you are not + * obligated to do so. If you do not wish to do so, delete this exception + * statement from your version. If you delete this exception statement from all + * source files in the program, then also delete it here. + * + */ + +#include + +#ifdef HAVE_LIBMESODE +#include +#endif +#ifdef HAVE_LIBSTROPHE +#include +#endif + +#include + +#include "log.h" +#include "common.h" +#include "ui/ui.h" +#include "xmpp/connection.h" +#include "xmpp/stanza.h" + +static int _blocklist_result_handler(xmpp_stanza_t *const stanza, void *const userdata); +static int _block_add_result_handler(xmpp_stanza_t *const stanza, void *const userdata); +static int _block_remove_result_handler(xmpp_stanza_t *const stanza, void *const userdata); + +static GList *blocked; +static Autocomplete blocked_ac; + +void +blocking_request(void) +{ + char *id = create_unique_id("blocked_list_request"); + xmpp_ctx_t *ctx = connection_get_ctx(); + xmpp_stanza_t *iq; + + if (blocked) { + g_list_free_full(blocked, free); + blocked = NULL; + } + + if (blocked_ac) { + autocomplete_free(blocked_ac); + } + blocked_ac = autocomplete_new(); + + id_handler_add(id, _blocklist_result_handler, id); + + iq = stanza_create_blocked_list_request(ctx); + xmpp_stanza_set_id(iq, id); + send_iq_stanza(iq); + xmpp_stanza_release(iq); + free(id); +} + +GList* +blocked_list(void) +{ + return blocked; +} + +char* +blocked_ac_find(const char *const search_str) +{ + return autocomplete_complete(blocked_ac, search_str, TRUE); +} + +void +blocked_ac_reset(void) +{ + if (blocked_ac) { + autocomplete_reset(blocked_ac); + } +} + +gboolean +blocked_add(char *jid) +{ + GList *found = g_list_find_custom(blocked, jid, (GCompareFunc)g_strcmp0); + if (found) { + return FALSE; + } + + xmpp_ctx_t *ctx = connection_get_ctx(); + + xmpp_stanza_t *iq = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(iq, STANZA_NAME_IQ); + xmpp_stanza_set_type(iq, STANZA_TYPE_SET); + char *id = create_unique_id("block"); + xmpp_stanza_set_id(iq, id); + + xmpp_stanza_t *block = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(block, STANZA_NAME_BLOCK); + xmpp_stanza_set_ns(block, STANZA_NS_BLOCKING); + + xmpp_stanza_t *item = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(item, STANZA_NAME_ITEM); + xmpp_stanza_set_attribute(item, STANZA_ATTR_JID, jid); + + xmpp_stanza_add_child(block, item); + xmpp_stanza_release(item); + + xmpp_stanza_add_child(iq, block); + xmpp_stanza_release(block); + + id_handler_add(id, _block_add_result_handler, strdup(jid)); + + send_iq_stanza(iq); + xmpp_stanza_release(iq); + free(id); + + return TRUE; +} + +gboolean +blocked_remove(char *jid) +{ + GList *found = g_list_find_custom(blocked, jid, (GCompareFunc)g_strcmp0); + if (!found) { + return FALSE; + } + + xmpp_ctx_t *ctx = connection_get_ctx(); + + xmpp_stanza_t *iq = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(iq, STANZA_NAME_IQ); + xmpp_stanza_set_type(iq, STANZA_TYPE_SET); + char *id = create_unique_id("unblock"); + xmpp_stanza_set_id(iq, id); + + xmpp_stanza_t *block = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(block, STANZA_NAME_UNBLOCK); + xmpp_stanza_set_ns(block, STANZA_NS_BLOCKING); + + xmpp_stanza_t *item = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(item, STANZA_NAME_ITEM); + xmpp_stanza_set_attribute(item, STANZA_ATTR_JID, jid); + + xmpp_stanza_add_child(block, item); + xmpp_stanza_release(item); + + xmpp_stanza_add_child(iq, block); + xmpp_stanza_release(block); + + id_handler_add(id, _block_remove_result_handler, strdup(jid)); + + send_iq_stanza(iq); + xmpp_stanza_release(iq); + free(id); + + return TRUE; +} + +int +blocked_set_handler(xmpp_stanza_t *stanza) +{ + xmpp_stanza_t *block = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_BLOCK); + if (block) { + xmpp_stanza_t *child = xmpp_stanza_get_children(block); + while (child) { + if (g_strcmp0(xmpp_stanza_get_name(child), STANZA_NAME_ITEM) == 0) { + const char *jid = xmpp_stanza_get_attribute(child, STANZA_ATTR_JID); + if (jid) { + blocked = g_list_append(blocked, strdup(jid)); + autocomplete_add(blocked_ac, jid); + } + + } + + child = xmpp_stanza_get_next(child); + } + } + + xmpp_stanza_t *unblock = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_UNBLOCK); + if (unblock) { + xmpp_stanza_t *child = xmpp_stanza_get_children(unblock); + if (!child) { + g_list_free_full(blocked, free); + blocked = NULL; + autocomplete_clear(blocked_ac); + } else { + while (child) { + if (g_strcmp0(xmpp_stanza_get_name(child), STANZA_NAME_ITEM) == 0) { + const char *jid = xmpp_stanza_get_attribute(child, STANZA_ATTR_JID); + if (jid) { + GList *found = g_list_find_custom(blocked, jid, (GCompareFunc)g_strcmp0); + if (found) { + blocked = g_list_remove_link(blocked, found); + g_list_free_full(found, free); + autocomplete_remove(blocked_ac, jid); + } + } + + } + + child = xmpp_stanza_get_next(child); + } + } + } + + return 1; +} + +static int +_block_add_result_handler(xmpp_stanza_t *const stanza, void *const userdata) +{ + char *jid = (char*)userdata; + + const char *type = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_TYPE); + if (type == NULL) { + log_info("Block response received for %s with no type attribute.", jid); + free(jid); + return 0; + } + + if (g_strcmp0(type, "result") != 0) { + log_info("Block response received for %s with unrecognised type attribute.", jid); + free(jid); + return 0; + } + + cons_show("User %s successfully blocked.", jid); + free(jid); + + return 0; +} + +static int +_block_remove_result_handler(xmpp_stanza_t *const stanza, void *const userdata) +{ + char *jid = (char*)userdata; + + const char *type = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_TYPE); + if (type == NULL) { + log_info("Unblock response received for %s with no type attribute.", jid); + free(jid); + return 0; + } + + if (g_strcmp0(type, "result") != 0) { + log_info("Unblock response received for %s with unrecognised type attribute.", jid); + free(jid); + return 0; + } + + cons_show("User %s successfully unblocked.", jid); + free(jid); + + return 0; +} + +static int +_blocklist_result_handler(xmpp_stanza_t *const stanza, void *const userdata) +{ + log_info("Blocked list result handler fired."); + + const char *type = xmpp_stanza_get_type(stanza); + if (g_strcmp0(type, "result") != 0) { + log_info("Received blocklist without result type"); + return 0; + } + + xmpp_stanza_t *blocklist = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_BLOCKLIST); + if (!blocklist) { + log_info("Received blocklist without blocklist element"); + return 0; + } + + if (blocked) { + g_list_free_full(blocked, free); + blocked = NULL; + } + + xmpp_stanza_t *items = xmpp_stanza_get_children(blocklist); + if (!items) { + log_info("No blocked users."); + return 0; + } + + xmpp_stanza_t *curr = items; + while (curr) { + const char *name = xmpp_stanza_get_name(curr); + if (g_strcmp0(name, "item") == 0) { + const char *jid = xmpp_stanza_get_attribute(curr, STANZA_ATTR_JID); + if (jid) { + blocked = g_list_append(blocked, strdup(jid)); + autocomplete_add(blocked_ac, jid); + } + } + curr = xmpp_stanza_get_next(curr); + } + + return 0; +} diff --git a/src/xmpp/blocking.h b/src/xmpp/blocking.h new file mode 100644 index 00000000..f541cef1 --- /dev/null +++ b/src/xmpp/blocking.h @@ -0,0 +1,41 @@ +/* + * blocking.h + * + * Copyright (C) 2012 - 2016 James Booth + * + * This file is part of Profanity. + * + * Profanity is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Profanity is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Profanity. If not, see . + * + * In addition, as a special exception, the copyright holders give permission to + * link the code of portions of this program with the OpenSSL library under + * certain conditions as described in each individual source file, and + * distribute linked combinations including the two. + * + * You must obey the GNU General Public License in all respects for all of the + * code used other than OpenSSL. If you modify file(s) with this exception, you + * may extend this exception to your version of the file(s), but you are not + * obligated to do so. If you do not wish to do so, delete this exception + * statement from your version. If you delete this exception statement from all + * source files in the program, then also delete it here. + * + */ + +#ifndef XMPP_BLOCKING_H +#define XMPP_BLOCKING_H + +void blocking_request(void); +int blocked_set_handler(xmpp_stanza_t *stanza); + +#endif diff --git a/src/xmpp/connection.c b/src/xmpp/connection.c index 710e71b2..7db3b2ed 100644 --- a/src/xmpp/connection.c +++ b/src/xmpp/connection.c @@ -55,6 +55,7 @@ #include "profanity.h" #include "event/server_events.h" #include "xmpp/bookmark.h" +#include "xmpp/blocking.h" #include "xmpp/capabilities.h" #include "xmpp/connection.h" #include "xmpp/iq.h" @@ -343,6 +344,21 @@ jabber_get_disco_items(void) return (disco_items); } +gboolean +jabber_service_supports(const char *const feature) +{ + DiscoInfo *disco_info; + while (disco_items) { + disco_info = disco_items->data; + if (g_hash_table_lookup_extended(disco_info->features, feature, NULL, NULL)) { + return TRUE; + } + disco_items = g_slist_next(disco_items); + } + + return FALSE; +} + void jabber_set_disco_items(GSList *_disco_items) { @@ -678,6 +694,7 @@ _connection_handler(xmpp_conn_t *const conn, const xmpp_conn_event_t status, con roster_request(); bookmark_request(); + blocking_request(); // items discovery DiscoInfo *info = malloc(sizeof(struct disco_info_t)); diff --git a/src/xmpp/iq.c b/src/xmpp/iq.c index c874a721..cba3d4d3 100644 --- a/src/xmpp/iq.c +++ b/src/xmpp/iq.c @@ -58,6 +58,7 @@ #include "config/preferences.h" #include "event/server_events.h" #include "xmpp/capabilities.h" +#include "xmpp/blocking.h" #include "xmpp/connection.h" #include "xmpp/stanza.h" #include "xmpp/form.h" @@ -170,6 +171,11 @@ _iq_handler(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *const us roster_result_handler(stanza); } + xmpp_stanza_t *blocking = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_BLOCKING); + if (blocking && (g_strcmp0(type, STANZA_TYPE_SET) == 0)) { + blocked_set_handler(stanza); + } + const char *id = xmpp_stanza_get_id(stanza); if (id) { ProfIdHandler *handler = g_hash_table_lookup(id_handlers, id); diff --git a/src/xmpp/stanza.c b/src/xmpp/stanza.c index dc2fb68a..13450e20 100644 --- a/src/xmpp/stanza.c +++ b/src/xmpp/stanza.c @@ -117,6 +117,26 @@ stanza_create_bookmarks_storage_request(xmpp_ctx_t *ctx) return iq; } +xmpp_stanza_t* +stanza_create_blocked_list_request(xmpp_ctx_t *ctx) +{ + xmpp_stanza_t *iq, *blocklist; + + iq = xmpp_stanza_new(ctx); + blocklist = xmpp_stanza_new(ctx); + + xmpp_stanza_set_name(iq, STANZA_NAME_IQ); + xmpp_stanza_set_type(iq, STANZA_TYPE_GET); + + xmpp_stanza_set_name(blocklist, STANZA_NAME_BLOCKLIST); + xmpp_stanza_set_ns(blocklist, STANZA_NS_BLOCKING); + + xmpp_stanza_add_child(iq, blocklist); + xmpp_stanza_release(blocklist); + + return iq; +} + #if 0 xmpp_stanza_t* stanza_create_bookmarks_pubsub_add(xmpp_ctx_t *ctx, const char *const jid, diff --git a/src/xmpp/stanza.h b/src/xmpp/stanza.h index 977e34c5..d557b5c7 100644 --- a/src/xmpp/stanza.h +++ b/src/xmpp/stanza.h @@ -54,6 +54,9 @@ #define STANZA_NAME_MESSAGE "message" #define STANZA_NAME_BODY "body" +#define STANZA_NAME_BLOCK "block" +#define STANZA_NAME_UNBLOCK "unblock" +#define STANZA_NAME_BLOCKLIST "blocklist" #define STANZA_NAME_PRESENCE "presence" #define STANZA_NAME_PRIORITY "priority" #define STANZA_NAME_X "x" @@ -181,6 +184,7 @@ #define STANZA_NS_ENCRYPTED "jabber:x:encrypted" #define STANZA_NS_HTTP_UPLOAD "urn:xmpp:http:upload" #define STANZA_NS_X_OOB "jabber:x:oob" +#define STANZA_NS_BLOCKING "urn:xmpp:blocking" #define STANZA_DATAFORM_SOFTWARE "urn:xmpp:dataforms:softwareinfo" @@ -205,6 +209,8 @@ typedef enum { xmpp_stanza_t* stanza_create_bookmarks_storage_request(xmpp_ctx_t *ctx); +xmpp_stanza_t* stanza_create_blocked_list_request(xmpp_ctx_t *ctx); + xmpp_stanza_t* stanza_create_http_upload_request(xmpp_ctx_t *ctx, const char *const id, const char *const jid, HTTPUpload *upload); xmpp_stanza_t* stanza_enable_carbons(xmpp_ctx_t *ctx); diff --git a/src/xmpp/xmpp.h b/src/xmpp/xmpp.h index d6e6031b..bf7bbbac 100644 --- a/src/xmpp/xmpp.h +++ b/src/xmpp/xmpp.h @@ -54,6 +54,8 @@ #define JABBER_PRIORITY_MIN -128 #define JABBER_PRIORITY_MAX 127 +#define XMPP_FEATURE_BLOCKING "urn:xmpp:blocking" + typedef enum { JABBER_UNDEFINED, JABBER_STARTED, @@ -126,6 +128,7 @@ TLSCertificate* jabber_get_tls_peer_cert(void); #endif gboolean jabber_conn_is_secured(void); gboolean jabber_send_stanza(const char *const stanza); +gboolean jabber_service_supports(const char *const feature); // message functions char* message_send_chat(const char *const barejid, const char *const msg, const char *const oob_url); @@ -206,6 +209,12 @@ void roster_send_remove_from_group(const char *const group, PContact contact); 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_remove(char *jid); +char* blocked_ac_find(const char *const search_str); +void blocked_ac_reset(void); + void form_destroy(DataForm *form); char* form_get_form_type_field(DataForm *form); void form_set_value(DataForm *form, const char *const tag, char *value); -- cgit 1.4.1-2-gfad0