:type 'boolean
:group 'spam)
-(defcustom spam-use-bbdb nil
+(defcustom spam-use-BBDB nil
"Whether BBDB should be used by spam-split."
:type 'boolean
:group 'spam)
(spam-mark-spam-as-expired-and-move-routine (gnus-parameter-spam-process-destination gnus-newsgroup-name)))
(when (spam-group-ham-contents-p gnus-newsgroup-name)
- ;; TODO: the ham processors here
(when (or spam-use-whitelist
(spam-group-processor-whitelist-p gnus-newsgroup-name))
(spam-whitelist-register-routine))
((memq article gnus-newsgroup-saved))
((memq mark ham-mark-values) (push article ham-articles))))
(when (and ham-articles ham-func)
- (funcall ham-func ham-articles))
+ (mapc ham-func ham-articles)) ; we use mapc because unlike mapcar it discards the return values
(when (and spam-articles spam-func)
- (funcall spam-func spam-articles))))
+ (mapc spam-func spam-articles)))) ; we use mapc because unlike mapcar it discards the return values
+
+(defun spam-fetch-field-from-fast (article)
+ "Fetch the `from' field quickly, using the Gnus internal gnus-data-list function"
+ (if (and (numberp article)
+ (assoc article (gnus-data-list nil)))
+ (mail-header-from (gnus-data-header (assoc article (gnus-data-list nil))))
+ nil))
\f
;;;; Spam determination.
-
(defvar spam-list-of-checks
'((spam-use-blacklist . spam-check-blacklist)
(spam-use-whitelist . spam-check-whitelist)
- (spam-use-bbdb . spam-check-bbdb)
+ (spam-use-BBDB . spam-check-BBDB)
(spam-use-ifile . spam-check-ifile)
(spam-use-blackholes . spam-check-blackholes)
(spam-use-bogofilter . spam-check-bogofilter))
(when matches
spam-split-group)))
\f
+;;;; BBDB
+;;; original idea for spam-check-BBDB from Alexander Kotelnikov <sacha@giotto.sj.ru>
+
+;; all this is done inside a condition-case to trap errors
+(condition-case nil
+ (progn
+
+ (require 'bbdb-com)
+
+ (defun spam-enter-ham-BBDB (from)
+ "Enter an address into the BBDB; implies ham (non-spam) sender"
+ (when (stringp from)
+ (let* ((parsed-address (gnus-extract-address-components from))
+ (name (or (car parsed-address) "Ham Sender"))
+ (net-address (car (cdr parsed-address))))
+ (message "Adding address %s to BBDB" from)
+ (when (and net-address
+ (not (bbdb-search (bbdb-records) nil nil net-address)))
+ (bbdb-create-internal name nil net-address nil nil "ham sender added by spam.el")))))
+
+ (defun spam-BBDB-register-routine ()
+ (spam-generic-register-routine
+ ;; spam function
+ nil
+ ;; ham function
+ (lambda (article)
+ (spam-enter-ham-BBDB (spam-fetch-field-from-fast article)))))
+
+ (defun spam-check-BBDB ()
+ "Mail from people in the BBDB is never considered spam"
+ (let ((who (message-fetch-field "from")))
+ (when who
+ (setq who (regexp-quote (cadr (gnus-extract-address-components who))))
+ (if (bbdb-search (bbdb-records) nil nil who) nil spam-split-group)))))
+
+ (file-error (progn
+ (setq spam-list-of-checks
+ (delete (assoc 'spam-use-BBDB spam-list-of-checks)
+ spam-list-of-checks))
+ (defun spam-check-BBDB ()
+ message "spam-check-BBDB was invoked, but it shouldn't have")
+ (defun spam-BBDB-register-routine ()
+ (spam-generic-register-routine nil nil)))))
+
+\f
+;;;; ifile
+;;; uses ifile-gnus.el from http://www.ai.mit.edu/people/jhbrown/ifile-gnus.html
+;;; check the ifile backend; return nil if the mail was NOT classified as spam
+;;; TODO: we can't (require 'ifile-gnus), because it will insinuate itself automatically
+(defun spam-check-ifile ()
+ (let ((ifile-primary-spam-group spam-split-group))
+ (ifile-spam-filter nil)))
+
+;; TODO: add ifile registration
+;; We need ifile-gnus.el to support nnimap; we could feel the message
+;; directly to ifile like we do with bogofilter but that's ugly.
+(defun spam-ifile-register-routine ()
+ (spam-generic-register-routine nil nil))
+
+\f
;;;; Blacklists and whitelists.
(defvar spam-whitelist-cache nil)
(setq spam-whitelist-cache (spam-parse-list spam-whitelist)))
(if (spam-from-listed-p spam-whitelist-cache) nil spam-split-group))
-;;; original idea from Alexander Kotelnikov <sacha@giotto.sj.ru>
-(condition-case nil
- (progn
- (require 'bbdb-com)
- (defun spam-check-bbdb ()
- "We want messages from people who are in the BBDB not to be split to spam"
- (let ((who (message-fetch-field "from")))
- (when who
- (setq who (regexp-quote (cadr (gnus-extract-address-components who))))
- (if (bbdb-search (bbdb-records) nil nil who) nil spam-split-group)))))
- (file-error (setq spam-list-of-checks
- (delete (assoc 'spam-use-bbdb spam-list-of-checks)
- spam-list-of-checks))))
-
-;; TODO: add BBDB registration
-(defun spam-BBDB-register-routine
- (spam-generic-register-routine nil nil))
-
-;;; check the ifile backend; return nil if the mail was NOT classified as spam
-;;; TODO: we can't (require) ifile, because it will insinuate itself automatically
-(defun spam-check-ifile ()
- (let ((ifile-primary-spam-group spam-split-group))
- (ifile-spam-filter nil)))
-
-;; TODO: add ifile registration
-(defun spam-ifile-register-routine
- (spam-generic-register-routine nil nil))
-
(defun spam-check-blacklist ()
;; FIXME! Should it detect when file timestamps change?
(unless spam-blacklist-cache
cache nil)))
found))
-;; TODO: add blacklist and whitelist registrations
-(defun spam-blacklist-register-routine
- (spam-generic-register-routine nil nil))
-(defun spam-whitelist-register-routine
- (spam-generic-register-routine nil nil))
+(defun spam-blacklist-register-routine ()
+ (spam-generic-register-routine
+ ;; the spam function
+ (lambda (article)
+ (let ((from (spam-fetch-field-from-fast article)))
+ (when (stringp from)
+ (spam-enter-blacklist from))))
+ ;; the ham function
+ nil))
+
+(defun spam-whitelist-register-routine ()
+ (spam-generic-register-routine
+ ;; the spam function
+ nil
+ ;; the ham function
+ (lambda (article)
+ (let ((from (spam-fetch-field-from-fast article)))
+ (when (stringp from)
+ (spam-enter-whitelist from))))))
\f
-;;;; Training via Bogofilter. Last updated 2002-09-02.
+;;;; Bogofilter
;;; See Paul Graham article, at `http://www.paulgraham.com/spam.html'.
\e$BI>2A!"\e(B@kbd{C-h v} \e$B$GJQ?t$r8!::!"\e(B@kbd{q} \e$B$G<B9T$rCfCG!"$"$k$$\e(B
\e$B$O\e(B @kbd{c} \e$B$+\e(B @kbd{g} \e$B$G<B9T$r:F3+$9$k$3$H$,$G$-$^$9!#\e(B
-@c TRANSLATEME!
@cindex elp
@cindex profile
@cindex slow
-Sometimes, a problem do not directly generate a elisp error but
-manifests itself by causing Gnus to be very slow. In these cases, you
-can use @kbd{M-x toggle-debug-on-quit} and press C-j when things are
-slow, and then try to analyze the backtrace (repeating the procedure
-helps isolating the real problem areas). A fancier approach is to use
-the elisp profiler, ELP. The profiler is (or should be) fully
-documented elsewhere, but to get you started there are a few steps
-that need to be followed. First, instrument the part of Gnus you are
-interested in for profiling, e.g. @kbd{M-x elp-instrument-package RET
-gnus} or @kbd{M-x elp-instrument-packagre RET message}. Then perform
-the operation that is slow and press @kbd{M-x elp-results}. You will
-then see which operations that takes time, and can debug them further.
-If the entire operation takes much longer than the time spent in the
-slowest function in the profiler output, you probably profiled the
-wrong part of Gnus. To reset profiling statistics, use @kbd{M-x
-elp-reset-all}. @kbd{M-x elp-restore-all} is supposed to remove
-profiling, but given the complexities and dynamic code generation in
-Gnus, it might not always work perfectly.
+\e$B$H$-$I$-!"D>@\$K\e(B elisp \e$B$N%(%i!<$r5/$3$5$J$$$b$N$N!"\e(Bgnus \e$B$,Hs>o$KCY$/$J$k\e(B
+\e$B$?$a$KL@$i$+$K$J$kLdBj$,$"$j$^$9!#$=$s$J>l9g$K$O\e(B @kbd{M-x
+toggle-debug-on-quit} \e$B$r;H$C$F!"CY$/$J$C$?$H$-$K\e(B C-g \e$B$r2!$7!"$7$+$k8e$K\e(B
+\e$B%P%C%/%H%l!<%9$r2r@O$7$F2<$5$$\e(B (\e$B$=$N<jB3$-$r7+$jJV$9$3$H$O!"??$NLdBjNN0h\e(B
+\e$B$NJ,N%$KLrN)$A$^$9\e(B)\e$B!#$h$j>eEy$J<h$jAH$_$O\e(B elisp \e$B%W%m%U%!%$%i!<\e(B (\e$BLuCm\e(B: \e$B%W\e(B
+\e$B%m%0%i%`$N<B9T;~$NF0$-$rJ,@O$9$kF;6q\e(B) ELP \e$B$r;HMQ$9$k$3$H$G$9!#%W%m%U%!%$\e(B
+\e$B%i!<$K$D$$$F$O$I$3$+B>$N>l=j$G40A4$KJ8=q2=$5$l$F$$$k$O$:$G$9$,!"$=$l$r;O\e(B
+\e$B$a$k$?$a$KI,MW$J<j=g$r>/!9=q$$$F$*$-$^$7$g$&!#Bh0l$K!"%W%m%U%!%$%k$7$F$_\e(B
+\e$B$?$$\e(B gnus \e$B$NItJ,$r7WB,$9$k$?$a$N@_Dj$r!"Nc$($P\e(B @kbd{M-x
+elp-instrument-package RET gnus} \e$B$d\e(B @kbd{M-x elp-instrument-package RET
+message} \e$B$G9T$J$C$F2<$5$$!#$=$7$F!"CY$$F0:n$r9T$J$o$;$F$+$i\e(B @kbd{M-x
+elp-results} \e$B$r2!$7$^$7$g$&!#$9$k$H!"$I$NF0:n$,;~4V$r?)$C$F$$$k$+$r8+$F!"\e(B
+\e$B8e$G$=$l$i$r%G%P%C%0$9$k$3$H$,$G$-$^$9!#F0:nA4BN$,!"%W%m%U%!%$%i!<$N=PNO\e(B
+\e$B$NCf$G$G:G$bCY$$4X?t$GHq$d$5$l$?;~4V$h$j$O$k$+$KD9$/$+$+$k$N$O!"$?$V\e(B
+\e$B$s\e(B gnus \e$B$N4V0c$C$F$$$kItJ,$r%W%m%U%!%$%k$7$?$;$$$G$7$g$&!#%W%m%U%!%$%k$N\e(B
+\e$BE}7W$r%j%;%C%H$9$k$K$O\e(B @kbd{M-x elp-reset-all} \e$B$r;H$C$F2<$5$$!#\e(B@kbd{M-x
+elp-restore-all} \e$B$O%W%m%U%!%$%k$9$kF0:n$r<h$j=|$/$3$H$K$J$C$F$$$^$9$,!"\e(B
+gnus \e$B$K$h$C$FJ#;($K$5$l$+$DF0E*$J%3!<%I@8@.$N1F6A$r<u$1$k$?$a!"$=$l$OI,\e(B
+\e$B$:$7$b40A4$KF0:n$9$k$H$O8B$i$J$$$+$b$7$l$^$;$s!#\e(B
\e$B$b$71g=u$,M_$7$$$@$1$G$"$l$P!"\e(B@samp{gnu.emacs.gnus} \e$B$G?R$M$k$N$,NI$$$G$7$g\e(B
\e$B$&!#;d$O$"$^$jLr$KN)$A$^$;$s!#\e(B