summaryrefslogtreecommitdiff
path: root/lisp/bbdb-gnus.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/bbdb-gnus.el')
-rw-r--r--lisp/bbdb-gnus.el835
1 files changed, 835 insertions, 0 deletions
diff --git a/lisp/bbdb-gnus.el b/lisp/bbdb-gnus.el
new file mode 100644
index 0000000..0ea33db
--- /dev/null
+++ b/lisp/bbdb-gnus.el
@@ -0,0 +1,835 @@
+;;; -*- Mode:Emacs-Lisp -*-
+
+;;; This file is part of the Insidious Big Brother Database (aka BBDB),
+;;; copyright (c) 1991, 1992, 1993 Jamie Zawinski <jwz@netscape.com>.
+;;; Interface to Gnus. See bbdb.texinfo.
+
+;;; The Insidious Big Brother Database is free software; you can redistribute
+;;; it and/or modify it under the terms of the GNU General Public License as
+;;; published by the Free Software Foundation; either version 1, or (at your
+;;; option) any later version.
+;;;
+;;; BBDB is distributed in the hope that it will be useful, but WITHOUT ANY
+;;; WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+;;; FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+;;; details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Emacs; see the file COPYING. If not, write to
+;;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+(require 'bbdb)
+(require 'bbdb-snarf)
+(require 'gnus)
+
+(eval-and-compile
+ (require 'bbdb-com)
+ (require 'rfc822))
+
+;; Cater for older emacs (19.34) with default Gnus installation.
+(eval-and-compile
+ (condition-case nil
+ (progn
+ (require 'gnus-win)
+ (require 'gnus-sum)
+ (require 'gnus-art))
+ (error nil)))
+
+;;; Compiler hushing
+(eval-when-compile
+ (defvar gnus-optional-headers)
+ (defvar gnus-summary-to-prefix))
+
+(defsubst bbdb/gnus-ignored-from-addresses ()
+ "Return the value of `gnus-ignored-from-addresses' handling both
+recent Gnus (>= 04/2007) and older ones."
+ (cond ((fboundp 'gnus-ignored-from-addresses)
+ (gnus-ignored-from-addresses))
+ ((boundp 'gnus-ignored-from-addresses)
+ gnus-ignored-from-addresses)
+ (t nil)))
+
+(defun bbdb/gnus-get-message-id ()
+ "Return the message-id of the current message."
+ (save-excursion
+ (set-buffer gnus-original-article-buffer)
+ (goto-char (point-min))
+ (let ((case-fold-search t))
+ (if (re-search-forward "^Message-ID:\\s-*\\(<.+>\\)" (point-max) t)
+ (match-string 1)))))
+
+(defcustom bbdb/gnus-update-records-mode 'annotating
+; '(if (gnus-new-flag msg) 'annotating 'searching)
+ "Controls how `bbdb/gnus-update-records' processes email addresses.
+Set this to an expression which evaluates either to 'searching or
+'annotating. When set to 'annotating email addresses will be fed to
+`bbdb-annotate-message-sender' in order to update existing records or create
+new ones. A value of 'searching will search just for existing records having
+the right net.
+
+The default is to annotate only new messages."
+ :group 'bbdb-mua-specific-gnus
+ :type '(choice (const :tag "annotating all messages"
+ annotating)
+ (const :tag "annotating no messages"
+ searching)
+ (const :tag "annotating only new messages"
+ (if (equal ""
+ (gnus-summary-article-mark
+ (gnus-summary-article-number)))
+ 'annotating 'searching))
+ (sexp :tag "user defined")))
+
+
+;;;###autoload
+(defun bbdb/gnus-update-record (&optional offer-to-create)
+ "Return the record corresponding to the current Gnus message, creating
+or modifying it as necessary. A record will be created if
+bbdb/news-auto-create-p is non-nil, or if OFFER-TO-CREATE is true and
+the user confirms the creation."
+ (let* ((bbdb-get-only-first-address-p t)
+ (records (bbdb/gnus-update-records offer-to-create)))
+ (if records (car records) nil)))
+
+;;;###autoload
+(defun bbdb/gnus-update-records (&optional offer-to-create)
+ "Return the records corresponding to the current Gnus message, creating
+or modifying it as necessary. A record will be created if
+`bbdb/news-auto-create-p' is non-nil or if OFFER-TO-CREATE is true
+and the user confirms the creation.
+
+The variable `bbdb/gnus-update-records-mode' controls what actions
+are performed and it might override `bbdb-update-records-mode'.
+
+When hitting C-g once you will not be asked anymore for new people listed
+in this message, but it will search only for existing records. When hitting
+C-g again it will stop scanning."
+ (let ((bbdb-update-records-mode (or bbdb/gnus-update-records-mode
+ bbdb-update-records-mode))
+ (bbdb/gnus-offer-to-create offer-to-create)
+ ;; here we may distiguish between different type of messages
+ ;; for those that have no message id we have to find something
+ ;; else as message key.
+ (msg-id (bbdb/gnus-get-message-id))
+ records cache)
+ (save-excursion
+ (set-buffer gnus-article-buffer)
+
+ (if (and msg-id (not bbdb/gnus-offer-to-create))
+ (setq cache (bbdb-message-cache-lookup msg-id)))
+
+ (if cache
+ (setq records (if bbdb-get-only-first-address-p
+ (list (car cache))
+ cache))
+
+ (let ((bbdb-update-records-mode (or bbdb/gnus-update-records-mode
+ bbdb-update-records-mode)))
+ (setq records (bbdb-update-records
+ (bbdb-get-addresses
+ bbdb-get-only-first-address-p
+ (or (bbdb/gnus-ignored-from-addresses)
+ bbdb-user-mail-names)
+ 'gnus-fetch-field)
+ bbdb/news-auto-create-p
+ offer-to-create)))
+ (if (and bbdb-message-caching-enabled msg-id)
+ (bbdb-encache-message msg-id records))))
+ records))
+
+;;;###autoload
+(defun bbdb/gnus-annotate-sender (string &optional replace)
+ "Add a line to the end of the Notes field of the BBDB record
+corresponding to the sender of this message. If REPLACE is non-nil,
+replace the existing notes entry (if any)."
+ (interactive (list (if bbdb-readonly-p
+ (error "The Insidious Big Brother Database is read-only.")
+ (read-string "Comments: "))))
+ (gnus-summary-select-article)
+ (bbdb-annotate-notes (bbdb/gnus-update-record t) string 'notes replace))
+
+(defun bbdb/gnus-edit-notes (&optional arg)
+ "Edit the notes field or (with a prefix arg) a user-defined field
+of the BBDB record corresponding to the sender of this message."
+ (interactive "P")
+ (gnus-summary-select-article)
+ (let ((record (or (bbdb/gnus-update-record t) (error "unperson"))))
+ (bbdb-display-records (list record))
+ (if arg
+ (bbdb-record-edit-property record nil t)
+ (bbdb-record-edit-notes record t))))
+
+;;;###autoload
+(defun bbdb/gnus-show-records (&optional address-class)
+ "Display the contents of the BBDB for all addresses of this message.
+This buffer will be in `bbdb-mode', with associated keybindings."
+ (interactive)
+ (gnus-summary-select-article)
+ (let ((bbdb-get-addresses-headers
+ (if address-class
+ (list (assoc address-class bbdb-get-addresses-headers))
+ bbdb-get-addresses-headers))
+ (bbdb/gnus-update-records-mode 'annotating)
+ (bbdb-message-cache nil)
+ (bbdb-user-mail-names nil)
+ (gnus-ignored-from-addresses nil)
+ records)
+ (setq records (bbdb/gnus-update-records t))
+ (if records
+ (bbdb-display-records records)
+ (bbdb-undisplay-records))
+ records))
+
+;;;###autoload
+(defun bbdb/gnus-show-all-recipients ()
+ "Show all recipients of this message. Counterpart to `bbdb/vm-show-sender'."
+ (interactive)
+ (let ((bbdb-get-only-first-address-p nil))
+ (bbdb/gnus-show-records 'recipients)))
+
+(defun bbdb/gnus-show-sender (&optional show-recipients)
+ "Display the contents of the BBDB for the senders of this message.
+With a prefix argument show the recipients instead,
+with two prefix arguments show all records.
+This buffer will be in `bbdb-mode', with associated keybindings."
+ (interactive "p")
+ (cond ((= 4 show-recipients)
+ (bbdb/gnus-show-all-recipients))
+ ((= 16 show-recipients)
+ (let ((bbdb-get-only-first-address-p nil))
+ (bbdb/gnus-show-records)))
+ (t
+ (if (null (bbdb/gnus-show-records 'authors))
+ (bbdb/gnus-show-all-recipients)))))
+
+(defun bbdb/gnus-pop-up-bbdb-buffer (&optional offer-to-create)
+ "Make the *BBDB* buffer be displayed along with the Gnus windows,
+displaying the record corresponding to the sender of the current message."
+ (let ((bbdb-gag-messages t)
+ (records (bbdb/gnus-update-records offer-to-create))
+ (bbdb-electric-p nil))
+
+ (when bbdb-use-pop-up
+ (let ((b (current-buffer)))
+ ;; display the bbdb buffer iff there is a record for this article.
+ (if records
+ (bbdb-pop-up-bbdb-buffer
+ (lambda (w)
+ (let ((b (current-buffer)))
+ (set-buffer (window-buffer w))
+ (prog1 (eq major-mode 'gnus-article-mode)
+ (set-buffer b)))))
+ (or bbdb-inside-electric-display
+ (not (get-buffer-window bbdb-buffer-name))
+ (let (w)
+ (delete-other-windows)
+ (gnus-configure-windows 'article)
+ (if (setq w (get-buffer-window gnus-summary-buffer))
+ (select-window w)))))
+ (set-buffer b))
+ (if records (bbdb-display-records records bbdb-pop-up-display-layout)))
+ records))
+
+;;
+;; Announcing BBDB entries in the summary buffer
+;;
+
+(defcustom bbdb/gnus-lines-and-from-length 18
+ "*The number of characters used to display From: info in Gnus, if you have
+set gnus-optional-headers to 'bbdb/gnus-lines-and-from."
+ :group 'bbdb-mua-specific-gnus
+ :type 'integer)
+
+(defcustom bbdb/gnus-summary-mark-known-posters t
+ "*If t, mark messages created by people with records in the BBDB.
+In Gnus, this marking will take place in the subject list (assuming
+`gnus-optional-headers' contains `bbdb/gnus-lines-and-from'). In Gnus, the
+marking will take place in the Summary buffer if the format code defined by
+`bbdb/gnus-summary-user-format-letter' is used in `gnus-summary-line-format'.
+This variable has no effect on the marking controlled by
+`bbdb/gnus-summary-in-bbdb-format-letter'."
+ :group 'bbdb-mua-specific-gnus
+ :type '(choice (const :tag "Mark known posters" t)
+ (const :tag "Do not mark known posters" nil)))
+(defvaralias 'bbdb/gnus-mark-known-posters
+ 'bbdb/gnus-summary-mark-known-posters)
+
+(defcustom bbdb/gnus-summary-known-poster-mark "+"
+ "This is the default character to prefix author names with if
+bbdb/gnus-summary-mark-known-posters is t. If the poster's record has
+an entry in the field named by bbdb-message-marker-field, then that will
+be used instead."
+ :group 'bbdb-mua-specific-gnus
+ :type 'character)
+
+(defcustom bbdb/gnus-summary-show-bbdb-names t
+ "*If both this variable and `bbdb/gnus-summary-prefer-real-names' are true,
+then for messages from authors who are in your database, the name
+displayed will be the primary name in the database, rather than the
+one in the From line of the message. This doesn't affect the names of
+people who aren't in the database, of course. (`gnus-optional-headers'
+must be `bbdb/gnus-lines-and-from' for Gnus users.)"
+ :group 'bbdb-mua-specific-gnus
+ :type 'boolean)
+(defvaralias 'bbdb/gnus-header-show-bbdb-names
+ 'bbdb/gnus-summary-show-bbdb-names)
+
+(defcustom bbdb/gnus-summary-prefer-bbdb-data t
+ "If t, then for posters who are in our BBDB, replace the information
+provided in the From header with data from the BBDB."
+ :group 'bbdb-mua-specific-gnus
+ :type 'boolean)
+
+(defcustom bbdb/gnus-summary-prefer-real-names t
+ "If t, then display the poster's name from the BBDB if we have one,
+otherwise display his/her primary net address if we have one. If it
+is set to the symbol bbdb, then real names will be used from the BBDB
+if present, otherwise the net address in the post will be used. If
+bbdb/gnus-summary-prefer-bbdb-data is nil, then this has no effect.
+See `bbdb/gnus-lines-and-from' for Gnus users, or
+`bbdb/gnus-summary-user-format-letter' for Gnus users."
+ :group 'bbdb-mua-specific-gnus
+ :type '(choice (const :tag "Prefer real names" t)
+ (const :tag "Prefer network addresses" nil)))
+(defvaralias 'bbdb/gnus-header-prefer-real-names
+ 'bbdb/gnus-summary-prefer-real-names)
+
+(defcustom bbdb/gnus-summary-user-format-letter "B"
+ "This is the gnus-user-format-function- that will be used to insert
+the information from the BBDB in the summary buffer (using
+`bbdb/gnus-summary-get-author'). This format code is meant to replace
+codes that insert sender names or addresses (like %A or %n). Unless
+you've already got other code using user format B, you might as well
+stick with the default. Additionally, if the value of this variable
+is nil, no format function will be installed for
+`bbdb/gnus-summary-get-author'. See also
+`bbdb/gnus-summary-in-bbdb-format-letter', which installs a format
+code for `bbdb/gnus-summary-author-in-bbdb'"
+ :group 'bbdb-mua-specific-gnus
+ :type 'character)
+
+(defcustom bbdb/gnus-summary-in-bbdb-format-letter "b"
+ "This is the gnus-user-format-function- that will be used to insert
+`bbdb/gnus-summary-known-poster-mark' (using
+`bbdb/gnus-summary-author-in-bbdb') if the poster is in the BBDB, and
+\" \" if not. If the value of this variable is nil, no format code
+will be installed for `bbdb/gnus-summary-author-in-bbdb'. See also
+`bbdb/gnus-summary-user-format-letter', which installs a format code
+for `bbdb/gnus-summary-get-author'."
+ :group 'bbdb-mua-specific-gnus
+ :type 'character)
+
+(defcustom bbdb-message-marker-field 'mark-char
+ "*The field whose value will be used to mark messages by this user in Gnus."
+ :group 'bbdb-mua-specific-gnus
+ :type 'symbol)
+
+(defun bbdb/gnus-summary-get-author (header)
+ "Given a Gnus message header, returns the appropriate piece of
+information to identify the author in a Gnus summary line, depending on
+the settings of the various configuration variables. See the
+documentation for the following variables for more details:
+ `bbdb/gnus-summary-mark-known-posters'
+ `bbdb/gnus-summary-known-poster-mark'
+ `bbdb/gnus-summary-prefer-bbdb-data'
+ `bbdb/gnus-summary-prefer-real-names'
+This function is meant to be used with the user function defined in
+ `bbdb/gnus-summary-user-format-letter'"
+ (let* ((from (mail-header-from header))
+ (to (let ((gifa (bbdb/gnus-ignored-from-addresses)))
+ (when (and gifa (string-match gifa from))
+ (let* ((extras (mail-header-extra header))
+ (to (or (cdr (assoc 'To extras))
+ (cdr (assoc 'CC extras))
+ (cdr (assoc 'Newgroups extras)))))
+ (if (and to (listp to))
+ (cdr (car to))
+ to)))))
+ (data (and bbdb/gnus-summary-show-bbdb-names
+ (condition-case nil
+ (mail-extract-address-components (or to from))
+ (error nil))))
+ (name (car data))
+ (net (car (cdr data)))
+ (record (and data
+ (bbdb-search-simple
+ name
+ (if (and net bbdb-canonicalize-net-hook)
+ (bbdb-canonicalize-address net)
+ net)))))
+
+ (if (and record name (member (downcase name) (bbdb-record-net record)))
+ ;; bogon!
+ (setq record nil))
+ (setq name
+ (or (and bbdb/gnus-summary-prefer-bbdb-data
+ (or (and bbdb/gnus-summary-prefer-real-names
+ (and record (bbdb-record-name record)))
+ (and record (bbdb-record-net record)
+ (nth 0 (bbdb-record-net record)))))
+ (and bbdb/gnus-summary-prefer-real-names
+ (or (and (equal bbdb/gnus-summary-prefer-real-names 'bbdb)
+ net)
+ name))
+ net from "**UNKNOWN**"))
+ (format "%s%s%s"
+ (if to
+ (if (and (boundp 'gnus-summary-to-prefix)
+ (stringp gnus-summary-to-prefix))
+ gnus-summary-to-prefix
+ "To: ")
+ "")
+ (or (and record bbdb/gnus-summary-mark-known-posters
+ (or (bbdb-record-getprop
+ record bbdb-message-marker-field)
+ bbdb/gnus-summary-known-poster-mark))
+ " ")
+ name)))
+
+;; DEBUG: (bbdb/gnus-summary-author-in-bbdb "From: simmonmt@acm.org")
+(defun bbdb/gnus-summary-author-in-bbdb (header)
+ "Given a Gnus message header, returns a mark if the poster is in the BBDB, \" \" otherwise. The mark itself is the value of the field indicated by `bbdb-message-marker-field' (`mark-char' by default) if the indicated field is in the poster's record, and `bbdb/gnus-summary-known-poster-mark' otherwise."
+ (let* ((from (mail-header-from header))
+ (data (condition-case ()
+ (mail-extract-address-components from)
+ (error nil)))
+ (name (car data))
+ (net (cadr data))
+ record)
+ (if (and data
+ (setq record
+ (bbdb-search-simple
+ name (if (and net bbdb-canonicalize-net-hook)
+ (bbdb-canonicalize-address net)
+ net))))
+ (or (bbdb-record-getprop
+ record bbdb-message-marker-field)
+ bbdb/gnus-summary-known-poster-mark) " ")))
+
+;;
+;; Gnus-specific snarfing (see also bbdb-snarf.el)
+;;
+
+;;;###autoload
+(defun bbdb/gnus-snarf-signature ()
+ "Snarf signature from the corresponding *Article* buffer."
+ (interactive)
+ (save-excursion
+ ;; this is a little bogus, since it will remain set after you've
+ ;; quit Gnus
+ (or gnus-article-buffer (error "Not in Gnus!"))
+ ;; This is wrong for non-ASCII text. Why not use
+ ;; gnus-article-hide-signature?
+ (set-buffer gnus-original-article-buffer)
+ (save-restriction
+ (or (gnus-article-narrow-to-signature) (error "No signature!"))
+ (bbdb-snarf-region (point-min) (point-max)))))
+
+;;
+;; Scoring
+;;
+
+(defcustom bbdb/gnus-score-field 'gnus-score
+ "This variable contains the name of the BBDB field which should be
+checked for a score to add to the net addresses in the same record."
+ :group 'bbdb-mua-specific-gnus-scoring
+ :type 'symbol)
+
+(defcustom bbdb/gnus-score-default nil
+ "If this is set, then every net address in the BBDB that does not have
+an associated score field will be assigned this score. A value of nil
+implies a default score of zero."
+ :group 'bbdb-mua-specific-gnus-scoring
+ :type '(choice (const :tag "Do not assign default score")
+ (integer :tag "Assign this default score" 0)))
+
+(defvar bbdb/gnus-score-default-internal nil
+ "Internal variable for detecting changes to
+`bbdb/gnus-score-default'. You should not set this variable directly -
+set `bbdb/gnus-score-default' instead.")
+
+(defvar bbdb/gnus-score-alist nil
+ "The text version of the scoring structure returned by
+bbdb/gnus-score. This is built automatically from the BBDB.")
+
+(defvar bbdb/gnus-score-rebuild-alist t
+ "Set to t to rebuild bbdb/gnus-score-alist on the next call to
+bbdb/gnus-score. This will be set automatically if you change a BBDB
+record which contains a gnus-score field.")
+
+(defun bbdb/gnus-score-invalidate-alist (rec)
+ "This function is called through `bbdb-after-change-hook',
+and sets `bbdb/gnus-score-rebuild-alist' to t if the changed
+record contains a gnus-score field."
+ (if (bbdb-record-getprop rec bbdb/gnus-score-field)
+ (setq bbdb/gnus-score-rebuild-alist t)))
+
+;;;###autoload
+(defun bbdb/gnus-score (group)
+ "This returns a score alist for Gnus. A score pair will be made for
+every member of the net field in records which also have a gnus-score
+field. This allows the BBDB to serve as a supplemental global score
+file, with the advantage that it can keep up with multiple and changing
+addresses better than the traditionally static global scorefile."
+ (list (list
+ (condition-case nil
+ (read (bbdb/gnus-score-as-text group))
+ (error (setq bbdb/gnus-score-rebuild-alist t)
+ (message "Problem building BBDB score table.")
+ (ding) (sit-for 2)
+ nil)))))
+
+(defun bbdb/gnus-score-as-text (group)
+ "Returns a SCORE file format string built from the BBDB."
+ (cond ((or (cond ((/= (or bbdb/gnus-score-default 0)
+ (or bbdb/gnus-score-default-internal 0))
+ (setq bbdb/gnus-score-default-internal
+ bbdb/gnus-score-default)
+ t))
+ (not bbdb/gnus-score-alist)
+ bbdb/gnus-score-rebuild-alist)
+ (setq bbdb/gnus-score-rebuild-alist nil)
+ (setq bbdb/gnus-score-alist
+ (concat "((touched nil) (\"from\"\n"
+ (mapconcat
+ (lambda (rec)
+ (let ((score (or (bbdb-record-getprop rec
+ bbdb/gnus-score-field)
+ bbdb/gnus-score-default))
+ (net (bbdb-record-net rec)))
+ (if (not (and score net)) nil
+ (mapconcat
+ (lambda (addr)
+ (format "(\"%s\" %s)\n" addr score))
+ net ""))))
+ (bbdb-records) "")
+ "))"))))
+ bbdb/gnus-score-alist)
+
+;;;###autoload
+(defun bbdb/gnus-summary-show-all-recipients (not-elided)
+ "Display BBDB records for all recipients of the message."
+ (interactive "P")
+ (let ((bbdb-display-layout (or (not not-elided)
+ bbdb-pop-up-display-layout
+ bbdb-display-layout))
+ (bbdb-get-only-first-address-p nil))
+ (gnus-summary-select-article)
+ (bbdb/gnus-show-records 'recipients)))
+
+;;; from Brian Edmonds' gnus-bbdb.el
+;;;
+;;; Filing with gnus-folder REQUIRES (ding) 0.50 OR HIGHER
+;;;
+;;; To use this feature, you need to put this file somewhere in your
+;;; load-path and add the following lines of code to your .gnus file:
+;;;
+;;; (setq nnmail-split-methods 'bbdb/gnus-split-method)
+;;;
+;;; You should also examine the variables defvar'd below and customize
+;;; them to your taste. They're listed roughly in descending likelihood
+;;; of your wanting to change them. Once that is done, you need to add
+;;; filing information to your BBDB. There are two fields of interest:
+;;;
+;;; 1. gnus-private. This field contains the name of the group in which
+;;; mail to you from any of the addresses associated with this record
+;;; will be filed. Also, any self-copies of mail you send any of the
+;;; same addresses will be filed here.
+;;; 2. gnus-public. This field is used to keep mail from mailing lists
+;;; out of the private mailboxes. It should be added to a record for
+;;; the list submission address, and is formatted as follows:
+;;; "group regexp"
+;;; where group is where mail from the list should be filed, and
+;;; regexp is a regular expression which is checked against the
+;;; envelope sender (from the From_ header) to verify that this is
+;;; the copy which came from the list. For example, the entry for
+;;; the ding mailing list might be:
+;;; "mail.emacs.ding ding-request@ifi.uio.no"
+;;; Yes, the second part *is* a regexp, so those dots may match
+;;; something other than dots. Sue me.
+;;;
+;;; Note that you can also specify a gnus-private field for mailing list
+;;; addresses, in which case self-copies of mail you send to the list
+;;; will be filed there. Also, the field names can be changed below if
+;;; the defaults aren't hip enough for you. Lastly, if you specify a
+;;; gnus-private field for your *own* BBDB record, then all self-copies
+;;; of mail you send will be filed to that group.
+;;;
+;;; This documentation should probably be expanded and moved to a
+;;; separate file, but it's late, and *I* know what I'm trying to
+;;; say. :)
+
+;;; custom bits
+(defcustom bbdb/gnus-split-default-group "mail.misc"
+ "*If the BBDB doesn't indicate any group to spool a message to, it will
+be spooled to this group. If bbdb/gnus-split-crosspost-default is not
+nil, and if the BBDB did not indicate a specific group for one or more
+addresses, messages will be crossposted to this group in addition to any
+group(s) which the BBDB indicated."
+ :group 'bbdb-mua-specific-gnus-splitting
+ :type 'string)
+
+(defcustom bbdb/gnus-split-nomatch-function nil
+ "*This function will be called after searching the BBDB if no place to
+file the message could be found. It should return a group name (or list
+of group names) -- nnmail-split-fancy as provided with Gnus is an
+excellent choice."
+ :group 'bbdb-mua-specific-gnus-splitting
+ :type 'function)
+
+(defcustom bbdb/gnus-split-myaddr-regexp
+ (concat "^" (user-login-name) "$\\|^"
+ (user-login-name) "@\\([-a-z0-9]+\\.\\)*"
+ (or gnus-local-domain (message-make-domain)
+ (system-name) "") "$")
+ "*This regular expression should match your address as found in the
+From header of your mail. You should make sure gnus-local-domain or
+gnus-use-generic-from are set before loading this module, if they differ
+from (system-name). If you send mail/news from multiple addresses, then
+you'll likely have to set this yourself anyways."
+ :group 'bbdb-mua-specific-gnus-splitting
+ :type 'string)
+
+(defcustom bbdb/gnus-split-crosspost-default nil
+ "*If this variable is not nil, then if the BBDB could not identify a
+group for every mail address, messages will be filed in
+bbdb/gnus-split-default-group in addition to any group(s) which the BBDB
+identified."
+ :group 'bbdb-mua-specific-gnus-splitting
+ :type 'boolean)
+
+(defcustom bbdb/gnus-split-private-field 'gnus-private
+ "*This variable is used to determine the field to reference to find the
+associated group when saving private mail for a network address known to
+the BBDB. The value of the field should be the name of a mail group."
+ :group 'bbdb-mua-specific-gnus-splitting
+ :type 'string)
+
+(defcustom bbdb/gnus-split-public-field 'gnus-public
+ "*This variable is used to determine the field to reference to find the
+associated group when saving non-private mail (received from a mailing
+list) for a network address known to the BBDB. The value of the field
+should be the name of a mail group, followed by a space, and a regular
+expression to match on the envelope sender to verify that this mail came
+from the list in question."
+ :group 'bbdb-mua-specific-gnus-splitting
+ :type 'string)
+
+;; The split function works by assigning one of four spooling priorities
+;; to each group that is associated with an address in the message. The
+;; priorities are assigned as follows:
+;;
+;; 0. This priority is assigned when crosspost-default is nil to To/Cc
+;; addresses which have no private group defined in the BBDB. If the
+;; user's own address has no private group defined, then it will
+;; always be given this priority.
+;; 1. This priority is assigned to To/Cc addresses which have a private
+;; group defined in the BBDB. If crosspost-default is not nil, then
+;; To/Cc addresses which have no private group will also be assigned
+;; this priority. This is also assigned to the user's own address in
+;; the From position if a private group is defined for it.
+;; 2. This priority is assigned to From addresses which have a private
+;; group defined in the BBDB, except for the user's own address as
+;; described under priorities 0 and 1.
+;; 3. This priority is assigned to To/Cc addresses which have a public
+;; group defined in the BBDB, and whose associated regular expression
+;; matches the envelope sender (found in the header From_).
+;;
+;; The split function evaluates the spool priority for each address in
+;; the headers of the message, and returns as a list all the groups
+;; associated with the addresses which share the highest calculated
+;; priority.
+
+;;;#autoload
+(defun bbdb/gnus-split-method nil
+ "This function expects to be called in a buffer which contains a mail
+message to be spooled, and the buffer should be narrowed to the message
+headers. It returns a list of groups to which the message should be
+spooled, using the addresses in the headers and information from the
+BBDB."
+ (let ((prq (list (cons 0 nil) (cons 1 nil) (cons 2 nil) (cons 3 nil))))
+ ;; the From: header is special
+ (let* ((hdr (or (mail-fetch-field "resent-from")
+ (mail-fetch-field "from")
+ (user-login-name)))
+ (rv (bbdb/gnus-split-to-group hdr t)))
+ (setcdr (nth (cdr rv) prq) (cons (car rv) nil)))
+ ;; do the rest of the headers
+ (let ((hdr (or (concat (or (mail-fetch-field "resent-to" nil t)
+ (mail-fetch-field "to" nil t))
+ ", "
+ (mail-fetch-field "cc" nil t)
+ ", "
+ (mail-fetch-field "apparently-to" nil t))
+ "")))
+ (setq hdr (rfc822-addresses hdr))
+ (while hdr
+ (let* ((rv (bbdb/gnus-split-to-group (car hdr)))
+ (pr (nth (cdr rv) prq)))
+ (or (member (car rv) pr) (setcdr pr (cons (car rv) (cdr pr)))))
+ (setq hdr (cdr hdr))))
+ ;; find the highest non-empty queue
+ (setq prq (reverse prq))
+ (while (and prq (not (cdr (car prq)))) (setq prq (cdr prq)))
+ ;; and return...
+ (if (not (or (not (cdr (car prq)))
+ (and (equal (cdr (car prq)) (list bbdb/gnus-split-default-group))
+ (symbolp bbdb/gnus-split-nomatch-function)
+ (fboundp bbdb/gnus-split-nomatch-function))))
+ (cdr (car prq))
+ (goto-char (point-min))
+ (funcall bbdb/gnus-split-nomatch-function))))
+
+(defun bbdb/gnus-split-to-group (addr &optional source)
+ "This function is called from bbdb/gnus-split-method in order to
+determine the group and spooling priority for a single address."
+ (condition-case tmp
+ (progn
+ (setq tmp (mail-extract-address-components addr))
+ (let* ((nam (car tmp))
+ (net (if (not bbdb-canonicalize-net-hook) (car (cdr tmp))
+ (bbdb-canonicalize-address (car (cdr tmp)))))
+ (rec (bbdb-search-simple nam net))
+ pub prv rgx)
+ (if (not rec) nil
+ (setq prv (bbdb-record-getprop rec bbdb/gnus-split-private-field)
+ pub (bbdb-record-getprop rec bbdb/gnus-split-public-field))
+ (if (and pub (not source) (string-match "^\\([^ ]+\\) \\(.*\\)$" pub))
+ (setq rgx (substring pub (match-beginning 2) (match-end 2))
+ pub (substring pub (match-beginning 1) (match-end 1)))
+ (setq pub nil)))
+ (cond
+ ((and rgx pub
+ (goto-char (point-min))
+ (re-search-forward "^From: \\([^ \n]+\\)[ \n]" nil t)
+ (string-match rgx (buffer-substring (match-beginning 1)
+ (match-end 1))))
+ (cons pub 3))
+ (prv
+ (cons prv
+ (- 1 (if source -1 0)
+ (if (string-match bbdb/gnus-split-myaddr-regexp net) 1 0))))
+ (t
+ (cons bbdb/gnus-split-default-group
+ (if (string-match bbdb/gnus-split-myaddr-regexp net) 0
+ (if source 2 (if bbdb/gnus-split-crosspost-default 1 0))))))))
+ (error (cons bbdb/gnus-split-default-group 0))))
+
+;;
+;; Insinuation
+;;
+
+;;;###autoload
+(defun bbdb-insinuate-gnus ()
+ "Call this function to hook BBDB into Gnus."
+ (setq gnus-optional-headers 'bbdb/gnus-lines-and-from)
+ (add-hook 'gnus-article-prepare-hook 'bbdb/gnus-pop-up-bbdb-buffer)
+ (add-hook 'gnus-save-newsrc-hook 'bbdb-offer-save)
+ (define-key gnus-summary-mode-map ":" 'bbdb/gnus-show-sender)
+ (define-key gnus-summary-mode-map ";" 'bbdb/gnus-edit-notes)
+
+ ;; Set up user field for use in gnus-summary-line-format
+ (let ((get-author-user-fun (intern
+ (concat "gnus-user-format-function-"
+ bbdb/gnus-summary-user-format-letter)))
+ (in-bbdb-user-fun (intern
+ (concat "gnus-user-format-function-"
+ bbdb/gnus-summary-in-bbdb-format-letter))))
+ ; The big one - whole name
+ (cond (bbdb/gnus-summary-user-format-letter
+ (if (and (fboundp get-author-user-fun)
+ (not (eq (symbol-function get-author-user-fun)
+ 'bbdb/gnus-summary-get-author)))
+ (bbdb-warn
+ (format "`gnus-user-format-function-%s' already seems to be in use.
+Please redefine `bbdb/gnus-summary-user-format-letter' to a different letter."
+ bbdb/gnus-summary-user-format-letter))
+ (fset get-author-user-fun 'bbdb/gnus-summary-get-author))))
+
+ ; One tick. One tick only, please
+ (cond (bbdb/gnus-summary-in-bbdb-format-letter
+ (if (and (fboundp in-bbdb-user-fun)
+ (not (eq (symbol-function in-bbdb-user-fun)
+ 'bbdb/gnus-summary-author-in-bbdb)))
+ (bbdb-warn
+ (format "`gnus-user-format-function-%s' already seems to be in use.
+Redefine `bbdb/gnus-summary-in-bbdb-format-letter' to a different letter."
+ bbdb/gnus-summary-in-bbdb-format-letter))
+ (fset in-bbdb-user-fun 'bbdb/gnus-summary-author-in-bbdb)))))
+
+ ;; Scoring
+ (add-hook 'bbdb-after-change-hook 'bbdb/gnus-score-invalidate-alist)
+; (setq gnus-score-find-score-files-function
+; (if (boundp 'gnus-score-find-score-files-function)
+; (cond ((functionp gnus-score-find-score-files-function)
+; (list gnus-score-find-score-files-function
+; 'bbdb/gnus-score))
+; ((listp gnus-score-find-score-files-function)
+; (append gnus-score-find-score-files-function
+; 'bbdb/gnus-score))
+; (t 'bbdb/gnus-score))
+; 'bbdb/gnus-score))
+ )
+
+;; Uwe Brauer
+(defun bbdb/gnus-nnimap-folder-list-from-bbdb ()
+ "Return a list of \( \"From\" email-regexp imap-folder-name\) tuples
+based on the contents of the bbdb.
+
+The folder-name is the value of the 'imap attribute on the bbdb
+record; the email-regexp consists of all the email addresses for the
+bbdb record concatenated with with OR. bbdb records without a 'imap
+attribute are ignored.
+Here is an example of a relevant BBDB entry:
+
+Uwe Brauer
+ net: oub@mat.ucm.es
+ imap: testimap
+
+
+This function uses regexp-opt to generate the email-regexp which
+automatically regexp-quotes its arguments. Please note: in oder that
+this will work with the nnimap-split-fancy method you have to use
+macros, that is your setting will look like:
+
+\(setq
+ nnimap-split-rule 'nnimap-split-fancy
+ nnimap-split-inbox \"INBOX\"
+ nnimap-split-fancy
+ `\(|
+ ,@\(bbdb/gnus-nnimap-folder-list-from-bbdb\)
+ ...
+\)\)
+Note that `\( is the backquote NOT the quote '\(. "
+
+ ;(interactive)
+ (let ( ;; the raw-notes attribute of a bbdb record
+ notes-attr
+ ;; the value of the 'imap attribute of a bbdb record
+ folder-attr
+ ;; strings to put before and after the folder-attr
+ (folder-prefix "")
+ (folder-postfix "")
+ ;; a regexp matching all the email addresses from a bbdb record
+ email-regexp
+ ;; the list of (folder email) tuples to return
+ new-elmnt-list
+ )
+ ;; loop over the bbdb-records; if a imap attribute exists on
+ ;; the record, generate a regexp matching all the email addresses
+ ;; and add a tuple (folder email-regexp) to the new-elmnt-list
+ (dolist (record (bbdb-records))
+ (setq notes-attr (bbdb-record-raw-notes record))
+ (when (and (listp notes-attr)
+ (setq folder-attr (cdr (assq 'imap notes-attr))))
+ (setq email-regexp (regexp-opt (mapcar 'downcase
+ (bbdb-record-net record))))
+ (unless (zerop (length email-regexp))
+ (setq new-elmnt-list
+ (cons (list "From" email-regexp (concat folder-prefix
+ folder-attr folder-postfix))
+ new-elmnt-list)))))
+ new-elmnt-list))
+
+
+(provide 'bbdb-gnus)