Compare commits

..

1 Commits

Author SHA1 Message Date
55c73ee21a security: validate <delay> 'from' per XEP-0203 §4
Some checks failed
CI Code / Check spelling (pull_request) Successful in 19s
CI Code / Check coding style (pull_request) Successful in 34s
CI Code / Code Coverage (pull_request) Failing after 9m56s
CI Code / Linux (arch) (pull_request) Failing after 13m41s
CI Code / Linux (debian) (pull_request) Failing after 15m0s
CI Code / Linux (ubuntu) (pull_request) Failing after 15m9s
Reject client-forged <delay> timestamps in the incoming message handlers. The displayed timestamp was taken from an unfiltered delay scan, so a remote peer could place a message anywhere in history by injecting <delay from='...' stamp='...'>.

- _handle_groupchat: oldest delay from room bare JID -> room domain -> our server domain, else now.
- _handle_muc_private_message / _handle_chat: delay from our server domain -> sender domain, else now.

Add stanza_get_oldest_delay_from(); stanza_get_oldest_delay() delegates with from=NULL. The MAM <forwarded> path stays unfiltered (trusted by construction). The original PR's XEP-0359 <stanza-id> 'by' edits are dropped: master already validates 'by'.
2026-06-19 10:53:11 +03:00
30 changed files with 109 additions and 475 deletions

View File

@@ -71,10 +71,6 @@ AC_ARG_ENABLE([omemo-qrcode],
[AS_HELP_STRING([--enable-omemo-qrcode], [enable ability to display omemo qr code])])
AC_ARG_ENABLE([coverage],
[AS_HELP_STRING([--enable-coverage], [enable code coverage analysis])])
AC_ARG_ENABLE([sanitizers],
[AS_HELP_STRING([--enable-sanitizers], [enable AddressSanitizer + UndefinedBehaviorSanitizer + unsigned-integer-overflow])])
AC_ARG_ENABLE([hardening],
[AS_HELP_STRING([--enable-hardening], [enable extra hardening warnings (e.g. -Wnull-dereference) that may produce false positives under -O2])])
m4_include([m4/ax_valgrind_check.m4])
AX_VALGRIND_DFLT([drd], [off])
@@ -399,7 +395,7 @@ AC_SUBST([FORKPTY_LIB])
## Default parameters
AM_CFLAGS="$AM_CFLAGS -Wall -Wextra -Wformat=2 -Wno-format-zero-length"
AM_CFLAGS="$AM_CFLAGS -Wno-deprecated-declarations -Wno-unused-parameter -Wno-missing-field-initializers -Wno-cast-function-type"
AM_CFLAGS="$AM_CFLAGS -Wno-deprecated-declarations -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-cast-function-type"
AM_CFLAGS="$AM_CFLAGS -Wpointer-arith"
AM_CFLAGS="$AM_CFLAGS -Wimplicit-function-declaration"
AM_CFLAGS="$AM_CFLAGS -Wundef"
@@ -410,8 +406,7 @@ AM_CFLAGS="$AM_CFLAGS -std=gnu99 -ggdb3"
# GCC-specific warnings (not supported by clang) — test each one
saved_CFLAGS="$CFLAGS"
for _flag in -Wlogical-op -Wduplicated-cond -Wduplicated-branches \
-Wstringop-overflow -Warray-bounds=2 -Walloc-zero \
-Wsign-compare; do
-Wstringop-overflow -Warray-bounds=2 -Walloc-zero; do
AC_MSG_CHECKING([whether $CC supports $_flag])
CFLAGS="$saved_CFLAGS $_flag -Werror"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM()],
@@ -438,46 +433,11 @@ AS_IF([test "x$enable_coverage" = xyes],
AM_LDFLAGS="$AM_LDFLAGS --coverage"
AC_MSG_NOTICE([Code coverage analysis enabled])])
AS_IF([test "x$enable_sanitizers" = xyes],
[SAN_FLAGS="-fsanitize=address,undefined"
saved_CFLAGS="$CFLAGS"
CFLAGS="$saved_CFLAGS -fsanitize=unsigned-integer-overflow -Werror"
AC_MSG_CHECKING([whether $CC supports -fsanitize=unsigned-integer-overflow])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM()],
[AC_MSG_RESULT([yes]); SAN_FLAGS="$SAN_FLAGS,unsigned-integer-overflow"],
[AC_MSG_RESULT([no])])
CFLAGS="$saved_CFLAGS"
AM_CFLAGS="$AM_CFLAGS $SAN_FLAGS -fno-sanitize-recover=all -fno-omit-frame-pointer"
AM_LDFLAGS="$AM_LDFLAGS -fsanitize=address,undefined"
AC_MSG_NOTICE([Sanitizers enabled: $SAN_FLAGS])])
AS_IF([test "x$enable_hardening" = xyes],
[saved_CFLAGS="$CFLAGS"
for _flag in -Wnull-dereference; do
AC_MSG_CHECKING([whether $CC supports $_flag])
CFLAGS="$saved_CFLAGS $_flag -Werror"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM()],
[AC_MSG_RESULT([yes]); AM_CFLAGS="$AM_CFLAGS $_flag"],
[AC_MSG_RESULT([no])])
done
CFLAGS="$saved_CFLAGS"
AC_MSG_NOTICE([Extra hardening warnings enabled])])
# Skip _FORTIFY_SOURCE=2 if env already set one (Pikaur/makepkg) or under coverage -O0 (FORTIFY needs -O>0).
AS_IF([test "x$enable_coverage" != xyes],
[AS_CASE([" ${CPPFLAGS} ${CFLAGS} "],
[*_FORTIFY_SOURCE=*], [AC_MSG_NOTICE([_FORTIFY_SOURCE preset from environment, leaving as-is])],
[AM_CFLAGS="$AM_CFLAGS -D_FORTIFY_SOURCE=2"])])
AS_IF([test "x$PACKAGE_STATUS" = xdevelopment],
[AM_CFLAGS="$AM_CFLAGS -Wunused -Werror"])
AS_IF([test "x$PLATFORM" = xosx],
[AM_CFLAGS="$AM_CFLAGS -Qunused-arguments"])
# Treat glib/gio headers as system to keep their macros out of our -W* set.
glib_CFLAGS=$(echo "$glib_CFLAGS" | sed 's/-I/-isystem /g')
gio_CFLAGS=$(echo "$gio_CFLAGS" | sed 's/-I/-isystem /g')
AM_CFLAGS="$AM_CFLAGS $PTHREAD_CFLAGS $glib_CFLAGS $gio_CFLAGS $curl_CFLAGS ${SQLITE_CFLAGS}"
AM_CFLAGS="$AM_CFLAGS $libnotify_CFLAGS ${GTK_CFLAGS} $python_CFLAGS"
AM_CFLAGS="$AM_CFLAGS -DTHEMES_PATH=\"\\\"$THEMES_PATH\\\"\" -DICONS_PATH=\"\\\"$ICONS_PATH\\\"\" -DGLOBAL_PYTHON_PLUGINS_PATH=\"\\\"$GLOBAL_PYTHON_PLUGINS_PATH\\\"\" -DGLOBAL_C_PLUGINS_PATH=\"\\\"$GLOBAL_C_PLUGINS_PATH\\\"\""

View File

@@ -47,7 +47,6 @@ 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);
@@ -1057,7 +1056,6 @@ 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");
@@ -1409,7 +1407,6 @@ 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);
@@ -1937,6 +1934,7 @@ _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 },
};
@@ -4266,20 +4264,6 @@ _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,14 +1964,12 @@ static const struct cmd_t command_defs[] = {
CMD_TAG_CONNECTION)
CMD_SYN(
"/autoping set <seconds>",
"/autoping timeout <seconds>",
"/autoping warning on|off")
"/autoping timeout <seconds>")
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." },
{ "warning on|off", "Enable or disable autoping availability warning." })
{ "timeout <seconds>", "Seconds to wait for autoping responses, after which the connection is considered broken." })
},
{ CMD_PREAMBLE("/ping",

View File

@@ -6374,8 +6374,6 @@ 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);
}
@@ -10065,14 +10063,8 @@ cmd_vcard_remove(ProfWin* window, const char* const command, gchar** args)
}
if (args[1]) {
int index;
auto_gchar gchar* err_msg = NULL;
if (!strtoi_range(args[1], &index, 0, INT_MAX, &err_msg)) {
cons_show_error("%s", err_msg);
return TRUE;
}
vcard_user_remove_element((unsigned int)index);
cons_show("Removed element at index %d", index);
vcard_user_remove_element(atoi(args[1]));
cons_show("Removed element at index %d", atoi(args[1]));
vcardwin_update();
} else {
cons_bad_cmd_usage(command);

View File

@@ -294,7 +294,7 @@ str_replace(const char* string, const char* substr,
wr = newstr;
while ((tok = strstr(head, substr))) {
size_t l = g_diff_to_gsize(tok, head);
size_t l = tok - head;
memcpy(wr, head, l);
wr += l;
memcpy(wr, replacement, len_replacement);
@@ -309,16 +309,6 @@ str_replace(const char* string, const char* substr,
return newstr;
}
gsize
g_diff_to_gsize(const void* end, const void* start)
{
if (end < start) {
log_error("g_diff_to_gsize: end < start (%p < %p)", end, start);
return 0;
}
return (gsize)((const char*)end - (const char*)start);
}
gboolean
strtoi_range(const char* str, int* saveptr, int min, int max, gchar** err_msg)
{
@@ -369,9 +359,9 @@ string_matches_one_of(const char* what, const char* is, gboolean is_can_be_null,
char errmsg[256] = { 0 };
size_t sz = 0;
int s = snprintf(errmsg, sizeof(errmsg) - sz, "%s must be one of:", what);
if (s < 0 || (size_t)s + sz >= sizeof(errmsg))
if (s < 0 || s + sz >= sizeof(errmsg))
return ret;
sz += (size_t)s;
sz += s;
cur = first;
va_start(ap, first);
@@ -385,12 +375,12 @@ string_matches_one_of(const char* what, const char* is, gboolean is_can_be_null,
errmsg[sz] = '\0';
s = snprintf(errmsg + sz, sizeof(errmsg) - sz, " or '%s'.", cur);
}
if (s < 0 || (size_t)s + sz >= sizeof(errmsg)) {
if (s < 0 || s + sz >= sizeof(errmsg)) {
log_debug("Error message too long or some other error occurred (%d).", s);
s = -1;
break;
}
sz += (size_t)s;
sz += s;
cur = next;
}
va_end(ap);
@@ -441,7 +431,7 @@ str_xml_sanitize(const char* const str)
return NULL;
}
GString* sanitized = g_string_new_len(NULL, (gssize)strlen(str));
GString* sanitized = g_string_new_len(NULL, strlen(str));
const char* curr = str;
while (*curr != '\0') {
@@ -700,7 +690,7 @@ get_file_paths_recursive(const char* path, GSList** contents)
}
gchar*
get_random_string(size_t length)
get_random_string(int length)
{
GRand* prng;
gchar* rand;
@@ -711,7 +701,7 @@ get_random_string(size_t length)
prng = g_rand_new();
for (size_t i = 0; i < length; i++) {
for (int i = 0; i < length; i++) {
rand[i] = alphabet[g_rand_int_range(prng, 0, endrange)];
}
g_rand_free(prng);

View File

@@ -158,7 +158,6 @@ 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(const char* str, int* saveptr, int min, int max, char** err_msg);
gsize g_diff_to_gsize(const void* end, const void* start);
int utf8_display_len(const char* const str);
gchar* str_xml_sanitize(const char* const str);
@@ -179,7 +178,7 @@ int is_regular_file(const char* path);
int is_dir(const char* path);
void get_file_paths_recursive(const char* directory, GSList** contents);
gchar* get_random_string(size_t length);
gchar* get_random_string(int length);
gboolean call_external(gchar** argv);
gchar** format_call_external_argv(const char* template_fmt, const char* url, const char* filename);

View File

@@ -43,8 +43,8 @@ _sanitize_account_name(const char* const name)
gchar* sanitized = g_strdup(name);
gchar* p = sanitized;
while (*p) {
// GKeyFile group header forbids these characters.
if (*p == '[' || *p == ']' || *p == '\n' || *p == '\r') {
// GKeyFile special characters: [ ] = # \n \r
if (*p == '[' || *p == ']' || *p == '=' || *p == '#' || *p == '\n' || *p == '\r') {
*p = '_';
}
p++;

View File

@@ -2290,7 +2290,6 @@ _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:
@@ -2647,8 +2646,6 @@ _get_key(preference_t pref)
return "enabled";
case PREF_SPELLCHECK_LANG:
return "lang";
case PREF_AUTOPING_WARNING:
return "autoping.warning";
default:
return NULL;
}
@@ -2702,7 +2699,6 @@ _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,7 +169,6 @@ typedef enum {
PREF_FORCE_ENCRYPTION_MODE,
PREF_SPELLCHECK_ENABLE,
PREF_SPELLCHECK_LANG,
PREF_AUTOPING_WARNING
} preference_t;
typedef struct prof_alias_t

View File

@@ -282,7 +282,7 @@ _export_one_contact(const char* contact)
gchar* key = _make_dedup_key(pl->stanza_id, pl->timestamp_str, pl->from_jid, pl->message);
g_hash_table_add(seen_keys, key);
}
int existing_count = (int)g_slist_length(existing);
int existing_count = g_slist_length(existing);
// 2. Query SQLite for this contact — get ALL messages via direct SQL
GSList* sqlite_lines = db_sqlite_get_all_chat(contact);
@@ -581,7 +581,7 @@ log_database_import_from_flatfile(const gchar* const contact_jid)
// Wrap in a transaction for atomicity and performance
int contact_imported = 0;
int contact_skipped = 0;
int total_lines = (int)g_slist_length(ff_lines);
int total_lines = g_slist_length(ff_lines);
db_sqlite_begin_transaction();
gboolean import_ok = TRUE;

View File

@@ -88,7 +88,7 @@ _ff_cache_line_ids(ff_contact_state_t* state, const char* line)
return;
// Split metadata on unescaped '|'
auto_gchar gchar* meta_str = g_strndup(bracket + 1, g_diff_to_gsize(close, bracket + 1));
auto_gchar gchar* meta_str = g_strndup(bracket + 1, close - bracket - 1);
char** parts = ff_split_meta(meta_str);
char* stanza_id = NULL;
@@ -111,9 +111,9 @@ _ff_cache_line_ids(ff_contact_state_t* state, const char* line)
const char* sender_start = close + 2;
const char* colonspace = ff_find_unescaped_colonspace(sender_start);
if (colonspace) {
const char* slash = memchr(sender_start, '/', g_diff_to_gsize(colonspace, sender_start));
const char* slash = memchr(sender_start, '/', colonspace - sender_start);
const char* jid_end = slash ? slash : colonspace;
from_jid = g_strndup(sender_start, g_diff_to_gsize(jid_end, sender_start));
from_jid = g_strndup(sender_start, jid_end - sender_start);
}
}
@@ -140,7 +140,7 @@ _ff_maybe_index_line(ff_contact_state_t* state, const char* buf, off_t pos)
if (!space)
return;
auto_gchar gchar* ts_str = g_strndup(buf, g_diff_to_gsize(space, buf));
auto_gchar gchar* ts_str = g_strndup(buf, space - buf);
GDateTime* dt = g_date_time_new_from_iso8601(ts_str, NULL);
if (!dt) {
log_warning("flatfile: unparsable timestamp in %s at offset %ld: %s",
@@ -771,7 +771,7 @@ _flatfile_get_previous_chat(const gchar* const contact_barejid, const gchar* sta
// For "last N messages": back up from read_to using index
if (!from_start) {
size_t margin_entries = (MESSAGES_TO_RETRIEVE * 3) / FF_INDEX_STEP + 2;
int margin_entries = (MESSAGES_TO_RETRIEVE * 3) / FF_INDEX_STEP + 2;
// Find index entry closest to (but before) read_to
size_t entry_idx = 0;
@@ -781,7 +781,7 @@ _flatfile_get_previous_chat(const gchar* const contact_barejid, const gchar* sta
entry_idx = i;
}
size_t start_entry = entry_idx > margin_entries
size_t start_entry = entry_idx > (size_t)margin_entries
? entry_idx - margin_entries
: 0;
off_t backed = state->entries[start_entry].byte_offset;

View File

@@ -553,7 +553,7 @@ ff_split_meta(const char* meta)
continue;
}
if (*p == '|' || *p == '\0') {
g_ptr_array_add(arr, g_strndup(start, g_diff_to_gsize(p, start)));
g_ptr_array_add(arr, g_strndup(start, p - start));
if (*p == '\0')
break;
start = p + 1;
@@ -710,7 +710,7 @@ ff_parse_line(const char* line)
if (bracket_start && first_space && first_space < bracket_start) {
// Standard format with metadata: {timestamp} [{meta}] {sender}: {msg}
result->timestamp_str = g_strndup(work, g_diff_to_gsize(first_space, work));
result->timestamp_str = g_strndup(work, first_space - work);
// Parse metadata section [...]
const char* bracket_end = ff_find_unescaped_char(bracket_start + 1, ']');
@@ -721,7 +721,7 @@ ff_parse_line(const char* line)
return NULL;
}
auto_gchar gchar* meta_content = g_strndup(bracket_start + 1, g_diff_to_gsize(bracket_end, bracket_start + 1));
auto_gchar gchar* meta_content = g_strndup(bracket_start + 1, bracket_end - bracket_start - 1);
char** parts = ff_split_meta(meta_content);
if (parts) {
_ff_parse_meta_parts(parts, result);
@@ -736,12 +736,12 @@ ff_parse_line(const char* line)
// Find first *unescaped* ': ' which separates sender from message.
const char* colon = ff_find_unescaped_colonspace(after_meta);
if (colon) {
auto_gchar gchar* raw_sender = g_strndup(after_meta, g_diff_to_gsize(colon, after_meta));
auto_gchar gchar* raw_sender = g_strndup(after_meta, colon - after_meta);
// Split sender into jid/resource, then unescape resource
char* slash = strchr(raw_sender, '/');
if (slash) {
result->from_jid = g_strndup(raw_sender, g_diff_to_gsize(slash, raw_sender));
result->from_jid = g_strndup(raw_sender, slash - raw_sender);
result->from_resource = ff_unescape_sender_resource(slash + 1);
} else {
result->from_jid = g_strdup(raw_sender);
@@ -755,7 +755,7 @@ ff_parse_line(const char* line)
}
} else if (first_space) {
// Legacy/simple format without metadata: {timestamp} - {sender}: {msg}
result->timestamp_str = g_strndup(work, g_diff_to_gsize(first_space, work));
result->timestamp_str = g_strndup(work, first_space - work);
result->type = g_strdup("chat");
result->enc = g_strdup("none");
@@ -767,10 +767,10 @@ ff_parse_line(const char* line)
char* colon = strstr(rest, ": ");
if (colon) {
char* sender = g_strndup(rest, g_diff_to_gsize(colon, rest));
char* sender = g_strndup(rest, colon - rest);
char* slash = strchr(sender, '/');
if (slash) {
result->from_jid = g_strndup(sender, g_diff_to_gsize(slash, sender));
result->from_jid = g_strndup(sender, slash - sender);
result->from_resource = g_strdup(slash + 1);
} else {
result->from_jid = g_strdup(sender);

View File

@@ -260,12 +260,7 @@ omemo_on_connect(ProfAccount* account)
return;
}
if (!_omemo_finalize_identity_load(account)) {
omemo_ctx.loaded = FALSE;
log_error("[OMEMO] failed to load OMEMO state from disk for %s", account->jid);
cons_show_error("OMEMO: could not load encryption state from disk; OMEMO will be unavailable this session.");
return;
}
_omemo_finalize_identity_load(account);
wins_omemo_trust_changed(NULL);
}
@@ -1283,12 +1278,8 @@ omemo_is_trusted_identity(const char* const jid, const char* const fingerprint)
.device_id = GPOINTER_TO_UINT(device_id),
};
size_t fingerprint_len = 0;
size_t fingerprint_len;
unsigned char* fingerprint_raw = _omemo_fingerprint_decode(fingerprint, &fingerprint_len);
if (!fingerprint_raw) {
log_error("[OMEMO] omemo_is_trusted_identity: failed to decode fingerprint for %s", jid);
return FALSE;
}
unsigned char djb_type[] = { '\x05' };
signal_buffer* buffer = signal_buffer_create(djb_type, 1);
buffer = signal_buffer_append(buffer, fingerprint_raw, fingerprint_len);
@@ -1472,10 +1463,6 @@ _omemo_fingerprint(ec_public_key* identity, gboolean formatted)
static unsigned char*
_omemo_fingerprint_decode(const char* const fingerprint, size_t* len)
{
if (!fingerprint) {
*len = 0;
return NULL;
}
unsigned char* output = malloc(strlen(fingerprint) / 2 + 1);
if (!output) {
*len = 0;
@@ -1542,11 +1529,6 @@ omemo_trust(const char* const jid, const char* const fingerprint_formatted)
};
unsigned char* fingerprint_raw = _omemo_fingerprint_decode(fingerprint_formatted, &len);
if (!fingerprint_raw) {
log_error("[OMEMO] omemo_trust: failed to decode fingerprint for %s", jid);
cons_show_error("Failed to trust device: could not decode fingerprint.");
return;
}
unsigned char djb_type[] = { '\x05' };
signal_buffer* buffer = signal_buffer_create(djb_type, 1);
buffer = signal_buffer_append(buffer, fingerprint_raw, len);
@@ -1564,10 +1546,8 @@ omemo_untrust(const char* const jid, const char* const fingerprint_formatted)
{
size_t len;
unsigned char* identity = _omemo_fingerprint_decode(fingerprint_formatted, &len);
if (!identity) {
log_error("[OMEMO] omemo_untrust: failed to decode fingerprint for %s", jid);
if (!identity)
return;
}
GHashTableIter iter;
gpointer key, value;

View File

@@ -278,8 +278,7 @@ otr_on_message_recv(const char* const barejid, const char* const resource, const
if (strstr(message, OTRL_MESSAGE_TAG_V2) && strstr(message, OTRL_MESSAGE_TAG_V1)) {
tag_length = 32;
}
// Move the rest of the message (with NUL) over the tag.
memmove(whitespace_base, whitespace_base + tag_length, strlen(whitespace_base + tag_length) + 1);
memmove(whitespace_base, whitespace_base + tag_length, tag_length);
char* otr_query_message = otr_start_query();
cons_show("OTR Whitespace pattern detected. Attempting to start OTR session…");
free(message_send_chat_otr(barejid, otr_query_message, FALSE, NULL));

View File

@@ -138,7 +138,7 @@ prof_run(gchar* log_level, gchar* account_name, gchar* config_file, gchar* log_f
* so we can be sure there's runtime left after executing
* the last command.
*/
min_runtime += (unsigned int)waittime;
min_runtime += waittime;
} else {
log_error("%s", err_msg);
g_free(err_msg);

View File

@@ -1703,11 +1703,6 @@ 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");
@@ -2008,26 +2003,21 @@ 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

@@ -67,20 +67,6 @@ _win_ensure_pad_capacity(ProfWin* window, WINDOW* win, int lines_needed)
}
}
}
// upper-bound estimate of rows a message body occupies (hard newlines + soft-wrap), to reserve pad space before printing so a tall message isn't clipped (a clipped height desyncs the scroll offset)
static int
_win_estimated_lines(WINDOW* win, const char* const message, int indent, int pad_indent)
{
int usable = MAX(1, getmaxx(win) - (indent + pad_indent));
int newlines = 0;
for (const char* p = message; *p; p++) {
if (*p == '\n') {
newlines++;
}
}
return newlines + utf8_display_len(message) / usable + 2;
}
static const char* LOADING_MESSAGE = "Loading older messages…";
static const char* CONS_WIN_TITLE = "CProof. Type /help for help information.";
static const char* XML_WIN_TITLE = "XML Console";
@@ -2015,7 +2001,7 @@ _win_print_internal(ProfWin* window, const char* show_char, int pad_indent, GDat
}
}
_win_ensure_pad_capacity(window, window->layout->win, getcury(window->layout->win) + _win_estimated_lines(window->layout->win, message + offset, indent, pad_indent));
_win_ensure_pad_capacity(window, window->layout->win, getcury(window->layout->win));
if (prefs_get_boolean(PREF_WRAP)) {
_win_print_wrapped(window->layout->win, message + offset, indent, pad_indent);

View File

@@ -99,7 +99,6 @@ 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);
@@ -2430,11 +2429,6 @@ _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)
if (from && g_ascii_strcasecmp(from, connection_get_domain()) == 0) {
_disco_autoping_warning_message(features);
}
}
connection_features_received(from);
@@ -2442,21 +2436,6 @@ _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

@@ -117,7 +117,7 @@ jid_is_valid(const gchar* const str)
// Localpart validation
if (at) {
size_t local_len = g_diff_to_gsize(at, str);
size_t local_len = at - str;
if (local_len == 0 || local_len > JID_MAX_PART_LEN) {
return FALSE;
}
@@ -135,7 +135,7 @@ jid_is_valid(const gchar* const str)
// Resourcepart validation if present
if (slash) {
domain_len = g_diff_to_gsize(slash, domain_start);
domain_len = slash - domain_start;
size_t resource_len = strlen(slash + 1);
if (resource_len > JID_MAX_PART_LEN) {
return FALSE;

View File

@@ -65,7 +65,6 @@ static void _handle_pubsub(xmpp_stanza_t* const stanza, xmpp_stanza_t* const eve
static gboolean _handle_form(xmpp_stanza_t* const stanza);
static gboolean _handle_jingle_message(xmpp_stanza_t* const stanza);
static gboolean _should_ignore_based_on_silence(xmpp_stanza_t* const stanza);
static gboolean _stanza_id_by_trusted(const char* by);
#ifdef HAVE_OMEMO
static void _receive_omemo(xmpp_stanza_t* const stanza, ProfMessage* message);
#endif
@@ -1089,7 +1088,7 @@ _handle_groupchat(xmpp_stanza_t* const stanza)
xmpp_stanza_t* stanzaidst = xmpp_stanza_get_child_by_name_and_ns(stanza, STANZA_NAME_STANZA_ID, STANZA_NS_STABLE_ID);
if (stanzaidst) {
const char* by = xmpp_stanza_get_attribute(stanzaidst, "by");
if (by && g_strcmp0(by, from_jid->barejid) == 0 && _stanza_id_by_trusted(by)) {
if (by && (g_strcmp0(by, from_jid->barejid) == 0)) {
stanzaid = (char*)xmpp_stanza_get_attribute(stanzaidst, STANZA_ATTR_ID);
if (stanzaid) {
message->stanzaid = strdup(stanzaid);
@@ -1133,12 +1132,17 @@ _handle_groupchat(xmpp_stanza_t* const stanza)
message->timestamp = NULL;
}
// we want to display the oldest delay
message->timestamp = stanza_get_oldest_delay(stanza);
// now this has nothing to do with MUC history
// it's just setting the time to the received time so upon displaying we can use this time
// for example in win_println_incoming_muc_msg()
// XEP-0203 §4: trust <delay> only from the room or our server, not a client-forged stamp
message->timestamp = stanza_get_oldest_delay_from(stanza, from_jid->barejid);
if (!message->timestamp && from_jid->domainpart) {
message->timestamp = stanza_get_oldest_delay_from(stanza, from_jid->domainpart);
}
if (!message->timestamp) {
const char* my_domain = connection_get_domain();
if (my_domain) {
message->timestamp = stanza_get_oldest_delay_from(stanza, my_domain);
}
}
if (!message->timestamp) {
message->timestamp = g_date_time_new_now_local();
}
@@ -1269,7 +1273,15 @@ _handle_muc_private_message(xmpp_stanza_t* const stanza)
_receive_omemo(stanza, message);
#endif
message->timestamp = stanza_get_delay(stanza);
// XEP-0203 §4: trust <delay> only from a server domain, not a client-forged stamp
const char* my_domain = connection_get_domain();
message->timestamp = stanza_get_delay_from(stanza, my_domain);
if (!message->timestamp && message->from_jid->domainpart) {
message->timestamp = stanza_get_delay_from(stanza, message->from_jid->domainpart);
}
if (!message->timestamp) {
message->timestamp = g_date_time_new_now_local();
}
message->body = xmpp_message_get_body(stanza);
if (!message->plain && !message->body) {
@@ -1398,7 +1410,7 @@ _handle_chat(xmpp_stanza_t* const stanza, gboolean is_mam, gboolean is_carbon, c
xmpp_stanza_t* stanzaidst = xmpp_stanza_get_child_by_name_and_ns(stanza, STANZA_NAME_STANZA_ID, STANZA_NS_STABLE_ID);
if (stanzaidst) {
const char* by = xmpp_stanza_get_attribute(stanzaidst, "by");
if (by && equals_our_barejid(by) && _stanza_id_by_trusted(by)) {
if (by && equals_our_barejid(by)) {
stanzaid = (char*)xmpp_stanza_get_attribute(stanzaidst, STANZA_ATTR_ID);
if (stanzaid) {
message->stanzaid = strdup(stanzaid);
@@ -1420,8 +1432,12 @@ _handle_chat(xmpp_stanza_t* const stanza, gboolean is_mam, gboolean is_carbon, c
// timestamp provided outside like in a <forwarded> by MAM
message->timestamp = timestamp;
} else {
// timestamp in the message stanza or use time of receival (now)
message->timestamp = stanza_get_delay(stanza);
// XEP-0203 §4: trust <delay> only from a server domain, not a client-forged stamp
const char* my_domain = connection_get_domain();
message->timestamp = stanza_get_delay_from(stanza, my_domain);
if (!message->timestamp && jid->domainpart) {
message->timestamp = stanza_get_delay_from(stanza, jid->domainpart);
}
if (!message->timestamp) {
message->timestamp = g_date_time_new_now_local();
}
@@ -1736,22 +1752,3 @@ _should_ignore_based_on_silence(xmpp_stanza_t* const stanza)
}
return FALSE;
}
// XEP-0359 §6 disco-gate; falls back to domain since that's what disco is run against.
static gboolean
_stanza_id_by_trusted(const char* by)
{
if (!by) {
return FALSE;
}
GHashTable* features = connection_get_features(by);
if (features && g_hash_table_contains(features, STANZA_NS_STABLE_ID)) {
return TRUE;
}
const char* at = strchr(by, '@');
if (!at) {
return FALSE;
}
features = connection_get_features(at + 1);
return features && g_hash_table_contains(features, STANZA_NS_STABLE_ID);
}

View File

@@ -707,7 +707,7 @@ muc_autocomplete(ProfWin* window, const char* const input, gboolean previous)
} else {
search_str = last_space + 1;
if (!chat_room->autocomplete_prefix) {
chat_room->autocomplete_prefix = g_strndup(input, g_diff_to_gsize(search_str, input));
chat_room->autocomplete_prefix = g_strndup(input, search_str - input);
}
}

View File

@@ -621,20 +621,15 @@ _available_handler(xmpp_stanza_t* const stanza)
}
if (g_strcmp0(xmpp_presence->jid->barejid, my_jid->barejid) == 0) {
// Copy what we read before connection_add_available_resource() takes ownership.
Resource* resource_for_roster = resource_copy(resource);
auto_gchar gchar* resource_name = g_strdup(resource->name);
auto_gchar gchar* resource_status = resource->status ? g_strdup(resource->status) : NULL;
int resource_priority = resource->priority;
resource_presence_t resource_presence = resource->presence;
connection_add_available_resource(resource);
Resource* resource_for_roster = resource_copy(resource);
sv_ev_contact_online(xmpp_presence->jid->barejid, resource_for_roster, xmpp_presence->last_activity, pgpsig);
const char* account_name = session_get_account_name();
int max_sessions = accounts_get_max_sessions(account_name);
if (max_sessions > 0) {
auto_gchar gchar* cur_resource = accounts_get_resource(account_name);
int res_count = connection_count_available_resources();
if (res_count > max_sessions && g_strcmp0(cur_resource, resource_name)) {
if (res_count > max_sessions && g_strcmp0(cur_resource, resource->name)) {
ProfWin* console = wins_get_console();
ProfWin* current_window = wins_get_current();
auto_gchar gchar* message = g_strdup_printf("Max sessions alarm! (%d/%d devices in use)", res_count, max_sessions);
@@ -644,14 +639,14 @@ _available_handler(xmpp_stanza_t* const stanza)
}
notify(message, 10000, "Security alert");
const char* resource_presence_str = string_from_resource_presence(resource_presence);
win_print(console, THEME_DEFAULT, "|", "New device info: \n %s (%d), %s", resource_name, resource_priority, resource_presence_str);
const char* resource_presence = string_from_resource_presence(resource->presence);
win_print(console, THEME_DEFAULT, "|", "New device info: \n %s (%d), %s", resource->name, resource->priority, resource_presence);
if (resource_status) {
win_append(console, THEME_DEFAULT, ", \"%s\"", resource_status);
if (resource->status) {
win_append(console, THEME_DEFAULT, ", \"%s\"", resource->status);
}
win_appendln(console, THEME_DEFAULT, "");
auto_jid Jid* jidp = jid_create_from_bare_and_resource(my_jid->barejid, resource_name);
auto_jid Jid* jidp = jid_create_from_bare_and_resource(my_jid->barejid, resource->name);
EntityCapabilities* caps = caps_lookup(jidp->fulljid);
if (caps) {

View File

@@ -1163,7 +1163,7 @@ _stanza_get_delay_timestamp_xep0091(xmpp_stanza_t* const x_stanza)
}
GDateTime*
stanza_get_delay_from(xmpp_stanza_t* const stanza, gchar* from)
stanza_get_delay_from(xmpp_stanza_t* const stanza, const char* const from)
{
xmpp_stanza_t* delay = NULL;
@@ -1195,6 +1195,12 @@ stanza_get_delay_from(xmpp_stanza_t* const stanza, gchar* from)
GDateTime*
stanza_get_oldest_delay(xmpp_stanza_t* const stanza)
{
return stanza_get_oldest_delay_from(stanza, NULL);
}
GDateTime*
stanza_get_oldest_delay_from(xmpp_stanza_t* const stanza, const char* const from)
{
xmpp_stanza_t* child;
const char* child_name;
@@ -1205,27 +1211,37 @@ stanza_get_oldest_delay(xmpp_stanza_t* const stanza)
child_name = xmpp_stanza_get_name(child);
if (child_name && g_strcmp0(child_name, STANZA_NAME_DELAY) == 0) {
if (from) {
const char* child_from = xmpp_stanza_get_attribute(child, STANZA_ATTR_FROM);
if (!child_from || g_strcmp0(child_from, from) != 0)
continue;
}
GDateTime* tmp = _stanza_get_delay_timestamp_xep0203(child);
if (oldest == NULL) {
oldest = tmp;
} else if (g_date_time_compare(oldest, tmp) == 1) {
} else if (tmp && g_date_time_compare(oldest, tmp) == 1) {
g_date_time_unref(oldest);
oldest = tmp;
} else {
} else if (tmp) {
g_date_time_unref(tmp);
}
}
if (child_name && g_strcmp0(child_name, STANZA_NAME_X) == 0) {
if (from) {
const char* child_from = xmpp_stanza_get_attribute(child, STANZA_ATTR_FROM);
if (!child_from || g_strcmp0(child_from, from) != 0)
continue;
}
GDateTime* tmp = _stanza_get_delay_timestamp_xep0091(child);
if (oldest == NULL) {
oldest = tmp;
} else if (g_date_time_compare(oldest, tmp) == 1) {
} else if (tmp && g_date_time_compare(oldest, tmp) == 1) {
g_date_time_unref(oldest);
oldest = tmp;
} else {
} else if (tmp) {
g_date_time_unref(tmp);
}
}

View File

@@ -305,8 +305,9 @@ xmpp_stanza_t* stanza_create_mediated_invite(xmpp_ctx_t* ctx, const char* const
gboolean stanza_contains_chat_state(xmpp_stanza_t* stanza);
GDateTime* stanza_get_delay(xmpp_stanza_t* const stanza);
GDateTime* stanza_get_delay_from(xmpp_stanza_t* const stanza, gchar* from);
GDateTime* stanza_get_delay_from(xmpp_stanza_t* const stanza, const char* const from);
GDateTime* stanza_get_oldest_delay(xmpp_stanza_t* const stanza);
GDateTime* stanza_get_oldest_delay_from(xmpp_stanza_t* const stanza, const char* const from);
gboolean stanza_is_muc_presence(xmpp_stanza_t* const stanza);
gboolean stanza_is_muc_self_presence(xmpp_stanza_t* const stanza,

View File

@@ -150,14 +150,6 @@ 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),
@@ -261,10 +253,6 @@ main(int argc, char* argv[])
PROF_FUNC_TEST(message_receive_console),
PROF_FUNC_TEST(message_receive_chatwin),
/* XEP-0359 disco gate for stanza-id trust */
PROF_FUNC_TEST(stanza_id_dedup_fires_when_server_announces_sid0),
PROF_FUNC_TEST(stanza_id_not_trusted_when_server_does_not_announce_sid0),
#ifdef HAVE_SQLITE
/* MUC (groupchat) database — export/import of type="muc" messages */
PROF_FUNC_TEST(muc_export_sqlite_to_flatfile),

View File

@@ -119,9 +119,10 @@ _create_dir(const char* name)
gboolean
_mkdir_recursive(const char* dir)
{
int i;
gboolean result = TRUE;
for (size_t i = 1; i <= strlen(dir); i++) {
for (i = 1; i <= strlen(dir); i++) {
if (dir[i] == '/' || dir[i] == '\0') {
gchar* next_dir = g_strndup(dir, i);
result = _create_dir(next_dir);

View File

@@ -112,144 +112,3 @@ 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,9 +4,3 @@ 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);

View File

@@ -56,70 +56,3 @@ message_receive_chatwin(void **state)
assert_true(prof_output_regex("someuser@chatserv.org/laptop: .+How are you?"));
}
// XEP-0359 disco gate: server announces urn:xmpp:sid:0 -> stanza-id trusted -> replay flagged as duplicate.
void
stanza_id_dedup_fires_when_server_announces_sid0(void **state)
{
stbbr_for_query("http://jabber.org/protocol/disco#info",
"<iq type='result' to='stabber@localhost/profanity' from='localhost'>"
"<query xmlns='http://jabber.org/protocol/disco#info'>"
"<identity category='server' type='im' name='TestServer'/>"
"<feature var='urn:xmpp:sid:0'/>"
"</query>"
"</iq>"
);
prof_connect();
stbbr_send(
"<message id='m-trust-1' to='stabber@localhost' from='someuser@chatserv.org/laptop' type='chat'>"
"<body>first</body>"
"<stanza-id xmlns='urn:xmpp:sid:0' by='stabber@localhost' id='archive-id-42'/>"
"</message>"
);
assert_true(prof_output_exact("<< chat message: someuser@chatserv.org/laptop (win 2)"));
stbbr_send(
"<message id='m-trust-2' to='stabber@localhost' from='someuser@chatserv.org/laptop' type='chat'>"
"<body>replay</body>"
"<stanza-id xmlns='urn:xmpp:sid:0' by='stabber@localhost' id='archive-id-42'/>"
"</message>"
);
assert_true(prof_output_exact("Got a message with duplicate (server-generated) stanza-id from someuser@chatserv.org/laptop."));
}
// XEP-0359 disco gate: server does NOT announce urn:xmpp:sid:0 -> stanza-id untrusted -> no replay error.
void
stanza_id_not_trusted_when_server_does_not_announce_sid0(void **state)
{
stbbr_for_query("http://jabber.org/protocol/disco#info",
"<iq type='result' to='stabber@localhost/profanity' from='localhost'>"
"<query xmlns='http://jabber.org/protocol/disco#info'>"
"<identity category='server' type='im' name='TestServer'/>"
"<feature var='urn:xmpp:ping'/>"
"</query>"
"</iq>"
);
prof_connect();
stbbr_send(
"<message id='m-untrust-1' to='stabber@localhost' from='someuser@chatserv.org/laptop' type='chat'>"
"<body>first</body>"
"<stanza-id xmlns='urn:xmpp:sid:0' by='stabber@localhost' id='archive-id-77'/>"
"</message>"
);
assert_true(prof_output_exact("<< chat message: someuser@chatserv.org/laptop (win 2)"));
stbbr_send(
"<message id='m-untrust-2' to='stabber@localhost' from='someuser@chatserv.org/laptop' type='chat'>"
"<body>replay</body>"
"<stanza-id xmlns='urn:xmpp:sid:0' by='stabber@localhost' id='archive-id-77'/>"
"</message>"
);
prof_timeout(2);
assert_false(prof_output_exact("Got a message with duplicate (server-generated) stanza-id"));
prof_timeout_reset();
}

View File

@@ -1,5 +1,3 @@
void message_send(void **state);
void message_receive_console(void **state);
void message_receive_chatwin(void **state);
void stanza_id_dedup_fires_when_server_announces_sid0(void **state);
void stanza_id_not_trusted_when_server_does_not_announce_sid0(void **state);