Deprecate /logging command in favor of /history for chat logging control.
/history off now stops persistence (sets PREF_DBLOG=off + PREF_CHLOG=false)
in addition to hiding history on open. /history on restores persistence
(re-enabling PREF_DBLOG=on if it was off) and PREF_CHLOG.
statusbar
- PREF_STATUSBAR_SHOW_DBBACKEND (default ON) gates the [sqlite] /
[flatfile] indicator; toggle via "/statusbar show|hide dbbackend"
/history off|on
- "/history off" now stops persistence as well as hiding history on
open: sets PREF_DBLOG=off + PREF_CHLOG=false in addition to
PREF_HISTORY=false
- "/history on" restores persistence (re-enabling PREF_DBLOG=on if
it was off) and PREF_CHLOG, in addition to PREF_HISTORY=true
/logging
- Deprecated /logging command. It now prints a single notice
pointing to /history; CMD_PREAMBLE trimmed (min_args=0, no
setting_func, syntax/args/examples dropped); subcommand 'group'
removed from autocomplete
- All "use '/logging chat on' to enable" hints replaced with
"/history on" across /omemo-log, /pgp-log, /otr-log, and /ox-log
/privacy logging
- Single-pass validation across {on, off, redact, flatfile}
- Backend-switching values (on, flatfile) now take effect
immediately when connected (via log_database_switch_backend),
matching "/history switch" behaviour; off/redact only flip
pref bits and keep the live backend open
/correction off
- win_print_outgoing and win_print_outgoing_with_receipt now gate
_win_correct on PREF_CORRECTION_ALLOW, matching incoming-msg
paths. Previously a peer's correction reflected via XEP-0280
carbons (or the user's own /correct invocation) was applied
in-buffer regardless of the pref
console
- Drop orphaned /logging chat reference from cons_privacy_setting()
- Delete cons_logging_setting() and remove its call from
cons_show_log_prefs()
autocomplete
- Add dbbackend to statusbar_show_ac
- Remove logging_ac entries for chat/group subcommands
- Remove _logging_autocomplete() param handler for chat subcommand
database_flatfile
- Two local g_strndup allocations switched from char* with manual
g_free to auto_gchar gchar* for automatic cleanup
tests
- Update test_cmd_otr.c to expect new /history-on warning messages
@author: jabber.developer2 <jabber.developer2@jabber.space>
A flat-file alternative to the SQLite chatlog backend with runtime
switching, full migration tooling, integrity verification, and a
synthetic load harness. SQLite remains the default; both backends share
one dispatch layer (db_backend_t vtable) so callers don't change.
Storage layout
- Per-contact append-only `flatlog/<account>/<contact>/history.log`
under XDG_DATA_HOME, one line per message
- Single-line file header with embedded format-version marker
(FLATFILE_FORMAT_VERSION); reader warns on missing or mismatched
marker, writer and checker stay in sync via preprocessor
stringification
- Deterministic key=value metadata (`id`, `aid`, `corrects`, `to`,
`to_res`, `read`) plus escaped body \u2014 `\|`, `\]`, `\\`, `\n`, `\r`
literals prevent log injection
- Sparse byte-offset index (FF_INDEX_STEP=500) per contact for
O(log n) time-range lookups; rebuilt on inode / size / mtime
change, extended in-place when the file just grew
- Per-contact GHashTable caches for archive_id presence and
stanza_id \u2192 from_jid mapping (O(1) MAM dedup, O(1) LMC sender
validation)
Hardening
- Path-traversal protection: JID directory name normalisation
(`@` \u2192 `_at_`, slashes and `..` rejected at construction); every
per-contact path is anchored under the account's flatlog/
directory and validated before open
- Symlink-attack protection: every fopen / open uses O_NOFOLLOW; on
ELOOP the operation aborts with an error rather than following
- Filesystem permissions: log files created with mode 0600,
directories with mode 0700; both enforced at creation, verified
on each open and reported on drift by `/history verify`
- Atomic crash-safe export: write to a temp file via mkstemp (mode
0600, random suffix, no name collisions between concurrent
exports), fsync, then rename \u2014 partial state never replaces the
live file
- Concurrency: advisory flock(LOCK_EX) held for the duration of
every write, including append from live messages and full rewrite
from export, so two profanity processes can't interleave bytes
on the same log
- DoS / abuse guards:
* FF_MAX_LINE_LEN = 10 MB \u2014 lines longer than this are rejected
at read with a warning; the parser will not allocate
unbounded memory for a single record
* FF_MAX_LMC_DEPTH = 100 \u2014 `corrects:` chain walk stops at this
depth and emits a warning, preventing a malicious correction
cycle from spinning the apply pass
* FF_VERSION_SCAN_MAX = 16 \u2014 header version probe never reads
past 16 leading comment lines, even on garbage input
* Empty / inverted byte-range early-return in page-up read path
so a malformed time filter cannot cause an unbounded scan
* Zero-entry index guard so a file whose every line failed to
parse cannot cause a NULL deref on later page-up
- LMC sender validation: an incoming correction whose sender does
not match the original message's sender is rejected at write
time and surfaced via cons_show_error; a cycle in the apply pass
is broken via a visited-set
- jid_create_from_bare_and_resource treats NULL, empty string, and
the literal "(null)" as no resource and returns a bare jid;
similar normalisation for barejid eliminates the legacy
"user@host/(null)" artefact that leaked into stored fulljids
whenever g_strdup_printf("%s", NULL) ran inside create_fulljid
Commands
- `/history switch sqlite|flatfile` \u2014 runtime backend swap, closes
the old backend and opens the new one without reconnecting
- `/history export [<jid>]` \u2014 SQLite -> flat-file, merging with any
existing flatlog (dedup keyed on a SHA-256 hash mixing stanza_id,
timestamp, from_jid, body \u2014 robust against id reuse by older
clients)
- `/history import [<jid>]` \u2014 flat-file -> SQLite, same merge
semantics, runs inside a single SQLite transaction with rollback
on per-contact failure
- `/history verify [<jid>]` \u2014 integrity check; emits a structured
list of issues (ERROR / WARNING / INFO) per file:
* file-level: missing log, wrong permissions (\u2260 0600), UTF-8
BOM present, CRLF line endings, empty file
* line-level: invalid UTF-8 (with byte offset), embedded
control characters, unparsable lines, timestamps out of
order, duplicate `id:` and `aid:` (tracked separately so a
stanza/archive id collision isn't double-reported)
* cross-line: broken `corrects:` references whose target id is
not present in the file
- `/history backend` \u2014 show currently active backend
- Active backend indicator `[sqlite]` / `[flatfile]` in the status
bar next to the JID
- Roster-JID autocomplete for verify / export / import
- export and import open a SQLite handle on demand when the
flatfile backend is currently active, so migration works
regardless of which backend is live
Tests
- Unit: database_export (parser round-trip, escape/unescape, dedup
key stability, JID normalisation), database_stress (14 cases
exercising rapid writes, large messages, deep LMC chains, MAM
dedup, concurrent contacts)
- Functional: history persistence across reconnects, export /
import round-trip with content equality, MUC migration,
timestamp normalisation across timezones
- Bench harness P1\u2013P5 (synthetic load: bulk insert, time-range
read, page-up scroll, MAM ingest, mixed workload) and failure
modes F1\u2013F17 (page-up cursor and forward-iteration symmetry,
oversized lines, MAM dedup, LMC depth and cycles, BOM/CRLF,
missing log, empty file, mtime+inode flip, broken corrects, etc.)
- All bench tests integrate with the existing make targets and
emit CSV rows for baseline comparison
Author: jabber.developer2 <jabber.developer2@jabber.space>
Reviewed-by: jabber.developer <jabber.developer@jabber.space>
While trying to get the unit tests working again I stumbled over all those
things that I thought could be better^TM.
Now we also know "TODO: why does this make the test fail?" - because
the unit tests are brittle AF ... and we have to init the subsystems
we use in the test, otherwise the cleanup will fail...
BTW. you can now also only run a single test ... or a pattern or so ...
you'd have to read how `cmocka_set_test_filter()` works exactly.
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
When using `/statusbar tabmode actlist` and `/statusbar self user` then we get `[00:23] [user]` and now `[00:23] [1:user]` where `1` is the active tab. The active tab is only displayed with fulljid.
This commit fixes 7d290b04d.
Fix https://github.com/profanity-im/profanity/issues/1980
This lets users use the actlist and decide if they want to see name or
numbers.
The old behaviour can be achieved with:
```
/statusbar hide read
/statusbar hide name
/statusbar show number
/statusbar tabmode actlist
```
Fix https://github.com/profanity-im/profanity/issues/1974
Return static tabmode as default,
separate previous change in `dynamic` mode.
Despite usefulness of the solution,
it was not approved to be a new default.
Vote (link below) amongst users to change default
has shown inconclusive result and it was not representative,
as it had low number of participants.
https://github.com/profanity-im/profanity/pull/1912#issuecomment-1816232546
This commit changes the current behaviour which displays always the first
tabs until `maxtabs` to display N tabs around the currently selected tab.
So if we are having a maxtab of 1 and the actively selected window is 2,
then 2 is displayed only.
So far we have only displayed `>` to indicate that there are more windows.
Since this PR shifts the range of tabs that are displayed we also add `<`
indicator now to indicate windows to the left of the currently displayed.
Fix https://github.com/profanity-im/profanity/issues/1283
Fix https://github.com/profanity-im/profanity/issues/1764
- Fixes statusbar tabs.
Bug: Statusbar used nickname if it was set in roster,
irrelevant to /statusbar chat setting.
Expected behaviour would be using this setting set as "user" to show nickname,
and to show jid with "jid" setting.
Other solution is to give a user control over it with another settings.
- _status_bar_draw_maintext cleaned up, no changes to behaviour
The existing way how active tabs are displayed didn't allow showing more
than 10 tabs. This patch adds a mode where the statusbar shows a
comma-separated list of tabs which were active since the last time viewed.
This view is inspired by how `irssi` shows the active tabs, therefore
it is also called `actlist`.
Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
- Add NetBSD as a recognized platform without -ldl.
- Allow building with NetBSD libcurses instead of ncurses.
- Portability to NetBSD sh - use POSIX '=' instead of '=='.
If users input strange stuff and we can't create a jid from it even the
setting is set to 'user' we still should fallback to the regular
identifer.
For example with `/msg @name%matrix.domain.org@matrix.org hi`.
From @xaizek s comment on issue #1235:
```
If the move would cause the window to be off the screen, it is an error and the window
is not moved.
Resize on the other hand doesn't fail like this according to its documentation. So new size needs to be applied first.
```
Big thanks to @xaizek for taking a look at our code and helping us!!
Regards https://github.com/profanity-im/profanity/issues/1235
This theme color applies to the tab title text of the statusbar tab that
is currently shown.
The (somewhat confusingly named) `statusbar.active` theme color now
applies to all other tabs (before, it applied to all tabs).
Coloring of a tab that is highlighted/has new messages is done as before
using the `statusbar.new` theme color.
The default color is set to `cyan`, and thus causes no visible change
for users -- until modified.
Profanity uses the node part of a JID as display name for a tab. If such
a JID doesn't contain the node part, Profanity crashes on NULL pointer
dereference.
In the above case, use barejid which is just a domain. Fixes#1153.
We destory the roster in ev_disconnect_cleanup().
Adding a function to test if the roster has been destroyed and testing
for it in the statusbar.
So now when the connection is lost 'Lost connection' is printed in all
open windows.
We can then reconnect with `/connect accountname`.
Should fix https://github.com/profanity-im/profanity/issues/1083
status_bar_new() and status_bar_active() are almost identical.
Let's use one helper function to not duplicate code.
I thought about renaming both functions into one and adding another
parameter but didn't come up with a good name for the function that
clearly describes what it does. So staying with current names + helper
functions.
Save the name for displaying the windows in the statusbar inside the tab
object.
So far we calculated them repeatedly and this created issues when we
lost the connection.
Regards https://github.com/profanity-im/profanity/issues/1083