Compare commits

...

5 Commits

Author SHA1 Message Date
622054fc6d fix(pgp): prevent plaintext fallback in PGP/OX encryption
All checks were successful
CI Code / Check spelling (pull_request) Successful in 17s
CI Code / Check coding style (pull_request) Successful in 32s
CI Code / Code Coverage (pull_request) Successful in 3m41s
CI Code / Linux (debian) (pull_request) Successful in 5m15s
CI Code / Linux (ubuntu) (pull_request) Successful in 5m21s
CI Code / Linux (arch) (pull_request) Successful in 13m55s
CI Code / Check spelling (push) Successful in 1m24s
CI Code / Check coding style (push) Successful in 1m38s
CI Code / Linux (arch) (push) Successful in 7m14s
CI Code / Linux (debian) (push) Successful in 8m27s
CI Code / Linux (ubuntu) (push) Successful in 8m29s
CI Code / Code Coverage (push) Successful in 7m50s
Block all three code paths in message_send_chat_pgp that previously
fell back to sending unencrypted messages: encryption failure, missing
PGP key, and missing LibGPGME. Each path now shows a specific error
in the chat window and returns NULL so the caller skips logging and
display.

Also replace the generic cons_show for OX signcrypt failure with a
win_println on the current window, ensuring the user sees the error
even when the chat window is not focused.

Improve p_gpg_encrypt error reporting by adding a gchar** err
out-parameter. Each failure path now sets a descriptive message
(e.g. missing recipient key, missing sender key, GPGME context
failure, encryption failure) so the caller can display the exact
reason to the user.

Fix a memory leak: gpgme_key_unref(receiver_key) was missing from
the sender_key failure path.
2026-06-29 14:08:40 +00:00
ca92d29179 fix(ui): measure dead pad space from cursor instead of absolute height
All checks were successful
CI Code / Check spelling (push) Successful in 20s
CI Code / Check coding style (push) Successful in 38s
CI Code / Code Coverage (push) Successful in 4m2s
CI Code / Linux (ubuntu) (push) Successful in 5m22s
CI Code / Linux (debian) (push) Successful in 7m21s
CI Code / Linux (arch) (push) Successful in 7m39s
Remove the /autoping warning subcommand and PREF_AUTOPING_WARNING
preference entirely. The warning was shown at connect time when
autoping was disabled but the server supported XEP-0199 ping.

The pad threshold logic previously used an absolute height
(PAD_THRESHOLD=12000) which was above the buffer cap and never
fired during normal scrolling. Replace with a dead-space measurement
(cursor position minus live buffer lines) that only triggers a redraw
when dead space exceeds PAD_DEAD_SPACE_LIMIT (2000 lines).

Author: jabber.developer2 <jabber.developer2@jabber.space>
2026-06-24 16:22:52 +00:00
72aa603147 build: untrack generated src/gitversion.h.in
All checks were successful
CI Code / Check coding style (push) Successful in 1m33s
CI Code / Check spelling (push) Successful in 1m39s
CI Code / Linux (debian) (push) Successful in 5m20s
CI Code / Code Coverage (push) Successful in 4m30s
CI Code / Linux (arch) (push) Successful in 7m4s
CI Code / Linux (ubuntu) (push) Successful in 7m9s
The file is gitignored and regenerated by the build recipe from
.git/HEAD/.git/index; it was committed by mistake during the upstream
sync. Untrack it so builds no longer dirty the working tree. The
version string behaviour is unchanged.
2026-06-23 12:33:49 +00:00
15dfc2bdb4 fix(xmpp): guard NULL domain in autoping disco warning gate
Some checks failed
CI Code / Check coding style (pull_request) Successful in 29s
CI Code / Check spelling (pull_request) Successful in 19s
CI Code / Linux (debian) (pull_request) Successful in 5m16s
CI Code / Linux (ubuntu) (pull_request) Successful in 5m25s
CI Code / Linux (arch) (pull_request) Successful in 7m37s
CI Code / Code Coverage (pull_request) Successful in 7m59s
CI Code / Check spelling (push) Successful in 15s
CI Code / Check coding style (push) Successful in 33s
CI Code / Linux (ubuntu) (push) Has been cancelled
CI Code / Code Coverage (push) Has been cancelled
CI Code / Linux (debian) (push) Has been cancelled
CI Code / Linux (arch) (push) Has been cancelled
g_ascii_strcasecmp() is not NULL-safe (unlike the g_strcmp0 it replaced), and connection_get_domain() can be NULL (init NULL, FREE_SET_NULL on teardown). A disco#info response racing a disconnect would dereference NULL. Capture the domain and skip the check when it is NULL.
2026-06-23 14:23:11 +03:00
02e679c277 feat(autoping): autoping availability warning
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
## 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
15 changed files with 264 additions and 31 deletions

View File

@@ -47,6 +47,7 @@ static char* _notify_autocomplete(ProfWin* window, const char* const input, gboo
static char* _theme_autocomplete(ProfWin* window, const char* const input, gboolean previous);
static char* _spellcheck_autocomplete(ProfWin* window, const char* const input, gboolean previous);
static char* _autoaway_autocomplete(ProfWin* window, const char* const input, gboolean previous);
static char* _autoping_autocomplete(ProfWin* window, const char* const input, gboolean previous);
static char* _autoconnect_autocomplete(ProfWin* window, const char* const input, gboolean previous);
static char* _account_autocomplete(ProfWin* window, const char* const input, gboolean previous);
static char* _who_autocomplete(ProfWin* window, const char* const input, gboolean previous);
@@ -1056,6 +1057,7 @@ cmd_ac_init(void)
autocomplete_add(autoping_ac, "set");
autocomplete_add(autoping_ac, "timeout");
autocomplete_add(autoping_ac, "warning");
autocomplete_add(plugins_ac, "install");
autocomplete_add(plugins_ac, "update");
@@ -1407,6 +1409,7 @@ cmd_ac_init(void)
g_hash_table_insert(ac_funcs, "/alias", _alias_autocomplete);
g_hash_table_insert(ac_funcs, "/autoaway", _autoaway_autocomplete);
g_hash_table_insert(ac_funcs, "/autoconnect", _autoconnect_autocomplete);
g_hash_table_insert(ac_funcs, "/autoping", _autoping_autocomplete);
g_hash_table_insert(ac_funcs, "/avatar", _avatar_autocomplete);
g_hash_table_insert(ac_funcs, "/ban", _ban_autocomplete);
g_hash_table_insert(ac_funcs, "/blocked", _blocked_autocomplete);
@@ -1934,7 +1937,6 @@ _cmd_ac_complete_params(ProfWin* window, const char* const input, gboolean previ
{ "/prefs", prefs_ac },
{ "/disco", disco_ac },
{ "/room", room_ac },
{ "/autoping", autoping_ac },
{ "/mainwin", winpos_ac },
{ "/inputwin", winpos_ac },
};
@@ -4264,6 +4266,20 @@ _executable_autocomplete(ProfWin* window, const char* const input, gboolean prev
return result;
}
static char*
_autoping_autocomplete(ProfWin* window, const char* const input, gboolean previous)
{
char* result = NULL;
result = autocomplete_param_with_ac(input, "/autoping", autoping_ac, TRUE, previous);
if (result) {
return result;
}
result = autocomplete_param_with_func(input, "/autoping warning", prefs_autocomplete_boolean_choice, previous, NULL);
return result;
}
static char*
_lastactivity_autocomplete(ProfWin* window, const char* const input, gboolean previous)
{

View File

@@ -1964,12 +1964,14 @@ static const struct cmd_t command_defs[] = {
CMD_TAG_CONNECTION)
CMD_SYN(
"/autoping set <seconds>",
"/autoping timeout <seconds>")
"/autoping timeout <seconds>",
"/autoping warning on|off")
CMD_DESC(
"Set the interval between sending ping requests to the server to ensure the connection is kept alive.")
CMD_ARGS(
{ "set <seconds>", "Number of seconds between sending pings, a value of 0 disables autoping." },
{ "timeout <seconds>", "Seconds to wait for autoping responses, after which the connection is considered broken." })
{ "timeout <seconds>", "Seconds to wait for autoping responses, after which the connection is considered broken." },
{ "warning on|off", "Enable or disable autoping availability warning." })
},
{ CMD_PREAMBLE("/ping",

View File

@@ -6374,6 +6374,8 @@ cmd_autoping(ProfWin* window, const char* const command, gchar** args)
cons_bad_cmd_usage(command);
}
} else if (g_strcmp0(cmd, "warning") == 0) {
_cmd_set_boolean_preference(value, "Autoping availability warning", PREF_AUTOPING_WARNING);
} else {
cons_bad_cmd_usage(command);
}

View File

@@ -2290,6 +2290,7 @@ _get_group(preference_t pref)
case PREF_TRAY:
case PREF_TRAY_READ:
case PREF_ADV_NOTIFY_DISCO_OR_VERSION:
case PREF_AUTOPING_WARNING:
return PREF_GROUP_NOTIFICATIONS;
case PREF_DBLOG:
case PREF_CHLOG:
@@ -2646,6 +2647,8 @@ _get_key(preference_t pref)
return "enabled";
case PREF_SPELLCHECK_LANG:
return "lang";
case PREF_AUTOPING_WARNING:
return "autoping.warning";
default:
return NULL;
}
@@ -2699,6 +2702,7 @@ _get_default_boolean(preference_t pref)
case PREF_MOOD:
case PREF_STROPHE_SM_ENABLED:
case PREF_STROPHE_SM_RESEND:
case PREF_AUTOPING_WARNING:
return TRUE;
case PREF_SPELLCHECK_ENABLE:
case PREF_PGP_PUBKEY_AUTOIMPORT:

View File

@@ -169,6 +169,7 @@ typedef enum {
PREF_FORCE_ENCRYPTION_MODE,
PREF_SPELLCHECK_ENABLE,
PREF_SPELLCHECK_LANG,
PREF_AUTOPING_WARNING
} preference_t;
typedef struct prof_alias_t

View File

@@ -1,6 +0,0 @@
#ifndef PROF_GIT_BRANCH
#define PROF_GIT_BRANCH @PROF_GIT_BRANCH@
#endif
#ifndef PROF_GIT_REVISION
#define PROF_GIT_REVISION @PROF_GIT_REVISION@
#endif

View File

@@ -519,13 +519,15 @@ p_gpg_sign(const gchar* const str, const gchar* const fp)
}
gchar*
p_gpg_encrypt(const gchar* const barejid, const gchar* const message, const gchar* const fp)
p_gpg_encrypt(const gchar* const barejid, const gchar* const message, const gchar* const fp, gchar** err)
{
ProfPGPPubKeyId* pubkeyid = g_hash_table_lookup(pubkeys, barejid);
if (!pubkeyid) {
*err = g_strdup("No PGP key found for recipient");
return NULL;
}
if (!pubkeyid->id) {
*err = g_strdup("No key ID found for recipient");
return NULL;
}
@@ -538,6 +540,7 @@ p_gpg_encrypt(const gchar* const barejid, const gchar* const message, const gcha
gpgme_ctx_t ctx;
gpgme_error_t error = gpgme_new(&ctx);
if (error) {
*err = g_strdup_printf("Failed to create GPGME context: %s %s", gpgme_strsource(error), gpgme_strerror(error));
log_error("GPG: Failed to create gpgme context. %s %s", gpgme_strsource(error), gpgme_strerror(error));
return NULL;
}
@@ -545,6 +548,7 @@ p_gpg_encrypt(const gchar* const barejid, const gchar* const message, const gcha
gpgme_key_t receiver_key;
error = gpgme_get_key(ctx, pubkeyid->id, &receiver_key, 0);
if (error || receiver_key == NULL) {
*err = g_strdup_printf("Failed to get receiver key '%s': %s %s", pubkeyid->id, gpgme_strsource(error), gpgme_strerror(error));
log_error("GPG: Failed to get receiver_key. %s %s", gpgme_strsource(error), gpgme_strerror(error));
gpgme_release(ctx);
return NULL;
@@ -554,8 +558,10 @@ p_gpg_encrypt(const gchar* const barejid, const gchar* const message, const gcha
gpgme_key_t sender_key = NULL;
error = gpgme_get_key(ctx, fp, &sender_key, 0);
if (error || sender_key == NULL) {
*err = g_strdup_printf("Failed to get sender key '%s': %s %s", fp, gpgme_strsource(error), gpgme_strerror(error));
log_error("GPG: Failed to get sender_key. %s %s", gpgme_strsource(error), gpgme_strerror(error));
gpgme_release(ctx);
gpgme_key_unref(receiver_key);
return NULL;
}
keys[1] = sender_key;
@@ -574,7 +580,8 @@ p_gpg_encrypt(const gchar* const barejid, const gchar* const message, const gcha
gpgme_key_unref(sender_key);
if (error) {
log_error("GPG: Failed to encrypt message. %s %s", gpgme_strsource(error), gpgme_strerror(error));
*err = g_strdup_printf("Encryption failed: (%s) %s", gpgme_strsource(error), gpgme_strerror(error));
log_error("GPG: Failed to encrypt message. (%s) %s", gpgme_strsource(error), gpgme_strerror(error));
return NULL;
}

View File

@@ -42,7 +42,7 @@ gboolean p_gpg_available(const gchar* const barejid);
const gchar* p_gpg_libver(void);
gchar* p_gpg_sign(const gchar* const str, const gchar* const fp);
void p_gpg_verify(const gchar* const barejid, const gchar* const sign);
gchar* p_gpg_encrypt(const gchar* const barejid, const gchar* const message, const gchar* const fp);
gchar* p_gpg_encrypt(const gchar* const barejid, const gchar* const message, const gchar* const fp, gchar** err);
gchar* p_gpg_decrypt(const gchar* const cipher);
void p_gpg_free_decrypted(gchar* decrypted);
gchar* p_gpg_autocomplete_key(const gchar* const search_str, gboolean previous, void* context);

View File

@@ -1703,6 +1703,11 @@ cons_notify_setting(void)
else
cons_show("Subscription requests (/notify sub) : OFF");
if (prefs_get_boolean(PREF_AUTOPING_WARNING))
cons_show("Autoping warning (/autoping warning): ON");
else
cons_show("Autoping warning (/autoping warning): OFF");
gint remind_period = prefs_get_notify_remind();
if (remind_period == 0) {
cons_show("Reminder period (/notify remind) : OFF");
@@ -2003,21 +2008,26 @@ cons_autoping_setting(void)
{
gint autoping_interval = prefs_get_autoping();
if (autoping_interval == 0) {
cons_show("Autoping interval (/autoping) : OFF");
cons_show("Autoping interval (/autoping) : OFF");
} else if (autoping_interval == 1) {
cons_show("Autoping interval (/autoping) : 1 second");
cons_show("Autoping interval (/autoping) : 1 second");
} else {
cons_show("Autoping interval (/autoping) : %d seconds", autoping_interval);
cons_show("Autoping interval (/autoping) : %d seconds", autoping_interval);
}
gint autoping_timeout = prefs_get_autoping_timeout();
if (autoping_timeout == 0) {
cons_show("Autoping timeout (/autoping) : OFF");
cons_show("Autoping timeout (/autoping) : OFF");
} else if (autoping_timeout == 1) {
cons_show("Autoping timeout (/autoping) : 1 second");
cons_show("Autoping timeout (/autoping) : 1 second");
} else {
cons_show("Autoping timeout (/autoping) : %d seconds", autoping_timeout);
cons_show("Autoping timeout (/autoping) : %d seconds", autoping_timeout);
}
if (prefs_get_boolean(PREF_AUTOPING_WARNING))
cons_show("Autoping warning (/autoping warning): ON");
else
cons_show("Autoping warning (/autoping warning): OFF");
}
void

View File

@@ -43,7 +43,7 @@
#include "ai/ai_client.h"
static const int PAD_MIN_HEIGHT = 100;
static const int PAD_THRESHOLD = 12000; // above buffer cap (~9000): reclaims dead pad space, never fires while scrolling
static const int PAD_DEAD_SPACE_LIMIT = 2000; // reclaim once the pad holds this much dead space (cursor past live buffer); size-independent, so it never fires while scrolling
static gboolean _in_redraw = FALSE;
static void
@@ -56,9 +56,10 @@ _win_ensure_pad_capacity(ProfWin* window, WINDOW* win, int lines_needed)
int cur_height = getmaxy(win);
int cur_width = getmaxx(win);
if (lines_needed >= cur_height - 1) {
// If we are getting too large, trigger a redraw to clean up old lines
// but only if we are not already in a redraw process.
if (window && cur_height >= PAD_THRESHOLD && !_in_redraw) {
// redraw to reclaim dead pad space (cursor far past live buffer, e.g. a long
// append-only session); dead space never accrues while scrolling, only here
int dead = window ? getcury(win) - window->layout->buffer->lines : 0;
if (window && dead > PAD_DEAD_SPACE_LIMIT && !_in_redraw) {
win_redraw(window);
} else {
// resize to required lines + some buffer for next messages

View File

@@ -99,6 +99,7 @@ static void _disco_items_result_handler(xmpp_stanza_t* const stanza);
static void _last_activity_get_handler(xmpp_stanza_t* const stanza);
static void _version_get_handler(xmpp_stanza_t* const stanza);
static void _ping_get_handler(xmpp_stanza_t* const stanza);
static void _disco_autoping_warning_message(GHashTable* features);
static int _version_result_id_handler(xmpp_stanza_t* const stanza, void* const userdata);
static int _disco_info_response_id_handler(xmpp_stanza_t* const stanza, void* const userdata);
@@ -2429,6 +2430,12 @@ _disco_info_response_id_handler_onconnect(xmpp_stanza_t* const stanza, void* con
}
child = xmpp_stanza_get_next(child);
}
// Prevent repetitions by avoiding checks of disco items (from connection_set_disco_items)
const char* domain = connection_get_domain();
if (from && domain && g_ascii_strcasecmp(from, domain) == 0) {
_disco_autoping_warning_message(features);
}
}
connection_features_received(from);
@@ -2436,6 +2443,21 @@ _disco_info_response_id_handler_onconnect(xmpp_stanza_t* const stanza, void* con
return 0;
}
static void
_disco_autoping_warning_message(GHashTable* features)
{
gboolean server_supports_ping = g_hash_table_contains(features, "urn:xmpp:ping");
gboolean user_prefers_warning = prefs_get_boolean(PREF_AUTOPING_WARNING);
gboolean is_autoping_enabled = prefs_get_autoping() != 0;
if (!is_autoping_enabled && server_supports_ping && user_prefers_warning) {
cons_show("This server supports XEP-0199: XMPP Ping (better keepalive detection),\n"
"but autoping feature is currently disabled in settings.\n"
"Consider enabling it (e.g., `/autoping set 30`) for improved connection stability.\n"
"Use `/autoping warning off` to disable this message.");
}
}
static int
_http_upload_response_id_handler(xmpp_stanza_t* const stanza, void* const userdata)
{

View File

@@ -467,12 +467,15 @@ message_send_chat_pgp(const char* const barejid, const char* const msg, gboolean
auto_char char* jid = chat_session_get_jid(barejid);
char* id = connection_create_stanza_id();
ProfWin* current = wins_get_current();
xmpp_stanza_t* message = NULL;
#ifdef HAVE_LIBGPGME
ProfAccount* account = accounts_get_account(session_get_account_name());
if (account->pgp_keyid) {
auto_jid Jid* jidp = jid_create(jid);
auto_gchar gchar* encrypted = p_gpg_encrypt(jidp->barejid, msg, account->pgp_keyid);
auto_gchar gchar* err = NULL;
auto_gchar gchar* encrypted = p_gpg_encrypt(jidp->barejid, msg, account->pgp_keyid, &err);
if (encrypted) {
message = xmpp_message_new(ctx, STANZA_TYPE_CHAT, jid, id);
xmpp_message_set_body(message, "This message is encrypted (XEP-0027).");
@@ -486,18 +489,31 @@ message_send_chat_pgp(const char* const barejid, const char* const msg, gboolean
xmpp_stanza_add_child(message, x);
xmpp_stanza_release(x);
} else {
message = xmpp_message_new(ctx, STANZA_TYPE_CHAT, jid, id);
xmpp_message_set_body(message, msg);
if (current) {
win_println(current, THEME_ERROR, "-", "Unable to encrypt message for %s: %s.", jid, err);
}
log_error("Message not encrypted for %s: %s.", jid, err);
account_free(account);
free(id);
return NULL;
}
} else {
message = xmpp_message_new(ctx, STANZA_TYPE_CHAT, jid, id);
xmpp_message_set_body(message, msg);
if (current) {
win_println(current, THEME_ERROR, "-", "No PGP key configured for this account.");
}
log_error("No PGP key configured, message not sent.");
account_free(account);
free(id);
return NULL;
}
account_free(account);
#else
// ?
message = xmpp_message_new(ctx, STANZA_TYPE_CHAT, jid, id);
xmpp_message_set_body(message, msg);
if (current) {
win_println(current, THEME_ERROR, "-", "LibGPGME not available, message not sent.");
}
log_error("LibGPGME not available, message not sent.");
free(id);
return NULL;
#endif
if (state) {
@@ -546,7 +562,10 @@ message_send_chat_ox(const char* const barejid, const char* const msg, gboolean
xmpp_stanza_to_text(signcrypt, &c, &s);
char* signcrypt_e = p_ox_gpg_signcrypt(account->jid, barejid, c);
if (signcrypt_e == NULL) {
cons_show("Unable to send OX message. Check log file and profanity-ox-setup man page for details.");
ProfWin* current = wins_get_current();
if (current) {
win_println(current, THEME_ERROR, "-", "Unable to send OX message. Check log file and profanity-ox-setup man page for details.");
}
log_error("Message not signcrypted.");
return NULL;
}

View File

@@ -150,6 +150,14 @@ main(int argc, char* argv[])
PROF_FUNC_TEST(autoping_sends_ping_after_interval),
PROF_FUNC_TEST(autoping_server_not_supporting_ping),
/* Autoping availability warning - negative cases wait ~3s */
PROF_FUNC_TEST(autoping_warning_shown_when_disabled),
PROF_FUNC_TEST(autoping_warning_not_shown_when_server_unsupported),
PROF_FUNC_TEST(autoping_warning_not_shown_when_autoping_enabled),
PROF_FUNC_TEST(autoping_warning_not_shown_when_user_disabled),
PROF_FUNC_TEST(autoping_warning_not_shown_for_user_jid),
PROF_FUNC_TEST(autoping_warning_not_shown_for_subdomain_service),
/* Service Discovery - XEP-0030 */
PROF_FUNC_TEST(disco_info_shows_identity),
PROF_FUNC_TEST(disco_info_shows_features),

View File

@@ -112,3 +112,144 @@ autoping_server_not_supporting_ping(void** state)
// Should show error about ping not being supported
assert_true(prof_output_regex("Server ping not supported"));
}
/*
* Autoping availability warning.
*
* The warning fires from the on-connect disco handler, so the three inputs
* (server ping support, autoping interval, warning preference) must all be
* set before prof_connect().
*/
/* Stable substring of the warning text emitted by iq.c. */
#define AUTOPING_WARNING_MATCH "This server supports XEP-0199"
static void
_stub_server_disco(gboolean with_ping)
{
if (with_ping) {
stbbr_for_query("http://jabber.org/protocol/disco#info",
"<iq to='stabber@localhost/profanity' type='result' from='localhost'>"
"<query xmlns='http://jabber.org/protocol/disco#info'>"
"<identity category='server' type='im' name='Stabber'/>"
"<feature var='urn:xmpp:ping'/>"
"</query>"
"</iq>"
);
} else {
stbbr_for_query("http://jabber.org/protocol/disco#info",
"<iq to='stabber@localhost/profanity' type='result' from='localhost'>"
"<query xmlns='http://jabber.org/protocol/disco#info'>"
"<identity category='server' type='im' name='Stabber'/>"
"</query>"
"</iq>"
);
}
}
void
autoping_warning_shown_when_disabled(void** state)
{
// All conditions met: server supports ping, autoping off, warning on (default).
_stub_server_disco(TRUE);
prof_input("/autoping set 0");
assert_true(prof_output_exact("Autoping disabled."));
prof_connect();
assert_true(prof_output_regex(AUTOPING_WARNING_MATCH));
}
void
autoping_warning_not_shown_when_server_unsupported(void** state)
{
// Server does not advertise urn:xmpp:ping -> no warning even with autoping off.
_stub_server_disco(FALSE);
prof_input("/autoping set 0");
assert_true(prof_output_exact("Autoping disabled."));
prof_connect();
prof_timeout(NEGATIVE_ASSERT_TIMEOUT);
assert_false(prof_output_regex(AUTOPING_WARNING_MATCH));
}
void
autoping_warning_not_shown_when_autoping_enabled(void** state)
{
// Warning is suppressed while autoping is enabled.
_stub_server_disco(TRUE);
prof_input("/autoping set 30");
assert_true(prof_output_exact("Autoping interval set to 30 seconds."));
prof_connect();
prof_timeout(NEGATIVE_ASSERT_TIMEOUT);
assert_false(prof_output_regex(AUTOPING_WARNING_MATCH));
}
void
autoping_warning_not_shown_when_user_disabled(void** state)
{
// User opted out -> no warning.
_stub_server_disco(TRUE);
prof_input("/autoping set 0");
assert_true(prof_output_exact("Autoping disabled."));
prof_input("/autoping warning off");
assert_true(prof_output_exact("Autoping availability warning disabled."));
prof_connect();
prof_timeout(NEGATIVE_ASSERT_TIMEOUT);
assert_false(prof_output_regex(AUTOPING_WARNING_MATCH));
}
void
autoping_warning_not_shown_for_user_jid(void** state)
{
// disco#info from a user JID (with '@') should not trigger the warning,
// only server responses (no '@') should.
stbbr_for_query("http://jabber.org/protocol/disco#info",
"<iq to='stabber@localhost/profanity' type='result' from='buddy1@localhost'>"
"<query xmlns='http://jabber.org/protocol/disco#info'>"
"<identity category='person' type='chat' name='Buddy1'/>"
"<feature var='urn:xmpp:ping'/>"
"</query>"
"</iq>"
);
prof_input("/autoping set 0");
assert_true(prof_output_exact("Autoping disabled."));
prof_connect();
prof_timeout(NEGATIVE_ASSERT_TIMEOUT);
assert_false(prof_output_regex(AUTOPING_WARNING_MATCH));
}
void
autoping_warning_not_shown_for_subdomain_service(void** state)
{
// disco#info from a subdomain service should not trigger the warning, only the server's own domain should
stbbr_for_query("http://jabber.org/protocol/disco#info",
"<iq to='stabber@localhost/profanity' type='result' from='conf.localhost'>"
"<query xmlns='http://jabber.org/protocol/disco#info'>"
"<identity category='conference' type='text' name='Conference Service'/>"
"<feature var='urn:xmpp:ping'/>"
"</query>"
"</iq>"
);
prof_input("/autoping set 0");
assert_true(prof_output_exact("Autoping disabled."));
prof_connect();
prof_timeout(NEGATIVE_ASSERT_TIMEOUT);
assert_false(prof_output_regex(AUTOPING_WARNING_MATCH));
}

View File

@@ -4,3 +4,9 @@ void autoping_timeout_set(void** state);
void autoping_timeout_zero_disables(void** state);
void autoping_sends_ping_after_interval(void** state);
void autoping_server_not_supporting_ping(void** state);
void autoping_warning_shown_when_disabled(void** state);
void autoping_warning_not_shown_when_server_unsupported(void** state);
void autoping_warning_not_shown_when_autoping_enabled(void** state);
void autoping_warning_not_shown_when_user_disabled(void** state);
void autoping_warning_not_shown_for_user_jid(void** state);
void autoping_warning_not_shown_for_subdomain_service(void** state);