From c9f6a78f574ce2a90ae7871a08a5ddcfb9ab7270 Mon Sep 17 00:00:00 2001 From: Paul Fariello Date: Thu, 22 Mar 2018 04:47:52 +0220 Subject: [PATCH] Add command subcommands: list and exec Also handle list result --- src/command/cmd_defs.c | 21 +++++--- src/command/cmd_funcs.c | 29 +++++++++-- src/command/cmd_funcs.h | 3 +- src/ui/ui.h | 2 + src/ui/window.c | 28 +++++++++++ src/xmpp/iq.c | 85 ++++++++++++++++++++++++++++---- src/xmpp/stanza.c | 7 ++- src/xmpp/stanza.h | 4 +- src/xmpp/xmpp.h | 3 +- tests/unittests/xmpp/stub_xmpp.c | 2 + 10 files changed, 158 insertions(+), 26 deletions(-) diff --git a/src/command/cmd_defs.c b/src/command/cmd_defs.c index c3ab395d..14e49b47 100644 --- a/src/command/cmd_defs.c +++ b/src/command/cmd_defs.c @@ -2300,19 +2300,24 @@ static struct cmd_t command_defs[] = "/export ~/contacts.csv") }, - { "/command", - parse_args, 1, 1, NULL, - CMD_NOSUBFUNCS - CMD_MAINFUNC(cmd_command) + { "/cmd", + parse_args, 1, 3, NULL, + CMD_SUBFUNCS( + { "list", cmd_command_list }, + { "exec", cmd_command_exec }) + CMD_NOMAINFUNC CMD_NOTAGS CMD_SYN( - "/command ") + "/otr list", + "/otr exec ") CMD_DESC( - "Execute an ad hoc command") + "Execute ad hoc commands.") CMD_ARGS( - { "", "Command to be executed" }) + { "list", "List supported ad hoc commands." }, + { "exec ", "Execute a command." }) CMD_EXAMPLES( - "/command ping") + "/cmd list", + "/cmd exec ping") } }; diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 048bdd6c..2e606ef5 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -7470,7 +7470,7 @@ cmd_encwarn(ProfWin *window, const char *const command, gchar **args) } gboolean -cmd_command(ProfWin *window, const char *const command, gchar **args) +cmd_command_list(ProfWin *window, const char *const command, gchar **args) { jabber_conn_status_t conn_status = connection_get_status(); @@ -7479,14 +7479,37 @@ cmd_command(ProfWin *window, const char *const command, gchar **args) return TRUE; } - if (args[0] == NULL && connection_supports(XMPP_FEATURE_COMMANDS) == FALSE) { + if (connection_supports(XMPP_FEATURE_COMMANDS) == FALSE) { cons_show("Server does not support ad hoc commands."); return TRUE; } ProfMucWin *mucwin = (ProfMucWin*)window; - iq_send_command(mucwin->roomjid, args[0]); + iq_command_list(mucwin->roomjid); + + cons_show("List available ad hoc commands"); + return TRUE; +} + +gboolean +cmd_command_exec(ProfWin *window, const char *const command, gchar **args) +{ + jabber_conn_status_t conn_status = connection_get_status(); + + if (conn_status != JABBER_CONNECTED) { + cons_show("You are not currently connected."); + return TRUE; + } + + if (connection_supports(XMPP_FEATURE_COMMANDS) == FALSE) { + cons_show("Server does not support ad hoc commands."); + return TRUE; + } + + ProfMucWin *mucwin = (ProfMucWin*)window; + + iq_command_exec(mucwin->roomjid, args[0]); cons_show("Execute %s...", args[0]); return TRUE; diff --git a/src/command/cmd_funcs.h b/src/command/cmd_funcs.h index 089d8227..5b909fed 100644 --- a/src/command/cmd_funcs.h +++ b/src/command/cmd_funcs.h @@ -158,7 +158,8 @@ gboolean cmd_script(ProfWin *window, const char *const command, gchar **args); 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_command(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_command_list(ProfWin *window, const char *const command, gchar **args); +gboolean cmd_command_exec(ProfWin *window, const char *const command, gchar **args); gboolean cmd_plugins(ProfWin *window, const char *const command, gchar **args); gboolean cmd_plugins_sourcepath(ProfWin *window, const char *const command, gchar **args); diff --git a/src/ui/ui.h b/src/ui/ui.h index d344f855..f1d8161f 100644 --- a/src/ui/ui.h +++ b/src/ui/ui.h @@ -377,6 +377,8 @@ void win_show_info(ProfWin *window, PContact contact); void win_clear(ProfWin *window); char* win_get_tab_identifier(ProfWin *window); char* win_to_string(ProfWin *window); +void win_command_list_error(ProfWin *window, const char *const error); +void win_handle_command_list(ProfWin *window, GSList *cmds); // desktop notifications void notifier_initialise(void); diff --git a/src/ui/window.c b/src/ui/window.c index 5543707d..3ba6b387 100644 --- a/src/ui/window.c +++ b/src/ui/window.c @@ -1724,3 +1724,31 @@ win_sub_newline_lazy(WINDOW *win) wmove(win, cury+1, 0); } } + +void +win_command_list_error(ProfWin *window, const char *const error) +{ + assert(window != NULL); + + win_println(window, THEME_ERROR, '!', "Error retrieving command list: %s", error); +} + +void +win_handle_command_list(ProfWin *window, GSList *cmds) +{ + assert(window != NULL); + + if (cmds) { + win_println(window, THEME_DEFAULT, '!', "Ad hoc commands:"); + GSList *curr_cmd = cmds; + while (curr_cmd) { + const char *cmd = curr_cmd->data; + win_println(window, THEME_DEFAULT, '!', " %s", cmd); + curr_cmd = g_slist_next(curr_cmd); + } + win_println(window, THEME_DEFAULT, '!', ""); + } else { + win_println(window, THEME_DEFAULT, '!', "No commands found"); + win_println(window, THEME_DEFAULT, '!', ""); + } +} diff --git a/src/xmpp/iq.c b/src/xmpp/iq.c index 33400912..ad7ef54d 100644 --- a/src/xmpp/iq.c +++ b/src/xmpp/iq.c @@ -120,7 +120,8 @@ static int _caps_response_for_jid_id_handler(xmpp_stanza_t *const stanza, void * static int _caps_response_legacy_id_handler(xmpp_stanza_t *const stanza, void *const userdata); static int _auto_pong_id_handler(xmpp_stanza_t *const stanza, void *const userdata); static int _room_list_id_handler(xmpp_stanza_t *const stanza, void *const userdata); -static int _command_response_handler(xmpp_stanza_t *const stanza, void *const userdata); +static int _command_list_result_handler(xmpp_stanza_t *const stanza, void *const userdata); +static int _command_exec_response_handler(xmpp_stanza_t *const stanza, void *const userdata); static void _iq_free_room_data(ProfRoomInfoData *roominfo); static void _iq_free_affiliation_set(ProfPrivilegeSet *affiliation_set); @@ -320,7 +321,7 @@ iq_room_list_request(gchar *conferencejid, gchar *filter) xmpp_ctx_t * const ctx = connection_get_ctx(); char *id = connection_create_stanza_id("confreq"); - xmpp_stanza_t *iq = stanza_create_disco_items_iq(ctx, id, conferencejid); + xmpp_stanza_t *iq = stanza_create_disco_items_iq(ctx, id, conferencejid, NULL); iq_id_handler_add(id, _room_list_id_handler, NULL, filter); @@ -522,7 +523,7 @@ void iq_disco_items_request(gchar *jid) { xmpp_ctx_t * const ctx = connection_get_ctx(); - xmpp_stanza_t *iq = stanza_create_disco_items_iq(ctx, "discoitemsreq", jid); + xmpp_stanza_t *iq = stanza_create_disco_items_iq(ctx, "discoitemsreq", jid, NULL); iq_send_stanza(iq); xmpp_stanza_release(iq); } @@ -531,7 +532,7 @@ void iq_disco_items_request_onconnect(gchar *jid) { xmpp_ctx_t * const ctx = connection_get_ctx(); - xmpp_stanza_t *iq = stanza_create_disco_items_iq(ctx, "discoitemsreq_onconnect", jid); + xmpp_stanza_t *iq = stanza_create_disco_items_iq(ctx, "discoitemsreq_onconnect", jid, NULL); iq_send_stanza(iq); xmpp_stanza_release(iq); } @@ -698,13 +699,26 @@ iq_send_ping(const char *const target) } void -iq_send_command(const char *const target, const char *const command) +iq_command_list(const char *const target) { xmpp_ctx_t * const ctx = connection_get_ctx(); - xmpp_stanza_t *iq = stanza_create_command_iq(ctx, target, command); + const char *id = connection_create_stanza_id("cmdlist"); + xmpp_stanza_t *iq = stanza_create_disco_items_iq(ctx, id, target, STANZA_NS_COMMAND); + + iq_id_handler_add(id, _command_list_result_handler, NULL, NULL); + + iq_send_stanza(iq); + xmpp_stanza_release(iq); +} + +void +iq_command_exec(const char *const target, const char *const command) +{ + xmpp_ctx_t * const ctx = connection_get_ctx(); + xmpp_stanza_t *iq = stanza_create_command_exec_iq(ctx, target, command); const char *id = xmpp_stanza_get_id(iq); - iq_id_handler_add(id, _command_response_handler, free, strdup(command)); + iq_id_handler_add(id, _command_exec_response_handler, free, strdup(command)); iq_send_stanza(iq); xmpp_stanza_release(iq); @@ -1026,9 +1040,62 @@ _room_list_id_handler(xmpp_stanza_t *const stanza, void *const userdata) } static int -_command_response_handler(xmpp_stanza_t *const stanza, void *const userdata) +_command_list_result_handler(xmpp_stanza_t *const stanza, void *const userdata) { - cons_show("Plop", NULL); + const char *id = xmpp_stanza_get_id(stanza); + const char *type = xmpp_stanza_get_type(stanza); + char *from = strdup(xmpp_stanza_get_from(stanza)); + + if (id) { + log_debug("IQ command list result handler fired, id: %s.", id); + } else { + log_debug("IQ command list result handler fired."); + } + + // handle error responses + if (g_strcmp0(type, STANZA_TYPE_ERROR) == 0) { + char *error_message = stanza_get_error_message(stanza); + log_debug("Error retrieving command list for %s: %s", from, error_message); + ProfWin *win = wins_get_by_string(from); + if (win) { + win_command_list_error(win, error_message); + } + free(error_message); + free(from); + return 0; + } + + GSList *cmds = NULL; + + xmpp_stanza_t *query = xmpp_stanza_get_child_by_ns(stanza, XMPP_NS_DISCO_ITEMS); + if (query) { + xmpp_stanza_t *child = xmpp_stanza_get_children(query); + while (child) { + const char *name = xmpp_stanza_get_name(child); + if (g_strcmp0(name, "item") == 0) { + const char *node = xmpp_stanza_get_attribute(child, STANZA_ATTR_NODE); + if (node) { + cmds = g_slist_insert_sorted(cmds, (gpointer)node, (GCompareFunc)g_strcmp0); + } + } + child = xmpp_stanza_get_next(child); + } + } + + ProfWin *win = wins_get_by_string(from); + if (win) { + win_handle_command_list(win, cmds); + } + g_slist_free(cmds); + free(from); + + return 0; +} + +static int +_command_exec_response_handler(xmpp_stanza_t *const stanza, void *const userdata) +{ + cons_show("%s", NULL, __func__); return 0; } diff --git a/src/xmpp/stanza.c b/src/xmpp/stanza.c index ef4f8af4..cc842ff6 100644 --- a/src/xmpp/stanza.c +++ b/src/xmpp/stanza.c @@ -924,7 +924,7 @@ stanza_create_disco_info_iq(xmpp_ctx_t *ctx, const char *const id, const char *c xmpp_stanza_t* stanza_create_disco_items_iq(xmpp_ctx_t *ctx, const char *const id, - const char *const jid) + const char *const jid, const char *const node) { xmpp_stanza_t *iq = xmpp_iq_new(ctx, STANZA_TYPE_GET, id); xmpp_stanza_set_to(iq, jid); @@ -932,6 +932,9 @@ stanza_create_disco_items_iq(xmpp_ctx_t *ctx, const char *const id, xmpp_stanza_t *query = xmpp_stanza_new(ctx); xmpp_stanza_set_name(query, STANZA_NAME_QUERY); xmpp_stanza_set_ns(query, XMPP_NS_DISCO_ITEMS); + if (node) { + xmpp_stanza_set_attribute(query, STANZA_ATTR_NODE, node); + } xmpp_stanza_add_child(iq, query); xmpp_stanza_release(query); @@ -2039,7 +2042,7 @@ stanza_parse_presence(xmpp_stanza_t *stanza, int *err) } xmpp_stanza_t* -stanza_create_command_iq(xmpp_ctx_t *ctx, const char *const target, +stanza_create_command_exec_iq(xmpp_ctx_t *ctx, const char *const target, const char *const node) { char *id = create_unique_id("command"); diff --git a/src/xmpp/stanza.h b/src/xmpp/stanza.h index 40461637..a3cf2c4c 100644 --- a/src/xmpp/stanza.h +++ b/src/xmpp/stanza.h @@ -280,7 +280,7 @@ xmpp_stanza_t* stanza_create_room_subject_message(xmpp_ctx_t *ctx, const char *c xmpp_stanza_t* stanza_create_room_kick_iq(xmpp_ctx_t *const ctx, const char *const room, const char *const nick, const char *const reason); -xmpp_stanza_t* stanza_create_command_iq(xmpp_ctx_t *ctx, const char *const target, const char *const node); +xmpp_stanza_t* stanza_create_command_exec_iq(xmpp_ctx_t *ctx, const char *const target, const char *const node); int stanza_get_idle_time(xmpp_stanza_t *const stanza); @@ -296,7 +296,7 @@ EntityCapabilities* stanza_create_caps_from_query_element(xmpp_stanza_t *query); const char* stanza_get_presence_string_from_type(resource_presence_t presence_type); xmpp_stanza_t* stanza_create_software_version_iq(xmpp_ctx_t *ctx, const char *const fulljid); -xmpp_stanza_t* stanza_create_disco_items_iq(xmpp_ctx_t *ctx, const char *const id, const char *const jid); +xmpp_stanza_t* stanza_create_disco_items_iq(xmpp_ctx_t *ctx, const char *const id, const char *const jid, const char *const node); char* stanza_get_status(xmpp_stanza_t *stanza, char *def); char* stanza_get_show(xmpp_stanza_t *stanza, char *def); diff --git a/src/xmpp/xmpp.h b/src/xmpp/xmpp.h index 2e7d1a25..c0e1477d 100644 --- a/src/xmpp/xmpp.h +++ b/src/xmpp/xmpp.h @@ -183,7 +183,8 @@ void iq_room_role_set(const char *const room, const char *const nick, char *role void iq_room_role_list(const char * const room, char *role); void iq_autoping_check(void); void iq_http_upload_request(HTTPUpload *upload); -void iq_send_command(const char *const target, const char *const command); +void iq_command_list(const char *const target); +void iq_command_exec(const char *const target, const char *const command); EntityCapabilities* caps_lookup(const char *const jid); void caps_close(void); diff --git a/tests/unittests/xmpp/stub_xmpp.c b/tests/unittests/xmpp/stub_xmpp.c index 45db6649..5590a8e1 100644 --- a/tests/unittests/xmpp/stub_xmpp.c +++ b/tests/unittests/xmpp/stub_xmpp.c @@ -205,6 +205,8 @@ void iq_room_role_list(const char * const room, char *role) {} void iq_last_activity_request(gchar *jid) {} void iq_autoping_check(void) {} void iq_rooms_cache_clear(void) {} +void iq_command_list(const char *const target) {} +void iq_command_exec(const char *const target, const char *const command) {} // caps functions void caps_add_feature(char *feature) {}