Compare commits

...

2 Commits

Author SHA1 Message Date
b7dec705c3 fix(autocomplete): guard history completion safely
All checks were successful
CI Code / Check spelling (pull_request) Successful in 12s
CI Code / Check coding style (pull_request) Successful in 23s
CI Code / Code Coverage (pull_request) Successful in 3m40s
CI Code / Linux (debian) (pull_request) Successful in 5m14s
CI Code / Linux (ubuntu) (pull_request) Successful in 5m20s
CI Code / Linux (arch) (pull_request) Successful in 7m23s
Prevent autocomplete crashes on asset failures when executing history subcommands without an active connection by validating the session state beforehand. Standardize JID resolution across command handlers to ensure consistent contact lookup behavior.

Use auto_gchar gchar instead of GString for performance and readability purposes.
2026-07-11 16:09:36 +00:00
b208e48ee1 feat(cli): resolve contact names to JIDs in multiple commands and improve autocomplete
Introduce _resolve_contact_jid helper to convert contact names to JIDs.
Apply this helper to /caps, /software, /disco, /lastactivity, and /ping
commands, allowing users to specify contacts by name instead of full JIDs.

Enhance command-line autocomplete by adding _disco_autocomplete and
_roster_jid_autocomplete functions. Update /caps autocomplete to utilize
the new roster resolution logic and ensure it only returns results when
connected.
2026-07-11 15:54:32 +00:00
2 changed files with 121 additions and 92 deletions

View File

@@ -110,6 +110,8 @@ static char* _software_autocomplete(ProfWin* window, const char* const input, gb
static char* _url_autocomplete(ProfWin* window, const char* const input, gboolean previous);
static char* _executable_autocomplete(ProfWin* window, const char* const input, gboolean previous);
static char* _lastactivity_autocomplete(ProfWin* window, const char* const input, gboolean previous);
static char* _disco_autocomplete(ProfWin* window, const char* const input, gboolean previous);
static char* _roster_jid_autocomplete(const char* const input, const char* const prefix, gboolean previous);
static char* _intype_autocomplete(ProfWin* window, const char* const input, gboolean previous);
static char* _mood_autocomplete(ProfWin* window, const char* const input, gboolean previous);
static char* _strophe_autocomplete(ProfWin* window, const char* const input, gboolean previous);
@@ -122,6 +124,7 @@ static char* _ai_autocomplete(ProfWin* window, const char* const input, gboolean
static char* _script_autocomplete_func(const char* const prefix, gboolean previous, void* context);
static char* _cmd_ac_complete_params(ProfWin* window, const char* const input, gboolean previous);
static gboolean _is_connected(void);
static Autocomplete commands_ac;
static Autocomplete who_room_ac;
@@ -1426,6 +1429,7 @@ cmd_ac_init(void)
g_hash_table_insert(ac_funcs, "/color", _color_autocomplete);
g_hash_table_insert(ac_funcs, "/connect", _connect_autocomplete);
g_hash_table_insert(ac_funcs, "/console", _console_autocomplete);
g_hash_table_insert(ac_funcs, "/disco", _disco_autocomplete);
g_hash_table_insert(ac_funcs, "/correct", _correct_autocomplete);
g_hash_table_insert(ac_funcs, "/correction", _correction_autocomplete);
g_hash_table_insert(ac_funcs, "/executable", _executable_autocomplete);
@@ -1862,10 +1866,12 @@ _cmd_ac_complete_params(ProfWin* window, const char* const input, gboolean previ
}
gchar* history_jid_subcmds[] = { "/history verify", "/history export", "/history import" };
for (size_t i = 0; i < ARRAY_SIZE(history_jid_subcmds); i++) {
result = autocomplete_param_with_func(input, history_jid_subcmds[i], roster_barejid_autocomplete, previous, NULL);
if (result) {
return result;
if (conn_status == JABBER_CONNECTED) {
for (size_t i = 0; i < ARRAY_SIZE(history_jid_subcmds); i++) {
result = autocomplete_param_with_func(input, history_jid_subcmds[i], roster_barejid_autocomplete, previous, NULL);
if (result) {
return result;
}
}
}
@@ -1941,7 +1947,6 @@ _cmd_ac_complete_params(ProfWin* window, const char* const input, gboolean previ
Autocomplete completer;
} ac_cmds[] = {
{ "/prefs", prefs_ac },
{ "/disco", disco_ac },
{ "/room", room_ac },
{ "/mainwin", winpos_ac },
{ "/inputwin", winpos_ac },
@@ -1991,6 +1996,31 @@ _cmd_ac_complete_params(ProfWin* window, const char* const input, gboolean previ
return NULL;
}
static gboolean
_is_connected(void)
{
return connection_get_status() == JABBER_CONNECTED;
}
static char*
_roster_jid_autocomplete(const char* const input, const char* const prefix, gboolean previous)
{
if (!_is_connected()) {
return NULL;
}
char* result = NULL;
result = autocomplete_param_with_func(input, prefix, roster_contact_autocomplete, previous, NULL);
if (result) {
return result;
}
result = autocomplete_param_with_func(input, prefix, roster_barejid_autocomplete, previous, NULL);
if (result) {
return result;
}
result = autocomplete_param_with_func(input, prefix, roster_fulljid_autocomplete, previous, NULL);
return result;
}
static char*
_caps_autocomplete(ProfWin* window, const char* const input, gboolean previous)
{
@@ -2006,20 +2036,29 @@ _caps_autocomplete(ProfWin* window, const char* const input, gboolean previous)
if (nick_ac) {
result = autocomplete_param_with_ac(input, "/caps", nick_ac, TRUE, previous);
}
} else if (connection_get_status() == JABBER_CONNECTED) {
result = autocomplete_param_with_func(input, "/caps", roster_contact_autocomplete, previous, NULL);
if (result) {
return result;
}
result = autocomplete_param_with_func(input, "/caps", roster_barejid_autocomplete, previous, NULL);
if (result) {
return result;
}
result = autocomplete_param_with_func(input, "/caps", roster_fulljid_autocomplete, previous, NULL);
} else {
result = _roster_jid_autocomplete(input, "/caps", previous);
}
return result;
}
static char*
_disco_autocomplete(ProfWin* window, const char* const input, gboolean previous)
{
char* result = NULL;
result = autocomplete_param_with_ac(input, "/disco", disco_ac, TRUE, previous);
if (result) {
return result;
}
result = _roster_jid_autocomplete(input, "/disco info", previous);
if (result) {
return result;
}
result = _roster_jid_autocomplete(input, "/disco items", previous);
return result;
}
static char*
_sub_autocomplete(ProfWin* window, const char* const input, gboolean previous)
{
@@ -3698,7 +3737,7 @@ _win_autocomplete(ProfWin* window, const char* const input, gboolean previous)
}
char* unquoted = strip_arg_quotes(input);
result = autocomplete_param_with_func(unquoted, "/win", roster_contact_autocomplete, previous, NULL);
result = _roster_jid_autocomplete(input, "/win", previous);
free(unquoted);
return result;
}
@@ -4032,18 +4071,14 @@ _invite_autocomplete(ProfWin* window, const char* const input, gboolean previous
return result;
}
jabber_conn_status_t conn_status = connection_get_status();
result = _roster_jid_autocomplete(input, "/invite send", previous);
if (result) {
return result;
}
if (conn_status == JABBER_CONNECTED) {
result = autocomplete_param_with_func(input, "/invite send", roster_contact_autocomplete, previous, NULL);
if (result) {
return result;
}
result = autocomplete_param_with_func(input, "/invite decline", muc_invites_find, previous, NULL);
if (result) {
return result;
}
result = autocomplete_param_with_func(input, "/invite decline", muc_invites_find, previous, NULL);
if (result) {
return result;
}
return NULL;
@@ -4059,43 +4094,38 @@ _status_autocomplete(ProfWin* window, const char* const input, gboolean previous
return result;
}
jabber_conn_status_t conn_status = connection_get_status();
// complete with: online, away etc.
result = autocomplete_param_with_ac(input, "/status set", account_status_ac, TRUE, previous);
if (result) {
return result;
}
if (conn_status == JABBER_CONNECTED) {
// Remove quote character before and after names when doing autocomplete
char* unquoted = strip_arg_quotes(input);
// complete with: online, away etc.
result = autocomplete_param_with_ac(input, "/status set", account_status_ac, TRUE, previous);
if (result) {
return result;
}
// Remove quote character before and after names when doing autocomplete
char* unquoted = strip_arg_quotes(input);
// MUC completion with nicknames
if (window->type == WIN_MUC) {
ProfMucWin* mucwin = (ProfMucWin*)window;
assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
Autocomplete nick_ac = muc_roster_ac(mucwin->roomjid);
if (nick_ac) {
result = autocomplete_param_with_ac(unquoted, "/status get", nick_ac, TRUE, previous);
if (result) {
free(unquoted);
return result;
}
}
// roster completion
} else {
result = autocomplete_param_with_func(unquoted, "/status get", roster_contact_autocomplete, previous, NULL);
// MUC completion with nicknames
if (window->type == WIN_MUC) {
ProfMucWin* mucwin = (ProfMucWin*)window;
assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
Autocomplete nick_ac = muc_roster_ac(mucwin->roomjid);
if (nick_ac) {
result = autocomplete_param_with_ac(unquoted, "/status get", nick_ac, TRUE, previous);
if (result) {
free(unquoted);
return result;
}
}
free(unquoted);
// roster completion
} else {
result = _roster_jid_autocomplete(input, "/status get", previous);
if (result) {
free(unquoted);
return result;
}
}
free(unquoted);
return NULL;
}
@@ -4202,14 +4232,12 @@ _software_autocomplete(ProfWin* window, const char* const input, gboolean previo
{
char* result = NULL;
if (window->type == WIN_CHAT) {
if (window->type == WIN_CHAT && _is_connected()) {
ProfChatWin* chatwin = (ProfChatWin*)window;
assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
GString* search_str = g_string_new("/software ");
g_string_append(search_str, chatwin->barejid);
result = autocomplete_param_with_func(search_str->str, "/software", roster_fulljid_autocomplete, previous, NULL);
g_string_free(search_str, TRUE);
auto_gchar gchar* search_str = g_strdup_printf("/software %s", chatwin->barejid);
result = autocomplete_param_with_func(search_str, "/software", roster_fulljid_autocomplete, previous, NULL);
} else if (window->type == WIN_MUC) {
ProfMucWin* mucwin = (ProfMucWin*)window;
assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
@@ -4223,10 +4251,7 @@ _software_autocomplete(ProfWin* window, const char* const input, gboolean previo
}
}
} else {
result = autocomplete_param_with_func(input, "/software", roster_fulljid_autocomplete, previous, NULL);
if (result) {
return result;
}
result = _roster_jid_autocomplete(input, "/software", previous);
}
return result;
@@ -4325,16 +4350,11 @@ _lastactivity_autocomplete(ProfWin* window, const char* const input, gboolean pr
return result;
}
jabber_conn_status_t conn_status = connection_get_status();
if (conn_status == JABBER_CONNECTED) {
result = autocomplete_param_with_func(input, "/lastactivity set", prefs_autocomplete_boolean_choice, previous, NULL);
if (result) {
return result;
}
result = autocomplete_param_with_func(input, "/lastactivity get", roster_barejid_autocomplete, previous, NULL);
result = autocomplete_param_with_func(input, "/lastactivity set", prefs_autocomplete_boolean_choice, previous, NULL);
if (result) {
return result;
}
result = _roster_jid_autocomplete(input, "/lastactivity get", previous);
return result;
}
@@ -4514,19 +4534,19 @@ _vcard_autocomplete(ProfWin* window, const char* const input, gboolean previous)
} else {
char* unquoted = strip_arg_quotes(input);
result = autocomplete_param_with_func(unquoted, "/vcard get", roster_contact_autocomplete, previous, NULL);
result = _roster_jid_autocomplete(input, "/vcard get", previous);
if (result) {
free(unquoted);
return result;
}
result = autocomplete_param_with_func(unquoted, "/vcard photo open", roster_contact_autocomplete, previous, NULL);
result = _roster_jid_autocomplete(input, "/vcard photo open", previous);
if (result) {
free(unquoted);
return result;
}
result = autocomplete_param_with_func(unquoted, "/vcard photo save", roster_contact_autocomplete, previous, NULL);
result = _roster_jid_autocomplete(input, "/vcard photo save", previous);
if (result) {
free(unquoted);
return result;

View File

@@ -110,6 +110,15 @@ static gboolean _cmd_execute_default(ProfWin* window, const char* inp);
static gboolean _cmd_execute_alias(ProfWin* window, const char* const inp, gboolean* ran);
static gboolean
_download_install_plugin(ProfWin* window, gchar* url, gchar* path);
static const gchar*
_resolve_contact_jid(const char* const name)
{
char* barejid = roster_barejid_from_name(name);
if (barejid == NULL) {
barejid = (char*)name;
}
return barejid;
}
static void
_vcard_editor_finished_cb(gchar* message, void* user_data)
@@ -3414,7 +3423,7 @@ cmd_caps(ProfWin* window, const char* const command, gchar** args)
case WIN_CONSOLE:
case WIN_XML:
if (args[0]) {
auto_jid Jid* jid = jid_create(args[0]);
auto_jid Jid* jid = jid_create(_resolve_contact_jid(args[0]));
if (jid == NULL || jid->fulljid == NULL) {
cons_show("You must provide a full jid to the /caps command.");
@@ -3456,7 +3465,7 @@ cmd_caps(ProfWin* window, const char* const command, gchar** args)
}
static void
_send_software_version_iq_to_fulljid(char* request)
_send_software_version_iq_to_fulljid(const gchar* const request)
{
auto_jid Jid* jid = jid_create(request);
@@ -3497,7 +3506,7 @@ cmd_software(ProfWin* window, const char* const command, gchar** args)
break;
case WIN_CHAT:
if (args[0]) {
_send_software_version_iq_to_fulljid(args[0]);
_send_software_version_iq_to_fulljid(_resolve_contact_jid(args[0]));
break;
} else {
ProfChatWin* chatwin = (ProfChatWin*)window;
@@ -3521,7 +3530,7 @@ cmd_software(ProfWin* window, const char* const command, gchar** args)
}
case WIN_CONSOLE:
if (args[0]) {
_send_software_version_iq_to_fulljid(args[0]);
_send_software_version_iq_to_fulljid(_resolve_contact_jid(args[0]));
} else {
cons_show("You must provide a jid to the /software command.");
}
@@ -4831,7 +4840,7 @@ cmd_disco(ProfWin* window, const char* const command, gchar** args)
return TRUE;
}
auto_gchar gchar* jid = g_strdup_printf("%s", args[1] ?: connection_get_jid()->domainpart);
auto_gchar gchar* jid = g_strdup_printf("%s", args[1] ? _resolve_contact_jid(args[1]) : connection_get_jid()->domainpart);
if (g_strcmp0(args[0], "info") == 0) {
iq_disco_info_request(jid);
@@ -5053,7 +5062,7 @@ cmd_lastactivity(ProfWin* window, const char* const command, gchar** args)
if (args[1] == NULL) {
iq_last_activity_request(connection_get_jid()->domainpart);
} else {
iq_last_activity_request(args[1]);
iq_last_activity_request(_resolve_contact_jid(args[1]));
}
return TRUE;
}
@@ -6406,17 +6415,17 @@ cmd_ping(ProfWin* window, const char* const command, gchar** args)
return TRUE;
}
if (args[0] != NULL && caps_jid_has_feature(args[0], XMPP_FEATURE_PING) == FALSE) {
cons_show("%s does not support ping requests.", args[0]);
return TRUE;
}
iq_send_ping(args[0]);
if (args[0] == NULL) {
cons_show("Pinged server…");
if (args[0] != NULL) {
const gchar* ping_target = _resolve_contact_jid(args[0]);
if (caps_jid_has_feature(ping_target, XMPP_FEATURE_PING) == FALSE) {
cons_show("%s does not support ping requests.", ping_target);
return TRUE;
}
iq_send_ping(ping_target);
cons_show("Pinged %s…", ping_target);
} else {
cons_show("Pinged %s…", args[0]);
iq_send_ping(NULL);
cons_show("Pinged server…");
}
return TRUE;
}