#!/bin/sh ## Get info on one or more specific rules ## Usage: sa-rule-info [-s|-v[v]] [-l|-L] RULE [RULE...] ## -s Short output; show just the score(s); no description or regex ## -l Enable local configurations to trump upstream rule info (default) ## -L Ignore local configurations (check defaults + updates) ## -v Turns on verbose, which reveals alternate language entries ## -vv Also shows ALL rule hits in descending priority, with filenames ## RULE Rule to check. Tip: you can use a basic regex (like egrep) ## sa-rule-info v0.4, Copyright (C) 2008 by Adam Katz ## Licensed under the GNU AGPL v3+ (relicensing available upon request). unset short type egrep >/dev/null 2>&1 || egrep() { grep "$@"; } # failover for egrep no_comm() { cat; } # default to showing comments hide_fname() { sed 's/^[^:]*://g'; } # default: hide filenames. override w/ -vv DO_LOCAL=true while [ -n "$1" ]; do case $1 in -v|-vv ) [ "x$1" = "x-vv${1#-vv}" ] && very_verbose=true # -vv lang=true # parse languages unset short ;; -s ) short=true unset lang very_verbose ;; -l ) DO_LOCAL=true ;; -L ) unset DO_LOCAL ;; -- ) shift break ;; -?*) # --help sed -e '/^## /!d' -e 's///' "`which $0 2>/dev/null || echo $0`" [ "x$1" = x-h ]; exit $? # exit true if passed -h, else exit false ;; -|/dev/stdin ) shift; set `cat` ${1:+"$@"}; break ;; * ) break ;; esac shift done [ -n "$very_verbose" ] && hide_fname() { cat; } # -vv # strip comments and redundant score prefix, so "score FOO 3 # hi" -> "FOO 3" [ -n "$short" ] && \ no_comm() { sed -e 's/[ ]*#.*//g' -e 's/^.*score[ ][ ]*//'; } # find most recently modified of the given dirs or else failover to /dev/null latest_dir() { ls -td "$@" 2>/dev/null |head -n1 |grep . || echo '/dev/null'; } s="[ ()]" # match whitespace, aka "[\t ]" and almost like "\s" update_dir=`latest_dir /var/lib/spamassassin*/*/ /var/db/spamassassin*/*/` share_dir=`latest_dir /usr/local/share/spamassassin*/ /usr/share/spamassassin*/` conf_dir=`latest_dir /usr/local/etc/mail/spamassassin*/ /etc/spamassassin*/ \ /etc/mail/spamassassin*/` local_cf="${conf_dir%/}/local.cf" user_dir=`latest_dir "$HOME/.spamassassin"` # note, this ignores the possibility of spamd --virtual-config-dir TMP=`tempfile 2>/dev/null || mktemp /tmp/tmp$$XX 2>/dev/null || echo /tmp/tmp$$` TMP2=`tempfile 2>/dev/null || mktemp /tmp/tmp$$XX 2>/dev/null || echo /tmp/tp$$` trap "rm -f $TMP $TMP2" 0 1 2 5 15 # clean up temp files when we exit for RULE in "$@"; do if [ "$RULE" != "$1" ]; then echo "--"; fi # delimiter between RULEs [ "$RULE" = "^${RULE#?}" ] && b="$s" RULE="${RULE#?}" || b='' [ "$RULE" = "${RULE%?}$" ] && a="$s" RULE="${RULE%?}" || a='' # dot should match non-whitespace, force capitals RULE=`echo $RULE |sed "s/\./[^ . ]/g" |tr '[a-z]' '[A-Z]'` if [ "${#RULE}" -lt 4 ]; then echo "Please use a query longer than '$RULE'" >&2 continue fi # find files in all those dirs that are smaller than 2mb, search for RULE # (local.cf gets priority over other things in $conf_dir) find ${DO_LOCAL:+"$user_dir" "$local_cf" "$conf_dir"} \ "$update_dir" "$share_dir" -size -4096 -print0 2>/dev/null \ |xargs -0 egrep "^[^#]*$b$RULE$a" 2>/dev/null \ |grep -v "^Binary file" |hide_fname >$TMP for setting in `sed -e "s/^$s*//" -e "s/$s*[^ . ]*$RULE$a.*//" -e "s/$s/./g" $TMP`; do egrep "^$setting" $TMP2 >/dev/null && continue # skip token if found already [ -z "$lang" ] && [ "$setting" != "${setting#lang}" ] && continue # no langs [ -n "$short" ] && [ "$setting" = "${setting#score}" ] && continue # short? echo "$setting" >>$TMP2 # note that we have found this token egrep "^$s*$setting" $TMP |head -n1 |no_comm done # end settings parsing [ -s $TMP ] && y=y || echo "No SpamAssassin rules found for query '$RULE'" >&2 printf '' >$TMP done # end loop of given rules [ -n "$y" ]; exit $? # exits 0 if we had one hit, 1 if no queries matched.