70.1.46. pepsi-helper-mailbox-scan

list the addresses a mailbox’s owner has written to

Manual section:

1

70.1.46.1.1. Name

pepsi-helper-mailbox-scan - extract correspondent addresses from a mailbox.

70.1.46.1.2. Synopsis

pepsi-helper-mailbox-scan –local-domain DOMAIN… [OPTIONS] [PATH…]

pepsi-helper-mailbox-scan –local-domain DOMAIN–imap URL –imap-password-stdin

pepsi-helper-mailbox-scan –local-domain DOMAIN–doveadm-stdin

70.1.46.1.3. Description

pepsi-helper-mailbox-scan reads a mailbox and prints, one per line, the addresses that its owner has sent mail to. It is the scanning half of pepsi-whitelist(1)’s import subcommand, which spawns it and turns its output into pepsi.whitelist rows; it can also be run by hand, since its output is nothing but a list of addresses.

A message counts as “sent by us” when its From: — or Sender:, for mail submitted on somebody’s behalf — is at one of the –local-domain domains, or is one of the –own-address addresses when those are given. The To:, Cc: and Bcc: addresses of such messages are the correspondents; addresses at a local domain are omitted, because a local sender never passes through the inbound whitelist gate. Addresses are deduplicated, lower-cased and syntax-checked, and each is printed at most once.

Only the header block of each message is read: every reader stops at the blank line that ends the headers, so scanning a mailbox of any size costs no more memory than its longest header block, and message bodies are never touched.

70.1.46.1.3.1. Why this is a separate, unprivileged program

This is the only pepsi-helper-* program that carries no setuid or setgid bit (mode 0755); the others are setuid-root.

The reason is the direction of privilege. pepsi-whitelist(1) is setuid because it needs a database identity, and a setuid process keeps its owner in the saved set-user-id and can regain it at any point. Parsing a mailbox is the opposite kind of work: it processes megabytes of attacker-controlled RFC 5322 — every message anybody ever sent the user — and it needs no privilege beyond the user’s own. So the parsing runs here, in a process pepsi-whitelist spawns with setresuid setting the real, effective and saved ids to the target user. With no privileged id left anywhere in its credentials, this program cannot become the whitelist account, cannot reach the database, and can read nothing the user could not read anyway.

70.1.46.1.4. Options

–local-domain DOMAIN

A domain whose senders count as “us”. Repeatable. At least one –local-domain or –own-address is required — without one, no message can be recognised as sent by us.

–own-address ADDRESS

Count only mail from this exact address as ours, rather than any sender at a local domain. Repeatable.

–format auto|mbox|maildir

Format of the given paths. auto (the default) treats a directory as a Maildir tree and a file as an mbox.

An mbox is split on From `` lines that stand at the start of the file or directly after a blank line. A Maildir is read from ``cur/ and new/ (never tmp/, whose files may be half-written), and every directory below the one given that itself contains cur/ or new/ is scanned too — so the Maildir++ layout (.Sent, .Archive.2025) is covered without naming each folder.

–imap URL

Scan a remote store instead of files. imaps://user@host[:port]/[PATTERN] uses implicit TLS (port 993), imap:// uses STARTTLS (port 143), and imap+plain:// disables TLS entirely (which sends the password in the clear, and is only sensible against a loopback test server). PATTERN is an IMAP mailbox pattern, * by default.

The server certificate is always verified against the system trust store. Mailboxes are opened with EXAMINE (read-only) and only BODY.PEEK[HEADER] is fetched, so a scan neither marks messages \Seen nor changes anything else.

–imap-user NAME

IMAP login, if it is not in the URL.

–imap-password-file FILE

Read the password from the first line of FILE, which must not be readable by other users.

–imap-password-stdin

Read the password from the first line of standard input. This is how pepsi-whitelist(1) passes it, so it never appears in a command line where other users could read it out of ps.

–doveadm-stdin

Read a doveadm -f json fetch -u LOGIN hdr mailbox '*' all stream from standard input instead of opening files. Used for Dovecot’s own mail formats (mdbox/sdbox): the caller runs the privileged doveadm, and only its output crosses into this unprivileged process. JSON is required because a header block contains blank lines and colons of its own, which doveadm’s human-readable formats do not escape.

–max-header-bytes BYTES

Cap on the header bytes retained per message (default 65536).

–max-messages N

Stop after examining N messages (0, the default, means no limit).

-h, –help

Print a usage summary and exit.

-V, –version

Print the version and exit.

70.1.46.1.5. Output

One tab-separated record per line on standard output:

a<TAB>ADDRESS

A correspondent address, emitted at most once.

m<TAB>COUNT

How many messages were examined.

s<TAB>COUNT

How many of them were sent by us.

w<TAB>MESSAGE

A non-fatal problem: an unreadable folder, a skipped record, a mailbox with no cur/new. Warnings are emitted after the addresses and are capped.

A reader ignores record types it does not know, so later versions may add some.

70.1.46.1.6. Exit Status

0

The scan completed. Any w records are non-fatal.

1

A fatal error: bad arguments, an unreadable mailbox path, an IMAP login refused. The diagnostic is on standard error.

70.1.46.1.7. Examples

Scan your own mail:

pepsi-helper-mailbox-scan --local-domain example.com ~/Maildir /var/mail/alice

Only mail you sent from one particular address:

pepsi-helper-mailbox-scan --own-address alice@example.com ~/Maildir

A remote account over IMAP, with the password in a file:

pepsi-helper-mailbox-scan --local-domain example.com \
    --imap imaps://alice@mail.example.com/ --imap-password-file ~/.imap-pw

A Dovecot store, as root:

doveadm -f json fetch -u alice hdr mailbox '*' all \
  | pepsi-helper-mailbox-scan --local-domain example.com --doveadm-stdin

70.1.46.1.8. See Also

pepsi-whitelist(1), pepsi-stage-check-whitelist(1), pepsi-stage-auto-whitelist(1), pepsi.conf(5)

70.1.46.1.9. Bugs

Report bugs to the Pepsi issue tracker.