Files
cproof/src/config/preferences.h
Jabber Developer 02e679c277
All checks were successful
CI Code / Check spelling (push) Successful in 16s
CI Code / Check coding style (push) Successful in 30s
CI Code / Linux (debian) (push) Successful in 5m25s
CI Code / Linux (ubuntu) (push) Successful in 5m33s
CI Code / Code Coverage (push) Successful in 7m54s
CI Code / Linux (arch) (push) Successful in 12m7s
feat(autoping): autoping availability warning
## Introduced change

A new warning that notifies users when the connected XMPP server
advertises XEP-0199 (urn:xmpp:ping) support but the autoping feature
is disabled in settings. The warning can be toggled with
`/autoping warning on|off`.

The warning fires during the on-connect disco#info exchange, only for
responses from the server's own domain — responses from user JIDs or
subdomain services (e.g., conference servers) are excluded.

### Capabilities

- **On-connect detection**: Warning is emitted automatically when the
  server's disco#info response indicates `urn:xmpp:ping` support while
  autoping is disabled and the preference is enabled (default: on)
- **`/autoping warning on|off`**: Toggle the warning via the existing
  autoping command
- **Domain-scoped**: Only triggers for disco#info from the bound server
  domain; user JIDs and subdomain service JIDs are skipped
- **Case-insensitive domain matching**: Uses `g_ascii_strcasecmp` instead
  of `g_strcmp0` to handle case differences between the stanza `from`
  field and the bound domain
- **Display in settings**: Shown in both `/notify` and `/autoping`
  settings dumps, with consistent alignment and `(/autoping warning)`
  reference in each line
- **Autocomplete**: Dedicated `_autoping_autocomplete` function handles
  the `warning` subcommand with `on|off` completion

## Reasoning behind the change

Users connecting to servers that support XEP-0199 ping but have autoping
disabled may experience poorer connection stability. The warning draws
attention to this configuration mismatch without requiring users to read
documentation or dig into settings.

The warning is scoped to the server domain (not user JIDs or subdomain
services) because autoping is a connection-level feature — it only makes
sense in the context of the server's keepalive capabilities.

The warning preference defaults to `on` so users are informed by default,
but can be disabled with `/autoping warning off` if they prefer not to
see it.

## Implementation details

### Warning logic (`src/xmpp/iq.c`)

`_disco_autoping_warning_message()` is called from the on-connect
disco#info handler when the `from` field matches the bound domain. It
checks three conditions:

1. Server features contain `urn:xmpp:ping`
2. Autoping interval is 0 (disabled)
3. `PREF_AUTOPING_WARNING` is true

All three must be true for the warning to display.

### Domain matching

`g_ascii_strcasecmp(from, connection_get_domain())` is used instead of
`g_strcmp0` to handle case differences. This prevents the warning from
silently skipping if a server echoes the `from` field in a different
case than the bound domain.

### Command integration (`src/command/cmd_*.c`)

- `cmd_defs.c`: Added `/autoping warning on|off` syntax and argument
  description
- `cmd_funcs.c`: Added `warning` subcommand handler that calls
  `_cmd_set_boolean_preference` with `PREF_AUTOPING_WARNING`
- `cmd_ac.c`: Registered dedicated `_autoping_autocomplete` that handles
  the `warning` subcommand with `on|off` boolean completion

### Preference storage (`src/config/preferences.c`)

- Group: `PREF_GROUP_NOTIFICATIONS`
- Key: `autoping.warning`
- Default: `TRUE`

### Settings display (`src/ui/console.c`)

The autoping warning preference is shown in both `cons_notify_setting()`
and `cons_autoping_setting()` with consistent column alignment and a
`(/autoping warning)` reference in each line.

### Tests (`tests/functionaltests/test_autoping.c`)

Six functional tests cover all condition combinations:

| Test | Server ping | Autoping | Warning pref | Expected |
|---|---|---|---|---|
| `autoping_warning_shown_when_disabled` | yes | off | on | warning shown |
| `autoping_warning_not_shown_when_server_unsupported` | no | off | on | no warning |
| `autoping_warning_not_shown_when_autoping_enabled` | yes | on | on | no warning |
| `autoping_warning_not_shown_when_user_disabled` | yes | off | off | no warning |
| `autoping_warning_not_shown_for_user_jid` | yes (from user JID) | off | on | no warning |
| `autoping_warning_not_shown_for_subdomain_service` | yes (from subdomain) | off | on | no warning |

Co-authored-by: Jabber Developer2 <jabber.developer2@jabber.space>
2026-06-23 11:11:25 +00:00

380 lines
12 KiB
C

/*
* preferences.h
* vim: expandtab:ts=4:sts=4:sw=4
*
* Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
* Copyright (C) 2019 - 2026 Michael Vetter <jubalh@iodoru.org>
*
* SPDX-License-Identifier: GPL-3.0-or-later WITH OpenSSL-exception
*/
#ifndef CONFIG_PREFERENCES_H
#define CONFIG_PREFERENCES_H
#include "config.h"
#include <glib.h>
#define PREFS_MIN_LOG_SIZE 64
#define PREFS_MAX_LOG_SIZE (10 * 1024 * 1024)
// represents all settings in .profrc
// each enum value is mapped to a group and key in .profrc (see preferences.c)
typedef enum {
PREF_CLEAR_PERSIST_HISTORY,
PREF_SPLASH,
PREF_BEEP,
PREF_VERCHECK,
PREF_THEME,
PREF_WINTITLE_SHOW,
PREF_WINTITLE_GOODBYE,
PREF_FLASH,
PREF_TRAY,
PREF_TRAY_READ,
PREF_ADV_NOTIFY_DISCO_OR_VERSION,
PREF_INTYPE,
PREF_INTYPE_CONSOLE,
PREF_HISTORY,
PREF_CARBONS,
PREF_RECEIPTS_SEND,
PREF_RECEIPTS_REQUEST,
PREF_REVEAL_OS,
PREF_OCCUPANTS,
PREF_OCCUPANTS_SIZE,
PREF_OCCUPANTS_JID,
PREF_OCCUPANTS_OFFLINE,
PREF_ROSTER,
PREF_ROSTER_SIZE,
PREF_ROSTER_OFFLINE,
PREF_ROSTER_RESOURCE,
PREF_ROSTER_PRESENCE,
PREF_ROSTER_STATUS,
PREF_ROSTER_EMPTY,
PREF_ROSTER_BY,
PREF_ROSTER_ORDER,
PREF_ROSTER_UNREAD,
PREF_ROSTER_COUNT,
PREF_ROSTER_COUNT_ZERO,
PREF_ROSTER_PRIORITY,
PREF_ROSTER_WRAP,
PREF_ROSTER_RESOURCE_JOIN,
PREF_ROSTER_CONTACTS,
PREF_ROSTER_UNSUBSCRIBED,
PREF_ROSTER_ROOMS,
PREF_ROSTER_ROOMS_POS,
PREF_ROSTER_ROOMS_BY,
PREF_ROSTER_ROOMS_ORDER,
PREF_ROSTER_ROOMS_UNREAD,
PREF_ROSTER_ROOMS_TITLE,
PREF_ROSTER_PRIVATE,
PREF_MUC_PRIVILEGES,
PREF_PRESENCE,
PREF_WRAP,
PREF_TIME_CONSOLE,
PREF_TIME_CHAT,
PREF_TIME_MUC,
PREF_TIME_CONFIG,
PREF_TIME_PRIVATE,
PREF_TIME_XMLCONSOLE,
PREF_TIME_STATUSBAR,
PREF_TIME_LASTACTIVITY,
PREF_TIME_VCARD,
PREF_STATUSES,
PREF_STATUSES_CONSOLE,
PREF_STATUSES_CHAT,
PREF_STATUSES_MUC,
PREF_STATES,
PREF_OUTTYPE,
PREF_NOTIFY_TYPING,
PREF_NOTIFY_TYPING_CURRENT,
PREF_NOTIFY_CHAT,
PREF_NOTIFY_CHAT_CURRENT,
PREF_NOTIFY_CHAT_TEXT,
PREF_NOTIFY_ROOM,
PREF_NOTIFY_ROOM_MENTION,
PREF_NOTIFY_ROOM_TRIGGER,
PREF_NOTIFY_ROOM_CURRENT,
PREF_NOTIFY_ROOM_TEXT,
PREF_NOTIFY_INVITE,
PREF_NOTIFY_SUB,
PREF_NOTIFY_MENTION_CASE_SENSITIVE,
PREF_NOTIFY_MENTION_WHOLE_WORD,
PREF_CHLOG,
PREF_DBLOG,
PREF_GRLOG,
PREF_AUTOAWAY_CHECK,
PREF_AUTOAWAY_MODE,
PREF_AUTOAWAY_MESSAGE,
PREF_AUTOXA_MESSAGE,
PREF_CONNECT_ACCOUNT,
PREF_DEFAULT_ACCOUNT,
PREF_LOG_ROTATE,
PREF_LOG_SHARED,
PREF_OTR_LOG,
PREF_OTR_POLICY,
PREF_OTR_SENDFILE,
PREF_RESOURCE_TITLE,
PREF_RESOURCE_MESSAGE,
PREF_INPBLOCK_DYNAMIC,
PREF_ENC_WARN,
PREF_TITLEBAR_MUC_TITLE,
PREF_PGP_LOG,
PREF_PGP_SENDFILE,
PREF_PGP_PUBKEY_AUTOIMPORT,
PREF_TLS_CERTPATH,
PREF_TLS_SHOW,
PREF_LASTACTIVITY,
PREF_CONSOLE_MUC,
PREF_CONSOLE_PRIVATE,
PREF_CONSOLE_CHAT,
PREF_COLOR_NICK,
PREF_COLOR_NICK_OWN,
PREF_ROSTER_COLOR_NICK,
PREF_OCCUPANTS_COLOR_NICK,
PREF_BOOKMARK_INVITE,
PREF_ROOM_LIST_CACHE,
PREF_STATUSBAR_SHOW_NAME,
PREF_STATUSBAR_SHOW_NUMBER,
PREF_STATUSBAR_SHOW_READ,
PREF_STATUSBAR_SHOW_DBBACKEND,
PREF_STATUSBAR_SELF,
PREF_STATUSBAR_CHAT,
PREF_STATUSBAR_ROOM_TITLE,
PREF_OMEMO_LOG,
PREF_OMEMO_POLICY,
PREF_OMEMO_TRUST_MODE,
PREF_OCCUPANTS_WRAP,
PREF_CORRECTION_ALLOW,
PREF_AVATAR_CMD,
PREF_SLASH_GUARD,
PREF_MAM,
PREF_URL_OPEN_CMD,
PREF_URL_SAVE_CMD,
PREF_COMPOSE_EDITOR,
PREF_SILENCE_NON_ROSTER,
PREF_OUTGOING_STAMP,
PREF_INCOMING_STAMP,
PREF_NOTIFY_ROOM_OFFLINE,
PREF_OX_LOG,
PREF_MOOD,
PREF_STROPHE_VERBOSITY,
PREF_AI_PROVIDER,
PREF_AI_API_KEY,
PREF_AI_DEFAULT_MODEL,
PREF_STROPHE_SM_ENABLED,
PREF_STROPHE_SM_RESEND,
PREF_VCARD_PHOTO_CMD,
PREF_STATUSBAR_TABMODE,
PREF_FORCE_ENCRYPTION,
PREF_FORCE_ENCRYPTION_MODE,
PREF_SPELLCHECK_ENABLE,
PREF_SPELLCHECK_LANG,
PREF_AUTOPING_WARNING
} preference_t;
typedef struct prof_alias_t
{
gchar* name;
gchar* value;
} ProfAlias;
typedef struct prof_winplacement_t
{
int titlebar_pos;
int mainwin_pos;
int statusbar_pos;
int inputwin_pos;
} ProfWinPlacement;
void prefs_load(const gchar* config_file);
void prefs_changes(void);
void prefs_save(void);
void prefs_close(void);
void prefs_reload(void);
gchar* prefs_autocomplete_boolean_choice(const gchar* const prefix, gboolean previous, void* context);
void prefs_reset_boolean_choice(void);
gchar* prefs_autocomplete_room_trigger(const gchar* const prefix, gboolean previous, void* context);
void prefs_reset_room_trigger_ac(void);
gint prefs_get_gone(void);
void prefs_set_gone(gint value);
void prefs_set_notify_remind(gint period);
gint prefs_get_notify_remind(void);
void prefs_set_max_log_size(gint value);
gint prefs_get_max_log_size(void);
gint prefs_get_priority(void);
void prefs_set_reconnect(gint value);
gint prefs_get_reconnect(void);
void prefs_set_autoping(gint value);
gint prefs_get_autoping(void);
void prefs_set_autoping_timeout(gint value);
gint prefs_get_autoping_timeout(void);
gint prefs_get_inpblock(void);
void prefs_set_inpblock(gint value);
void prefs_set_statusbartabs(gint value);
gint prefs_get_statusbartabs(void);
void prefs_set_statusbartablen(gint value);
gint prefs_get_statusbartablen(void);
void prefs_set_occupants_size(gint value);
gint prefs_get_occupants_size(void);
void prefs_set_roster_size(gint value);
gint prefs_get_roster_size(void);
gint prefs_get_autoaway_time(void);
void prefs_set_autoaway_time(gint value);
gint prefs_get_autoxa_time(void);
void prefs_set_autoxa_time(gint value);
gchar** prefs_get_plugins(void);
void prefs_add_plugin(const gchar* const name);
void prefs_remove_plugin(const gchar* const name);
gchar* prefs_get_otr_char(void);
gboolean prefs_set_otr_char(gchar* ch);
gchar* prefs_get_pgp_char(void);
gboolean prefs_set_pgp_char(gchar* ch);
gchar* prefs_get_omemo_char(void);
gboolean prefs_set_omemo_char(gchar* ch);
// XEP-0373: OpenPGP for XMPP
char* prefs_get_ox_char(void);
gboolean prefs_set_ox_char(gchar* ch);
gchar* prefs_get_roster_header_char(void);
void prefs_set_roster_header_char(gchar* ch);
void prefs_clear_roster_header_char(void);
gchar* prefs_get_roster_contact_char(void);
void prefs_set_roster_contact_char(gchar* ch);
void prefs_clear_roster_contact_char(void);
gchar* prefs_get_roster_resource_char(void);
void prefs_set_roster_resource_char(gchar* ch);
void prefs_clear_roster_resource_char(void);
gchar* prefs_get_roster_private_char(void);
void prefs_set_roster_private_char(gchar* ch);
void prefs_clear_roster_private_char(void);
gchar* prefs_get_roster_room_char(void);
void prefs_set_roster_room_char(gchar* ch);
void prefs_clear_roster_room_char(void);
gchar* prefs_get_roster_room_private_char(void);
void prefs_set_roster_room_private_char(gchar* ch);
void prefs_clear_roster_room_private_char(void);
gchar* prefs_get_occupants_char(void);
void prefs_set_occupants_char(gchar* ch);
void prefs_clear_occupants_char(void);
gchar* prefs_get_occupants_header_char(void);
void prefs_set_occupants_header_char(gchar* ch);
void prefs_clear_occupants_header_char(void);
gint prefs_get_roster_contact_indent(void);
void prefs_set_roster_contact_indent(gint value);
gint prefs_get_roster_resource_indent(void);
void prefs_set_roster_resource_indent(gint value);
gint prefs_get_roster_presence_indent(void);
void prefs_set_roster_presence_indent(gint value);
gint prefs_get_occupants_indent(void);
void prefs_set_occupants_indent(gint value);
gchar* prefs_get_correction_char(void);
void prefs_set_correction_char(gchar* ch);
void prefs_add_login(const gchar* jid);
void prefs_set_tray_timer(gint value);
gint prefs_get_tray_timer(void);
gboolean prefs_add_alias(const gchar* const name, const gchar* const value);
gboolean prefs_remove_alias(const gchar* const name);
gchar* prefs_get_alias(const gchar* const name);
GList* prefs_get_aliases(void);
void prefs_free_aliases(GList* aliases);
gboolean prefs_add_room_notify_trigger(const gchar* const text);
gboolean prefs_remove_room_notify_trigger(const gchar* const text);
GList* prefs_get_room_notify_triggers(void);
ProfWinPlacement* prefs_get_win_placement(void);
void prefs_free_win_placement(ProfWinPlacement* placement);
gboolean prefs_titlebar_pos_up(void);
gboolean prefs_titlebar_pos_down(void);
gboolean prefs_mainwin_pos_up(void);
gboolean prefs_mainwin_pos_down(void);
gboolean prefs_statusbar_pos_up(void);
gboolean prefs_statusbar_pos_down(void);
gboolean prefs_inputwin_pos_up(void);
gboolean prefs_inputwin_pos_down(void);
ProfWinPlacement* prefs_create_profwin_placement(int titlebar, int mainwin, int statusbar, int inputwin);
void prefs_save_win_placement(ProfWinPlacement* placement);
gboolean prefs_get_boolean(preference_t pref);
void prefs_set_boolean(preference_t pref, gboolean value);
gchar* prefs_get_string(preference_t pref);
gchar* prefs_get_string_with_locale(preference_t pref, gchar* locale);
void prefs_set_string(preference_t pref, const gchar* new_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);
char* prefs_get_tls_certpath(void);
gboolean prefs_do_chat_notify(gboolean current_win);
gboolean prefs_do_room_notify(gboolean current_win, const char* const roomjid, const char* const mynick,
const char* const theirnick, const char* const message, gboolean mention, gboolean trigger_found);
gboolean prefs_do_room_notify_mention(const char* const roomjid, int unread, gboolean mention, gboolean trigger);
GList* prefs_message_get_triggers(const char* const message);
void prefs_set_room_notify(const char* const roomjid, gboolean value);
void prefs_set_room_notify_mention(const char* const roomjid, gboolean value);
void prefs_set_room_notify_trigger(const char* const roomjid, gboolean value);
gboolean prefs_reset_room_notify(const char* const roomjid);
gboolean prefs_has_room_notify(const char* const roomjid);
gboolean prefs_has_room_notify_mention(const char* const roomjid);
gboolean prefs_has_room_notify_trigger(const char* const roomjid);
gboolean prefs_get_room_notify(const char* const roomjid);
gboolean prefs_get_room_notify_mention(const char* const roomjid);
gboolean prefs_get_room_notify_trigger(const char* const roomjid);
/* AI token management */
gboolean prefs_ai_set_token(const char* const provider, const char* const token);
gboolean prefs_ai_remove_token(const char* const provider);
char* prefs_ai_get_token(const char* const provider);
GList* prefs_ai_list_tokens(void);
void prefs_free_ai_tokens(GList* tokens);
/* AI provider management */
gboolean prefs_ai_set_provider(const char* const provider, const char* const url);
gboolean prefs_ai_remove_provider(const char* const provider);
char* prefs_ai_get_provider_url(const char* const provider);
GList* prefs_ai_list_providers(void);
void prefs_free_ai_providers(GList* providers);
GList* prefs_ai_get_providers(void);
/* Wipe the whole [ai/<provider>] subsection (url, key, models, default_model,
* custom settings) in one call. */
gboolean prefs_ai_remove_section(const char* const provider);
/* AI model cache management */
gboolean prefs_ai_set_models(const char* const provider, const gchar* const* models, gint count);
gboolean prefs_ai_remove_models(const char* const provider);
GList* prefs_ai_get_models(const char* const provider);
void prefs_free_ai_models(GList* models);
/* AI default model per provider */
gboolean prefs_ai_set_default_model(const char* const provider, const char* const model);
gboolean prefs_ai_remove_default_model(const char* const provider);
char* prefs_ai_get_default_model(const char* const provider);
/* AI per-provider custom settings (e.g. tools, search) — persisted as
* setting.<name>=<value> inside [ai/<provider>]. Pass NULL value to remove. */
gboolean prefs_ai_set_setting(const char* const provider, const char* const setting, const char* const value);
gboolean prefs_ai_remove_setting(const char* const provider, const char* const setting);
char* prefs_ai_get_setting(const char* const provider, const char* const setting);
GList* prefs_ai_list_settings(const char* const provider);
void prefs_free_ai_settings(GList* settings);
#endif