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.
_win_printf dropped buffer append and render for any window in paged
state, but only WIN_CHAT can recover lost messages via chatwin_db_history()
on scroll-down. WIN_MUC, WIN_PRIVATE and WIN_AI have no such fallback, so
incoming and outgoing messages were silently lost when the user was viewing
history.
Introduce log_database_can_recover_messages() to check whether the DB
backend can replay messages (returns FALSE when PREF_DBLOG is "off",
"redact", or no backend is active). Gate the WIN_CHAT early return in
_win_printf() on this check: when recovery is impossible, fall through
and append to the buffer so messages remain visible on scroll-down.
Add a buffer-bottom reset in win_page_down() for non-chat windows.
WIN_SCROLL_REACHED_BOTTOM is only set on the is_chat DB branch, so
non-chat windows never clear paged on their own; reset paged and
unread_msg when the last line of the buffer reaches the screen.
Add missing scroll rendering for AI windows in the title bar draw
function, ensuring AI windows display their scrolled state consistently
with other window types.
Remove the manual paged/unread_msg reset before printing the user
message in cl_ev_send_ai_msg() -- it was a local workaround for the
same drop and is no longer needed.
Add an AI client module that integrates with OpenAI-compatible API
providers (OpenAI, Perplexity, and custom endpoints) to provide
AI-assisted chat within CProof. Users can start sessions with /ai start,
send prompts, receive responses in a dedicated AI window, switch between
providers and models, and manage API keys — all with tab-completion.
Providers are configured via /ai set commands with per-provider API keys,
endpoints, default models, and custom settings. Two default providers
(openai, perplexity) are seeded on first use. Provider state persists in
[ai/<name>] sections of the preferences keyfile with automatic migration
from the previous flat-key format.
The /ai command integrates into the existing command system with 8
subcommands covering provider management, session lifecycle, model
fetching, and conversation clearing. Autocomplete uses the standard
flat prefix-matching chain for reliable tab-completion at every nesting
level. A new ProfAiWin window type is added to the window system.
Architecture:
Async design: HTTP requests run on a background thread (pthread) to avoid blocking the ncurses UI loop; results are displayed on the main thread via direct function calls
Thread safety: AIProvider and AISession use atomic ref-counting and mutex-protected session state; the request thread snapshots all session data before making the HTTP call
Window validation: wins_ai_exists() prevents use-after-free when the user closes the AI window during an in-flight HTTP request (~60s)
Privacy: store:false is sent with every request to prevent providers from persisting conversations or using them for training
Response size limit: 10MB cap with immediate curl abort via CURL_WRITEFUNC_ERROR to prevent OOM
JSON parsing uses unified helpers for both chat responses and error
envelopes with consistent escape decoding. The response parser tries
Perplexity /v1/responses "text" field first, then falls back to OpenAI
"content". Error parsing extracts provider error.message from the
standard envelope format. Model parsing handles multiple API response
formats (OpenAI list, Perplexity, array) including edge cases.
Tests include 470+ lines of unit tests covering provider management,
session lifecycle, JSON parsing (multiple formats), autocomplete cycling,
and error handling, plus functional tests for /ai command dispatch.
A stub_ai.c module isolates unit tests from UI dependencies.
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.
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
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).
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
Implement /force-encryption command to configure forced encryption
settings with on|off and policy resend-to-confirm|block options.
The changes enhance user experience by clarifying encryption requirements
and providing actionable commands. Users can now press Enter again to
confirm unencrypted messages in resend-to-confirm mode.
Simple `ui_resize` is sufficient for rerendering all the elements.
Other calls create complexity, slow down the applications and even introduce mem leaks.
`ui_resize` technique is already used widely, including `cmd_editor` case.
Fixes#4
Instead of always returning a `strdup()`'ed version, maybe return NULL and
handle this in the calling code.
This is still not true for `plugins_pre_chat_message_display()`, where we
return the passed `messag` in case there are no plugins, since this would
require a bigger refactor of more parts.
This also
* merges the implementation of `sv_ev_incoming_private_message()` and
`sv_ev_delayed_private_message()`, since they differed only by a single
line.
* untangles the `#ifdef` mess in `cl_ev_send_muc_msg_corrected()`.
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
In the documentation of the `muc_nick` function we can read:
> The nickname is owned by the chat room and should not be modified or freed
We should reflect that in the return type and `strdup` the value for the
plugin API.
Fixes: https://github.com/profanity-im/profanity/issues/2013
Feature discovery was marked as finished once we received a reply to the
initial request. The discovery mechanism allows to delegate the real
feature discovery to another service running on different domain and those
requests are created dynamically.
This was another instance causing the warning message described in #1940Fixes#1940 (once more)
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
Use a singleton `Jid` inside the connection instead of always re-creating
a `Jid` from the same string.
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
There were multiple reports where after a reconnect the window of the
MUC that was last opened, was empty.
`muc_join()` creates an instance of a MUC, `presence_join_room()` works
with this instance. Therefore the instance has to exist before working on
it.
I'm not sure if this really fixes the issue, but at least it didn't
happen anymore after I applied this modification.
I can't remember how I stumbled over this, either while looking at debug
logs or while looking at Valgrind output while a reconnect happened, but
something went wrong. Then I came to the conclusion that this may fix
the issue and for now it did ... maybe it comes back, then my RCA was
wrong.
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
Sometimes after a reconnect the current window would get cleared. This
was a deliberate change to fix the profanity window looking all garbled
up after providing the passphrase for a gpg key using pinentry-curses.
Fixes https://github.com/profanity-im/profanity/issues/1556
I think this PR already solves and improves the MAM situation a lot.
What's @MarcoPolo-PasTonMolo still wanted to do in this branch is:
* MAM for mucs
* Check if url and quotes autocompletion works fine
* Check if the api still works fine
* Resolve conflicts
Conflicts are solved with this commit.
MAM for mucs can be another feature PR.
The rest we can check while being on master. And more people can help
testing.
Only nicknames, photos, birthdays, addresses, telephone numbers, emails,
JIDs, titles, roles, notes, and URLs are supported
Due to the synopsis array not having enough space, `/vcard photo
open-self` and `/vcard photo save-self` are not documented properly in
the synopsis section of the `/vcard` command, but they are documented in
the arguments section
Fixed memory leak in vcard autocomplete (thanks to debXwoody)