Next Previous Contents
 

10. Filtering and Sorting

10.1. Sorting with `vm-auto-archive-messages'

Date: June 20, 1998
Updated: January 6, 2003

You can use VM's `vm-auto-folder-alist' to automatically send messages to a default destination. This is described in the previous section. Use the `A' key (bound to `vm-auto-archive-messages') to automatically save messages to their folders.

If you'd like this to happen automatically when new mail is received, you can add the `vm-auto-archive-messages' function to `vm-arrived-messages-hook'.

If you wanted new mail, upon arrival, to be automatically moved to the folders defined in `vm-auto-folder-alist' and then deleted, you could do this:

(setq vm-delete-after-archiving t)

(defun my-vm-arrived-messages-hook ()
  (vm-auto-archive-messages)
  (vm-expunge-folder))

(add-hook 'vm-arrived-messages-hook 'my-vm-arrived-messages-hook)

10.2 Automatically generate folder name based on the username of the sender

Date: July 25, 1999
Updated: October 19, 1999

Use the code below to automatically generate the folder name to archive the message based on the user name in the From: address. The code will usually match any message. So if you want some messages to go to other folders, put those alist entries ahead of the comment in the code below.

(setq vm-auto-folder-alist
;; code to make auto-folder default to the user name of the sender
   '(("From"
      ("<\\([^ \t\n\f@%()<>]+\\)[@%]\\([^ \t\n\f<>()]+\\)>" .
       (buffer-substring (match-beginning 1) (match-end 1)))
      ("<\\([^>]+\\)>" . 
       (buffer-substring (match-beginning 1) (match-end 1)))
      ("\\([^ \t\n\f@%()<>]+\\)\\([@%]\\([^ \t\n\f<>()]+\\)\\)?" .
       (buffer-substring (match-beginning 1) (match-end 1)))
    ))
)

10.3. Auto-deleting with virtual folders

From: Stan Lanning Date: June 20, 1998
Updated: May 16, 2000

Here's my code for auto-deleting spam.

;;; Auto-delete spam mail.
;;; Just set up a virtual folder spec for the auto-delete folder, like this:
;;;
;;; Where it says (inbox) below, you should put ("/the/path/to/your/inbox")
;;; Or use backqoute and the value of vm-primary-inbox (1998 June 20 SOM)
;;;
;;;    (setq vm-virtual-folder-alist
;;;       (list
;;;        '("auto-delete"
;;;          ((inbox)
;;;           (and (or (unread) (new))
;;;                (or (author "AlphaCONNECT")
;;;                    (author "credit@usa.net")
;;;                    (and (author "mailer-daemon@rational.com")
;;;                         (subject "Account no longer active"))
;;;                    ))))))

(defun vm-auto-delete ()
  "*Mark for deletion any message matched by the
auto-delete virtual folder specification."
  (interactive)
  (condition-case c
      (if (and (boundp 'vm-virtual-folder-alist)
               (assoc "auto-delete" vm-virtual-folder-alist))
          (let (n)
            (save-window-excursion
              (save-excursion
                (vm-apply-virtual-folder "auto-delete")
                (setq n (length vm-message-list))
                (if (> n 0)
                    (progn
                      (vm-goto-message 1)
                      (vm-delete-message n)))
                (vm-quit)))
            (if (> n 0)
                (message "%d messages marked for deletion." n)
              (message "No messages marked for deletion."))))
    (error (message "vm-auto-delete error"))))

(add-hook 'vm-arrived-messages-hook 'vm-auto-delete)

10.4. Filtering with procmail

Date: June 20, 1998
Updated: February 8, 1999

See the FAQ for using Procmail with VM.

10.5. An example virtual-folder method

From: Greg A. Woods Date: June 20, 1998

As I mentioned in my posting to bug-vm on Tuesday I've recently started working with virtual folders in VM, and am generally very impressed, but have found some features lacking, namely the poor integration of virtual folders with real folders (i.e. incorporation of new mail isn't dynamic, etc.) and some user-interface features are less than ideal (a [virtual? both?] folders summary window would be great, as would dynamic updating of the "Virtual:Visit" menu (and the "Folder:Visit" one) to show the number of messages in each folder -- it's too easy to ignore mail now!).

I've since refined my implementation of workarounds to one of the features I've found lacking: namely I've implemented a hack to automatically incorporate new mail into virtual folders when new mail is incorporated into their underlying buffers and to automatically quit from virtual folders when their underlying folder is closed. These hacks are far from perfect yet -- I assume only one primary "INBOX", and that all virtual folders are derived from this one real folder.

I enclose the code here in hopes it will be helpful to someone, and in hopes that it will encourage other users to think about these features and perhaps someone will implement these features in a more integrated way.

I've been reading mail using these functions for the past few days and I don't know how I ever managed with the jumble before! (I.e. after I start VM I then immediately hit 'G' to get new mail and open all the virtual folders, then I read mail almost exclusively from the virtual folders, and I switch to INBOX and 'q' before I go to work to close everything at home so I can read from there, and vice versa when coming home; and if I need to read something new quick I use 'g' and read it directly from the INBOX folder.

(defvar my-vm-virtual-leftovers-folder "General Delivery"
  "The name of the virtual folder containing all the messages which are not
members of other virtual folders.  (It's created after all the others are
created by my-vm-visit-all-virtual-folders.)")

(defun my-vm-quit-all-virtual-folders ()
  "Quit from all of the virtual folders in vm-virtual-folder-alist."
  (interactive)
  (let ((vfolder vm-virtual-folder-alist))
    (while vfolder
      (save-excursion
        (condition-case error-data
            (let ((vm-confirm-quit nil))
              (set-buffer (concat "(" (car (car vfolder)) ")"))
              (vm-quit))
          (error nil))
        (setq vfolder (cdr vfolder))))))

(defun my-vm-visit-all-virtual-folders ()
  "Visit all of the virtual folders in vm-virtual-folder-alist."
  (interactive)
  (let ((vfolder vm-virtual-folder-alist))
    (while vfolder
      (let ((vfname (car (car vfolder))))
        (if (not (string-equal vfname my-vm-virtual-leftovers-folder))
            (vm-visit-virtual-folder vfname))
        (setq vfolder (cdr vfolder)))))
  (vm-visit-virtual-folder my-vm-virtual-leftovers-folder))

(defun my-vm-resync-all-virtual-folders ()
  "Revisit all virtual folders to update them."
  (interactive)
  (save-excursion
    (my-vm-quit-all-virtual-folders)
    (my-vm-visit-all-virtual-folders)))

(defun my-vm-get-new-mail ()
  "Local version of vm-get-new-mail that updates all virtual folders in
vm-virtual-folder-alist."
  (interactive)
  (save-excursion
    (my-vm-quit-all-virtual-folders)
    (set-buffer (vm-get-file-buffer vm-primary-inbox))
    (vm-get-new-mail)
    (my-vm-visit-all-virtual-folders)))

;; This overrides the normal binding of 'G' to vm-sort-messages....
(define-key vm-mode-map "G" 'my-vm-get-new-mail)

(defun my-vm-quit ()
  "Local version of vm-quit that quits all virtual buffers if quitting
vm-primary-inbox."
  (interactive)
  (vm-select-folder-buffer)
  (if (eq major-mode 'vm-virtual-mode)
      (vm-quit)
    (if (string-equal (buffer-file-name)
                      (expand-file-name vm-primary-inbox))
        (my-vm-quit-all-virtual-folders))
    (vm-quit)))

(define-key vm-mode-map "q" 'my-vm-quit)

;; This is the list of virtual folders created by my-vm-visit-all-virtual-folders
;;
;; XXX It would be nice to use vm-primary-inbox in some way instead of "INBOX"
;; XXX Wouldn't it?
;;
;; WARNING:  these definitions are based on the assumption that VM has
;; been patched to ignore case when comparing selector values to data.
;;
(setq vm-virtual-folder-alist
      ;; note the magic of creating the first element of the alist with
      ;; a symbol that needs to be eval'ed at assignment time
      (append (list (list (symbol-value 'my-vm-virtual-leftovers-folder)
                          (list (list "INBOX")
                                '(not (virtual-folder-member)))))
              '(("automake"
                 (("INBOX")
                  (sender-or-recipient "automake@")))
                ("cvs"
                 (("INBOX")
                  (or (sender-or-recipient "bug-cvs@")
                      (sender-or-recipient "info-cvs@"))))
                ("gnu-emacs"
                 (("INBOX")
                  (or (sender-or-recipient "bug-gnu-emacs@")
                      (sender-or-recipient "gnu-emacs-sources@")
                      (sender-or-recipient "info-gnu-emacs@")
                      (sender-or-recipient "gnu-emacs-bug@"))))
                ("VM"
                 (("INBOX")
                  (or (sender-or-recipient "bug-vm@")
                      (sender-or-recipient "info-vm@"))))
                ("exim"
                 (("INBOX")
                  (or (sender-or-recipient "exim-users@")
                      (sender-or-recipient "exim-announce@"))))
                ("ipfilter"
                 (("INBOX")
                  (sender-or-recipient "ipfilter@")))
                ("nanog"
                 (("INBOX")
                  (or (sender-or-recipient "nanog@")
                      (sender-or-recipient "nanog-announce@"))))
                ("smail3"
                 (("INBOX")
                  (sender-or-recipient "smail3")))
                ("vmailer"
                 (("INBOX")
                  (sender-or-recipient "vmailer-testers@")))
                ("other-lists"
                 (("INBOX")
                  (or (sender-or-recipient "ssh@")
                      (sender-or-recipient "stk@"))))
                ("ssh"
                 (("INBOX")
                  (sender-or-recipient "ssh@")))
                ("current-users"
                 (("INBOX")
                  (sender-or-recipient "current-users@")))
                ("netbsd-bugs"
                 (("INBOX")
                  (or (sender-or-recipient "gnats-bugs@gnats.netbsd.org")
                      (sender-or-recipient "netbsd-bugs@"))))
                ("netbsd-other"
                 (("INBOX")
                  (or (sender-or-recipient "netbsd-announce@")
                      (sender-or-recipient "netbsd-help@")
                      (sender-or-recipient "netbsd-ports@")
                      (sender-or-recipient "netbsd-users@"))))
                ("source-changes"
                 (("INBOX")
                  (or (sender-or-recipient "source-changes@")
                      (sender-or-recipient "source@netbsd.org"))))
                ("netbsd-tech"
                 (("INBOX")
                  (or (sender-or-recipient "tech-install@")
                      (sender-or-recipient "tech-kern@")
                      (sender-or-recipient "tech-net@")
                      (sender-or-recipient "tech-ports@")
                      (sender-or-recipient "tech-security@")
                      (sender-or-recipient "tech-toolchain@")
                      (sender-or-recipient "tech-userlevel@"))))
                ("port-alpha"
                 (("INBOX")
                  (sender-or-recipient "port-alpha@")))
                ("port-arm32"
                 (("INBOX")
                  (sender-or-recipient "port-arm32@")))
                ("port-i386"
                 (("INBOX")
                  (sender-or-recipient "port-i386@")))
                ("port-pmax"
                 (("INBOX")
                  (sender-or-recipient "port-pmax@")))
                ("port-powerpc"
                 (("INBOX")
                  (sender-or-recipient "port-powerpc@")))
                ("port-sparc"
                 (("INBOX")
                  (sender-or-recipient "port-sparc@")))
                ("port-sun3"
                 (("INBOX")
                  (sender-or-recipient "port-sun3@"))))))

10.6. Another virtual-folder example

From: David Bakhash Date: June 20, 1998

I am posting a new way to auto-archive mail. I think it beats how VM does auto-archiving, and for those of you who have taken the trouble to figure out how do do it, well, here's an alternative. More importantly, for those who thought that it was too much trouble to do it before, well now is your chance. It is almost identical to VM's `vm-auto-archive-messages', but different in that it uses virtual folders to sort things out.

If you have defined your very own virtual folder selectors in `vm-virtual-folder-alist', then you know how easy VM makes it, and you're already more than half-way there. For example, here's a piece of mine...

(setq vm-virtual-folder-alist
      '(("anneh"
         (("INBOX")
          (and (author "anneh")
               (header "jobslist"))))
        ("spam"
         (("INBOX")
          (or (not (or (recipient "cadet\\|dave")
                       (author "cadet\\|dave\\|mit\.edu\\|anneh")
                       (header "xemacs\.org\\|dismal-users")
                       (subject "\\bmit\\b")
                       (text "\\bmit\\b")))
              (header "^X-advertisement"))))))

well, now I can use those selectors (like "spam") to archive my messages. Here's how easy it is...

1) add this somewhere in you .vm file:

(defun db-vm-auto-archive-messages (arg)
  "Auto archive messages using applied virtual folders.

The prefix arg is meaningless here, unlike in `vm-auto-archive-messages'
where it does the following:
 Prefix arg means to ask user for confirmation before saving each message.
Here, you will not be prompted before saving at all.

When invoked on marked messages (via vm-next-command-uses-marks),
only marked messages are checked against vm-auto-folder-alist.

Archiving depends on the value of `db-vm-auto-folder-alist':
given each entry in `db-vm-auto-folder-alist', this function will
archive messages in the virtual folder which is the car of the entry
into the folder which is the cdr of the entry."
  (interactive "P")
  (save-excursion
    (save-window-excursion
      (flet ((bbdb/vm-update-record (&rest args) nil))
        (let ((use-marked (eq last-command 'vm-next-command-uses-marks))
              (vm-startup-with-summary nil) ; for efficiency
              (last-command 'vm-next-command-uses-marks))
          (vm-select-folder-buffer)
          (vm-check-for-killed-summary)
          (vm-error-if-folder-empty)
          (if use-marked
              (vm-create-virtual-folder 'marked)
            (vm-create-virtual-folder 'unfiled))
          (loop for entry in db-vm-auto-folder-alist
                for selector = (if (stringp (car entry))
                                   (car entry)
                                 (symbol-name (car entry)))
                for folder = (if (cdr entry)
                                 (concat (file-truename (or vm-folder-directory
                                                            default-directory))
                                         (cdr entry)))
                do (save-excursion
                     (vm-apply-virtual-folder selector)
                     (condition-case nil
                         (progn
                           (vm-mark-all-messages)
                           (if folder
                               (vm-save-message folder)
                             (call-interactively 'vm-delete-message)))
                       (error nil))
                     (vm-quit))))
          (vm-quit)))))         ; quit unfiled/marked folder

(defvar db-vm-auto-folder-alist nil
  "*Alist of virtual folder selectors and folder names for auto archiving.
\(see `db-vm-auto-archive-messages' for details\)
For each entry in the alist, the car must be a symbol or string matching a
virtual folder selector defined in `vm-virtual-folder-alist'.
The cdr can be any folder name, such as \"spam\" \(understood to be in
`vm-folder-directory', otherwise in `default-directory' if
`vm-folder-directory' is nil\).  If the cdr is `nil', then the messages
will be deleted.

An example of how to use this variable is:

\(setq db-vm-auto-folder-alist '\(\(spam . nil\)
                                \(jobfind . \"MAILING-LISTS\"\)
                                \(spousefind . \"MAILING-LISTS\"\)
                                \(mom . \"MOM\"\)\)\)")

(define-key vm-mode-map "A" 'db-vm-auto-archive-messages)

2) read the docs on how to use `db-vm-auto-archive-messages'. Here's an example of what I might put for `db-vm-auto-folder-alist':

(setq db-vm-auto-folder-alist   '((spam . nil)
                                  (anneh . "ANNEH")))

and that's it. for lot's of people, this won't be very hard to do at all, since you've already gone through the trouble of writing your virtual folder selectors, and if you haven't, you'll find that they're quite easy and fun to write.

10.7. VM and spam filtering

From: Bjorn Knutsson Date: June 20, 1998
Updated: May 15, 2004

VM in itself has no provisions explicitly for spam filtering, but the vm-auto-folder-alist can be used for simple spam filtering, e.g.:

(setq vm-auto-folder-alist
      '(("^Subject: "
         ("viagra\\|mortgage\\|enlarge" . "spam")
         ("weight.*loss" . "spam"))
        ("^X_Mailer: "
          ("BulkMailer" . "spam")
          ("Extractor Pro" . "spam"))))

Auto-folders can also be used with externally (on the mail server) run spam filters, such as SpamAssassin or bogofilter using the auto-folders to select the tagged mails, e.g.:

(setq vm-auto-folder-alist
      '(("^X-Spam-Status: " ("POSSIBLE SPAM" . "spam"))
        ("^X-Spam-Flag: " ("YES" . "spam"))
        ("^X-Bogosity: " ("Yes," . "spam"))))

These spam filters can also be run locally on your own machine, giving you better control over them. In this case, you also may want to try one of the VM-interfaces to these spamfilters:


Next Previous Contents