QSF(1)
๐ QSF(1) โ the qsf girlies' guide ๐
User Commands โจ spam filter edition
okay so bestie, welcome to qsf, aka the quick spam filter ๐ she's a little Unix program who reads your email and is like "hmm. this is giving spam." and does something about it. let's get into it ๐
โจ NAME โจ
qsf โ quick spam filter ๐ฆ
โจ SYNOPSIS โจ
Filtering:
qsf [-snrAtav] [-d DB] [-g DB] [-L LVL] [-S SUBJ] [-H MARK]
[-Q NUM] [-X NUM]
Training:
qsf -T SPAM NONSPAM [MAXROUNDS] [-d DB]
Retraining:
qsf -[m|M] [-d DB] [-w WEIGHT] [-ayN]
Database:
qsf -[p|D|R|O] [-d DB]
Database merge:
qsf -E OTHERDB [-d DB]
Allow-list query:
qsf -e EMAIL [-m|-M|-t] [-d DB] [-g DB]
Deny-list query:
qsf -y -e EMAIL [-m -m|-M -M|-t] [-d DB] [-g DB]
Help: qsf -[h|V]
โจ DESCRIPTION โจ
so here's the vibe: qsf reads a single email on standard input, and by default outputs it on standard output ๐ like, it's not out here being dramatic, it just passes your mail through. BUT. if the email is determined to be spam, an additional header (X-Spam: YES) gets added, and optionally the subject line can have [SPAM] prepended to it ๐จ periodt.
qsf is intended to be used in a procmail(1) recipe, in a ruleset such as this:
:0 wf
| qsf -ra
:0 H:
* X-Spam: YES
$HOME/mail/spam
for more examples, including sample procmail(1) recipes, see the EXAMPLES section below ๐
๐ Training ๐
before qsf can be used properly, she needs to be trained. no cap, she doesn't just know things, she has to learn. a good way to train qsf is to collect a copy of all your email into two folders โ one for spam, and one for non-spam ๐๏ธ once you have done this, you can use the training function, like this:
qsf -aT spam-folder non-spam-folder
this will generate a database that qsf can use to guess whether email received in the future is spam or not ๐ฎ note that this initial training run may take a long time, but you should only need to do it once. slay.
to mark a single message as spam, pipe it to qsf with the --mark-spam or -m ("mark as spam") option ๐ this will update the database accordingly and discard the email.
to mark a single message as non-spam, pipe it to qsf with the --mark-nonspam or -M ("mark as non-spam") option ๐ซถ again, this will discard the email.
if a message has been mis-tagged, send it to qsf as the opposite type. for example, if it has been mistakenly tagged as spam, pipe it into qsf --mark-nonspam --weight=2 to add it to the non-spam side of the database with double the usual weighting ๐ช
๐ The allow-list ๐
a feature called the "allow-list" can be switched on by specifying the --allowlist or -a option โจ with the allow-list, messages' From: and Return-Path: addresses are checked against a list of people you have said to allow all messages from, and if a message matches, it is never marked as spam ๐ฅฐ this means you can add all your friends to the allow-list and qsf will then never mis-file their messages. bestie protection squad fr fr.
a quick way to add everyone is to use the -a option with -T ๐ everyone in your non-spam folder who has sent you an email will be added to the allow-list automatically during training. iconic behaviour, no notes.
you can manually add and remove addresses to and from the allow-list using the -e (email) option โ๏ธ for examples, see the EXAMPLES section.
in general, you probably always want to enable the allow-list, so always specify the -a option when using qsf ๐ this will automatically maintain the allow-list based on what you classify as spam or non-spam.
the only times you might want to turn it off are when people on your allow-list are prone to getting viruses ๐ฆ or if a virus is causing email to be sent to you that is pretending to be from someone on your allow-list. like, catfish but for email. rude.
๐ Backup and restore ๐
because the database format is platform-specific, it is a good idea to periodically dump the database to a text file using qsf -D so that, if necessary, it can be transferred to another machine and restored with qsf -R later on ๐พ
note that since the actual contents of email messages are never stored in the database (see the "Technical details" sub-section), you can safely share your qsf database with others, by sending them a text dump which they can restore into their own database ๐ซ sharing is caring.
โจ OPTIONS โจ
the qsf options are listed below ๐
๐ Database selection options ๐
-d,--database [TYPE:]FILEโ useFILEas the spam/non-spam database. the default is to use/var/lib/qsfdband, if that is not available or is read-only,$HOME/.qsfdb. this option can also be useful if there is a system-wide database but you do not want to use it โ specifying your own here will override the default.
if you prefix the filename with a TYPE, of the form btree:$HOME/.qsfdb, then this will specify what kind of database FILE is, such as list, btree, gdbm, sqlite and so on ๐
check the output of qsf -V to see which database backends are available. the default is to auto-detect the type, or, if the file does not already exist, use list.
note that TYPE is not case-sensitive.
-g,--global [TYPE:]FILEโ useFILEas the default global database, instead of/var/lib/qsfdb. if you also specify a database with-d, then this "global" database will be used in read-only mode in conjunction with the read-write database specified with-d.
the -g option can be used a second time to specify a third database, which will also be used in read-only mode.
as with -d, the filename can optionally be prefixed with a TYPE which specifies the database type.
๐ Filtering options ๐
-s,--subjectโ rewrite the Subject line of any email that turns out to be spam, adding[SPAM]to the start of the line ๐ฉ-S,--subject-marker SUBJECTโ instead of adding[SPAM], addSUBJECTto the start of the Subject line of any email that turns out to be spam. implies-sโ๏ธ-H,--header-marker MARKโ instead of setting the X-Spam header toYES, set it toMARKif email turns out to be spam. this can be useful if your email client can only search all headers for a string, rather than one particular header (so searching forYESmight match more than just the output ofqsf) ๐-n,--no-headerโ do not add an X-Spam header to messages ๐ซ-r,--add-ratingโ insert an additional headerX-Spam-Ratingwhich is a rating of the "spamminess" of a message from 0 to 100; 90 and above are counted as spam, anything under 90 is not considered spam ๐ if combined with-t, then the rating (0-100) will be output, on its own, on standard output.-A,--asteriskโ insert an additional headerX-Spam-Levelwhich will contain between 0 and 20 asterisks (*), depending on the spam rating โญโญโญ-v,--verboseโ add extraX-QSF-Infoheaders to any filtered email, containing error messages and so on if applicable ๐ฃ๏ธ specify-vmore than once to increase verbosity.
๐ Classification and behavioural options ๐
-L,--level,--threshold LEVELโ change the spam scoring threshold level which must be reached before an email is classified as spam. the default is90๐-a,--allowlistโ enable the allow-list ๐ this causes the email addresses given in the message'sFrom:andReturn-Path:headers to be checked against a list; if either one matches, then the message is always treated as non-spam, regardless of what the token database says.
when specified with a retraining flag, -a -m (mark as spam) will remove that address from the allow-list as well as marking the message as spam, and -a -M (mark as non-spam) will add that address to the allow-list as well as marking the message as non-spam.
the idea is that you add all of your friends to the allow-list, and then none of their messages ever get marked as spam ๐ซถ besties stay winning.
-y,--denylistโ enable the deny-list ๐ค this causes the email addresses given in the message'sFrom:andReturn-Path:headers to be checked against a second list; if either one matches, then the message is always treated as spam.
training works in the same way as with -a, except that you must specify -m or -M twice to modify the deny-list instead of the allow-list, and with the reverse syntax: -y -m -m (mark as spam) will add that address to the deny-list, whereas -y -M -M (mark as non-spam) will remove that address from the deny-list. this double specification is so that the usual retraining process never touches the deny-list; the deny-list should be carefully maintained rather than automatically generated ๐ง
normally you would not need to use the deny-list.
-Q,--min-tokens NUMโ only give a score if more thanNUMtokens are found in the message โ otherwise the message is assumed to be non-spam, and it is not modified in any way. the default is0๐ข this option might be useful if you find that very short messages are being frequently miscategorised.-t,--testโ instead of passing the message out on standard output, output nothing, and exit0if the message is not spam, or exit1if the message is spam ๐งช if combined with-r, then the spam rating will be output on standard output.-e,--email,--email-only EMAILโ query or update the allow-list entry for the email addressEMAIL๐ง with no other options, this will simply outputYESifEMAILis in the allow-list, orNOif it is not.
with -t, it will not output anything, but will exit 0 (success) if EMAIL is in the allow-list, or 1 (failure) if it is not.
with the -m (mark-spam) option, any previous allow-list entry for EMAIL will be removed.
with the the -M (mark-nonspam) option, EMAIL will be added to the allow-list if it is not already on it ๐ซถ
if EMAIL is just the word MSG on its own, then an email will be read from standard input, and the email addresses given in the From: and Return-Path: headers will be used.
using -e automatically switches on -a โจ
if you also specify -y, then the deny-list will be operated on. remember that -m and -M are reversed with the deny-list ๐ต๐ซ
if you specify an email address of the form @domain (nothing before the @), then the whole domain will be allow or deny listed ๐
๐ Training options ๐
-m,--mark-spamโ instead of passing the message out on standard output, mark its contents as spam and update the database accordingly ๐ if the allow-list (-a) is enabled, the message'sFrom:andReturn-Path:addresses are removed from the allow-list.
okay so real quick, still on the -m tip: if the deny-list (-y) is on and you hit -m twice, the message's addresses get added to the deny-list instead. ๐
-M,--mark-nonspamโ instead of pushing the message out on standard output, this marks its contents as non-spam and updates the database accordingly. If the allow-list (-a) is enabled, the message's โFrom:โ and โReturn-Path:โ addresses get added to the allow-list (peep the-aoption above bestie). And if the deny-list (-y) is enabled and you specify-Mtwice, the message's addresses get removed from the deny-list instead. ๐ซถ-w,--weightWEIGHT โ when you're marking something as spam or non-spam, this updates the database with a weighting of WEIGHT per token instead of the default of1. So good for fixing mistakes โ like, a message that got wrongly detected as spam should be marked as non-spam with a weighting of2, i.e. double the usual weighting, to counteract the error. ๐ฅ-T,--trainSPAM NONSPAM [MAXROUNDS] โ trains the database using the two mbox folders SPAM and NONSPAM, by testing every message in each folder and updating the database each time a message gets miscategorised. This runs several times, and yeah it may take a while to run. Pass the-a(allow-list) flag to add every sender in the NONSPAM folder to your allow-list as a side-effect of the training process. ๐
If MAXROUNDS is specified, training will end after this number of rounds if the results are still not good enough. The default is a maximum of 200 rounds.
๐พ Database management options ๐พ
-D,--dump[FILE] โ dumps the contents of the database as a platform-independent text file, which is giving archival, transfer-to-another-machine, all of that. The data goes out on stdout, or into the given FILE. ๐ฆ-R,--restore[FILE] โ rebuilds the database from scratch from the text file on stdin. If a FILE is given, data is read from there instead of from stdin. ๐ฑ-E,--mergeOTHERDB โ merges the OTHERDB database into the current database. Super useful if you wanna take one user's database and merge it into the system-wide one, for instance (this would be done by, as root, doingqsf -d /var/lib/qsfdb -E /home/user/.qsfdband then removing/home/user/.qsfdb). ๐ซฑ๐ซฒ-P,--plain-mapFILE โ maintains a mapping of all database tokens to their non-hashed counterparts in FILE, one token per line. This can be useful if you wanna be able to list the contents of your database at a later date, for instance to get a list of email addresses in your allow-list. Note that using this option may slow qsf down, and only entries written to the database while this option is active will be stored in FILE. โ ๏ธ
โจ Miscellaneous options โจ
-O,--tokensโ instead of filtering, this outputs a list of the tokens found in the message read from standard input, along with the number of times each token was found. Only useful if you wanna use qsf as a general tokeniser for use with another filtering package. ๐-B,--benchmarkSPAM NONSPAM [MAXROUNDS] โ benchmarks the training process using the two mbox folders SPAM and NONSPAM. A temporary database is created and trained using the first75%of the messages in each folder, and then the entire contents of each folder is tested to see how many false positives and false negatives occur. The temporary database is removed afterwards. Some timing information is also displayed. โฑ๏ธ
This can be used to decide which backend is best on your system. Use -d to select a backend. For example, qsf -B spam nonspam -d GDBM.
The exception to this is the MySQL backend, where a full database specification must be given, of the form -d MySQL:database=db;host=localhost;..., and the database table given will not be wiped beforehand or dropped afterwards.
As with -T, if MAXROUNDS is specified, training will never be done for more than this number of rounds; the default is 200.
-h,--helpโ prints a usage message on standard output and exits successfully. ๐ซถ-V,--versionโ prints version information, including a list of available database backends, on standard output and exits successfully. ๐
๐ฆ Deprecated options ๐ฆ
The following options are only for use with the old binary tree database backend or old databases that haven't been upgraded to the new format that came in with version 1.1.0. (iykyk, but also likeโฆ upgrade bestie ๐)
-N,--no-autopruneโ when marking as spam or nonspam, never automatically prune the database. Usually the database is pruned after every500marks; if you would rather--prunemanually, use-Nto disable automatic pruning. โ๏ธ-p,--pruneโ removes redundant entries from the database and cleans it up a little. This is automatically done after several calls to--mark-spamor--mark-nonspam, and during training with--trainif the training takes a large number of rounds, so it should rarely be necessary to use--prunemanually unless you are using-N/--no-autoprune. ๐งน-X,--prune-maxNUM โ when the database is being pruned, no more than NUM entries will be considered for removal. This is to prevent CPU and memory resources being taken over. The default is100,000, but in some circumstances (if you find that pruning takes too long) this option may be used to reduce it to a more manageable number. ๐งฎ
๐ FILES ๐
/var/lib/qsfdbโ the default (system-wide) spam database. If you wanna install qsf system-wide, this should be read-only to everyone; there should be one user with write access who can update the spam database withqsf --mark-spamandqsf --mark-nonspamwhen necessary. ๐/var/lib/qsfdb2โ a second, read-only, system-wide database. This can be useful when installing qsf system-wide and using third-party spam databases; the first global database can be updated with system-specific changes, and this second database can be periodically updated when the third-party spam database is updated. ๐$HOME/.qsfdbโ the default spam database for per-user data. Users without write access to the system-wide database will have their data written here, and the two databases will be read together. The per-user database will be given a weighting equivalent to10times the weighting of the global database. ๐
๐ NOTES ๐
Currently, you cannot use qsf to check for spam while the database is being updated. This means that while an update is in progress, all email is passed through as non-spam. ๐ญ
There is an upper size limit of 512Kb on incoming email; anything larger than this is just passed through as non-spam, to avoid tying up machine resources.
The plaintext token mapping maintained by --plain-map will never shrink, only grow. It is intended for use by housekeeping and user interface scripts that, for instance, the user can use to list all email addresses on their allow-list. These scripts should take care of weeding out entries for tokens that are no longer in the database. If you have no such scripts, there is probably no point in using --plain-map anyway.
Avoid using the deny-list (-y) in any automated retraining, as it can cause the filter to reject mail unnecessarily. In general the deny-list is probably best left unused unless explicitly required by your particular setup. ๐ซ
If both the allow-list and the deny-list are enabled, then email addresses will first be checked against the deny-list, then the allow-list, then the domain of the email address will be checked for matching @domain entries in the deny-list and then in the allow-list. ๐
๐ BUGS ๐
If you have problems with qsf, please check the list below; if this does not help, refer to the REPORTING BUGS section. ๐ซ
๐ Nothing is being marked as spam.
First, use the -r option to switch on the โX-Spam-Ratingโ header, and check that this header appears in email passed through qsf. If it does not, then it is likely that qsf is not being run at all โ check your configuration of procmail(1) or its equivalent.
If you are seeing โX-Spam-Ratingโ headers, and different emails have different scores, then you may simply need to retrain your database a little more. Take more spam email and pass it to qsf -m.
If you are seeing โX-Spam-Ratingโ headers but they all give the same spam rating, then the most likely reason is that qsf is not reading any database. Make sure that whatever is processing the email has read permissions on /var/lib/qsfdb and/or ~/.qsfdb โ and make sure that, if you are using ~/.qsfdb, what your database creator thought was ~ ($HOME) is the same as it is for whatever is processing the email. ๐
๐ด Retraining sometimes takes a very long time.
With the obtree backend or 2-column MySQL or SQLite tables, every 500th retrain (-m or -M), the database is pruned. On some systems this may take some time, and during this time the database is locked (except when using the MySQL or SQLite backends). If you constantly do a lot of retraining and want to avoid this, then use the -N option to suppress auto-pruning, and then have a cron(8) job or something run a manual prune (qsf -p) every now and again. โณ
๐ต Running qsf from procmail fails with an error.
If you can run qsf from the command line, but in your procmail log file you get errors about qsf: cannot execute binary file, then contact your system administrator for help. It may be that incoming email is handled by a different server to the one you normally shell into, and either they are of a different architecture or operating system, or the mail server is not permitted to execute user-owned binaries. ๐ง
โจ EXAMPLES โจ
Okay bestie, this is the part where it gets fun. Like, actually useful. Real recipes, copy-paste energy, no cap. ๐
๐ Filtering ALL your mail through qsf
Want every single message to go through qsf, with the allow-list turned on and the "spam rating" header getting added? Slap this into your .procmailrc file:
:0 wf
| qsf -ra
And if you want qsf to also yeet a literal โ[SPAM]โ onto the subject line of anything it clocks as spam? Do this instead, queen:
:0 wf
| qsf -sra
๐ธ Managing your allow-list (it's giving admin privileges)
Add โfoo@bar.comโ to your allow-list:
qsf -e foo@bar.com -M
Remove โbad@nasty.comโ from your allow-list (bye girl ๐):
qsf -e bad@nasty.com -m
And to check whether โsomeone@somewhere.comโ is in the allow-list or not:
qsf -e someone@somewhere.com
๐
The spambox@yourdomain.com situation
Say you wanna auto-mark anything sent to spambox@yourdomain.com as spam. Here's the "naive" version โ works, but it's lowkey a little reckless:
:0 H
* ^To:.*spambox@yourdomain.com
| qsf -am
Now here's the clever version. Same goal, except it only marks mail to spambox@yourdomain.com as spam in the database if qsf does NOT already think it's spam. Why? Because it stops the database from getting too heavily weighted. Iconic behavior tbh:
# If sent to spambox@yourdomain.com:
:0
* ^To:.*spambox@yourdomain.com
{
:0 wf
| qsf -a
# The above two lines can be skipped if you've
# already piped the message through qsf.
# If the qsf database says it's not spam,
# mark it as spam!
:0 H
* ^X-Spam: NO
| qsf -am
}
Pro tip from the original, not me: remove the -a option in the above examples if you don't want to use the allow-list. ๐
๐ฆ The "your (something) is on fire" filter
A more complicated filtering example โ this one only runs qsf on messages that don't have a subject line saying โyour (something) is on fireโ AND that don't have a sender address ending in โ@foobar.comโ. Meaning: messages with that subject line OR that sender address will NEVER be marked as spam, no matter what. Periodt.
:0 wf
* ! ^Subject: Your .* is on fire
* ! ^From: .*@foobar.com
| qsf -ra
For more on procmail(1) recipes, go read the procmailrc(5) and procmailex(5) manual pages. ๐
๐ mutt macros (iykyk)
A couple of macros to drop into your .muttrc file if you use mutt(1) as your mail user agent:
# Press F5 to mark a message as spam and delete it
macro index <f5> "<pipe-message>qsf -am\n<delete-message>"
macro pager <f5> "<pipe-message>qsf -am\n<delete-message>"
# Press F9 to mark a message as non-spam
macro index <f9> "<pipe-message>qsf -aM\n"
macro pager <f9> "<pipe-message>qsf -aM\n"
Again โ remove the -a option in the above examples if you don't wanna use the allow-list. Same deal as before.
โ ๏ธ Heads up bestie: the above macros won't work when you're operating on multiple tagged messages. For that you'd need something like this:
macro index <f5> ":set pipe_split\n<tag-prefix><pipe-message>qsf
-am\n<tag-prefix><delete-message>\n:unset pipe_split\n"
๐ qmail setup
If you use qmail(7), then to get procmail working with it you'll need to:
- Put a line containing just
โDEFAULT=./Maildir/โat the top of your~/.procmailrcfile โ that way procmail delivers to your Maildir folder instead of trying to deliver to/var/spool/mail/$USER. - Put this in your
~/.qmailfile:
| preline procmail
This will cause all your mail to be delivered via procmail instead of being delivered directly into your mail directory. โจ
See the qmail(7) documentation for more about mail delivery with qmail.
๐ postfix setup
If you use postfix(1), you can set up a system-wide mail filter by:
- Creating a user account for the purpose of filtering mail
- Populating that account's
.qsfdb - Creating a shell script, to run as that user, which runs
qsfon stdin and passes stdout tosendmail(8)
โ ๏ธ Doing this requires some knowledge of postfix configuration, and care needs to be taken to avoid mail loops. Like, genuinely. Be careful. ๐ญ
One qsf user's full HOWTO is included in the docs/ directory with this package. Go read it. It's giving tutorial energy. ๐ธ
TECHNICAL DETAILS โจ๐ง ๐
okay bestie, when a message gets passed to qsf, the whole thing goes through a glow-up: any attachments get decoded, all HTML elements get removed, and then the message text gets broken up into "tokens" โ and a "token" here is literally one single word or URL. Each token gets hashed with the MD5 algorithm (why? we'll get to that, bestie), and that hash is then used to look up each token in the qsf database. ๐
For full details of which parts of an email (headers, body, attachments, etc) are used to calculate the spam rating, see the โTokenisationโ sub-section below. ๐
Inside the database, each token has two numbers attached: the number of times that token has been seen in spam, and the number of times it has been seen in non-spam. Those two numbers, plus the total number of spam and non-spam messages seen, are then used to give a "spamminess" value for that particular token. That "spamminess" value ranges from "definitely not spammy" at one end, through "neutral" in the middle, up to "definitely spammy" at the other end. No cap.
Once a "spamminess" value has been calculated for all the tokens in the message, a summary calculation is made to give an overall "is this spam?" probability rating for the message. If the overall probability is 0.9 or above, the message is flagged as spam. ๐ฉ
In addition to the probability test is the "allow-list". If enabled (with the -a option), the whole probability check is skipped if the sender of the message is listed in the allow-list, and the message is not marked as spam. ๐ซถ
When training the database, a message is split up into tokens as described above, and then the numbers in the database for each token are simply added to: if you tell qsf that a message is spam, it adds one to the "number of times seen in spam" counter for each token, and if you tell it a message is not spam, it adds one to the "number of times seen in non-spam" counter for each token. If you specify a weight, with -w, then the number you specify is added instead of one. ๐
To stop the database growing uncontrollably, the database keeps track of when a token was last used. Under-used tokens are automatically removed from the database. (The old method was to "prune" every 500 updates). ๐งน
Finally, the reason MD5 hashes were used is privacy. If the actual tokens from the messages, and the actual email addresses in the allow-list, were stored, you could not share a single qsf database between multiple users because bits of everyone's messages would be in the database โ things like emailed passwords, keywords relating to personal gossip, and so on. So, a hash is stored instead. A hash is a "one-way" function; it is easy to turn a token into a hash but almost impossible to turn a hash back into the token that created it. This means that you end up with a database with no personal information in it. The MD5 hash was used because it has a reasonable balance of speed and security for this purpose. ๐โจ
Tokenisation ๐งโจ
When a message is broken up into tokens, various parts of the message are treated in different ways. First, all header fields are discarded, except for the important ones: From, Return-Path, Sender, To, Reply-To, and Subject. ๐
Next, any MIME-encoded attachments are decoded. Any attachments whose MIME type starts with text/ (generally, HTML and text) are tokenised, after having any HTML tags stripped. Any non-textual attachments are replaced with their MD5 hash (such that two identical attachments will have the same hash), and that hash is then used as a token. ๐ฆ
In addition to single-word tokens from textual message parts, qsf adds doubled-up tokens so that word pairs get added to the database. This makes the database a bit bigger (although the automatic pruning tends to take care of that) but makes matching more exact. ๐
Special filters ๐ต๏ธโ๏ธ๐
As well as using the textual content of email to detect spam, qsf also uses special filters which create "pseudo-tokens" based on various rules. This means that specific patterns, not just individual words, can be used to determine whether a message is spam or not. ๐
For example, if a message contains lots of words with multiple consonants, like "ashjkbnxcsdjh", then each time a word like that is seen the special token .GIBBERISH-CONSONANTS. is added to the list of tokens found in the message. If it turns out that most messages with words that trigger this filter rule are spam, then other messages with gibberish consonant strings will be more likely to be flagged as spam. ๐
Currently the special filters are:
GTUBEโ Flags any message containing the stringXJS*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34Xas spam - useful for testing that your qsf installation is working. ๐งชATTACH-SCR,ATTACH-PIF,ATTACH-EXE,ATTACH-VBS,ATTACH-VBA,ATTACH-LNK,ATTACH-COM,ATTACH-BATโ Adds a token for every attachment whose filename ends in.scr,.pif,.exe,.vbs,.vba,.lnk,.com, and.batrespectively (these are often viruses). ๐ฆATTACH-GIF,ATTACH-JPG,ATTACH-PNGโ Adds a token for every attachment whose filename ends in.gif,.jpgor.jpeg, and.pngrespectively. ๐ผ๏ธATTACH-DOC,ATTACH-XLS,ATTACH-PDFโ Adds a token for every attachment whose filename ends in.doc,.xls, or.pdfrespectively (these tend to indicate a non-spam email). ๐SINGLE-IMAGEโ Adds a token if the message contains exactly one attached image. ๐ธMULTIPLE-IMAGESโ Adds a token if the message contains more than one attached image. ๐GIBBERISH-CONSONANTSโ Adds a token for every word found that has multiple consonants in a row, as described above. Spam often contains strings of gibberish. ๐ตโ๐ซGIBBERISH-VOWELSโ Adds a token for every word found that has multiple vowels in a row, eg"aeaiaiaeeio". ๐ฃ๏ธGIBBERISH-FROMCONSโ LikeGIBBERISH-CONSONANTS, but only for the โFrom:โ and โReturn-Path:โ addresses on their own. ๐ฌGIBBERISH-FROMVOWLโ LikeGIBBERISH-VOWELS, but only for the โFrom:โ and โReturn-Path:โ addresses on their own. ๐ฎGIBBERISH-BADSTARTโ Adds a token for every word that starts with a bad character such as%. ๐ซGIBBERISH-HYPHENSโ Adds a token for every word with more than three hyphens or underscores in it. โGIBBERISH-LONGWORDSโ Adds a token for every word with over30characters in it (but less than60). ๐HTML-COMMENTS-IN-WORDSโ Adds a token for every HTML comment found in the middle of a word. Spam often contains HTML inside words, like this:w<!--dsgfhsdgjgh-->ord๐HTML-EXTERNAL-IMGโ Adds a token for every HTML<img>(image) tag found that contains://(that is, it refers to an external image). ๐HTML-FONTโ Adds a token for every HTML<font>tag found. ๐คHTML-IP-IN-URLSโ Adds a token for every URL found containing an IP address. ๐ฅ๏ธHTML-INT-IN-URLโ Adds a token for every URL found containing an integer in its hostname. ๐ขHTML-URLENCODED-URLโ Adds a token for every URL found containing a%sign in its hostname. ๐งฉ
Normally, filters will just cause a token to be added, and these tokens are processed by the normal weighting algorithm. However, the GTUBE filter will immediately flag any matching message as spam, bypassing the token matching. ๐จ
Database backends ๐พโจ
The inbuilt "list" database backend does not provide the best possible performance, but is always available because using it requires no external libraries. ๐
If, when qsf was compiled, the correct libraries were available, then it will be possible to use qsf with alternative database backends. To find out which backends you have available, run qsf -V (capital V). To see how well a backend performs, collect some spam and non-spam and use the benchmark tool: qsf -d BACKEND -B SPAM NONSPAM (refer to the description of the -B option). ๐
Some people find that they get the best performance out of the gdbm backend; this is a library that is widely available on many systems. ๐
To efficiently use the same qsf database on multiple machines, you may find the MySQL backend useful. However, using it is a little more complicated. ๐ฌ
To use the MySQL backend you will need to create a table with the fields key1, key2, token, value1, value2 and value3. The token, value1, value2, and value3 fields must be VARCHAR(64), BIGINT or INT, and BIGINT or INT respectively, and indexing on the token field is a good idea. The key1 and key2 fields can be anything, but they must be present. ๐
For example:
USE mydatabase;
CREATE TABLE qsfdb (
key1 BIGINT UNSIGNED NOT NULL,
key2 BIGINT UNSIGNED NOT NULL,
token VARCHAR(64) DEFAULT '' NOT NULL,
value1 INT UNSIGNED NOT NULL,
value2 INT UNSIGNED NOT NULL,
value3 INT UNSIGNED NOT NULL,
PRIMARY KEY (key1,key2,token),
KEY (key1),
KEY (key2),
KEY (token)
);
The key1 and key2 fields allow you to have multiple qsf databases in one table, by specifying different key1 and key2 values on invocation. ๐
Instead of specifying a database file with the --database/-d option, you must specify either a specification string as described below, or the name of a file containing such a string on its first line. ๐
The specification string is as follows:
database=DATABASE;host=HOST;port=PORT;
user=USER;pass=PASS;table=TABLE;
key1=KEY1;key2=KEY2
This string must be all on one line, with no spaces. ๐ซ
DATABASEโ is the name of the MySQL database. ๐๏ธHOSTโ is the hostname of the database server (eg"localhost"). ๐PORTโ is the TCP port to connect on (eg3306). ๐USERโ is the username to connect with. ๐คPASSโ is the password to connect with. ๐TABLEโ is the database table to use. If a table with this name does not exist when qsf is called in update or training mode, then it will be created if permissions allow this to be done. ๐KEY1โ is the value to use for thekey1field. ๐ขKEY2โ is the value to use for thekey2field. ๐ข
Since command lines can be seen in the process list, it is advisable to specify a filename (such as qsf -d mysql:qsfdb.spec) and put the specification string inside that file. ๐
REPORTING BUGS ๐๐
Please report bugs or feature requests via the issue tracker linked from the qsf home page. ๐ซถ
SEE ALSO ๐โจ
procmail(1), procmailrc(5), procmailex(5) ๐
๐ COPYRIGHT ๐
Copyright ยฉ 2003-2007, 2015, 2021, 2025 Andrew Wood. ๐
License Artistic-2.0: Artistic License 2.0.
Okay so bestie, here's the legal tea, and it's actually the good kind ๐ซถ โ this is free software: you are free to change it and redistribute it. Periodt. Do whatever, remix it, ship it, slay.
โ ๏ธ But there is NO WARRANTY, to the extent permitted by law. Ngl that's just how it is โ no cap, no promises, no takebacks. ๐
And if you wanna see literally everyone who helped make this happen, please see the package's ACKNOWLEDGEMENTS file for a complete list of contributors. The whole squad. The group chat. Every single icon ๐โจ
Okay that's the whole page, bestie โ you made it to the end ๐ฆ This one's qsf version 1.2.23, dated 2025-07-06, and honestly? Iconic. Go forth and query safely, main character ๐
๐ธ๐
๐ฆ where this page comes from
- fedora 43: qsf 1.2.23-1.fc43 ยท Artistic-2.0
- fedora 44: qsf 1.2.23-2.fc44 ยท Artistic-2.0
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms