Files
cproof/docs/profanity.1
Jabber Developer 3f36c303c2
All checks were successful
CI Code / Check spelling (push) Successful in 21s
CI Code / Check coding style (push) Successful in 31s
CI Code / Code Coverage (push) Successful in 2m21s
CI Code / Linux (ubuntu) (push) Successful in 4m30s
CI Code / Linux (debian) (push) Successful in 6m43s
CI Code / Linux (arch) (push) Successful in 10m8s
feat(history): flat-file backend with bidirectional SQLite migration
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>
2026-05-05 19:26:07 +00:00

265 lines
9.8 KiB
Groff
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

.TH man 1 "2025-03-27" "0.15.0" "Profanity XMPP client"
.SH NAME
Profanity \- a simple console based XMPP chat client.
.SH SYNOPSIS
.B profanity
[\-vh] [\-l level] [\-a account]
.SH DESCRIPTION
.B Profanity
is a simple lightweight console based XMPP chat client. Its emphasis is
on having a simple and configurable command driven UI, see the homepage
at:
.br
.PP
<https://profanity-im.github.io>
.SH OPTIONS
.TP
.BI "\-v, \-\-version"
Show version and build information.
.TP
.BI "\-h, \-\-help"
Show help on command line arguments.
.TP
.BI "\-a, \-\-account "ACCOUNT
Auto connect to an account on startup,
.I ACCOUNT
must be an existing account.
.TP
.BI "\-c, \-\-config"
Use an alternative config file.
.TP
.BI "\-l, \-\-log "LEVEL
Set the logging level,
.I LEVEL
may be set to DEBUG, INFO (the default), WARN or ERROR.
.TP
.BI "\-f, \-\-logfile"
Specify a different logfile
.TP
.BI "\-r, \-\-cmd"
Specify the commands that should be run right after starting up.
This can be given multiple times, e.g.
.EX
profanity --cmd /foo --cmd "/sleep 10" --cmd /quit
.EE
.TP
.BI "\-t, \-\-theme "THEME
Specify which theme to use.
.I THEME
must be one of the themes installed in $XDG_CONFIG_HOME/profanity/themes
.SH KEYBINDINGS
.TP
.BR Tab , " Shift+Tab"
Tab completion next, previous. Works for commands, nicks and
quotes (when input line starts with
.BR > ).
.TP
.BR ALT+1..Alt-0 " or " F1..F10
Choose window 1..10.
.TP
.BR ALT+q..Alt-p " (in QWERTY layout)"
Choose window 11..20.
.TP
.BI ALT+LEFT
Choose previous chat window.
.TP
.BI ALT+RIGHT
Choose next chat window.
.TP
.BI PAGEUP
Page the active window up.
.TP
.BI PAGEDOWN
Page the active window down.
.TP
.BI ALT+PAGEUP
Page the occupants or roster panel up.
.TP
.BI ALT+PAGEDOWN
Page the occupants or roster panel down.
.TP
.BI ALT+UP " or " ALT+WHEEL UP
Scroll up. Note: limited support for scrolling with mouse wheel,
please, get in touch with devs if you know how to add support
for other terminals.
.TP
.BI ALT+DOWN " or " ALT+WHEEL DOWN
Scroll down. See note above.
.TP
.BI ALT+a
Jump to the next unread window.
.TP
.BI ALT+v
Mark current window for later reading with an attention flag.
.TP
.BI ALT+m
Switch between windows marked with the attention flag.
.TP
.BI ALT+ENTER
Add newline character without sending a message/command.
.TP
.BI ALT+c
Run external editor (see
.BR profanity-editor (1))
for current input line.
.TP
.BI CTRL+DOWN
Store current input line in history but do not execute it.
.PP
.I Note:
Profanity is using GNU Readline library to handle input so
default configuration file
.I ~/.inputrc
affects operation. In addition to that
.I $XDG_CONFIG_HOME/profanity/inputrc
is read after all default keybindigs are set so one can override
or add settings there. For reference, see Readline documentation:
.I "info readline ""Command Line Editing"" ""Readline Init File"" ""Readline Init File Syntax"""
and the list of available Profanity commands in
.IR inputwin.c .
Standard Readline keybindings are supported, including
.B C-r
for interactive history search and
.B C-x C-r
for reloading inputrc without restart.
.SH USING PROFANITY
The first step is to connect to an XMPP Server. The \fB/connect\fR and \fB/account\fR command can be used to setup the XMPP accounts within profanity (see
.BR profanity-connect (1)
and
.BR profanity-account (1))
with an existing XMPP Account. The \fB/register\fR command can be used to register an account on a server.
.BR profanity-register (1)
Use the \fB/help commands connection\fR command in profanity to display more commands for managing your connection.
.PP
The roster is your address book. By default, your roster will be shown in "window 1" which is the profanity \fB/console\fR.
It's possible to \fB/roster hide\fR and to \fB/roster show\fR the address book in the profanity console. Buddies can be added via \fB/roster add JID\fR.
.PP
To see the online status of your contacts, the \fB/sub\fR command can be used to manage subscriptions to the presence of contacts.
\fB/sub request\fR will send a request. \fB/sub allow\fR is used to approve a contact's subscription request. Use \fB/help commands roster\fR to get a list of commands which can be used to manage your roster.
.PP
Open chat windows can been shown by \fB/wins\fR command. Jumping to a window can be done via \fB/win NUMBER\fR or \fB/win JID\fR. profanity supports autocompletion with TAB-Key.
.
The \fB/msg\fR command can be used to start a chat with your contact. Groupchats can be joined with the \fB/join\fR command or managed within the account's bookmark.
.PP
If the group chat is stored in the bookmarks, the group chat can be set to auto-join. If the auto-join is switched off, use \fB/bookmark join\fR to join the group chat.
The \fB/sendfile\fR and \fB/url\fR command can be used to share and download files. Enter > and press the TAB key to quote an existing message in the chat window.
.PP
The user guide can be found at <https://profanity-im.github.io/userguide.html>.
.SH ENCRYPTION
Profanity supports various kinds of encryption: OMEMO, OTR, PGP, OX.
You can only enable one of them per correspondent at a time.
.TP
.BR OMEMO
OMEMO (/omemo) is defined in XEP-0384. It uses an implementation of the Signal protocol for key management and to synchronize messages among different clients. It works even when other clients are offline. And offers Perfect Forward Secrecy and Plausible deniability. Servers need to support PEP (XEP-0163).
We implement the "siacs" version of OMEMO. Version 0.3.0, which is currently the widest adopted option.
OMEMO is the only encryption option in Profanity that also supports encryption for MUCs (XEP-0045) and file transfer via HTTP upload (XEP-0363).
.TP
.BR OTR
OTR (/otr) is defined in XEP-0364. It uses a combination of the AES symmetric-key algorithm, the DiffieHellman key exchange, and the SHA-1 hash function. It offers deniable authentication and Perfect Forward Secrecy. To initialize a session both clients need to be online at the same time. A session is between two clients, so multiclient chats are not working. Which is a feature not a bug. OTR does by design not work for MUCs.
.TP
.BR OpenPGP
OpenPGP (/pgp) is defined in XEP-0027. Is uses a public and secret key. It is also known as Legacy OpenPGP and has been deprecated. It doesn't provide protection against replay attacks. MUCs and file transfer via HTTP upload are not specified and thus not supported.
.TP
.BR OX
OX (/ox) is defined in XEP-0373 and XEP-0374. It's a more modern way to use OpenPGP on XMPP and tries to fix the shortcomings of legacy XEP-0027. Servers need to support PEP (XEP-0163). MUCs and file transfer via HTTP upload are not specified and thus not supported.
.TP
.BR DETAILS
For more details read the relevant XEPs and look at the overview at <https://wiki.xmpp.org/web/XMPP_E2E_Security>
.SH TERMINOLOGY
There is some XMPP specific terminology that might be unknown for fresh users. We will try to describe them here since they are often references in the help and man pages.
.TP
.BR JID
Stands for Jabber ID. It refers to an XMPP address. Historically XMPP was also known as Jabber.
.TP
.BR MAM
Stands for Message Archive Management (XEP-0313) and describes the ability to store messages on the server and retrieve them later.
.TP
.BR MUC
Stands for Mutli-User Chats (XEP-0045) and are also called, groups, group chats, chatrooms or conferences.
.TP
.BR Roster
The roster is your contact list. By default displayed at the right side on the console window. See RFC6121.
.TP
.BR XEP
XMPP is aa extendable protocol. There are core features and optional features described in XMPP Extension Protocols, short XEPs.
.SH SEE ALSO
.B Profanity
itself has a lot of built\-in help. Check the
.I /help
command for more information. Type "/help help" for information
on how to use help itself. Profanity ships with one man page for
each built-in command, e.g. there is
.BR profanity-account (1)
for
.IR /account .
.SH CONFIGURATION
Configuration for
.B Profanity
is stored in
.I $XDG_CONFIG_HOME/profanity/profrc
, details on commands for configuring Profanity can be found at <https://jabber.space/command-reference/> or the respective built\-in help or man pages.
.SS Message History Storage
By default, message history is stored in an SQLite database. An alternative flat-file backend
stores messages as human-readable plain text files that can be edited with any text editor.
.PP
To enable flat-file logging, set in
.IR profrc :
.PP
.EX
[logging]
dblog=flatfile
.EE
.PP
Or use the command:
.B /privacy logging flatfile
.PP
Flat-file logs are stored under
.IR $XDG_DATA_HOME/profanity/flatlog/ ,
organized as
.IR {account_jid}/{contact_jid}/history.log .
.PP
Each line has the format:
.br
.EX
{ISO8601} [{type}|{enc}|id:{id}|aid:{archive_id}|corrects:{id}] {sender}: {message}
.EE
.PP
Use
.B /history verify
to check integrity of stored history (both SQLite and flat-file).
.PP
Use
.B /history export [<jid>]
to copy messages from SQLite to flat-file format (merge with existing data), or
.B /history import [<jid>]
to copy from flat-file to SQLite.
Both operations skip duplicates.
Omit the JID to process all contacts.
.PP
Use
.B /history switch sqlite|flatfile
to change the active database backend at runtime without reconnecting.
.SH BUGS
Bugs can either be reported by raising an issue at the Github issue tracker:
.br
.PP
<https://github.com/profanity-im/profanity/issues>
.br
.PP
or to the mailing list at:
.br
.PP
<https://lists.posteo.de/listinfo/profanity>
.br
.SH LICENSE
Copyright (C) 2012 \- 2019 James Booth <boothj5web@gmail.com>.
Copyright (C) 2019 \- 2025 Michael Vetter <jubalh@iodoru.org>.
License GPLv3+: GNU GPL version 3 or later <https://www.gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
.SH AUTHORS/CREDITS
.B Profanity
was created by James Booth
.B <boothj5web@gmail.com>
with many contributions from others, see the full list at: <https://github.com/profanity-im/profanity/graphs/contributors>