merge/upstream-full #105

Manually merged
jabber.developer merged 407 commits from merge/upstream-full into master 2026-05-26 17:54:34 +00:00
Collaborator

Merge upstream profanity-im/profanity (373 commits)

Sync with upstream profanity-im/profanity — 373 commits from 2025-04-22 to 2026-04-09.

Upstream HEAD: 1ac05754be (Merge PR #2147 — omemo-heartbeats-fix)
Fork point: 5385cc28be
Stats: 305 files changed, +10,656 / −9,354


Taken from upstream

  • Memory management: g_new0 instead of malloc+memset, auto_gchar / auto_gcharv macros
  • Editor: new callback API for launch_editor
  • UI: win_warn_needed / win_warn_sent warning deduplication, PAD_MIN_HEIGHT dynamic pads
  • Contacts: connection_get_available_resources for querying resources
  • OMEMO: omemo_is_device_active, omemo_is_jid_trusted
  • PGP: _gpgme_key_get_email — parse email from UID
  • Database: UNIQUE constraint for deduplication, g_new0 in database.c
  • Tests: unit test restructuring into subdirectories (command/, config/, xmpp/, ui/, omemo/, otr/, pgp/), test_cmd_ac.c/.h, updated stubs

Preserved from cproof

  • XEP-0308 LMC: replace_id ?: id logic in message/stanza/omemo
  • Force encryption: cmd_force_encryption, test_forced_encryption.c/.h
  • CWE-134: format string protection (cons_show("%s", ...))
  • Scroll: y_start_pos-based paging in window.c
  • Database: db_history_result_t return type, _truncate_datetime_suffix()

Merge-time fixes

File Issue Fix
common.c format-security (-Werror) cons_show(errmsg)cons_show("%s", errmsg)
database.c null-deref !msg->timestampmsg && !msg->timestamp
console.c implicit size_t → int cast (int)(maxlen + 1)
tlscerts.c %d for size_t %zu

Build system

  • Kept autotools (Makefile.am, configure.ac) — upstream migrated to meson, we stay on autotools
  • Restored files deleted by upstream: bootstrap.sh, autogen.sh, ax_valgrind_check.m4, configure-debug
  • Makefile.am: updated test paths to subdirectory structure, added test_cmd_ac, test_forced_encryption

Tests

  • Unit: merged
  • Functional: use our version — upstream version requires stbbr_for_xmlns from updated stabber (profanity-im/stabber, 4 commits March 2026), not yet available in our devs/stabber fork

Follow-up

  • Pull profanity-im/stabber updates into devs/stabber (adds stbbr_for_xmlns)
  • Switch to upstream functional tests after stabber is updated

Closes #64

## Merge upstream profanity-im/profanity (373 commits) Sync with upstream `profanity-im/profanity` — 373 commits from 2025-04-22 to 2026-04-09. **Upstream HEAD:** `1ac05754be` (Merge PR #2147 — omemo-heartbeats-fix) **Fork point:** `5385cc28be` **Stats:** 305 files changed, +10,656 / −9,354 --- ### Taken from upstream - **Memory management:** `g_new0` instead of `malloc+memset`, `auto_gchar` / `auto_gcharv` macros - **Editor:** new callback API for `launch_editor` - **UI:** `win_warn_needed` / `win_warn_sent` warning deduplication, `PAD_MIN_HEIGHT` dynamic pads - **Contacts:** `connection_get_available_resources` for querying resources - **OMEMO:** `omemo_is_device_active`, `omemo_is_jid_trusted` - **PGP:** `_gpgme_key_get_email` — parse email from UID - **Database:** `UNIQUE` constraint for deduplication, `g_new0` in database.c - **Tests:** unit test restructuring into subdirectories (`command/`, `config/`, `xmpp/`, `ui/`, `omemo/`, `otr/`, `pgp/`), `test_cmd_ac.c/.h`, updated stubs ### Preserved from cproof - **XEP-0308 LMC:** `replace_id ?: id` logic in message/stanza/omemo - **Force encryption:** `cmd_force_encryption`, `test_forced_encryption.c/.h` - **CWE-134:** format string protection (`cons_show("%s", ...)`) - **Scroll:** `y_start_pos`-based paging in `window.c` - **Database:** `db_history_result_t` return type, `_truncate_datetime_suffix()` ### Merge-time fixes | File | Issue | Fix | |------|-------|-----| | common.c | format-security (`-Werror`) | `cons_show(errmsg)` → `cons_show("%s", errmsg)` | | database.c | null-deref | `!msg->timestamp` → `msg && !msg->timestamp` | | console.c | implicit size_t → int cast | `(int)(maxlen + 1)` | | tlscerts.c | `%d` for `size_t` | `%zu` | ### Build system - Kept autotools (Makefile.am, configure.ac) — upstream migrated to meson, we stay on autotools - Restored files deleted by upstream: bootstrap.sh, autogen.sh, ax_valgrind_check.m4, configure-debug - Makefile.am: updated test paths to subdirectory structure, added `test_cmd_ac`, `test_forced_encryption` ### Tests - **Unit:** merged - **Functional:** use our version — upstream version requires `stbbr_for_xmlns` from updated stabber (`profanity-im/stabber`, 4 commits March 2026), not yet available in our `devs/stabber` fork ### Follow-up - [ ] Pull `profanity-im/stabber` updates into `devs/stabber` (adds `stbbr_for_xmlns`) - [ ] Switch to upstream functional tests after stabber is updated Closes #64
jabber.developer2 added 374 commits 2026-04-11 10:19:52 +00:00
Print the final storage location when successfully decrypting a file with
`aesgcm://` source.

Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
Fixes: 9c2b3f6579 ("Re-factor `cmd_time()`")

Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
Similar to 9c2b3f6579, but no bugs were fixed. Maybe some were introduced
but only time will tell.

Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
Fixes: 75d76638 ("Free wins summary list")

Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
Will need to fix this soon. Lets temp remove arch to get results for the
other systems.

Build fail on Arch:
```
 src/pgp/gpg.c: In function ‘p_gpg_decrypt’:
src/pgp/gpg.c:659:36: error: implicit declaration of function ‘gpgme_key_get_string_attr’ [-Wimplicit-function-declaration]
  659 |                 const char* addr = gpgme_key_get_string_attr(key, GPGME_ATTR_EMAIL, NULL, 0);
      |                                    ^~~~~~~~~~~~~~~~~~~~~~~~~
src/pgp/gpg.c:659:67: error: ‘GPGME_ATTR_EMAIL’ undeclared (first use in this function)
  659 |                 const char* addr = gpgme_key_get_string_attr(key, GPGME_ATTR_EMAIL, NULL, 0);
      |                                                                   ^~~~~~~~~~~~~~~~
src/pgp/gpg.c:659:67: note: each undeclared identifier is reported only once for each function it appears in
make[1]: *** [Makefile:2085: src/pgp/gpg.o] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:1314: all] Error 2
```
Some improvements
Replace deprecated gpgme_key_get_string_attr with modern API

This commit fixes the build failure against GPGME >= 2.0.0 where
gpgme_key_get_string_attr and GPGME_ATTR_EMAIL were removed.

Changes made:
- Replaced direct call to gpgme_key_get_string_attr(key, GPGME_ATTR_EMAIL, NULL, 0)
- Added new helper function _gpgme_key_get_email() with backwards compatibility
- Function uses conditional compilation to support both old and new GPGME versions

Backwards Compatibility:
- GPGME < 2.0.0: Uses gpgme_key_get_string_attr() if GPGME_ATTR_EMAIL is available
- GPGME >= 2.0.0: Uses modern key->uids->email API

Forward Compatibility:
- Code compiles successfully with GPGME 2.0.0+ where deprecated functions are removed
- Uses modern GPGME API that iterates through key user IDs to find email addresses

Testing:
- Tested with GPGME 1.18.0 (backwards compatibility confirmed)
- Tested compilation compatibility for both old and new GPGME versions
- Verified the exact error from issue #2048 is resolved
- Confirmed no regression in functionality

Fixes: #2048
Resolves compilation error: 'gpgme_key_get_string_attr' undeclared
Resolves compilation error: 'GPGME_ATTR_EMAIL' undeclared
Fix GPGME >= 2.0.0 compatibility issue #2048
This reverts commit 7164d71992.
See fix 606eaac31d.
fixes: error: ‘uintptr_t’ undeclared, defined in header ‘<stdint.h>
Fix typo in examples.
Fix tests with gcc15 (uintptr_t)
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
In the past `Py_XDECREF()` was a macro. Preserve compat to ancient Python
versions by having a trampoline which calls `Py_XDECREF()`.

Fixes: #2043
Fixes: c0da36c4 ("Rage-cleanup.")

Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
Fixes #2054
Fixes: 95c2199c ("Some more memory improvements")

Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
* less allocations
* less duplicate code

Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
As 9f2abc75 accidentally got the ordering of some of the includes wrong,
I decided to propose my initial solution again.

Additional to that, I've opened a MR against CMocka to solve this on
their side, since I believe that the current way this is done is not
sustainable [0].

[0] https://gitlab.com/cmocka/cmocka/-/merge_requests/91

Fixes: 9f2abc75 ("Fix tests with gcc15 (uintptr_t)")
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
When running profanity under Valgrind with slashguard enabled, the
following error could occur:

```
[...]
==4021347== Invalid read of size 1
==4021347==    at 0x4851F49: memchr (vg_replace_strmem.c:986)
==4021347==    by 0x45CEAD: _inp_slashguard_check (inputwin.c:183)
==4021347==    by 0x45CEAD: inp_readline (inputwin.c:225)
==4021347==    by 0x431184: prof_run (profanity.c:121)
==4021347==    by 0x42C609: main (main.c:176)
==4021347==  Address 0xe850883 is 0 bytes after a block of size 3 alloc'd
==4021347==    at 0x48477C4: malloc (vg_replace_malloc.c:446)
[...]
```

`memchr()` requires the complete memory that shall be searched to be
accessible. Using `strchr()` could work for shorter strings, but we only
want to search in the first 4 chars.

Instead of somehow working around those limitations, simply search manually
in the first 4 bytes.

Fixes: 3c56b289 ("Add slashguard feature")
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
Some improvements
Seems like GH changed how the templates work.
According to the new way: https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/about-issue-and-pull-request-templates
Fixes #1911
Alternative to #2056

Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
If config keyfile does not exist, create it.
On Cygwin, ./configure failed with:

    ./configure: line XXXX: syntax error near unexpected token ACX_PTHREAD([], [AC_MSG_ERROR([pthread is required])])'

Root cause: ACX_PTHREAD is a legacy macro not provided by modern
Autoconf; the maintained macro is AX_PTHREAD (from autoconf-archive).

Changes:
- Replace ACX_PTHREAD with AX_PTHREAD in configure.ac.
- Rely on system autoconf-archive; do not vendor m4/ax_pthread.m4.
- Keep AC_CONFIG_MACRO_DIR([m4]) (harmless even if empty).

Result:
- autoreconf -fi && ./configure && make succeeds on Cygwin x86_64.
- Linux builds continue to work.

Fixes: #2059
build: replace ACX_PTHREAD with AX_PTHREAD
Build fail under macOS, Clang 16.0.0.
Add parameters to the following prototypes
- bookmark_ignore_on_connect()
- cons_show_qrcode()
Add types to function prototypes
docs: explain the different kinds of messages
```
 #11 0.137 --2025-09-18 10:41:06--  https://aur.archlinux.org/cgit/aur.git/snapshot/libstrophe-git.tar.gz
```

This currently blocks us at https://github.com/profanity-im/profanity/pull/2066.
Add /opt/homebrew/{include,lib} flags for apple silicon macs, which are not included as default search paths by Clang/GCC. (Homebrew uses /usr/local/{include,lib} on intel macs)
On macOS running ./configure --enable-omemo was failing to find gcrypt despite being installed.
The previous implementation used strstr() which would match OTR tag patterns anywhere in the message. This caused normal messages containing these patterns to incorrectly trigger OTR session initialization.

Changed to use strncmp() to verify that V1/V2 tags immediately follow the base tag, ensuring only legitimate OTR whitespace tags trigger session establishment.

Fixes #1957
Fix OTR whitespace tag detection to prevent false positives
Several users have reported segfaults when starting up profanity which
has OMEMO support, but OMEMO is not set up yet.

@StefanKropp has been able to reproduce this and tracked it down to
`_load_identity()` calling `omemo_known_devices_keyfile_save()`.
The latter then calls `save_keyfile()` which calls
`g_key_file_save_to_file()`. This can then fail if one of the first two
strings is NULL and won't set the `error` on return. In its error handling
`save_keyfile()` unconditionally dereferences `error` which leads to the
segfault.

Fix this and also go through the entire codebase and verify that the usage
of `GError` is done correctly.

Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
So one can easily see if there are two instances running, if they are
logging to the same file.

Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
When setting up OMEMO for the first time via `/omemo gen` one had
to reconnect in order to make OMEMO work. This is fixed now.

Fixes: 5b6b5130 ("Fix OMEMO keyfile loading")
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
With that command one can see the modifications of the runtime
configuration vs. the saved configuration.

Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
First let's make clear we're currently using SHA1 & untangle the tlscerts
API from fingerprint specific details.

Related-to: https://github.com/profanity-im/profanity/issues/2068
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
This also reads the certificate SHA256 and pubkey fingerprint from
libstrophe, but doesn't store it persistently yet.

Related-to: https://github.com/profanity-im/profanity/issues/2068
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
If a cert has a SHA256 use that one and only use SHA1 as fallback.

Closes: https://github.com/profanity-im/profanity/issues/2068
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
... as much as possible ... subject and issuer details excluded.

Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
* add can simply do a `memcpy()`.
* in remove we don't have to put the array in a list in order to put it
  back into an array again. Also we don't have to `strdup()` each entry,
  which leads to even less allocations.

Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
* use `gchar` instead of `char`.
* improve situations when strings must be duplicated or can pass ownership.
* encapsulate the X.509 name details into a struct.
* prevent memory leaks if a name detail is contained multiple times.

Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
* Add new TLS policy `direct` as a replacement for `legacy`.
* Document that `/[command]?` prints the help of a command.
* Add option to get help via `/command help`.
* Fix `my-prof.supp` generation and tests for out-of-source builds.

Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
* Less `GString`.
* Don't `g_free()` a `strdup()`'ed string.
* Don't lookup the `console` window X times, but only once.

Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
If one is running multiple instances of profanity, the behavior of the
accounts module was to constantly overwrite the accounts file with the
version that was on-disk of the first instance of profanity started.

This is changed now in order to only write what we modified, we keep a
copy of the accounts file and when "saving" we re-read accounts from disk
and only update the values of the modified account.

This is not 100% fool proof if one modifies the same account from two
different instances, but still better than before.

Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
* No need to call `g_key_file_has_key()` before calling a getter.
* Add helper to convert `gcharv` to `glist`.

Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
No need to have a fixed list of keys, we can simply copy all existing ones.

Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
e.g. if one connects with an account for the first time and the server
returns a `see-other-host` error.

Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
Related-to: https://github.com/profanity-im/profanity/issues/2078
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
Some fixes
Update compiler flags for homebrew packages on Apple silicon
Make profanity exit gracefully when it receives either SIGTERM or
SIGHUP. This means that various cleanups will happen as if the user
issued the /quit command, whereas before these signals would terminate
profanity immediately leaving for instance the connection to the server
unterminated.

The main motivation for this change is so that profanity will exit
gracefully when the user closes the terminal that it's running in, which
is a handy shortcut (but can also happen on accident) and won't now lead
to the connection slowly timing out on the server instead. Similarly for
SIGTERM: profanity now for instance needs not be manually closed before
shutting down the computer to avoid the above as it will instead receive
this signal from the init process.
exit gracefully on SIGTERM and SIGHUP
Add a GitHub Actions workflow to build with Cygwin on Windows.
Fixes issues by forcing Cygwin bash for all steps, handling temporary script paths,
stripping CRLF line endings, and enabling igncr.
ci: add Cygwin build workflow
In autotools we had `package_status` which we set to either
`release` or `development`.
When converting from autotools to meson we used that mechanism
as well. But actually meson has a default way of handling this
with the option `buildtype`.

The following values are possible:
debug, debugoptimized, release, plain

So our `package_status = development` will now be
`if get_option('buildtype') == 'debug' or get_option('buildtype') ==
'debugoptimized'`. Probably we could also use
`if get_option('buildtype') == 'release'. But like this the
`plain` will be really plain.

Usage:
```
meson setup build --buildtype=debug
meson compile -C build
```
`.found()` does not work on lists. And since this feature needs
several deps it is a list.
For autotools this was added in:
75bb00368f

Regarding:
https://github.com/profanity-im/profanity/issues/1334

I think libstrophe is rather common now and should be treated
like all the other dependencies anyways.
So I'll remove this from the meson files at least. Since my
goal is to have a cleaner build system.
In out autotools build we check for all kinds of curses and their
support for wide character.
Let's focus on more modern systems until someone complains.
For meson we dont just check for the presence of a dependency and
then auto enable it.
Users must enable features explicit. This helps with having
deterministic results.

Also remove the general `plugins` switch which was used to
enable/disable both python and c plugins. Users can just use
those switches.
We don't actually need to check whether they were found.
Meson will just ignore the empty ones.
gtk2 is pretty old now. Let's just focus on gtk3.
Use disabler() for optional dependencies.
Extract repeated build type checks into is_debug/is_release variables.
Consolidate compiler flags into single add_project_arguments() calls.
Simplify dependency list building (Meson auto-ignores disabled deps).
Streamline platform checks using 'in' operator.
Remove redundant variables (xscrnsaver_found, gtk_version, config_h_inc).
Simplify ncurses and header detection with clearer fallback chains.
Consolidate install_data() calls for files in same directory.
Most likely we don't need to check for gpgme-config on most modern
systems.
This probably could be nicer. But since we will drop one in the future
it's not really a priority to make this pretty.
Fix https://github.com/profanity-im/profanity/issues/1002.
Was already started in 2021 in https://github.com/profanity-im/profanity/pull/1619 but then abandoned it.

I want to take this opportunity to change a couple of other things regarding building:
* The `--plugins` switch is gone. Use `--python-plugins` and `--c-plugins` instead.
* We don't autoenable depending on dependencies being present. Every feature needs to be enabled explicitly. This helps with having deterministic builds.
* Instead of setting `PACKAGE_STATUS="development/release"` in the configure.ac file we will use the default meson `buildtype`.

Autotools will probably stay a while. The next release will contain both with a note for packagers to try to use meson and give us feedback.

Meson/Ninja is significantly faster and the syntax is much easier to read.
The faster build times eventually will also help when waiting for CI results.

There are some things missing still like our `make dist`, `make doublecheck` and such.

Usage example:
```
meson setup build --buildtype=release -Domemo=enabled -Dpgp=enabled
meson compile -C build
```

The changes described above are only on meson. Autotools still behaves the same. The plan is to keep it as is and remove it once we know everything works in meson.
Noone wants to sponsor us anyways :)
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
Fix `type qualifiers ignored on function return type`.
The gpgme_set_passphrase_cb function expects a function
pointer of type gpgme_passphrase_cb_t.

The gpgme_passphrase_cb_t typedef specifies a function that returns
gpgme_error_t so return GPG_ERR_NO_ERROR instead of 0.
GDestroyNotify takes a void* and returns void.

Wrap xmpp_stanza_release to get rid of the incompatible function type
warning.
`name` of the struct `color_def` was not initialized but is also not
needed in this function.
The `arg_description` field was forgotten in 034a98587.
Let's initialize them with NULL to make the compiler happy.
g_strstr_len() actually returns a gchar* to the position. So it is not
like in the case of strlen() where it returns the number of bytes.
Fixing a couple of -Wsign-compare warnings.
Found as LTO type-mismatch.
Also found via LTO mismatch.
Explicitly cast the void pointer returned by g_hash_table_lookup
to the expected function pointer signature.

Make `-Wpedantic` happy.
Seems like in signal address.device_id is int32 for some reason.
Cleanup
It seems like we didn't even check whether the operations succeed or
not.

* `clear_empty()`
  Add assertion to confirm a newly created autocomplete object is empty.
* `find_after_create()`:
  Add assertion to ensure `autocomplete_complete()` returns NULL when no
  entries are present in th object.
This causes build failures on macOS runners in Homebrew, see:
https://github.com/Homebrew/homebrew-core/pull/268970
https://github.com/Homebrew/homebrew-core/actions/runs/22305120219/job/64538952484?pr=268970

I didn't run `make format` because it modified other unrelated files
(guess they were not formatted before) but I assume this PR follow the
style guides

Signed-off-by: botantony <antonsm21@gmail.com>
fix: define `prefs_changes_print` outside of `prefs_changes`
This got introduced in configure.ac in
4ca6296fb7. I'll leave it there as is for
now but let's use the proper way in meson.

See discussion in https://github.com/profanity-im/profanity/pull/2090
Writing this test I'm unsure whether a function like
string_matches_one_of() should actually call cons_show().
Let's just note that for later.
See last commit with comment regarding cons_show().

In this test here we can see that if we pass NULL cons_show() isn't
called. Making it a bit different that other invalid test cases.
Optimize prof_occurrences by using pointer arithmetic and
g_utf8_next_char instead of calls to g_utf8_offset_to_pointer.

Improve list construction efficiency in prof_occurrences by
using g_slist_prepend and g_slist_reverse instead of appends.

Also fix a slight oversight that could lead to trouble:
When empty search strings (e.g. empty nicks) are passed
it caused matches at every position.

Update the test case to test for empty nicks.
Also refactor the function so we pass it the current version
and don't rely on the define inside of it.

This function only allows version format in the style of "x.y.z".
Improve unittests
Enable stack protection to detect buffer overflows and harden the binary.
This flag adds a canary to stack frames, causing the
program to terminate immediately if stack corruption is detected.

This helps identify memory safety bugs earlier during development by
turning silent corruption into an immediate crash, and provides
protection against stack-smashing exploits at runtime with negligible
performance overhead.

We don't add this generally because we wan't distributions/users to
decide if they want it. But it can help us find problems early on during
development.
Add security hardening via _FORTIFY_SOURCE=2 and switch to -Og to
provide the necessary optimization for these checks while remaining
debugger friendly.

The goal is to catch many buffer overflows and format string errors
at compile-time or runtime.

-Og may occasionally optimize out very short-lived
local variables, making them invisible in the debugger.
So i'm not sure this is a very good idea. But I hope that we will find
bugs earlier and don't even need to debug that often. We might revert
this change later though in case we run into problems too often.

I'm not aware of any other downsides.
Enable gccs static analyzer to detect memory bugs at compile-time. This
validates our __attribute__((__cleanup__)) macros and improves security
when parsing external XMPP data with zero runtime overhead.
It automatically aborts the program on allocation failure.
Also eliminates the need for repetitive manual NULL checks.
Migrate structure allocations from calloc to glibs g_new0 macro to
improve type safety and memory robustness.

* Type Safety: The macro takes the type name directly, ensuring the
  allocated size always matches the pointer type.
* Static Analysis: It guarantees a non-NULL return by aborting on
  failure, which silences -fanalyzer warnings regarding potential NULL
  pointer dereferences.
* Readability: Removes redundant sizeof() calls and is the glib way
Make get_random_string() return a gchar*.

This is part of the effort to migrate string declarations and
allocations from char to gchar, replacing standard C allocators
(malloc/calloc/strdup) with their glib equivalents
(g_malloc/g_new0/g_strdup).

The primary goal is to prevent mismatched allocator bugs. While char and
gchar are binary compatible, mixing their respective deallocators is
dangerous:

* Freeing malloc'd memory with g_free() (or vice versa) can lead to
  memory corruption, double-frees, or crashes.
* This is seems to be the case especiall on non Linux platforms or when
  using hardened memory allocators where the GLib slice allocator or
  wrappers may differ from the system heap.

By standardizing on gchar, we ensure that our automatic cleanup macros
(like auto_gchar) always invoke the correct deallocator (g_free).
This fixes a -fanalyzer uninitialized value warning.
Replace malloc() with g_malloc() to handle allocation failure and
ensure enough memory is allocated for the null terminator.
* Cache strlen() result
* Check for malloc() failure to prevent null pointer dereference
* Ensure the output buffer is null-terminated for safe use with cons_show()
* Simplify the quote escaping logic for better readability.
Modernize the /export command implementation by adopting GLib-based
patterns for string manipulation and file handling.

* Replace _writecsv with _append_csv_escaped to efficiently handle CSV
  quote doubling directly within a GString buffer
* Refactor cmd_export to build the CSV content in memory using GString,
  minimizing system calls
* Use g_file_set_contents for atomic writing
* Add NULL checks for malloc and strdup calls in cmd_sendfile
* Ensure file handles and descriptors are closed on all error paths
And move whole preferences module to gchar.
That's not very nice yet but we have to start somewhere.
Usually we use `/help command`. Additionally there is a shortcut
`/comamnd?`. 54fea4afcf tried to introduce
to have `/command help`. Which doesn't really work since most commands
get this as parameters. So it never worked. Additionally it introduced
at segfault because it tried to set *question_mark to NULL even we can
also get there without the `?` but by having the `help` as parameter.
recp elements were accessed without verifying the success of
_ox_key_lookup().

Also fix several memory leaks.

gpgme_data_new() followed 'gpgme_data_new_from_mem' was redundant.
Fix a potential NULL pointer dereference. And free the list properly.
data is allocated via g_new0() so cannot be NULL here.
And it was used before the check already so that didn't make any sense
in the first place.
Add NULL checks before calling 'strlen' and also ensures that the string
is long enough before attempting to offset it by 3.
`userdata` was not freed if a handler could not be registered.

This occurs when the 'id' parameter is NULL. In such cases, the
handler cannot be stored in the 'id_handlers' table, but ownership
of 'userdata' has already been transferred to this function. This
commit ensures 'free_func' is called if 'id' is NULL since we always
need to have an ID.
Move from popen() to g_spawn_command_line_sync().
Which is nicer and gets rid of a resource leak that was present here.
We were sequentially checking for errors from 'curl_easy_perform',
'ftell', and 'fclose'. Each time overwriting the last one, resulting in
a leak. This commit ensures that 'err' is only set if it is currently
NULL, preserving the first and most specific error encountered.
* _rotate_log_file(): guard against NULL mainlogfile before g_strdup/strlen
* log_stderr_init(): move dup2 after malloc checks to avoid fd leak on
  allocation failure.
  Also remove explicit close(STDERR_FILENO) before dup2
  since dup2 closes the target atomically. Close the fd returned by
  dup2 immediately.
Check for the memory before using it.
Not really a best practise. They should have been run before each commit
and updated accordingly. Next time..
Let's build with -fanalyzer in debug mode.
These commits are related to issues found by gccs static analyzer.

Also bulid with -fstack-protector-strong compiler flag.
This flag adds a canary to stack frames, causing the program to terminate immediately if stack corruption is detected.

This helps identify memory safety bugs earlier during development by
turning silent corruption into an immediate crash.
Even though we are using with GNU extensions I will add this flag
since I prefer the explicit writing style that we have to use wich
this flag enabled.
Adding an explicit NULL check for 'input' at the start of
cmd_ac_complete to prevent a potential crash and resolve
a GCC static analyzer warning.

Quirk Explanation:
The analyzer found  a "deref-before-check" warning for:
`if ((strncmp(input, "/", 1) == 0) && (!strchr(input, ' ')))`

The analyzer interpreted 'strchr(input, ' ')' as a point where the
validity of 'input' is being questioned (a "check"), even though it is
actually checking the return value. This is due to GCC's internal model
of certain standard string functions having a 'nonnull' attribute or
being categorized as pointer "interrogations" in its state machine.

Because 'strncmp' dereferences 'input' earlier in the same line, the
analyzer saw a logical contradiction: "You treat it as safe in strncmp,
but then you call a function (strchr) that 'requires/checks' it to be
safe, implying you weren't sure."
Let's add an explicit check.
Simplify how field values are updated and ensure memory is correctly
managed.

The previous implementation relied on manual manipulation of the
GSList internal `data` member and only handled cases where a field
had zero or one existing value.

The function now correctly handles fields that may
already contain multiple values by using 'g_slist_free_full' to
clear the entire list and its contents before setting the new
value.
Should help us find the following bugs early.

ASan:
* Out-of-bounds accesses
* Use-after-free
* Memory leaks
* Double free / Invalid free

UBSan:
* Undefined Behavior
* Signed integer overflow
* Null pointer dereference
* Pointer misalignment
* Division by zero
* Bit-shifting out of bounds

This needs works during runtime. But let's add it here so that at least
basic --version run is test and as a reminder for developer to add it.
I will also adjust documentation later on.
When an OMEMO bundle is received with an empty <prekeys/> element,
`prekeys_list` remains empty. In omemo_start_device_session(),
performing a modulo operation on the length of an empty list
(0) triggered a SIGFPE (Floating point exception).

torsocks likely affected the network environment where such empty
or incomplete bundles could be intercepted.

Fix https://github.com/profanity-im/profanity/issues/1997
Add more compiler flags
Only handle self-presence for rooms we are actively managing.

Earlier we blindly react to any self-presence
stanza by calling 'ui_room_join'.

The join could have been initiated by another client or an external
Gateway. In the case of #731 this was Slack.
I remember that more people reported this kind of bug in our MUC.

We then would open a window and the nick would be (null) because we
don't have any nickname for that room set.

So only react on self-presence if we manage the MUC.
Other XMPP clients do the same.

Fix https://github.com/profanity-im/profanity/issues/731
fix: Fix Floating Point Exception in OMEMO session building
fix: Ignore self-presence for untracked MUC rooms
For example cmd_account() is located in src/command/cmd_funcs.c.
The unit test was located in tests/unittests/test_cmd_account.c.
Let's move it to a subdirectory like tests/unittests/command/test_cmd_account.c
to correpsond to the same structure.
Use a behavior-driven naming convention for unit tests.

Functions are now named using the pattern: [unit]__[verb]__[scenario]
The __ works as a semantic separator.

Examples:
* jid_create__returns__null_from_null()
* cmd_connect__shows__usage_when_no_server_value

Benefits:
* Easy to find all tests associated with a specific function using the
  mandatory prefix.
* Test output in CI now explicitly describes the unit, the expected
  outcome, and the scenario being tested.

Also disabled keyhandlers tests due to missing code in src/ui.
Move the `make doublecheck` functionality into a build system agnostic
script.

`scripts/quality-check.sh` can now be used to check for spelling via
codespell, formatting clang-format and run the unit tests.

`make doublecheck` and `meson compile doublecheck` will call this
script.

Sometimes we have issues with different versions of clang-format locally
vs our CI. In this case SKIP_FORMAT env variable can be set.
By removing the trailing comma from the array initialization we hint
clang-format to maintain the compact layout.
Update the gh workflow to pin clang-format to version 21.

Mention the version we use in CONTRIBUTING.md.
And hint to the workflow file if we want to change it.
Usually we point to the website. But honestly it's cumbersome for
developers to update the website. Let's decide later whether we remove
the section there or how we keep it in sync.

But for now let's just add a detailed build section here.
Especially since now we ship autotools *and* meson.

Also mention the convention that we use `build_run` as the meson build
dir. The quality script will depend on this.
Cleanup unittests and improve documentation for new contibutors
Fix several issues in cmd_ac_complete_filepath:
* Prevent a segfault when input is empty.
* Fix a double free where acstring was managed by both auto_gchar and GArray.
* Fix a memory leak when reassigning inpcp during quote stripping.
* Restore the ability to cycle through files on repeated TAB presses by
  caching the last input and skipping updates if the input is already a
  known completion.
* Preserve user input style (e.g. ~, ./) in autocompletion strings to
  ensure matches are correctly displayed and filtered.

Bug got introduced when "cleaning" code with new compiler flags and
sanitizers: aec8e48268.
Add unit tests so this doesn't happen again.

Before that commit we didn't use the static variable but used
autocomplete_update instead. Now we avoid redundat updates and preserve
the state across tab presses. We should look at this again later.

Fix https://github.com/profanity-im/profanity/issues/2098
Fix file autocompletion bugs and restore cycling
The /connect command supports several optional parameters
server, port, tls, and auth.

This was implemented in ac410445af but
this change in the command definitions probably were forgotten.
When targeting a specific preference (`/time console off`)
the loop would continue to iterate through the remaining
categories after finding and applying the setting.

Because subsequent items would not match, it always resulted in
"invalid usage" printed as well after the success message.
They were disabled in 171b6e73c9.

Enable the functional test suite and replaces the
dependency on libexpect with a custom solution.

The native solution allows for specific optimizations like automatic
ANSI code stripping, which is essential for reliable pattern matching in
an ncurses interface.

Custom PTY management via libutil provides full control over the process
lifecycle, resolving issues where tests would hang indefinitely.
We use forkpty from libutil.

Addiiotnally this commit implements automatic ANSI escape sequence
filtering to improve matching consistency.
This will get rid of the colors in matches. It's still something that we
need to think about more since basically we will not test out
color/theme system this way.

We also add non-blocking poll() and SIGKILL based teardown logic to
ensure clean test termination.

We added the functional tests to both autotools and meson.
They are only build when tests are enabled and stabber + libutil are
present.
Meson will print this out nicely in the summary.

Regards https://github.com/profanity-im/profanity/issues/789
We were passing our raw strings to the regex engine. We need to escape
them with g_regex_escape_string() first.
Only request delivery receipts if the recipient is known to support them
via entity capabilities
As mentioned in the recommendations of XEP-0184.
Reenable functional tests
Add new `jid_is_valid()` function and improve the adherence to XMPP RFCs
in jid_create().

Add unit tests for them as well.
This one will need a localpart as well.
It should be used when we want to message a user for example.
While jid_is_valid() also returns TRUE for domain/server JIDs.
Only allow to message someone who has a nick in roster or when we pass a
user JID.
Otherwise notify the user.
If resource was NULL/"" we otherwise had a trailing slash.
Make jid_is_valid() allow JIDs ending with a slash (`user@domain/`).
In these cases, the parser now treats the input as a Bare JID with no
resource part, rather than rejecting it as invalid.

We will then just get `user@domain`.
log_database_get_limits_info should return NULL when a contact has no
history in the database.

We hit a bug with `/msg adsf@asd` before.
Improve JID validation and fix a bug when loading database messages
GKeyFile parses ini-like config files. So the characters '[', ']',
and '=' cannot be used.
We will replace them with '_' now.

This bug was found when a user wanted to connect to an IPv6 address (user@[ipv6:address]).

One problem could be that we get duplicate account names with they would
use one of those 3 characters. But I consider this very unlikely.

Fix https://github.com/profanity-im/profanity/issues/2103
fix: Sanitize account names
Previously the titlebar only showed [OMEMO], regardless of whether the
active session contained untrusted devices. This forced users to manually
run /omemo fingerprint to check the security status of their conversation,
making it difficult to know at a glance if a session was truly secure.

New function `omemo_is_jid_trusted()` to check if all active devices for a JID are trusted.
Update chat and MUC titlebar to display [trusted] or [untrusted] tags when an OMEMO session is active.
Not sure if MUC makes sense here?
Previously, Profanity silently updated OMEMO device lists and identity
caches in the background. This often led to confusion when a contact
added a new device, as encryption or decryption would eventually fail
due to untrusted keys without any prior warning to the user.

The `notifying` flag will distinguish between initial data loading and
realtime discovery.

We now print a message both when a contact publishes a new device ID or
when a new fingerprint is discovered.
When OMEMO encryption failed due to untrusted devices, the
user was presented with a generic error message. This made it difficult
to identify which specific fingerprints were causing the issue, forcing
users to manually hunt for untrusted keys using /omemo fingerprint.

New function `omemo_get_jid_untrusted_fingerprints` to specifically
identify unverified identities for a contact.
`omemo_on_message_send` will now catch encryption failures and
explicitly list the untrusted fingerprints in the chat window.
The /omemo fingerprint command provided a list of all keys ever
seen for a contact but did not indicate which keys were currently "active"
(present in the latest server device list). This made it difficult for users
to identify which fingerprints actually required verification to fix
encryption issues.

Users can now easily distinguish between devices currently in use and
historical keys that no longer matter.

When encryption fails users can immediately see which "active" key is
"untrusted," allowing them to verify the correct fingerprint fast.

Function `omemo_is_device_active` will check if a fingerprint belongs
to a device currently announced by the contacts server.
After typing `/omemo start` a user was not informed of what exactly
happens.
Profanity is fetching the device list and the key bundle for each
device. This happens asynchronously. It could be that it takes some time
and the user already starts typing a message, believing OMEMO is
working.

Now we print:
* Initiating OMEMO session..
* Fetching device list and key bundles
* OMEMO session ready

So the user understands much better the current situation.
If an incoming OMEMO message failed to decrypt (due to missing session
keys or untrusted identities), Profanity would fall back to displaying
the raw XMPP body. This usually contained a generic string like "This
message is encrypted with OMEMO,".

We wrote the detailed reason in the debug logs but the user only saw the
fallback message and probably wondered *why* the message wasn't
displayed properly.

I'm unsure if we should display the fallback message as well though.
Add __attribute__((nonnull) hints to omemo_receive_message and
omemo_on_message_recv declarations to catch NULL arguments at
compiletime.
So we can set the error state unconditionally in the functions.
Improve OMEMO UX
Commit 81f92f1fe introduced a selective update mechanism to
_accounts_save() to prevent overwrites when running multiple instances
by only modifying the specific account being saved.

There was one mistake: the logic for adding a new
account was tied to the final iteration of a loop over
existing accounts.

When starting with an empty accounts file, the
loop never executes though.

We can call g_key_file_set_value() directly and don't need the loop
because this function will only modify one entry or add it if it doesn't
exist.
fix: Fix not saving first created account
a26cdf386b / #2104 added OMEMO trust status to the
title bar, but it was being re-calculated on UI refresh.

Update only on certain operations (like `/omemo trust`, new device
discoveries and even incoming OMEMO messages for example).
The idea is that we keep our DOAP file updated.
Then we always know which version of a XEP we implemented.
By running this script we can then find out if a new version of a XEP
came out.
And we can check whether we need to update our code to adhere to the
latest version or whether we can just version bump.
Synchronize theme_template with the current codebase by adding all
themeable preferences and color attributes that were previously
undocumented.
Remove 'wins.autotidy' option which is no longer used.
fix: Dont OMEMO trust check so often
It's not guaranteed that `log_database_get_limits_info()` returns a
non-NULL value or that `timestamp` is non-NULL.

Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
Instead of flushing the OMEMO store to disk after each operation, do it
only once after all recipients are processed.

A user reported a long delay between hitting enter on the keyboard and the
message being sent on the wire in an OMEMO-enabled group chat. This patch
hopefully improves this situation.

Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
In the process of trying to bring down the time of sending an OMEMO
encrypted message I noticed that a lot of time was spent in generating the
random key and IV (~0.54s on my machine).
By changing this to a single call to `gcry_random_bytes_secure()` I have
been able to slash the time by 50% to ~0.27s.

Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
More fixes
Either there are buffer entries or there are none. No need to have negative
values in the API.

Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
Instead of repeating the same pattern over and over, introduce a helper
function that either outputs a formatted timestamp if one is given as
argument or returns the current time if no argument is given.

Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
Change `log_database_get_previous_chat()` to not take ownership of
`end_time`, so it's clear that whoever passes it in, has to free it.

Fixes: https://github.com/profanity-im/profanity/issues/2110
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
Fix 2110
When sending OMEMO messages, we print a warning message for every
participant without a device ID on every single send:

```
Can't find a OMEMO device id for my@jid.org
```

This clutters the UI due to the high volume of repetitive information.

This is solved by tracking suppressed warnings within the window
structure. A warning for a specific JID and type is only shown once
per window context.

Suppression state is stored in the base ProfWin structure and managed
via helper functions (win_warn_needed, win_warn_sent). The state is
lazily initialized to save resources and automatically cleaned up when
the window is closed.

Warnings are explicitly reset when an OMEMO session is started or
ended to ensure users see fresh alerts when toggling encryption.
Removing the UI notification in omemo_start_device_session to reduce
noise during /omemo start.
The information is still available in the debug logs.

And I think for existing sessions its just too much without value.

Ref: b20e3f1a
Modify omemo_set_device_list to check against the persistent
known_devices.txt file before notifying the user. This prevents
false-positive security alerts on every restart for devices
that have already been registered and trusted.

Check against the persistent known_devices.txt file before notifying
the user. This ensures that "New OMEMO device" alerts are only shown
for devices that are truly new to this client.

Ref: ce9b5140d
Autocompletion failed for nicknames using non Latin scripts. We used
`g_str_to_ascii`, which replaces characters it cannot transliterate with
'?', leading search failures and false matches between different
scripts.

Now we do:
Use `g_utf8_casefold` for case-insensitive UTF-8 comparison. This
ensures that like 'Σ' correctly match 'σ' across all Unicode scripts,
providing correct results for non English nicknames.

If we don't fine anything typing a base ASCII character matches an
accented one (typing `e` matches `è`). This pass uses `g_str_to_ascii`
followed by `g_ascii_strdown` for comparison. It is now restricted to
run if the search string itself is valid ASCII, preventing the
"everything matches '?'" bug in non Latin scripts.

Autocomplete items are sorted using `strcmp`. `g_utf8_collate` provides
linguistical ordering for a specific language. But its behavior is
locale dependent and undefined when comparing strings from different
scripts. `strcmp` does byte-wise ordering that correctly follows Unicode
code order for UTF-8 strings.
Allow sending byte sequences to the Profanity PTY without a newline.
Now we can simulate control characters, specifically the `TAB` key
(`\t`), to trigger autocompletion.
Test both latin and UTF-8.
Properly support UTF-8 characters in autocompletion
Reduce OMEMO noise
Filter out control characters (U+0000 to U+001F) from outgoing
messages, as they are illegal in XML 1.0 (except for \t, \n, and \r).
This prevents XMPP servers from closing the connection when such
characters are accidentally or intentionally included in a message.

Fixes: https://github.com/profanity-im/profanity/issues/1437
Update `blocked_add` to use the `<body>` tag with the `jabber:client`
namespace when reporting spam, as specified in XEP-0377 Example 5.
Other report types continue to use the `<text>` tag.

Fixes: https://github.com/profanity-im/profanity/issues/1971
feat: Sanitize illegal XML characters from outgoing messages
Servers might forward incoming reports to admins. So we need to display
them so they can react upon it.
We need the post-display hooks in DB, log, outgoing messages, and carbons as well.
Also add pre-display hook to carbons for consistency.

Fixes: https://github.com/profanity-im/profanity/issues/2011
fix: missing plugins_post_chat_message_display calls
Add support for <report-origin/> and <third-party/>.

When a server forwards a report to an administrator, the original reporter
(report-origin) and the offender (third-party) are preserved even
if the 'from' address is modified during forwarding.

Also display this information in case we receive a report.
Add the ability to choose between libsignal-protocol-c (default)
and libomemo-c when building with OMEMO support enabled in Meson.

Close: https://github.com/profanity-im/profanity/pull/2020
Add support for libomemo-c as OMEMO backend
Improve support for XEP-0377
The rest will stay with libsignal-protocol-c. So we test both.
Let's add the dependency to all Dockerfiles already.
ci: Build with libomemo-c for the "Linux Meson" job
The ncurses pad used for rendering window history had a fixed height of 100
lines, which caused "Ncurses Overflow!" errors and broke scrolling once
the content exceeded this limit.

This commit replaces the fixed limit with a dynamic resizing strategy.
We use a minimum height of 100 lines.
If a pad reaches its threshold of 3000 lines, a full redraw is triggered
to reclaim space from discarded messages and reset the pad height.
Pads are shrunk back to the minimum height during every redraw to
minimize memory usage for inactive windows.

Message history should always be smoothly scrollable now.

d7e46d64fe set it from 1000 to 10000.
f27fa98717 set it from 1000 to 100.
Probably also relevant is 4fe2c423b1.

Close: https://github.com/profanity-im/profanity/pull/2074
Fixes: https://github.com/profanity-im/profanity/issues/2045
a4cbf3e4 removed macOS from the CI.
We couldn't get it to build and no community member stepped up.
So let's remove this file now as well.
There once was OpenBSD CI from sr.ht sponsored by wstrm.
It's not available since a log time. So let's drop it.
Prevent `/register` and `/reconnect` if connection is not in correct state.

Fixes: https://github.com/profanity-im/profanity/issues/2083
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
Check connection state before accepting command.
Have a better overview, we are interested in which tests are run, which
distro, which build system and which omemo backend.

debian | Autotools | unit+func | signal
debian | Meson | unit+func | signal
debian | Meson | unit+func | libomemo
fedora | Autotools | unit+func | signal
ubuntu | Autotools | unit+func | signal
Remove stabber from Dockerfiles were we only build with autotools.
Locally valgrind is quiet. But on CI valgrind complains. This is due to
an older valgrind version which doesn't handle AVX2-optimized strings
properly. So we need to add it to the suppression file.
Triggered by unit tests. In Profanity itself the situation is probably
rarely happening. We would have to disconnect/exit after receiving
presence but before the roster arrives.
Instead of:

```
CI / Check coding style
CI / Check spelling
CI / debian | Autotools | unit | signal
CI / debian | Meson | unit+func | libomemo
CI / debian | Meson | unit+func | signal
CI / fedora | Autotools | unit | signal
CI / ubuntu | Autotools | unit | signal
Cygwin / cygwin-build
```

We will sort them into "Style", "Linux" and "Cygwin".
This will result in smaller and cleaner tarballs for distributions to use.

Additionally it is a logical step towards moving fully to the Meson build system.
While the last release provided two tarballs (one generated with
Autotools and one with Meson), the next release will only provide a
Meson-generated tarball.

These attributes are respected by `git archive` and `meson dist` which
means they will take effect for the Meson generated tarballs and the
GitHub autogenerated tarballs as well.

As a consequence of switching to `meson dist`, pre-generated files like
`configure` will no longer be included in the tarballs. Users and
distributions that still wish to use the Autotools build system from
these tarballs will now need to run `./bootstrap.sh` or `autoreconf -fi`
themselves, which requires automake, autoconf, and libtool to be
installed.

Since many distributions are slowly dropping the usage of vendor
generated tarballs and are moving to checking out git tags this should
not be a problem.
Arch Linux is for example doing
`source=("git+https://github.com/user/project.git#tag=v1.2.3")` and
Debian has the `git-buildpackage` too. So both of them require require
autoreconf already.

Autotools support might be removed entirely in a future
release since I find them much easier to edit and maintain.
Explain the how and why in CONTRIBUTING.md

Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Disable the functional tests in the case of meson as well.
They don't seem to work in the CI or take too long.
This needs further investigation. For now let's run them locally and
before each release.

Signed-off-by: Michael Vetter <jubalh@iodoru.org>
ci: Cleanup and improve CI jobs
Replace the hardcoded "me" stamp for outgoing messages that are
sometimes used with the value from PREF_OUTGOING_STAMP.

It was never implemented in all code paths. Like with receipts for
example.

Additionally fix the confusing description and variable names.

Ref: 2c003dd2 (Add /stamp feature)
Ref: 3a4cd7da (Broke the variables)
Fixes: https://github.com/profanity-im/profanity/issues/2125
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Ref: 2c003dd2
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
fix(ui): Fix custom outgoing stamp and fix stamp command help
Enable adding oneself to the roster.

Self subscriptions are implicit according to XEP-0060 / XEP-0163.
So we adapt the /sub command to handle this gracefully by just printing
an informative message.

Profanitys logic didn't handle own presence/when adding to roster
correctly. This got fixed now.

Fixes: https://github.com/profanity-im/profanity/issues/2084

Signed-off-by: Michael Vetter <jubalh@iodoru.org>
fix: allow adding own JID to roster
feat(ui): implement dynamic pad resizing
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Since we now use Conventional Commits we can create our Changelog
semi-automatically.

Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
This will make it easier for us to write Changelogs for example.
See https://www.conventionalcommits.org/en/v1.0.0.
Also mentioned in the CONTRIBUTING.md

Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Mentioned in the CONTRIBUTING.md as well.
Let's enforce it.

Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Earlier ci/meson-build.sh and ci/ci-build.sh.
The latter we then renamed to the more descriptive build-configuration-matrix.sh.

Both scripts are doing the same thing but for different build systems.
So lets merge them together.

Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Improve tooling for development and maintenance
Correctly handle OMEMO stanzas that contain a <header> but no <payload>.
These "Key Transport Messages" (heartbeats) are used by some clients
for session maintenance and establishing trust.

Ref: a2726b6a7 made the <payload> element mandatory in the parser.
Ref: 4d49c2b74 the bug became user-visible.

Fixes: https://github.com/profanity-im/profanity/issues/2129
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Centralize heartbeat detection and have a function for error to string
mapping. So we don't need the same code in multiple handlers.

Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Fix incorrect omemo decryption error for Key Transport Messages
Use the original aesgcm:// URL when downloading OMEMO downloads.  Then
in the final message use the decrypted file destination instead of the
internal temporary path.

Use the unique download ID instead of the URL for message updates to
have proper progress reporting.

Unify the final transfer status to "done" across both downloads and
uploads.

Fixes: https://github.com/profanity-im/profanity/issues/1939
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Introduce meaningful color feedback for background tasks like file
downloads and uploads. Progress is displayed in a neutral color,
while successful completions turn green and failures turn red.

We reuse the existing THEME colors for now.

Add new UI flags ENTRY_COMPLETED and ENTRY_ERROR to
the buffer entry system. So we don't misuse delivery receipts anymore.

Fixes: https://github.com/profanity-im/profanity/issues/1758
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Improve (encrypted) file downloads
We won't have it in the distribution tarballs.

Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Static analyzer hilighted this.

Ref: e39ffd981
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Introduce optional spellcheck highlighting in the input window using
the Enchant-2 library.

```
/spellcheck on
/spellcheck lang en_US
```

New theme color `input.misspelled`.

Fixes: https://github.com/profanity-im/profanity/issues/183
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
To list all available dictionaries.

Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Signed-off-by: Leon Adomaitis <leon@adomaitis.de>
fix(omemo): add missing includes for libomemo-c builds
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Add spellchecking
We will only use Meson from now on.

Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Ref: 01c9a51c70
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
So we can test if it works if libsignal isn't even installed on the
system.

Ref: 9a501e6ecd
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Signed-off-by: Leon Adomaitis <leon@adomaitis.de>
ci: add macOS Homebrew CI
feat: Remove autotools and adapt docu and scripts
Standardize how PreKeys are handled during OMEMO message reception to
align with XEP-0384 best practices.

Previously Profanity replaced a consumed PreKey by generating a new
one with the same ID. This behavior could lead to decryption failures if
multiple senders attempted to use the same PreKey ID before the client
could successfully update its bundle on the server.

Remove consumed PreKeys from the local store.
New PreKeys are generated in batches (100) when the local supply runs
low (< 10).
Republish OMEMO bundle after replenishment.

Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Implement a UNIQUE constraint on the archive_id column (XEP-0359 stanza-id)
to prevent duplicate messages in the chat log. This addresses problems where
the same message could be stored multiple times if received via both MAM
and regular.

Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Implement security checks to ensure the 'archive_id' (XEP-0359) used
for database deduplication originates from a trusted source.

XEP-0359 Section 3.1: "The 'by' attribute MUST be the XMPP
address (JID) of the entity assigning the unique and stable stanza ID."
Furthermore, Section 4 (Security Considerations) specifies: "A client
SHOULD only trust <stanza-id/> elements from its own server or from
a MUC service it is joined to."

XEP-0313 Section 4.1.2: "The 'by' attribute of the <result/> element is
the JID of the archive being queried." and "If the 'by' attribute is not
present, the recipient MUST assume that the results are from their own
personal archive."

Let _handle_chat verify <stanza-id/> 'by' attribute matches our bare JID.
Let _handle_groupchat verify <stanza-id/> 'by' attribute matches the MUC
s bare JID.
Let _handle_mam verify the <result/> 'by' attribute matches the outer
message 'from' (archive JID). If 'by' is missing then 'from' matches our
own bare JID (personal archive).

Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Implement backslash based escaping in the command parser and autocompletion
logic to handle contact names containing double quotes.
This stops the parser from splitting nicknames into multiple tokens, which
previously caused "Invalid usage" errors.

The parser now sees \ as an escape character for quotes and spaces in
_parse_args_helper, count_tokens and get_start. Autocomplete results are
escaped when they contain spaces, and search prefixes are unescaped before
matching. strip_arg_quotes() has also been updated to handle unescaping.

Fixes: https://github.com/profanity-im/profanity/issues/1844
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
fix(omemo): standardize PreKey management to prevent decryption failures
Unescape any character following a backslash. This fixes autocompletion
for names with escaped spaces.
For example: `/msg Thor\ Odinson hello`

Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Replace 'INSERT OR IGNORE' with 'INSERT ... ON CONFLICT(`archive_id`) DO
NOTHING RETURNING id'. This is only available since sqlite 3.35.0.
So deduplication only happens for `archive_id` and we don't silently
ignore other errors or constraints (like not null).

We can now detect if an insertion was skipped due to duplication by
checking the result of 'RETURNING id'.

We don't print out when we don't insert duplicated messages since this
will happen often and will be too noisy. So we match the behaviour of
what Dino is doing.

Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Use XSetIOErrorExitHandler on X11 to detect connection loss and
disable GTK features (tray, clipboard, notifications).

Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Signed-off-by: Paul Fertser <fercerpav@gmail.com>
Co-authored-by: Paul Fertser <fercerpav@gmail.com>
Allow building the latest libstrophe from upstream Git automatically when
-Dforce_fallback_for=libstrophe is passed.

Default to find and link against system-provided libstrophe stays the same.

Signed-off-by: Paul Fertser <fercerpav@gmail.com>
fix: handle X11 connection loss gracefully
chore: support building libstrophe as a subproject
refactor(database): enforce unique archive_id for reliable deduplication
Improve autocompleter and command parsing in relation to quoting
I think this bug happens when a user scrolls up a window, triggering
iq_mam_request_older() to load older history, in the case when the
database is empty for that contact.

The initial MAM fetch is triggered when the window is opened which
should get the latest history.

Fixes: https://github.com/profanity-im/profanity/issues/2142
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
fix: Fix crash when loading MAM for a contact with empty db
Replace g_spawn_command_line_sync with g_spawn_sync with the
G_SPAWN_CHILD_INHERITS_STDIN flag.
This is actually needed to so that interactive commands can access the
terminal.
Otherwise they cannot ask the user for the passphrase.

Ref: f5787fb31f
Fixes: https://github.com/profanity-im/profanity/issues/2143
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Commit 81f92f1fe introduced a selective update mechanism to
_accounts_save() to prevent overwrites when running multiple instances
by only modifying the specific account being saved.

Commit 5c484c fixed a problem with that commit regarding empty accounts.

It turns there was another bug introduced:
The new implementation only set the keys that existed in memory.
So removal of keys was not possible anymore.

Fixes: 81f92f1fed
Ref: 5c484c26ed
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
There was way too much repetition here.
This is in preparation for future changes regarding the editor.

Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Move from blocking fork/wait logic to nonblocking fork and
g_child_watch_add.

This ensures that the Profanity main loop continues to run while an
external editor is open. So we don't loose connection and react to
pings.

We change editor handling also in vcard and muc subject editing.
In the new implementation we are launching the editor and passing a
callback which we will use once the editor exited.

We use the recently added ui_susped() and ui_resume().

To not clutter the UI we need to check whether Profanity UI is suspended
and omit drawing in this case.

Fixes: https://github.com/profanity-im/profanity/issues/1888
Ref: 9b112904a9bc7250dc013d901187ca8622580d98
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
This was forgotten in a refactor commit.

Ref: c43c9567df
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Remove IDE specific entries, they belong in the global conf of the user.
Remove autotools specific files.

Ref: bc777c56b2
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
fix: restore TTY access for eval_password commands
And show how to escape whitespaces.

Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
ASan and Valgrind both intercept memory allocations and management at
runtime. Running them simultaneously might lead to conflicts in memory
tracking.

This change ensures that during the Valgrind phase of the build matrix,
only Valgrind is responsible for memory analysis, avoiding redundant
overhead and ensuring more reliable results.

Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Misc improvements
For key transport messages, Monal at least doesn't append the
authentication tag to the plaintext key contents as that only makes
sense if the key was used to encrypt something. This causes the key
length check to fail and show the

	OMEMO message received but decryption failed.

error to the user which is confusing because there is no user-originated
message involved.

Skip the length check for key transport messages as profanity only uses
these to advance the ratchet and makes no use of the decrypted contents.

Signed-off-by: Karel Balej <balejk@matfyz.cz>
omemo: ignore key contents if there is no payload
Merge upstream/master into merge/upstream-full
Some checks failed
CI Code / Linux (arch) (pull_request) Failing after 7s
CI Code / Linux (debian) (pull_request) Failing after 5s
CI Code / Linux (ubuntu) (pull_request) Failing after 6s
CI Code / Check coding style (pull_request) Failing after 7s
CI Code / Code Coverage (pull_request) Failing after 6s
Lint / DCO (pull_request) Failing after 8s
CI Code / Check spelling (pull_request) Successful in 52s
Lint / conventional commits (pull_request) Failing after 54s
Lint / spellcheck (pull_request) Successful in 1m57s
Build / ubuntu | func | signal (pull_request) Failing after 2m49s
Lint / coding style (pull_request) Failing after 3m26s
Build / cygwin / build only (pull_request) Has been cancelled
CodeQL / analyze (pull_request) Has been cancelled
090c147928
Merge profanity-im/profanity master (373 commits) into cproof fork.

Source changes (manual merge):
- src/command/: cmd_defs, cmd_funcs, cmd_ac — upstream g_new0, auto_gchar,
  launch_editor callback; keep our XEP-0308 LMC, force-encryption, CWE-134
- src/config/: preferences, tlscerts, account — upstream UNIQUE dedup,
  dynamic pad capacity; keep our db_history_result_t, scroll logic
- src/database.*: upstream g_new0 memory mgmt; keep our return types,
  add null-check fix for msg->timestamp
- src/omemo/, src/pgp/: upstream _gpgme_key_get_email, g_new0;
  keep our replace_id in omemo_on_message_send
- src/tools/: editor, http_upload, bookmark_ignore — upstream launch_editor
  callback API
- src/ui/: console, window, chatwin, mucwin, inputwin, buffer —
  upstream win_warn_needed/sent dedup, PAD_MIN_HEIGHT dynamic pads;
  keep our y_start_pos scroll, _truncate_datetime_suffix
- src/xmpp/: message, stanza, presence, roster_list, iq, session —
  upstream connection_get_available_resources; keep our LMC stanza logic
- src/common.c: fix format-security (cons_show)

Build system:
- Makefile.am: updated test paths to subdirectory structure, added
  test_cmd_ac, test_forced_encryption
- Restored autotools files deleted by upstream meson migration:
  bootstrap.sh, autogen.sh, m4/ax_valgrind_check.m4, configure-debug

Tests:
- Unit tests: upstream unittests.c base + our forced_encryption tests
  (482 passed, 0 failed across all 4 configurations)
- Functional tests: kept our version (93 passed, 0 failed)
  upstream version requires stbbr_for_xmlns not yet in our stabber fork
- Updated test stubs: stub_xmpp.c, stub_omemo.c from upstream

Compile fixes:
- src/common.c: cons_show(errmsg) -> cons_show("%s", errmsg)
- src/database.c: null-check before msg->timestamp access
- src/ui/console.c: size_t -> (int) cast for format width
- src/config/tlscerts.c: %d -> %zu for size_t
jabber.developer2 force-pushed merge/upstream-full from 090c147928 to 79832ef86c 2026-04-11 10:28:53 +00:00 Compare
jabber.developer2 force-pushed merge/upstream-full from 79832ef86c to c6bc85ccd5 2026-04-11 10:35:21 +00:00 Compare
jabber.developer2 force-pushed merge/upstream-full from c6bc85ccd5 to 4319172758 2026-04-11 10:45:30 +00:00 Compare
jabber.developer added a new dependency 2026-04-22 12:54:13 +00:00
jabber.developer requested changes 2026-04-23 15:04:45 +00:00
Dismissed
jabber.developer left a comment
Owner

Nice job. I generally tried to make less comments to avoid unnecessary increase in the workload, but there are many places which are incompatible with our code style/approach.

Nice job. I generally tried to make less comments to avoid unnecessary increase in the workload, but there are many places which are incompatible with our code style/approach.
RELEASE_GUIDE.md Outdated
@@ -82,2 +49,2 @@
* Update profanity_version.txt
* Take results from profanity.doap and put them into xeps.html
Make changes to the git repo including uploading the new artefacts at:
https://github.com/profanity-im/profanity-im.github.io

We have a different website. Please remove this instruction

We have a different website. Please remove this instruction
jabber.developer marked this conversation as resolved
@@ -61,2 +35,3 @@
static void _save_accounts(void);
static gchar*
_sanitize_account_name(const char* const name)

How will it reflect on existing accounts? Would it be possible to have accounts with such names previously?

How will it reflect on existing accounts? Would it be possible to have accounts with such names previously?
Author
Collaborator

Will be handled in [#112]

Will be handled in [#112]
jabber.developer marked this conversation as resolved
@@ -246,0 +241,4 @@
_checked_g_strdup(const char* in)
{
if (in == NULL)
return NULL;

Why though?
According to official doc, g_strdup:

Duplicates a string. If str is NULL it returns NULL.

Though maybe earlier glib versions are less safe...

Why though? According to [official doc](https://docs.gtk.org/glib/func.strdup.html), `g_strdup`: > Duplicates a string. If str is NULL it returns NULL. Though maybe earlier glib versions are less safe...
jabber.developer marked this conversation as resolved
src/database.c Outdated
@@ -634,0 +581,4 @@
if (rc == SQLITE_ROW) {
log_debug("Successfully inserted message into database.");
} else if (rc == SQLITE_DONE) {
log_debug("Message already exists in database (archive_id: %s), skipping.", message->stanzaid);

It deserves at least warning level. We can't just silently not write message to log: the message might contain important information for user. The error also should be extremely rare and it should represent invalid state, so it makes sense to cons_show_error it to alert the user.

It deserves at least `warning` level. We can't just silently not write message to log: the message might contain important information for user. The error also should be extremely rare and it should represent invalid state, so it makes sense to `cons_show_error` it to alert the user.
jabber.developer marked this conversation as resolved
src/database.c Outdated
@@ -738,0 +709,4 @@
"`timestamp` TEXT, "
"`type` TEXT, "
"`stanza_id` TEXT, "
"`archive_id` TEXT UNIQUE, "

Most of the messages will have this value as NULL. Luckily, SQLite treats them as unique values.

For the purposes of UNIQUE constraints, NULL values are considered distinct from all other values, including other NULLs.
https://www.sqlite.org/lang_createtable.html#unique_constraints

Just a consideration. Feel free to resolve it.

Most of the messages will have this value as `NULL`. Luckily, SQLite treats them as unique values. > For the purposes of UNIQUE constraints, NULL values are considered distinct from all other values, including other NULLs. https://www.sqlite.org/lang_createtable.html#unique_constraints Just a consideration. Feel free to resolve it.
jabber.developer marked this conversation as resolved
src/database.c Outdated
@@ -738,0 +758,4 @@
cleanup:
if (SQLITE_OK != sqlite3_exec(g_chatlog_database, "ROLLBACK;", NULL, 0, &err_msg)) {
log_error("DB Migration] Unable to ROLLBACK: %s", err_msg);

Why not?
log_error("[DB Migration] Unable to ROLLBACK: %s", err_msg);

[ is missing (likely a strange typo)

Why not? ` log_error("[DB Migration] Unable to ROLLBACK: %s", err_msg);` `[` is missing (likely a strange typo)
jabber.developer marked this conversation as resolved
@@ -266,3 +281,4 @@
}
free_keyfile(&omemo_ctx.identity);
g_hash_table_destroy(omemo_ctx.known_devices);

TODO: investigate double free/similar issues due to cleaning regardless of omemo_ctx.loaded. Likely, it's intentional, but I want to make sure. double free is worse than memleak

TODO: investigate double free/similar issues due to cleaning regardless of `omemo_ctx.loaded`. Likely, it's intentional, but I want to make sure. double free is worse than memleak
Author
Collaborator

Fixed in 60da899bd — NULL-checks added before each g_hash_table_destroy / signal_*_destroy, since neither is documented as NULL-safe and a partial init leaves the handles zero.

Fixed in 60da899bd — NULL-checks added before each `g_hash_table_destroy` / `signal_*_destroy`, since neither is documented as NULL-safe and a partial init leaves the handles zero.
@@ -506,0 +539,4 @@
} else {
ProfMucWin* mucwin = wins_get_muc(jid->barejid);
if (mucwin) {
win_println((ProfWin*)mucwin, THEME_DEFAULT, "!", "New OMEMO device (ID: %u) found for %s.", dev_id, jid->barejid);

extremely excessive nesting

extremely excessive nesting
jabber.developer marked this conversation as resolved
@@ -298,2 +272,3 @@
if (whitespace_base) {
if (strstr(message, OTRL_MESSAGE_TAG_V2) || strstr(message, OTRL_MESSAGE_TAG_V1)) {
char* tag_position = whitespace_base + strlen(OTRL_MESSAGE_TAG_BASE);
if (strncmp(tag_position, OTRL_MESSAGE_TAG_V2, strlen(OTRL_MESSAGE_TAG_V2)) == 0 || strncmp(tag_position, OTRL_MESSAGE_TAG_V1, strlen(OTRL_MESSAGE_TAG_V1)) == 0) {

TODO: test with other popular messenger to ensure that this logic works correctly.

TODO: test with other popular messenger to ensure that this logic works correctly.
Author
Collaborator

Will be handled in #112 (OTR interop matrix)

Will be handled in [#112](https://git.jabber.space/devs/cproof/issues/112) (OTR interop matrix)
@@ -409,0 +400,4 @@
// We only use transliterated match if the search string conversion didn't result
// in unknown characters ('?'), to avoid false matches between different scripts.
if (strchr(search_str_ascii_lower, '?') == NULL) {
if (g_str_has_prefix(curr_ascii_lower, search_str_ascii_lower)) {

could be just && operator instead of nested condition

could be just `&&` operator instead of nested condition
jabber.developer marked this conversation as resolved
@@ -234,13 +249,10 @@ editor_process(ProfWin* window)
// Please avoid using it, since it blocks execution of the message handling and other important functionality until the editor is closed.

need to remove this comment since it doesn't reflect new version of this method

need to remove this comment since it doesn't reflect new version of this method
jabber.developer marked this conversation as resolved
@@ -43,3 +16,1 @@
void http_print_transfer(ProfWin* window, char* id, const char* fmt, ...);
G_GNUC_PRINTF(3, 4)
void http_print_transfer_update(ProfWin* window, char* id, const char* fmt, ...);
G_GNUC_PRINTF(4, 5)

Nice catch. Feel free to close.

Nice catch. Feel free to close.
jabber.developer marked this conversation as resolved
@@ -1014,4 +1048,1 @@
auto_gchar gchar* message = NULL;
get_message_from_editor_async(rl_line_buffer);

note: we should also clean it up its definition later

note: we should also clean it up its definition later
Author
Collaborator

Done — switched to auto_gchar (4401e817d).

Done — switched to `auto_gchar` (4401e817d).

I mean get_message_from_editor_async function is rendered obsolete by new editor function from upsteam.

I mean `get_message_from_editor_async` function is rendered obsolete by new editor function from upsteam.
Author
Collaborator

Removed

Removed
jabber.developer marked this conversation as resolved
@@ -404,0 +400,4 @@
end += next_ch_len;
}
char* word = g_strndup(&line[start], end - start);

It would be better to use auto_gchar gchar* word instead of g_freeing it 2 lines below, because we would maintain consistency across the code (auto_-style), avoid risks of UAF in the future, and also match the type (g_strndup returns gchar*, not char*)

It would be better to use `auto_gchar gchar* word` instead of `g_free`ing it 2 lines below, because we would maintain consistency across the code (`auto_`-style), avoid risks of UAF in the future, and also match the type (`g_strndup` returns `gchar*`, not `char*`)
jabber.developer marked this conversation as resolved
@@ -331,4 +305,2 @@
_get_range_bounds(&start, &end, is_static);
pos = getmaxx(stdscr) - _tabs_width(start, end);
if (pos < 0) {

If it wasn't changed, it might've lead to the bug, because previous assignment would've led to overflow of the pos, thus creating contradicting condition (pos < 0 always false).

If it wasn't changed, it might've lead to the bug, because previous assignment would've led to overflow of the `pos`, thus creating contradicting condition (`pos < 0` always false).
Author
Collaborator

fixed by precheck rewrite

fixed by precheck rewrite
jabber.developer marked this conversation as resolved
@@ -87,3 +57,1 @@
static int _status_bar_draw_extended_tabs(int pos, gboolean prefix, int start, int end, gboolean is_static);
static int _status_bar_draw_tab(StatusBarTab* tab, int pos, int num, gboolean include_brackets);
static int _status_bar_draw_tabs(int pos);
void _get_range_bounds(guint* start, guint* end, gboolean is_static);

All these guint might lead to unexpected bugs.

Many developers (and some large development houses, such as Google) believe that developers should generally avoid unsigned integers.
https://www.learncpp.com/cpp-tutorial/unsigned-integers-and-why-to-avoid-them/

The fact that unsigned arithmetic doesn't model the behavior of a simple integer, but is instead defined by the standard to model modular arithmetic (wrapping around on overflow/underflow), means that a significant class of bugs cannot be diagnosed by the compiler. In other cases, the defined behavior impedes optimization.

That said, mixing signedness of integer types is responsible for an equally large class of problems. The best advice we can provide: try to use iterators and containers rather than pointers and sizes, try not to mix signedness, and try to avoid unsigned types (except for representing bitfields or modular arithmetic). Do not use an unsigned type merely to assert that a variable is non-negative.

https://google.github.io/styleguide/cppguide.html

We should discuss style guide for CProof.

All these `guint` might lead to unexpected bugs. > Many developers (and some large development houses, such as Google) believe that developers should generally avoid unsigned integers. https://www.learncpp.com/cpp-tutorial/unsigned-integers-and-why-to-avoid-them/ > The fact that unsigned arithmetic doesn't model the behavior of a simple integer, but is instead defined by the standard to model modular arithmetic (wrapping around on overflow/underflow), means that a significant class of bugs cannot be diagnosed by the compiler. In other cases, the defined behavior impedes optimization. > That said, mixing signedness of integer types is responsible for an equally large class of problems. The best advice we can provide: try to use iterators and containers rather than pointers and sizes, try not to mix signedness, and try to avoid unsigned types (except for representing bitfields or modular arithmetic). Do not use an unsigned type merely to assert that a variable is non-negative. https://google.github.io/styleguide/cppguide.html We should discuss style guide for CProof.
Author
Collaborator

Updated

Updated
jabber.developer marked this conversation as resolved
@@ -335,2 +307,2 @@
pos = 0;
}
// if the result of the calc is negative we take 0
pos = MAX(0, (getmaxx(stdscr) - (int)_tabs_width(start, end)));

Without (int) typecasting, _tabs_width always returns result > 0. And it's so hard to maintain such part since we need to keep in mind that result is unsigned. It's a hack rather than a normal implementation. I suggest we revert these changes: they decrease maintainability, and reduce compiler's ability to detect issues.

We should revert
ddd43f5e60
8970b5e61b

and other similar commits.

Without `(int)` typecasting, `_tabs_width` always returns result `> 0`. And it's so hard to maintain such part since we need to keep in mind that result is unsigned. It's a hack rather than a normal implementation. I suggest we revert these changes: they decrease maintainability, and reduce compiler's ability to detect issues. We should revert ddd43f5e60c7848f04bcfa806ad9a72e2beab7ee 8970b5e61bf253af33f6e46e67de4adde55c0a2e and other similar commits.
Author
Collaborator

Type handling updated

Type handling updated
jabber.developer marked this conversation as resolved
@@ -581,2 +588,4 @@
wattroff(win, bracket_attrs);
#ifdef HAVE_OMEMO
if (chatwin->omemo_trusted) {

Literally WET. This parts repeats one-to-one with the part above. It deserves its own method. More than that,

            wattron(win, bracket_attrs);
            wprintw(win, "[");
            wattroff(win, bracket_attrs);

It take too much space and reduce readability. We can make macro or at least a wrapper method to do such thing instead:

wprintw_withattr(win, "[", bracket_attrs);
Literally WET. This parts repeats one-to-one with the part above. It deserves its own method. More than that, ```c wattron(win, bracket_attrs); wprintw(win, "["); wattroff(win, bracket_attrs); ``` It take too much space and reduce readability. We can make macro or at least a wrapper method to do such thing instead: ```c wprintw_withattr(win, "[", bracket_attrs); ```

Nice refactoring.

Nice refactoring.
jabber.developer marked this conversation as resolved
src/ui/window.c Outdated
@@ -577,27 +577,9 @@ win_show_subwin(ProfWin* window)
}
ProfLayoutSplit* layout = (ProfLayoutSplit*)window->layout;
// If a subwindow already exists (e.g. repeated call), destroy it to avoid leaks

TODO: test leaks here

TODO: test leaks here
Author
Collaborator

Fixed in 60da899bdwin_show_subwin now delwins a previous pad before newpad, removing the leak on a duplicate show.

Fixed in 60da899bd — `win_show_subwin` now `delwin`s a previous pad before `newpad`, removing the leak on a duplicate show.
jabber.developer marked this conversation as resolved
@@ -66,0 +64,4 @@
wresize(win, new_height, cur_width);
}
}
}

We already have a similar method. TODO: understand interaction of these 2 fixes.

We already have a similar method. TODO: understand interaction of these 2 fixes.
Author
Collaborator

Will be handled in #112_win_ensure_pad_capacity and win_resize cover different concerns

Will be handled in [#112](https://git.jabber.space/devs/cproof/issues/112) — `_win_ensure_pad_capacity` and `win_resize` cover different concerns
jabber.developer marked this conversation as resolved
@@ -1095,3 +1070,1 @@
stanzaid = (char*)xmpp_stanza_get_attribute(stanzaidst, STANZA_ATTR_ID);
if (stanzaid) {
message->stanzaid = strdup(stanzaid);
const char* by = xmpp_stanza_get_attribute(stanzaidst, "by");

I am not sure right now, but I recall one XEP mentioning that we shouldn't trust by. Further investigation is required.

I am not sure right now, but I recall one XEP mentioning that we shouldn't trust `by`. Further investigation is required.
Author
Collaborator

Will be handled in #112 — XEP-0359 §7 + §8 require a disco check (urn:xmpp:sid:0) before trusting the by attribute; current code only matches the JID.

Will be handled in [#112](https://git.jabber.space/devs/cproof/issues/112) — XEP-0359 §7 + §8 require a disco check (`urn:xmpp:sid:0`) before trusting the `by` attribute; current code only matches the JID.
jabber.developer marked this conversation as resolved
@@ -1407,3 +1395,1 @@
stanzaid = (char*)xmpp_stanza_get_attribute(stanzaidst, STANZA_ATTR_ID);
if (stanzaid) {
message->stanzaid = strdup(stanzaid);
const char* by = xmpp_stanza_get_attribute(stanzaidst, "by");

same as earlier: trusting by is under question.

same as earlier: trusting `by` is under question.
Author
Collaborator

Same as previous — handled in #112

Same as previous — handled in [#112](https://git.jabber.space/devs/cproof/issues/112)
jabber.developer marked this conversation as resolved
@@ -1442,0 +1434,4 @@
if (message->plain == NULL) {
message->plain = omemo_error_to_string(message->omemo_err);
}
} else if (message->plain != NULL) {

this code is duplicated 3 times.

this code is duplicated 3 times.
jabber.developer marked this conversation as resolved
src/xmpp/omemo.c Outdated
@@ -723,2 +708,4 @@
return 0;
}
char*

It should be gchar*

It should be `gchar*`
jabber.developer marked this conversation as resolved
@@ -98,1 +72,3 @@
free(resource);
g_free(resource->name);
g_free(resource->status);
g_free(resource);

we should set it to NULL for safety

we should set it to NULL for safety

it wasn't addressed. The pointer still is not set to null, though with this function we can't actually do it, I guess. We need to pass pointer to pointer to actually do it. Instead, we can at least set it to null near each call to resource_destroy.

it wasn't addressed. The pointer still is not set to `null`, though with this function we can't actually do it, I guess. We need to pass pointer to pointer to actually do it. Instead, we can at least set it to null near each call to `resource_destroy`.
Author
Collaborator

Addressed in 60da899bd via the caller-side route — see #1160 reply.

Addressed in 60da899bd via the caller-side route — see #1160 reply.
jabber.developer marked this conversation as resolved
@@ -112,6 +87,7 @@ _pendingPresence_free(ProfPendingPresence* presence)
if (presence->last_activity)
g_date_time_unref(presence->last_activity);
free(presence->barejid);
resource_destroy(presence->resource);

We also clean it on line 138. Potential for double free. We need to set it to NULL in resource.c for safety. At least the resource itself.

We also clean it on line 138. Potential for double free. We need to set it to `NULL` in `resource.c` for safety. At least the resource itself.
Author
Collaborator

Fixed in 60da899bd — caller now nulls its pointer after resource_destroy.

Fixed in 60da899bd — caller now nulls its pointer after `resource_destroy`.

I am not sure where to reply, but in the roster_update_presence, resource is a param. setting it to null before returning does not increase safety. it will just override a local variable.

I am not sure where to reply, but in the `roster_update_presence`, `resource` is a param. setting it to `null` before returning does not increase safety. it will just override a local variable.
Author
Collaborator

Corrected

Corrected
jabber.developer marked this conversation as resolved
@@ -0,0 +1,4 @@
[wrap-git]
url = https://github.com/strophe/libstrophe.git

We might want to point to our fork

We might want to point to our fork
Author
Collaborator

Done — wrap now points at devs/libstrophe-gh-mirror (live auto-mirror, 8h). 1e372f6fa.

Done — wrap now points at `devs/libstrophe-gh-mirror` (live auto-mirror, 8h). 1e372f6fa.
jabber.developer marked this conversation as resolved
@@ -0,0 +17,4 @@
}
void
cmd_ac_complete_filepath__segfaults_when_empty(void** state)

segfaults_when_empty? Is it an actual behavior*? Or is it what we try to avoid? It's either a really strange test that expects undesired behavior or poor choice of a test name

*Wouldn't it also result in a CI failure?

`segfaults_when_empty`? Is it an actual behavior*? Or is it what we try to avoid? It's either a really strange test that expects undesired behavior or poor choice of a test name \*Wouldn't it also result in a CI failure?
jabber.developer marked this conversation as resolved
@@ -0,0 +78,4 @@
assert_non_null(res3);
assert_string_equal(res3, "/sendfile test_dir/file1.txt");
// SHIFT-TAB

This case doesn't test much :) Whether tab or shift+tab, we'll get file2.txt. We need at least 3 files to make an actual test.

This case doesn't test much :) Whether `tab` or `shift+tab`, we'll get `file2.txt`. We need at least 3 files to make an actual test.
jabber.developer marked this conversation as resolved
@@ -330,0 +479,4 @@
assert_string_equal("Could not convert \"23kk\" to a number.", err_msg);
g_free(err_msg);
err_msg = NULL;
}

We can add overflow and similar tests here.

#include <limits.h>
#include <stdio.h>

/**
 * This category tests the "Vulnerability" aspect.
 * It is distinct from 'out_of_range' because 'out_of_range' assumes 
 * the string is a valid integer. These tests check if the string 
 * violates the hardware limits of the 'int' type.
 */
void
strtoi_range__returns__false_for_integer_overflow_underflow(void** state)
{
    int value;
    gchar* err_msg = NULL;
    gboolean result;
    char buf[64];

    // 1. Test INT_MAX + 1 (Overflow)
    // We use long long to generate the string safely regardless of hardware
    snprintf(buf, sizeof(buf), "%lld", (long long)INT_MAX + 1);
    result = strtoi_range(buf, &value, INT_MIN, INT_MAX, &err_msg);
    assert_false(result);
    g_free(err_msg);
    err_msg = NULL;

    // 2. Test INT_MIN - 1 (Underflow)
    snprintf(buf, sizeof(buf), "%lld", (long long)INT_MIN - 1);
    result = strtoi_range(buf, &value, INT_MIN, INT_MAX, &err_msg);
    assert_false(result);
    g_free(err_msg);
    err_msg = NULL;

    // 3. Test Extreme Overflow (Larger than 'long')
    result = strtoi_range("9999999999999999999999999999999999", &value, INT_MIN, INT_MAX, &err_msg);
    assert_false(result);
    g_free(err_msg);
    err_msg = NULL;
}

/**
 * This category tests "Parsing Logic".
 * It ensures the function behaves exactly like strtol (base 0),
 * including hex, octal, and rejecting floating point.
 */
void
strtoi_range__returns_consistent_with_strtol_parsing(void** state)
{
    int value;
    gchar* err_msg = NULL;
    gboolean result;

    // 1. Hexadecimal (0x10 -> 16)
    result = strtoi_range("0x10", &value, 0, 100, &err_msg);
    assert_true(result);
    assert_int_equal(16, value);
    g_free(err_msg);
    err_msg = NULL;

    // 2. Octal (010 -> 8)
    result = strtoi_range("010", &value, 0, 100, &err_msg);
    assert_true(result);
    assert_int_equal(8, value);
    g_free(err_msg);
    err_msg = NULL;

    // 3. Floating Point (0.8)
    // This should fail because strtol sees the '.' and stops.
    // Since '.' != '\0', it is an invalid integer string.
    result = strtoi_range("0.8", &value, -10, 10, &err_msg);
    assert_false(result);
    g_free(err_msg);
    err_msg = NULL;
}
We can add overflow and similar tests here. ```c #include <limits.h> #include <stdio.h> /** * This category tests the "Vulnerability" aspect. * It is distinct from 'out_of_range' because 'out_of_range' assumes * the string is a valid integer. These tests check if the string * violates the hardware limits of the 'int' type. */ void strtoi_range__returns__false_for_integer_overflow_underflow(void** state) { int value; gchar* err_msg = NULL; gboolean result; char buf[64]; // 1. Test INT_MAX + 1 (Overflow) // We use long long to generate the string safely regardless of hardware snprintf(buf, sizeof(buf), "%lld", (long long)INT_MAX + 1); result = strtoi_range(buf, &value, INT_MIN, INT_MAX, &err_msg); assert_false(result); g_free(err_msg); err_msg = NULL; // 2. Test INT_MIN - 1 (Underflow) snprintf(buf, sizeof(buf), "%lld", (long long)INT_MIN - 1); result = strtoi_range(buf, &value, INT_MIN, INT_MAX, &err_msg); assert_false(result); g_free(err_msg); err_msg = NULL; // 3. Test Extreme Overflow (Larger than 'long') result = strtoi_range("9999999999999999999999999999999999", &value, INT_MIN, INT_MAX, &err_msg); assert_false(result); g_free(err_msg); err_msg = NULL; } /** * This category tests "Parsing Logic". * It ensures the function behaves exactly like strtol (base 0), * including hex, octal, and rejecting floating point. */ void strtoi_range__returns_consistent_with_strtol_parsing(void** state) { int value; gchar* err_msg = NULL; gboolean result; // 1. Hexadecimal (0x10 -> 16) result = strtoi_range("0x10", &value, 0, 100, &err_msg); assert_true(result); assert_int_equal(16, value); g_free(err_msg); err_msg = NULL; // 2. Octal (010 -> 8) result = strtoi_range("010", &value, 0, 100, &err_msg); assert_true(result); assert_int_equal(8, value); g_free(err_msg); err_msg = NULL; // 3. Floating Point (0.8) // This should fail because strtol sees the '.' and stops. // Since '.' != '\0', it is an invalid integer string. result = strtoi_range("0.8", &value, -10, 10, &err_msg); assert_false(result); g_free(err_msg); err_msg = NULL; } ```
jabber.developer marked this conversation as resolved
@@ -0,0 +315,4 @@
void
jid_is_valid_user_jid__is__true_for_valid_user_jid(void** state)
{
assert_true(jid_is_valid_user_jid("myuser@mydomain/laptop"));

We can add one more test for myuser@mydomain/user@laptop. As per RFC6122, section 2.4,

The '@' character is allowed in the resourcepart and is often used in the "nick" shown in XMPP chatrooms. For example, the JID <room@chat.example.com/user@host> describes an entity who is an occupant of the room room@chat.example.com with an (asserted) nick of user@host. However, chatroom services do not necessarily check such an asserted nick against the occupant's real JID.

We can add one more test for `myuser@mydomain/user@laptop`. As per [RFC6122, section 2.4](https://www.rfc-editor.org/rfc/rfc6122.html#section-2.4), > The '@' character is **allowed in the resourcepart** and is often used in the "nick" shown in XMPP chatrooms. For example, the JID <room@chat.example.com/user@host> describes an entity who is an occupant of the room <room@chat.example.com> with an (asserted) nick of <user@host>. However, chatroom services do not necessarily check such an asserted nick against the occupant's real JID.
jabber.developer marked this conversation as resolved
jabber.developer2 added 14 commits 2026-04-24 12:17:22 +00:00
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
If we build Profanity without GTK we dont iterate the context so we will
never know when the editor exited. Calling `g_main_context_iteration()`
or finally switching to GMainLoop fixes this.
So far calling `tray_update()` does this for us in GTK builds.
So this went unnoticed.

The editor will inherit ignored signal handlers (SIGINT, SIGTSTP, SIGPIPE)
from us. Neovim uses libuv which seems to check SIGINT on startup to
determine whether the environment is interactive or not.
We now reset these signals to SIG_DFL in the child process before execvp.

Profanity uses readline which still competed for terminal input with the
editor. We only took care about other Profanity UI processes in our
previous commit. This led to misordered characters in the editor.

In my tests with vim everything worked fine and these bugs were
discovered when a user used neovim.

Fixes: https://github.com/profanity-im/profanity/issues/2148
Ref: 36ec2b0ae1
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
We recently made the jid validation and creation functions more strict.
So this is a sideffect of it. We need to use `jid_fulljid_or_barejid`
instead of jidp->fulljid here since it now treats just the barejid not
as a fulljid.

Ref: 09757da5df
Ref: f251cc8bb3
Ref: fa64b135df
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
fix: resolve issues with async editor
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
Signed-off-by: Michael Vetter <jubalh@iodoru.org>
chore: Explain how to get a backtrace
Fixes: #2152
Signed-off-by: Benson Muite <benson_muite@emailplus.org>
Updated tooling builds supported XEP list from profanity.doap file
The profanity.doap file is a git submodule in the website repository.
It is used as an input to create the supported XEPs webpage.
3cd439d172

Fixes: #2154
Signed-off-by: Benson Muite <benson_muite@emailplus.org>
docs: XEP list does not need to be updated manually on a release
doc: Explain how to enable spell checking
# Conflicts:
#	CONTRIBUTING.md
#	src/profanity.c
#	src/tools/editor.c
refactor: address PR #105 review feedback
Some checks failed
CI Code / Check spelling (pull_request) Successful in 21s
CI Code / Check coding style (pull_request) Successful in 35s
CI Code / Linux (arch) (pull_request) Failing after 2m52s
CI Code / Code Coverage (pull_request) Successful in 2m56s
CI Code / Linux (debian) (pull_request) Successful in 6m48s
CI Code / Linux (ubuntu) (pull_request) Successful in 7m11s
4401e817d0
Apply fixes, refactors and test additions requested by reviewer on PR #105.

Fixes:
- database: warn and notify user on duplicate archive_id instead of
  silently debug-logging it (R05).
- database: add missing '[' in "[DB Migration]" log prefix (R06).
- xmpp/resource: NULL-out name/status after g_free to avoid double-free
  via roster_list.c cleanup path (R09, R23).
- common: widen strtoi_range internal storage from int to long so that
  values in (INT_MAX, LONG_MAX] are rejected as out-of-range instead of
  being silently truncated on 64-bit platforms (R25).

Refactors:
- xmpp/message: extract _receive_omemo helper, removing three copies of
  the OMEMO receive block in groupchat / MUC-PM / chat handlers (R04).
- omemo: flatten deeply nested device-list processing via guard-clause
  continues (R11).
- tools/autocomplete: merge two nested ifs into a single && condition
  (R13).
- ui/titlebar: extract _show_trust_indicator and inline _wprintw_withattr
  wrapper, collapsing three near-identical trust-indicator blocks (R22).
- config/tlscerts: drop _checked_g_strdup wrapper; g_strdup is
  NULL-safe per glib documentation (R19).
- ui/inputwin: use auto_gchar for spellcheck word instead of manual
  g_free (R20).
- tools/editor: drop outdated "Deprecated synchronous" comment that
  no longer matches the callback-based implementation (R28).

Tests:
- tests/command/cmd_ac: rename segfaults_when_empty ->
  no_segfault_when_empty; expand cycling coverage to three files plus
  backward SHIFT-TAB traversal (R16, R17).
- tests/common: add strtoi_range overflow/underflow and strtol-parsing
  consistency tests (R25).
- tests/xmpp/jid: add test for '@' inside resourcepart per RFC 6122
  section 2.4 (R26).

Misc:
- xmpp/omemo: change omemo_error_to_string return type from char* to
  gchar* for glib consistency (R01).
- subprojects/libstrophe: point wrap-git at our fork at
  git.jabber.space/devs/libstrophe-gh (R24).
- RELEASE_GUIDE: drop "Updating website" section referring to an
  upstream site that is not ours (R18).
jabber.developer2 added 1 commit 2026-04-24 12:42:25 +00:00
ci: fix Valgrind startup failure on Arch via DEBUGINFOD_URLS
All checks were successful
CI Code / Check spelling (pull_request) Successful in 22s
CI Code / Check coding style (pull_request) Successful in 33s
CI Code / Linux (debian) (pull_request) Successful in 6m54s
CI Code / Linux (ubuntu) (pull_request) Successful in 7m4s
CI Code / Code Coverage (pull_request) Successful in 6m49s
CI Code / Linux (arch) (pull_request) Successful in 8m46s
c815c39cdd
Arch Linux ships a stripped ld-linux-x86-64.so.2, so Valgrind cannot
find the memcmp/memcpy/strlen symbols it must redirect and exits at
startup with "Fatal error at startup: a function redirection which is
mandatory for this platform-tool combination cannot be set up".

debuginfod is already installed in the image but was never wired up.
Set DEBUGINFOD_URLS to the official Arch debuginfod server so Valgrind
fetches the missing glibc debug symbols on demand and caches them.

My feedback was partially addressed. I have updated the feedback to reflect current readiness.

My feedback was partially addressed. I have updated the feedback to reflect current readiness.

Acceptance criteria:

  • basic functionality works
    • OMEMO chat
    • OTR chat
    • PGP chat
  • editor (incl. alt+c) works in async without issues with nano, vim
  • OTR works with other CProof clients (including older versions), Psi+, Gajim, Pidgin, maybe Xabber/Conversation/other android apps + conversejs
  • extremely long messages (10-100k lines) do not lead to crash + long messages are well-handled (the issue we fixed previously; I worry that mix of 2 fixes might lead to unintended negative consequences)
Acceptance criteria: - basic functionality works - OMEMO chat - OTR chat - PGP chat - editor (incl. `alt+c`) works in async without issues with nano, vim - OTR works with other CProof clients (including older versions), Psi+, Gajim, Pidgin, maybe Xabber/Conversation/other android apps + conversejs - extremely long messages (10-100k lines) do not lead to crash + long messages are well-handled (the issue we fixed previously; I worry that mix of 2 fixes might lead to unintended negative consequences)
jabber.developer2 added 3 commits 2026-04-28 07:36:01 +00:00
Turn on the stricter conversion-safety warnings and wire in an optional
sanitizer build mode. Nothing is promoted to a hard error yet: the
warnings become part of the build output and can be cleaned up
incrementally without blocking development.

- Drop -Wno-sign-compare and add -Wsign-compare + -Wconversion to the
  GCC-specific warnings loop (probed for compiler support).
- Add --enable-sanitizers flag that enables ASan + UBSan +
  -fsanitize=unsigned-integer-overflow with -fno-sanitize-recover=all.
  Off by default; intended for a dedicated CI job.
- Under PACKAGE_STATUS=development (-Werror), opt out of turning the
  new conversion/sign-compare warnings into hard errors via
  -Wno-error=sign-conversion/-conversion/-float-conversion/-sign-compare.
  Existing codebase has ~240 sign-conversion, ~87 other narrowing and a
  handful of sign-compare warnings (mostly inherited from upstream);
  they stay visible but don't block development builds.
- Route glib/gio CFLAGS through -isystem so macros like
  GPOINTER_TO_UINT in glibconfig.h don't generate noise.
Cleanup of the conversion-safety warnings exposed by enabling
-Wconversion / -Wsign-compare in the previous commit, plus guard
clauses at the few places where unsigned arithmetic could actually
misbehave.

Build:
- configure.ac drops -Wno-error=conversion and
  -Wno-error=float-conversion. Only -Wno-error=sign-conversion and
  -Wno-error=sign-compare remain, gating the ~230 sign warnings
  inherited from upstream that will be cleaned up in follow-ups.

Type / conversion fixes (no behaviour change):
- Length-like locals in command/cmd_ac.c, command/cmd_funcs.c,
  pgp/gpg.c, tools/autocomplete.c, tools/parser.c and ui/mucwin.c
  switched from int to size_t / glong (matching strlen /
  g_utf8_strlen return type) so we no longer need an (int) cast and
  loop counters / array sizes stay in their natural unsigned domain.
- g_timer_elapsed / GTimeSpan -> int casts in session.c, iq.c,
  core.c, server_events.c, window.c.
- _win_print_wrapped: indent parameter and local curx/maxx switched
  from size_t to int to match _win_indent / getcurx / getmaxx.
- Port casts (int -> unsigned short) at the libstrophe boundary in
  connection.c and session.c, each preceded by
  g_assert(port >= 0 && port <= UINT16_MAX) so the truncation is
  documented at the call-site.
- curl_off_t / fread size_t results cast at usage in http_upload.c,
  http_download.c, omemo/crypto.c.
- strtoul results cast to uint32_t in xmpp/omemo.c and omemo/omemo.c
  where device/prekey IDs are genuinely 32-bit.
- config/color.c: fg/bg/palette indices switched to `short`
  end-to-end (find_col, color_hash, find_closest_col,
  _color_pair_cache_get, cache.pairs), so the ncurses init_pair
  boundary needs at most one (short)i cast for the cache index. Also
  TODO-noted: init_extended_pair is needed for >15-bit palettes.
- xmpp/avatar.c: float arithmetic explicitly casts its int operands.
- tests/functionaltests/proftest.c: read() result handling uses
  size_t for the accumulator, _read_output returns ssize_t, and the
  buffer-shift check happens before space subtraction so the
  expression cannot underflow.

Real-risk guard clauses (the part that actually fixes bugs):
- src/ui/statusbar.c _tabs_width: `end > opened_tabs - 1` rewritten
  as `end < opened_tabs` so opened_tabs == 0 no longer underflows.
- src/ui/statusbar.c _status_bar_draw_extended_tabs: the mirror
  comparison rewritten as `end >= opened_tabs`.
- src/ui/statusbar.c status_bar_draw: replaced
  `MAX(0, getmaxx - (int)_tabs_width)` with an explicit precheck
  before subtraction.
- src/omemo/omemo.c prekey selection: prekey_index is now uint32_t
  and randomized into an unsigned buffer, so modulo with prekeys_len
  cannot yield a negative index for g_list_nth_data.
- src/omemo/crypto.c omemo_decrypt_func: PKCS#5/PKCS#7 unpadding
  reads `plaintext[plaintext_len - 1]`, which would underflow on a
  malformed empty ciphertext and read past the heap buffer. Reject
  plaintext_len == 0 before the padding peek and validate the
  padding byte against the buffer length before the unpad loop.
  Initialise plaintext = NULL so the early `goto out` cannot free
  uninitialised memory.
- src/ui/inputwin.c (4 mbrlen sites) and src/ui/window.c
  _win_print_wrapped: mbrlen() returns 0 for the null wide
  character. The existing checks rejected (size_t)-1 / -2 but
  treated 0 as a valid step, so the surrounding loops would either
  advance by SIZE_MAX (i += ch_len - 1) or spin in place
  (word_pos += 0 forever). Add `|| ch_len == 0` to each guard;
  inside the spell-check word-emission loop also fall back to a
  one-byte advance.
- Defensive `len > 0 ? len - 1 : 0` prechecks at the strlen-based
  g_strndup / loop sites in ui/console.c, plugins/c_api.c and
  plugins/python_plugins.c.
fix(subprojects): point libstrophe.wrap at libstrophe-gh-mirror
All checks were successful
CI Code / Check spelling (pull_request) Successful in 22s
CI Code / Check coding style (pull_request) Successful in 36s
CI Code / Code Coverage (pull_request) Successful in 2m57s
CI Code / Linux (debian) (pull_request) Successful in 4m15s
CI Code / Linux (ubuntu) (pull_request) Successful in 4m40s
CI Code / Linux (arch) (pull_request) Successful in 10m13s
1e372f6fa8
devs/libstrophe-gh is a static fork; -mirror is the live one.
jabber.developer2 added 1 commit 2026-04-28 14:34:18 +00:00
fix: address open PR #105 review comments (leaks + null safety)
All checks were successful
CI Code / Check spelling (pull_request) Successful in 20s
CI Code / Check coding style (pull_request) Successful in 38s
CI Code / Linux (ubuntu) (pull_request) Successful in 4m36s
CI Code / Linux (arch) (pull_request) Successful in 4m42s
CI Code / Code Coverage (pull_request) Successful in 7m7s
CI Code / Linux (debian) (pull_request) Successful in 7m28s
60da899bd9
- ui/window.c win_show_subwin (#1144): unconditional newpad without
  freeing the previous pad leaks the ncurses subwin if the function
  is invoked twice without an intervening win_hide_subwin. Added a
  delwin guard.
- omemo/omemo.c omemo_on_disconnect (#1126): hash tables and
  libsignal handles are destroyed unconditionally even when a full
  init never ran (e.g. shutdown after a failed load), and neither
  g_hash_table_destroy nor signal_*_destroy are documented as
  NULL-safe. NULL-check each handle before tearing it down.
- xmpp/roster_list.c (#1140 / #1141 / #1160): null the caller's
  pointer immediately after resource_destroy() so a stale reference
  cannot trigger a double-free; resource_destroy itself only zeroes
  the struct fields it owns and cannot reach back to the caller.
jabber.developer2 added 2 commits 2026-04-28 15:51:24 +00:00
# Conflicts:
#	configure.ac
Revert build: enable -Wconversion/-Wsign-compare and add --enable-sanitizers
All checks were successful
CI Code / Check spelling (pull_request) Successful in 21s
CI Code / Check coding style (pull_request) Successful in 35s
CI Code / Code Coverage (pull_request) Successful in 2m50s
CI Code / Linux (debian) (pull_request) Successful in 4m19s
CI Code / Linux (ubuntu) (pull_request) Successful in 4m32s
CI Code / Linux (arch) (pull_request) Successful in 5m4s
a7ad9ac0a4
This reverts the configure.ac

Tracked as a TODO under issue #112.
jabber.developer added the
Priority
Low
4
label 2026-04-29 09:04:52 +00:00
jabber.developer2 added 1 commit 2026-05-18 08:48:59 +00:00
Merge branch 'master' into merge/upstream-full
Some checks failed
CI Code / Check spelling (pull_request) Successful in 17s
CI Code / Check coding style (pull_request) Successful in 31s
CI Code / Linux (debian) (pull_request) Failing after 39s
CI Code / Linux (ubuntu) (pull_request) Failing after 42s
CI Code / Linux (arch) (pull_request) Failing after 4m59s
CI Code / Code Coverage (pull_request) Failing after 4m48s
508de43da9
Resolve conflicts after master gained the flat-file database backend,
AI client, and other cproof-fork features:

- CHANGELOG: keep cproof-fork unreleased section atop full upstream
  history (0.17.0 / 0.16.0 / 0.15.1)
- src/command/cmd_ac.c: keep both upstream spellcheck AC and cproof
  /history switch|verify|export|import autocompletion
- src/config/preferences.c: merge upstream [spellcheck] group with the
  cproof [ai]/[ai/<provider>] groups
- src/database.c: take master (thin dispatcher); upstream SQLite
  improvements (v3 migration, auto_gchar) belong in database_sqlite.c
  and will land in a follow-up commit
- src/tools/autocomplete.c: take upstream unescape of search_str
- src/ui/statusbar.c: keep guint signatures (per project preference)
  and adapt _status_bar_draw_dbbackend to guint as well
- src/xmpp/jid.c: combine the new jid_is_valid/jid_is_valid_user_jid
  with the "(null)" legacy-placeholder guard in
  jid_create_from_bare_and_resource; use auto_gchar
- tests/functionaltests/proftest.c: keep unsigned-safe pre-check on
  output_len before subtracting OUTPUT_BUF_SIZE
- tests/unittests/unittests.c: keep upstream subdirectory include
  layout plus cproof-only test_ai_client/database_export/database_stress
- tests/unittests/test_jid.c: drop orphan top-level copy (the
  restructured version lives in tests/unittests/xmpp/test_jid.c)

Upstream is included up to 1ac05754b (release 0.18.0 era). Follow-up:
pull 1ac05754b..upstream/master (esp. 42a849d16 db migration fix and
4749645c7 receipt-request restore), and adapt the SQLite improvements
into database_sqlite.c.
jabber.developer2 added 1 commit 2026-05-18 09:01:45 +00:00
fix(merge): drop leftover <<<<<<< HEAD marker in jid_is_valid
Some checks failed
CI Code / Check spelling (pull_request) Successful in 17s
CI Code / Check coding style (pull_request) Successful in 32s
CI Code / Linux (debian) (pull_request) Failing after 4m54s
CI Code / Linux (ubuntu) (pull_request) Failing after 5m6s
CI Code / Linux (arch) (pull_request) Failing after 5m49s
CI Code / Code Coverage (pull_request) Failing after 6m22s
c0f8f1a0f1
My earlier merge-resolution Edit on src/xmpp/jid.c only covered the
=======/>>>>>>> half of the hunk and left an orphan <<<<<<< HEAD on
line 82 of jid_is_valid. CI caught it. Removed.
jabber.developer2 was assigned by jabber.developer 2026-05-20 11:44:45 +00:00
jabber.developer added
Priority
High
2
and removed
Priority
Low
4
labels 2026-05-20 11:45:23 +00:00
jabber.developer2 added 2 commits 2026-05-21 09:01:03 +00:00
Functional test stbbr fixture used the old Profanity caps verification
hash (hAkb1xZdJV9BQpgGNw8zG5Xsals=) for buddy1's simulated presence
and the corresponding disco#info reply. The current build computes
JNkIRQChhYM8+Co3IypmMtMJnOE= for the same identity+features set, so
align the fixture with what the running client now produces.
Merge branch 'master' into merge/upstream-full
All checks were successful
CI Code / Check spelling (pull_request) Successful in 17s
CI Code / Check coding style (pull_request) Successful in 31s
CI Code / Linux (debian) (pull_request) Successful in 4m48s
CI Code / Linux (ubuntu) (pull_request) Successful in 4m56s
CI Code / Code Coverage (pull_request) Successful in 7m0s
CI Code / Linux (arch) (pull_request) Successful in 10m5s
dfdca61073
Bring in master commits since the previous merge:
- 4776c1f1e fix(ai): properly decode \uXXXX JSON escape sequences as UTF-8
- 53cdf488b fix(ai): resolve UAF and require provider argument instead of defaults
- 3b673150b chore(chatlog): disable background message file logging (stage 1)

Conflict in src/chatlog.c: master stubs out all chat_log_* function
bodies (project policy — disable plain-text per-day chatlogs), while
the upstream-merged version in this branch still had upstream's full
implementation. Taking master's version entirely keeps the no-op
chatlog API surface that the ~20 call sites in event/, otr/ and
profanity.c depend on.
jabber.developer2 added 3 commits 2026-05-21 09:31:31 +00:00
The SQLite-backend code now lives in database_sqlite.c (master split
src/database.c into a thin backend dispatcher); the upstream changes
that PR 105 had folded into the monolithic database.c were not
ported across when we took master's dispatcher version. This brings
forward the v3 schema work and folds in upstream 42a849d16
(fix(database): resolve migration failure and improve init safety):

- latest_version bumped 2 -> 3; ChatLogs.archive_id gains UNIQUE
  constraint; _migrate_to_v3 dedupes existing rows via the
  ChatLogs_v3_migration intermediate table
- DbVersion bookkeeping uses DELETE+INSERT instead of UPDATE so
  multiple rows do not trip the unique-constraint failure reported
  upstream (#2105)
- Initial INSERT INTO DbVersion uses latest_version via
  sqlite3_mprintf, so brand-new databases are stamped at the current
  schema and skip redundant migrations
- server_events.c now checks log_database_init's return so a broken
  DB init is logged instead of silently leaving the handle half-open

(644/0, 605/0, 605/0, 644/0 unit + 130/0 functional each).
When the receipts-request preference is on but we have no cached
EntityCapabilities for the recipient (new session, bare-jid only,
disco not finished), the previous check via caps_jid_has_feature()
defaulted to 'not supported' and silently suppressed the receipt.
Per XEP-0184 we should send the receipt request unless we have
positive knowledge that the peer does not advertise the feature.

Restore that behaviour: look up caps with caps_lookup(); only flip
request_receipt off when caps are present AND the feature is
missing from the list. If caps are NULL, leave the request enabled
(falls back to the user preference).
tests: add ui_is_suspended stub (upstream 3326ce5c6)
All checks were successful
CI Code / Check spelling (pull_request) Successful in 18s
CI Code / Check coding style (pull_request) Successful in 31s
CI Code / Code Coverage (pull_request) Successful in 2m28s
CI Code / Linux (ubuntu) (pull_request) Successful in 4m20s
CI Code / Linux (debian) (pull_request) Successful in 6m45s
CI Code / Linux (arch) (pull_request) Successful in 9m36s
14380c8373
Add stub for ui_is_suspended() in tests/unittests/ui/stub_ui.c so
the unit-test binary links cleanly against any code path that calls
it from non-UI translation units.
jabber.developer2 added 1 commit 2026-05-21 09:50:39 +00:00
refactor: address PR #105 review (#1222, #1226)
All checks were successful
CI Code / Check spelling (pull_request) Successful in 17s
CI Code / Check coding style (pull_request) Successful in 30s
CI Code / Code Coverage (pull_request) Successful in 2m36s
CI Code / Linux (debian) (pull_request) Successful in 4m39s
CI Code / Linux (ubuntu) (pull_request) Successful in 4m48s
CI Code / Linux (arch) (pull_request) Successful in 5m36s
9b03e3a508
#1222 (src/xmpp/roster_list.c): remove dead 'resource = NULL' inside
roster_update_presence() — assigning to a value parameter never
affects the caller. Document the ownership contract: the function
consumes 'resource' on every return path and callers must null
their own pointer afterwards. The existing caller in
roster_process_pending_presence already does that, and
resource_destroy() is NULL-safe.

#1226 (src/tools/editor.{c,h}): remove get_message_from_editor[_async]
and the surrounding async-editor infrastructure (editor_thread,
editor_task global, editor_process polling, EditorTask typedef).
The upstream callback-based launch_editor API has replaced every
call site (cmd_funcs.c, inputwin.c). Also drop the now-unused
'background_mode' global (only the obsolete async path ever set it
to TRUE) and the editor_process() call in profanity.c main loop.

Verified with ci-build.sh in Debian docker — all 4 build configs
pass (644/0, 605/0, 605/0, 644/0 unit + 130/0 functional each).
jabber.developer2 added 1 commit 2026-05-21 12:34:49 +00:00
fix(review): address F1, F6, F9, F16 from deep review
All checks were successful
CI Code / Check spelling (pull_request) Successful in 15s
CI Code / Check coding style (pull_request) Successful in 32s
CI Code / Linux (debian) (pull_request) Successful in 5m1s
CI Code / Linux (ubuntu) (pull_request) Successful in 5m2s
CI Code / Linux (arch) (pull_request) Successful in 5m47s
CI Code / Code Coverage (pull_request) Successful in 6m35s
97fd81b60b
- src/log.c (F16): log_stderr_init no longer closes STDERR_FILENO
  immediately after dup2(). The previous 'close(dup_fd)' closed fd 2
  because dup2 returns newfd on success; the in-app stderr capture
  pipe was silently dead, dropping libstrophe/openssl error output.
  Close the original stderr_pipe[1] instead and remember that the
  write end now lives at STDERR_FILENO so _log_stderr_close stays
  correct.

- src/database_sqlite.c (F1): NULL-check sqlite3_mprintf result
  before passing it to sqlite3_exec in the DbVersion bootstrap path.

- src/tools/editor.c (F9): drop three orphan #includes
  (<fcntl.h>, <pthread.h>, <readline/readline.h>) left behind when
  9b03e3a50 removed the async-editor path.

- src/xmpp/jid.c (F6): match the g_new0 allocation in jid_create
  with g_free in jid_destroy. Same behaviour on glibc but stops
  being a foot-gun under custom glib allocators.

Verified with ci-build.sh in Debian docker: all 4 configs pass
(644/0, 605/0, 605/0, 644/0 unit + 130/0 functional).
jabber.developer2 force-pushed merge/upstream-full from 97fd81b60b to adab585c09 2026-05-21 12:40:40 +00:00 Compare
jabber.developer approved these changes 2026-05-22 15:49:33 +00:00
Dismissed
jabber.developer left a comment
Owner

LGTM.

LGTM.
jabber.developer changed title from WIP: merge/upstream-full to merge/upstream-full 2026-05-22 18:44:28 +00:00

in editor.c, there is double #include "ui/ui.h"

in `editor.c`, there is double `#include "ui/ui.h"`
jabber.developer2 added 2 commits 2026-05-23 14:00:19 +00:00
- Sanitize account names to prevent GKeyFile config injection by
  blocking special characters like #, \n, and \r.
- Ensure GLib's SIGCHLD handler is initialized before forking
  external editors to prevent zombie processes.
- Add missing error check for OMEMO random key generation.
- Replace hardcoded console tab ID with a named constant.
- Log invalid JIDs safely to prevent potential null pointer issues.
fix: follow-ups to cherry-picked d3d7c328b
All checks were successful
CI Code / Check spelling (pull_request) Successful in 18s
CI Code / Check coding style (pull_request) Successful in 32s
CI Code / Code Coverage (pull_request) Successful in 2m37s
CI Code / Linux (debian) (pull_request) Successful in 4m40s
CI Code / Linux (ubuntu) (pull_request) Successful in 4m50s
CI Code / Linux (arch) (pull_request) Successful in 5m39s
1dd22af294
- src/xmpp/jid.c: add missing #include "log.h" — d3d7c328b
  introduced a log_debug() call in jid_create without pulling in
  the header, so the source tree did not actually build.

- src/omemo/omemo.c omemo_on_message_send: add the missing
  NULL-check after the gcry_malloc_secure calls for 'tag' /
  'key_tag' so a real OOM does not sail into the memcpy/encrypt
  path. (The 'key' check d3d7c328b added is defensive only —
  gcry_random_bytes_secure aborts internally on failure.)

- src/omemo/omemo.c _cache_device_identity: drop the second
  'if (!fingerprint) return' on the autocomplete path. fingerprint
  is already NULL-checked at the top of the function and is not
  reassigned in between, so the second guard is dead code (upstream
  artefact).

- src/tools/editor.c launch_editor: replace the
  g_main_context_acquire/release pair with
  g_child_watch_source_new(getpid()) + g_source_unref.
  acquire/release does not install GLib's SIGCHLD handler — that
  handler is registered as a side effect of constructing a
  GChildWatchSource. Now we actually pre-arm SIGCHLD before
  forking, which is what d3d7c328b's comment claimed.

- src/ui/statusbar.c status_bar_inactive / _create_tab: switch the
  remaining two literal '10' tab-id values to the CONSOLE_TAB_ID
  define introduced by d3d7c328b, then inline the expression
  directly into the GINT_TO_POINTER() argument so the one-shot
  local 'true_win' goes away.

Verified with ci-build.sh in Debian docker: all 4 configs pass
(644/0, 605/0, 605/0, 644/0 unit + 130/0 functional).
jabber.developer2 added 1 commit 2026-05-24 08:14:19 +00:00
fix(editor): remove duplicate #include "ui/ui.h"
All checks were successful
CI Code / Check spelling (pull_request) Successful in 16s
CI Code / Check coding style (pull_request) Successful in 32s
CI Code / Code Coverage (pull_request) Successful in 2m36s
CI Code / Linux (debian) (pull_request) Successful in 4m42s
CI Code / Linux (ubuntu) (pull_request) Successful in 4m52s
CI Code / Linux (arch) (pull_request) Successful in 5m41s
36f4377b4c
editor.c included ui/ui.h twice in a row (lines 25 and 27). Harmless
due to the header guard but pure noise.
jabber.developer approved these changes 2026-05-26 17:39:49 +00:00
jabber.developer left a comment
Owner

LGTM

LGTM
jabber.developer manually merged commit 72f4f186da into master 2026-05-26 17:54:34 +00:00
Sign in to join this conversation.
No description provided.