diff --git a/src/command/cmd_ac.c b/src/command/cmd_ac.c index f0210713..e8d0680a 100644 --- a/src/command/cmd_ac.c +++ b/src/command/cmd_ac.c @@ -131,6 +131,7 @@ static char* _executable_autocomplete(ProfWin* window, const char* const input, static char* _lastactivity_autocomplete(ProfWin* window, const char* const input, 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); static char* _adhoc_cmd_autocomplete(ProfWin* window, const char* const input, gboolean previous); static char* _vcard_autocomplete(ProfWin* window, const char* const input, gboolean previous); @@ -276,6 +277,9 @@ static Autocomplete executable_ac; static Autocomplete intype_ac; static Autocomplete mood_ac; static Autocomplete mood_type_ac; +static Autocomplete strophe_ac; +static Autocomplete strophe_sm_ac; +static Autocomplete strophe_verbosity_ac; static Autocomplete adhoc_cmd_ac; static Autocomplete lastactivity_ac; static Autocomplete vcard_ac; @@ -1098,6 +1102,19 @@ cmd_ac_init(void) autocomplete_add(intype_ac, "console"); autocomplete_add(intype_ac, "titlebar"); + strophe_ac = autocomplete_new(); + autocomplete_add(strophe_ac, "sm"); + autocomplete_add(strophe_ac, "verbosity"); + strophe_sm_ac = autocomplete_new(); + autocomplete_add(strophe_sm_ac, "on"); + autocomplete_add(strophe_sm_ac, "no-resend"); + autocomplete_add(strophe_sm_ac, "off"); + strophe_verbosity_ac = autocomplete_new(); + autocomplete_add(strophe_verbosity_ac, "0"); + autocomplete_add(strophe_verbosity_ac, "1"); + autocomplete_add(strophe_verbosity_ac, "2"); + autocomplete_add(strophe_verbosity_ac, "3"); + mood_ac = autocomplete_new(); autocomplete_add(mood_ac, "set"); autocomplete_add(mood_ac, "clear"); @@ -1595,6 +1612,9 @@ cmd_ac_reset(ProfWin* window) autocomplete_reset(intype_ac); autocomplete_reset(mood_ac); autocomplete_reset(mood_type_ac); + autocomplete_reset(strophe_verbosity_ac); + autocomplete_reset(strophe_sm_ac); + autocomplete_reset(strophe_ac); autocomplete_reset(adhoc_cmd_ac); autocomplete_reset(vcard_ac); @@ -2057,6 +2077,7 @@ _cmd_ac_complete_params(ProfWin* window, const char* const input, gboolean previ g_hash_table_insert(ac_funcs, "/lastactivity", _lastactivity_autocomplete); g_hash_table_insert(ac_funcs, "/intype", _intype_autocomplete); g_hash_table_insert(ac_funcs, "/mood", _mood_autocomplete); + g_hash_table_insert(ac_funcs, "/strophe", _strophe_autocomplete); g_hash_table_insert(ac_funcs, "/cmd", _adhoc_cmd_autocomplete); g_hash_table_insert(ac_funcs, "/vcard", _vcard_autocomplete); @@ -4442,6 +4463,24 @@ _mood_autocomplete(ProfWin* window, const char* const input, gboolean previous) return result; } +static char* +_strophe_autocomplete(ProfWin* window, const char* const input, gboolean previous) +{ + char* result = NULL; + + result = autocomplete_param_with_ac(input, "/strophe sm", strophe_sm_ac, FALSE, previous); + if (result) { + return result; + } + + result = autocomplete_param_with_ac(input, "/strophe verbosity", strophe_verbosity_ac, FALSE, previous); + if (result) { + return result; + } + + return autocomplete_param_with_ac(input, "/strophe", strophe_ac, FALSE, previous); +} + static char* _adhoc_cmd_autocomplete(ProfWin* window, const char* const input, gboolean previous) { diff --git a/src/command/cmd_defs.c b/src/command/cmd_defs.c index 0a2a7440..e65e2f00 100644 --- a/src/command/cmd_defs.c +++ b/src/command/cmd_defs.c @@ -2862,6 +2862,25 @@ static struct cmd_t command_defs[] = { "/mood set amazed", "/mood clear") }, + + { "/strophe", + parse_args, 2, 2, &cons_strophe_setting, + CMD_NOSUBFUNCS + CMD_MAINFUNC(cmd_strophe) + CMD_TAGS( + CMD_TAG_CONNECTION) + CMD_SYN( + "/strophe verbosity 0-3", + "/strophe sm on|no-resend|off") + CMD_DESC( + "Modify libstrophe settings.") + CMD_ARGS( + { "verbosity 0-3", "Set libstrophe verbosity level when log level is 'DEBUG'." }, + { "sm on|no-resend|off", "Enable or disable Stream-Management (SM) as of XEP-0198. The 'no-resend' option enables SM, but won't re-send un-ACK'ed messages on re-connect." }) + CMD_EXAMPLES( + "/strophe verbosity 3", + "/strophe sm no-resend") + }, // NEXT-COMMAND (search helper) }; diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index da38f405..c0c0d036 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -9856,6 +9856,41 @@ cmd_mood(ProfWin* window, const char* const command, gchar** args) return TRUE; } +gboolean +cmd_strophe(ProfWin* window, const char* const command, gchar** args) +{ + if (g_strcmp0(args[0], "verbosity") == 0) { + int verbosity; + auto_gchar gchar* err_msg = NULL; + if (string_to_verbosity(args[1], &verbosity, &err_msg)) { + xmpp_ctx_set_verbosity(connection_get_ctx(), verbosity); + prefs_set_string(PREF_STROPHE_VERBOSITY, args[1]); + return TRUE; + } else { + cons_show(err_msg); + } + } else if (g_strcmp0(args[0], "sm") == 0) { + if (g_strcmp0(args[1], "no-resend") == 0) { + cons_show("Stream Management set to 'no-resend'."); + prefs_set_boolean(PREF_STROPHE_SM_ENABLED, TRUE); + prefs_set_boolean(PREF_STROPHE_SM_RESEND, FALSE); + return TRUE; + } else if (g_strcmp0(args[1], "on") == 0) { + cons_show("Stream Management enabled."); + prefs_set_boolean(PREF_STROPHE_SM_ENABLED, TRUE); + prefs_set_boolean(PREF_STROPHE_SM_RESEND, TRUE); + return TRUE; + } else if (g_strcmp0(args[1], "off") == 0) { + cons_show("Stream Management disabled."); + prefs_set_boolean(PREF_STROPHE_SM_ENABLED, FALSE); + prefs_set_boolean(PREF_STROPHE_SM_RESEND, FALSE); + return TRUE; + } + } + cons_bad_cmd_usage(command); + return FALSE; +} + gboolean cmd_vcard(ProfWin* window, const char* const command, gchar** args) { diff --git a/src/command/cmd_funcs.h b/src/command/cmd_funcs.h index 597164f3..58f42a61 100644 --- a/src/command/cmd_funcs.h +++ b/src/command/cmd_funcs.h @@ -253,6 +253,7 @@ gboolean cmd_correct_editor(ProfWin* window, const char* const command, gchar** gboolean cmd_silence(ProfWin* window, const char* const command, gchar** args); gboolean cmd_register(ProfWin* window, const char* const command, gchar** args); gboolean cmd_mood(ProfWin* window, const char* const command, gchar** args); +gboolean cmd_strophe(ProfWin* window, const char* const command, gchar** args); gboolean cmd_stamp(ProfWin* window, const char* const command, gchar** args); gboolean cmd_vcard(ProfWin* window, const char* const command, gchar** args); gboolean cmd_vcard_add(ProfWin* window, const char* const command, gchar** args); diff --git a/src/common.c b/src/common.c index d00e096f..86fd5612 100644 --- a/src/common.c +++ b/src/common.c @@ -70,6 +70,28 @@ struct curl_data_t static size_t _data_callback(void* ptr, size_t size, size_t nmemb, void* data); +void +auto_free_gchar(gchar** str) +{ + if (str == NULL) + return; + g_free(*str); +} + +void +auto_free_char(char** str) +{ + if (str == NULL) + return; + free(*str); +} + +gboolean +string_to_verbosity(const char* cmd, int* verbosity, gchar** err_msg) +{ + return strtoi_range(cmd, verbosity, 0, 3, err_msg); +} + gboolean create_dir(const char* name) { @@ -136,24 +158,24 @@ str_replace(const char* string, const char* substr, } gboolean -strtoi_range(char* str, int* saveptr, int min, int max, char** err_msg) +strtoi_range(const char* str, int* saveptr, int min, int max, gchar** err_msg) { char* ptr; int val; - + if (str == NULL) { + if (err_msg) + *err_msg = g_strdup_printf("'str' input pointer can not be NULL"); + return FALSE; + } errno = 0; val = (int)strtol(str, &ptr, 0); if (errno != 0 || *str == '\0' || *ptr != '\0') { - GString* err_str = g_string_new(""); - g_string_printf(err_str, "Could not convert \"%s\" to a number.", str); - *err_msg = err_str->str; - g_string_free(err_str, FALSE); + if (err_msg) + *err_msg = g_strdup_printf("Could not convert \"%s\" to a number.", str); return FALSE; } else if (val < min || val > max) { - GString* err_str = g_string_new(""); - g_string_printf(err_str, "Value %s out of range. Must be in %d..%d.", str, min, max); - *err_msg = err_str->str; - g_string_free(err_str, FALSE); + if (err_msg) + *err_msg = g_strdup_printf("Value %s out of range. Must be in %d..%d.", str, min, max); return FALSE; } diff --git a/src/common.h b/src/common.h index 27d1684d..7b30ec56 100644 --- a/src/common.h +++ b/src/common.h @@ -47,6 +47,11 @@ #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) +void auto_free_gchar(gchar** str); +#define auto_gchar __attribute__((__cleanup__(auto_free_gchar))) +void auto_free_char(char** str); +#define auto_char __attribute__((__cleanup__(auto_free_char))) + // assume malloc stores at most 8 bytes for size of allocated memory // and page size is at least 4KB #define READ_BUF_SIZE 4088 @@ -80,10 +85,12 @@ typedef enum { RESOURCE_XA } resource_presence_t; +gboolean string_to_verbosity(const char* cmd, int* verbosity, gchar** err_msg); + gboolean create_dir(const char* name); gboolean copy_file(const char* const src, const char* const target, const gboolean overwrite_existing); char* str_replace(const char* string, const char* substr, const char* replacement); -gboolean strtoi_range(char* str, int* saveptr, int min, int max, char** err_msg); +gboolean strtoi_range(const char* str, int* saveptr, int min, int max, char** err_msg); int utf8_display_len(const char* const str); char* release_get_latest(void); diff --git a/src/config/preferences.c b/src/config/preferences.c index de455245..f15952cd 100644 --- a/src/config/preferences.c +++ b/src/config/preferences.c @@ -533,7 +533,7 @@ prefs_set_boolean(preference_t pref, gboolean value) g_key_file_set_boolean(prefs, group, key, value); } -char* +gchar* prefs_get_string(preference_t pref) { const char* group = _get_group(pref); @@ -553,14 +553,14 @@ prefs_get_string(preference_t pref) } } -char* +gchar* prefs_get_string_with_option(preference_t pref, gchar* option) { const char* group = _get_group(pref); const char* key = _get_key(pref); char* def = _get_default_string(pref); - char* result = g_key_file_get_locale_string(prefs, group, key, option, NULL); + gchar* result = g_key_file_get_locale_string(prefs, group, key, option, NULL); if (result == NULL) { // check for user set default @@ -1869,6 +1869,9 @@ _get_group(preference_t pref) case PREF_CORRECTION_ALLOW: case PREF_MAM: case PREF_SILENCE_NON_ROSTER: + case PREF_STROPHE_VERBOSITY: + case PREF_STROPHE_SM_ENABLED: + case PREF_STROPHE_SM_RESEND: return PREF_GROUP_CONNECTION; case PREF_OTR_LOG: case PREF_OTR_POLICY: @@ -2165,6 +2168,12 @@ _get_key(preference_t pref) return "mood"; case PREF_VCARD_PHOTO_CMD: return "vcard.photo.cmd"; + case PREF_STROPHE_VERBOSITY: + return "strophe.verbosity"; + case PREF_STROPHE_SM_ENABLED: + return "strophe.sm.enabled"; + case PREF_STROPHE_SM_RESEND: + return "strophe.sm.resend"; default: return NULL; } @@ -2217,6 +2226,8 @@ _get_default_boolean(preference_t pref) case PREF_INTYPE_CONSOLE: case PREF_NOTIFY_MENTION_WHOLE_WORD: case PREF_MOOD: + case PREF_STROPHE_SM_ENABLED: + case PREF_STROPHE_SM_RESEND: return TRUE; default: return FALSE; @@ -2316,6 +2327,8 @@ _get_default_string(preference_t pref) return NULL; // Default to built-in method. case PREF_OX_LOG: return "on"; + case PREF_STROPHE_VERBOSITY: + return "0"; default: return NULL; } diff --git a/src/config/preferences.h b/src/config/preferences.h index 95a84865..ca8acea4 100644 --- a/src/config/preferences.h +++ b/src/config/preferences.h @@ -182,6 +182,9 @@ typedef enum { PREF_NOTIFY_ROOM_OFFLINE, PREF_OX_LOG, PREF_MOOD, + PREF_STROPHE_VERBOSITY, + PREF_STROPHE_SM_ENABLED, + PREF_STROPHE_SM_RESEND, PREF_VCARD_PHOTO_CMD, } preference_t; @@ -329,8 +332,8 @@ void prefs_save_win_placement(ProfWinPlacement* placement); gboolean prefs_get_boolean(preference_t pref); void prefs_set_boolean(preference_t pref, gboolean value); -char* prefs_get_string(preference_t pref); -char* prefs_get_string_with_option(preference_t pref, gchar* option); +gchar* prefs_get_string(preference_t pref); +gchar* prefs_get_string_with_option(preference_t pref, gchar* option); void prefs_set_string(preference_t pref, char* value); void prefs_set_string_with_option(preference_t pref, char* option, char* value); void prefs_set_string_list_with_option(preference_t pref, char* option, const gchar* const* values); diff --git a/src/ui/console.c b/src/ui/console.c index 5937a42a..b1720415 100644 --- a/src/ui/console.c +++ b/src/ui/console.c @@ -2311,6 +2311,7 @@ cons_show_connection_prefs(void) cons_autoping_setting(); cons_autoconnect_setting(); cons_rooms_cache_setting(); + cons_strophe_setting(); cons_alert(NULL); } @@ -2994,3 +2995,18 @@ cons_mood_setting(void) cons_show("Display user mood (/mood) : OFF"); } } + +void +cons_strophe_setting(void) +{ + const char* sm_setting = "OFF"; + if (prefs_get_boolean(PREF_STROPHE_SM_ENABLED)) { + if (prefs_get_boolean(PREF_STROPHE_SM_RESEND)) { + sm_setting = "ON"; + } else { + sm_setting = "NO-RESEND"; + } + } + cons_show("XEP-0198 Stream-Management : %s", sm_setting); + cons_show("libstrophe Verbosity : %s", prefs_get_string(PREF_STROPHE_VERBOSITY)); +} diff --git a/src/ui/ui.h b/src/ui/ui.h index 94d1c716..d9534ed5 100644 --- a/src/ui/ui.h +++ b/src/ui/ui.h @@ -341,6 +341,7 @@ void cons_slashguard_setting(void); void cons_mam_setting(void); void cons_silence_setting(void); void cons_mood_setting(void); +void cons_strophe_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/connection.c b/src/xmpp/connection.c index 2de4a842..d57a1376 100644 --- a/src/xmpp/connection.c +++ b/src/xmpp/connection.c @@ -140,6 +140,14 @@ connection_init(void) conn.requested_features = g_hash_table_new_full(g_str_hash, g_str_equal, free, NULL); conn.xmpp_ctx = xmpp_ctx_new(&prof_mem, &prof_log); + auto_gchar gchar* v = prefs_get_string(PREF_STROPHE_VERBOSITY); + auto_gchar gchar* err_msg = NULL; + int verbosity; + if (string_to_verbosity(v, &verbosity, &err_msg)) { + xmpp_ctx_set_verbosity(conn.xmpp_ctx, verbosity); + } else { + cons_show(err_msg); + } conn.xmpp_conn = xmpp_conn_new(conn.xmpp_ctx); _random_bytes_init(); @@ -1000,13 +1008,15 @@ _connection_handler(xmpp_conn_t* const xmpp_conn, const xmpp_conn_event_t status // lost connection for unknown reason if (conn.conn_status == JABBER_CONNECTED) { - int send_queue_len = xmpp_conn_send_queue_len(conn.xmpp_conn); - log_debug("Connection handler: Lost connection for unknown reason"); - conn.sm_state = xmpp_conn_get_sm_state(conn.xmpp_conn); - if (send_queue_len > 0) { - conn.queued_messages = calloc(send_queue_len + 1, sizeof(*conn.queued_messages)); - for (int n = 0; n < send_queue_len && conn.queued_messages[n]; ++n) { - conn.queued_messages[n] = xmpp_conn_send_queue_drop_element(conn.xmpp_conn, XMPP_QUEUE_OLDEST); + if (prefs_get_boolean(PREF_STROPHE_SM_ENABLED)) { + int send_queue_len = xmpp_conn_send_queue_len(conn.xmpp_conn); + log_debug("Connection handler: Lost connection for unknown reason"); + conn.sm_state = xmpp_conn_get_sm_state(conn.xmpp_conn); + if (send_queue_len > 0 && prefs_get_boolean(PREF_STROPHE_SM_RESEND)) { + conn.queued_messages = calloc(send_queue_len + 1, sizeof(*conn.queued_messages)); + for (int n = 0; n < send_queue_len && conn.queued_messages[n]; ++n) { + conn.queued_messages[n] = xmpp_conn_send_queue_drop_element(conn.xmpp_conn, XMPP_QUEUE_OLDEST); + } } } session_lost_connection(); diff --git a/tests/unittests/ui/stub_ui.c b/tests/unittests/ui/stub_ui.c index 204a8c35..d8272aab 100644 --- a/tests/unittests/ui/stub_ui.c +++ b/tests/unittests/ui/stub_ui.c @@ -1155,6 +1155,11 @@ cons_mood_setting(void) { } +void +cons_strophe_setting(void) +{ +} + void cons_show_bookmarks_ignore(gchar** list, gsize len) {