summaryrefslogtreecommitdiffstats
path: root/lisp/bbdb/bbdb-mua.el
diff options
context:
space:
mode:
authorAmin Bandali <bandali@gnu.org>2018-12-23 00:20:54 -0500
committerAmin Bandali <bandali@gnu.org>2018-12-23 00:20:54 -0500
commit59937519c5cf62fa7a6b83ff4a5f8f236449ae68 (patch)
tree0f2292d5d1d849d6e199f258383e6e35eb67593c /lisp/bbdb/bbdb-mua.el
parent66075b3deb7af9ce375f4655571eaea5d7bdae35 (diff)
downloadconfigs-59937519c5cf62fa7a6b83ff4a5f8f236449ae68.tar.gz
configs-59937519c5cf62fa7a6b83ff4a5f8f236449ae68.tar.xz
configs-59937519c5cf62fa7a6b83ff4a5f8f236449ae68.zip
[emacs] remove bbdb — using ebdb now
Diffstat (limited to 'lisp/bbdb/bbdb-mua.el')
-rw-r--r--lisp/bbdb/bbdb-mua.el1063
1 files changed, 0 insertions, 1063 deletions
diff --git a/lisp/bbdb/bbdb-mua.el b/lisp/bbdb/bbdb-mua.el
deleted file mode 100644
index db31b06..0000000
--- a/lisp/bbdb/bbdb-mua.el
+++ /dev/null
@@ -1,1063 +0,0 @@
-;;; bbdb-mua.el --- various MUA functionality for BBDB -*- lexical-binding: t -*-
-
-;; Copyright (C) 2010-2017 Free Software Foundation, Inc.
-
-;; This file is part of the Insidious Big Brother Database (aka BBDB),
-
-;; BBDB 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 3 of the License, 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 BBDB. If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-;; This file provides various additional functionality for BBDB
-;; See the BBDB info manual for documentation.
-
-;; This file lets you do stuff like
-;;
-;; o automatically add some string to some field(s) based on the
-;; contents of header fields of the current message
-;; o only automatically create records when certain header fields
-;; are matched
-;; o do not automatically create records when certain header fields
-;; are matched
-;;
-;; Read the docstrings; read the texinfo file.
-
-;;; Code:
-
-(require 'bbdb)
-(require 'bbdb-com)
-
-(eval-and-compile
- (autoload 'gnus-fetch-original-field "gnus-utils")
- (autoload 'gnus-summary-select-article "gnus-sum")
- (defvar gnus-article-buffer)
-
- (autoload 'bbdb/vm-header "bbdb-vm")
- (autoload 'vm-follow-summary-cursor "vm-motion")
- (autoload 'vm-select-folder-buffer "vm-macro")
- (autoload 'vm-check-for-killed-summary "vm-misc")
- (autoload 'vm-error-if-folder-empty "vm-misc")
-
- (autoload 'bbdb/rmail-header "bbdb-rmail")
- (defvar rmail-buffer)
-
- (autoload 'bbdb/mh-header "bbdb-mhe")
- (autoload 'mh-show "mh-show")
- (defvar mh-show-buffer)
-
- (defvar mu4e~view-buffer-name)
-
- (autoload 'bbdb/wl-header "bbdb-wl")
-
- (autoload 'message-field-value "message")
- (autoload 'mail-decode-encoded-word-string "mail-parse"))
-
-(defconst bbdb-mua-mode-alist
- '((vm vm-mode vm-virtual-mode vm-summary-mode vm-presentation-mode)
- (gnus gnus-summary-mode gnus-article-mode gnus-tree-mode)
- (rmail rmail-mode rmail-summary-mode)
- (mh mhe-mode mhe-summary-mode mh-folder-mode)
- (mu4e mu4e-view-mode) ; Tackle `mu4e-headers-mode' later
- (wl wl-summary-mode wl-draft-mode)
- (message message-mode mu4e-compose-mode notmuch-message-mode)
- (mail mail-mode))
- "Alist of MUA modes supported by BBDB.
-Each element is of the form (MUA MODE MODE ...), where MODEs are used by MUA.")
-
-(defun bbdb-mua ()
- "For the current message return the MUA.
-Return values include
- gnus Newsreader Gnus
- rmail Reading Mail in Emacs
- vm Viewmail
- mh Emacs interface to the MH mail system (aka MH-E)
- mu4e Mu4e
- wl Wanderlust
- message Mail and News composition mode that goes with Gnus
- mail Emacs Mail Mode."
- (let ((mm-alist bbdb-mua-mode-alist)
- elt mua)
- (while (setq elt (pop mm-alist))
- (if (memq major-mode (cdr elt))
- (setq mua (car elt)
- mm-alist nil)))
- (or mua (error "BBDB: MUA `%s' not supported" major-mode))))
-
-;;;###autoload
-(defun bbdb-message-header (header)
- "For the current message return the value of HEADER.
-MIME encoded headers are decoded. Return nil if HEADER does not exist."
- ;; RW: If HEADER was allowed to be a regexp and the content of multiple
- ;; matching headers was concatenated as in `message-field-value',
- ;; this would simplify the usage of `bbdb-accept-message-alist' and
- ;; `bbdb-ignore-message-alist'.
- ;; RW: If this function had a remember table, it could look up the value
- ;; of a header if we request the value of the same header multiple times.
- ;; (We would reset the remember table each time we move on to a new message.)
- (let* ((mua (bbdb-mua))
- (val (cond (;; It seems that `gnus-fetch-field' fetches decoded content of
- ;; `gnus-visible-headers', ignoring `gnus-ignored-headers'.
- ;; Here we use instead `gnus-fetch-original-field' that fetches
- ;; the encoded content of `gnus-original-article-buffer'.
- ;; Decoding makes this possibly a bit slower, but something like
- ;; `bbdb-select-message' does not get fooled by an apparent
- ;; absence of some headers.
- ;; See http://permalink.gmane.org/gmane.emacs.gnus.general/78741
- (eq mua 'gnus) (gnus-fetch-original-field header))
- ((eq mua 'vm) (bbdb/vm-header header))
- ((eq mua 'rmail) (bbdb/rmail-header header))
- ((eq mua 'mh) (bbdb/mh-header header))
- ((eq mua 'mu4e) (message-field-value header))
- ((eq mua 'wl) (bbdb/wl-header header))
- ((memq mua '(message mail)) (message-field-value header))
- (t (error "BBDB/%s: header function undefined" mua)))))
- (if val (mail-decode-encoded-word-string val))))
-
-(defsubst bbdb-message-header-re (header regexp)
- "Return non-nil if REGEXP matches value of HEADER."
- (let ((val (bbdb-message-header header))
- (case-fold-search t)) ; RW: Is this what we want?
- (and val (string-match regexp val))))
-
-;;; Update database
-
-;;;###autoload
-(defun bbdb-accept-message (&optional invert)
- "For use with variable `bbdb-mua-update-interactive-p' and friends.
-Return the value of variable `bbdb-update-records-p' for messages matching
-`bbdb-accept-message-alist'. If INVERT is non-nil, accept messages
-not matching `bbdb-ignore-message-alist'."
- (let ((rest (if invert bbdb-ignore-message-alist
- bbdb-accept-message-alist))
- done elt)
- (if (eq rest t)
- (setq done t)
- (while (and (setq elt (pop rest)) (not done))
- (dolist (header (if (stringp (car elt)) (list (car elt)) (car elt)))
- (if (bbdb-message-header-re header (cdr elt))
- (setq done t)))))
- (if invert (setq done (not done)))
- (if done bbdb-update-records-p)))
-
-;;;###autoload
-(defun bbdb-ignore-message (&optional invert)
- "For use with variable `bbdb-mua-update-interactive-p' and friends.
-Return the value of variable `bbdb-update-records-p' for messages not matching
-`bbdb-ignore-message-alist'. If INVERT is non-nil, accept messages
-matching `bbdb-accept-message-alist'."
- (bbdb-accept-message (not invert)))
-
-;;;###autoload
-(defun bbdb-select-message ()
- "For use with variable `bbdb-mua-update-interactive-p' and friends.
-Return the value of variable `bbdb-update-records-p' for messages both matching
-`bbdb-accept-message-alist' and not matching `bbdb-ignore-message-alist'."
- (and (bbdb-accept-message)
- (bbdb-ignore-message)))
-
-(defun bbdb-get-address-components (&optional header-class ignore-address)
- "Extract mail addresses from a message.
-Return list with elements (NAME EMAIL HEADER HEADER-CLASS MUA).
-HEADER-CLASS is defined in `bbdb-message-headers'. If HEADER-CLASS is nil,
-use all classes in `bbdb-message-headers'.
-If regexp IGNORE-ADDRESS matches NAME or EMAIL of an address, this address
-is ignored. If IGNORE-ADDRESS is nil, use value of `bbdb-user-mail-address-re'."
- ;; We do not use `bbdb-message-all-addresses' here because only when we
- ;; have compared the addresses with the records in BBDB do we know which
- ;; address(es) are relevant for us.
- (let ((message-headers (if header-class
- (list (assoc header-class bbdb-message-headers))
- bbdb-message-headers))
- (mua (bbdb-mua))
- (ignore-address (or ignore-address bbdb-user-mail-address-re))
- address-list name mail mail-list content)
- (dolist (headers message-headers)
- (dolist (header (cdr headers))
- (when (setq content (bbdb-message-header header))
- ;; Always extract all addresses because we do not know yet which
- ;; address might match IGNORE-ADDRESS.
- (dolist (address (bbdb-extract-address-components content t))
- ;; We canonicalize name and mail as early as possible.
- (setq name (car address)
- mail (cadr address))
- ;; ignore uninteresting addresses
- (unless (or (and (stringp ignore-address)
- (or (and name (string-match ignore-address name))
- (and mail (string-match ignore-address mail))))
- (and mail (member-ignore-case mail mail-list)))
- ;; Add each address only once. (Use MAIL-LIST for book keeping.)
- ;; Thus if we care about whether an address gets associated with
- ;; one or another header, the order of elements in
- ;; `bbdb-message-headers' is relevant. The "most important"
- ;; headers should be first in `bbdb-message-headers'.
- (if mail (push mail mail-list))
- (push (list name mail header (car headers) mua) address-list))))))
- (or (nreverse address-list)
- (and header-class bbdb-message-try-all-headers
- ;; Try again the remaining header classes
- (let ((bbdb-message-headers
- (remove (assoc header-class bbdb-message-headers)
- bbdb-message-headers)))
- (bbdb-get-address-components nil ignore-address))))))
-
-;;;###autoload
-(defun bbdb-update-records (address-list &optional update-p sort)
- "Return the list of BBDB records matching ADDRESS-LIST.
-ADDRESS-LIST is a list of mail addresses. (It can be extracted from
-a mail message using `bbdb-get-address-components'.)
-UPDATE-P may take the following values:
- search Search for existing records matching ADDRESS.
- update Search for existing records matching ADDRESS;
- update name and mail field if necessary.
- query Search for existing records matching ADDRESS;
- query for creation of a new record if the record does not exist.
- create or t Search for existing records matching ADDRESS;
- create a new record if it does not yet exist.
- nil Do nothing.
- a function This functions will be called with no arguments.
- It should return one of the above values.
-
-If SORT is non-nil, sort records according to `bbdb-record-lessp'.
-Ottherwise, the records are ordered according to ADDRESS-LIST.
-
-Usually this function is called by the wrapper `bbdb-mua-update-records'."
- ;; UPDATE-P allows filtering of complete messages.
- ;; Filtering of individual addresses within an accepted message
- ;; is done by `bbdb-get-address-components' using `bbdb-user-mail-address-re'.
- ;; We resolve UPDATE-P repeatedly. This is needed, for example,
- ;; with the chain `bbdb-mua-auto-update-p' -> `bbdb-select-message'
- ;; -> `bbdb-update-records-p'.
- (while (and (functionp update-p)
- ;; Bad! `search' is a function in `cl-seq.el'.
- (not (eq update-p 'search)))
- (setq update-p (funcall update-p)))
- (cond ((eq t update-p)
- (setq update-p 'create))
- ((not (memq update-p '(search update query create nil)))
- (error "Illegal value of arg update-p: %s" update-p)))
-
- (let (;; `bbdb-update-records-p' and `bbdb-offer-to-create' are used here
- ;; as internal variables for communication with `bbdb-query-create'.
- ;; This does not affect the value of the global user variable
- ;; `bbdb-update-records-p'.
- (bbdb-offer-to-create 'start)
- (bbdb-update-records-p update-p)
- address records)
-
- (when update-p
- (while (setq address (pop address-list))
- (let* ((bbdb-update-records-address address)
- hits
- (task
- (catch 'done
- (setq hits
- ;; We put the call of `bbdb-notice-mail-hook'
- ;; into `bbdb-annotate-message' so that this hook
- ;; runs only if the user agreed to change a record.
- (cond ((or bbdb-read-only
- (eq bbdb-update-records-p 'search))
- ;; Search for records having this mail address
- ;; but do not modify an existing record.
- ;; This does not run `bbdb-notice-mail-hook'.
- (bbdb-message-search (car address)
- (cadr address)))
- ((eq bbdb-update-records-p 'update)
- (bbdb-annotate-message address 'update))
- ((eq bbdb-update-records-p 'query)
- (bbdb-annotate-message
- address 'bbdb-query-create))
- ((eq bbdb-update-records-p 'create)
- (bbdb-annotate-message address 'create))))
- nil)))
- (cond ((eq task 'quit)
- (setq address-list nil))
- ((not (eq task 'next))
- (dolist (hit (delq nil (nreverse hits)))
- (bbdb-pushnew hit records))))
- (if (and records (not bbdb-message-all-addresses))
- (setq address-list nil))))
- (setq records
- (if sort (sort records 'bbdb-record-lessp)
- ;; Make RECORDS a list ordered like ADDRESS-LIST.
- (nreverse records))))
-
- ;; `bbdb-message-search' might yield multiple records
- (if (and records (not bbdb-message-all-addresses))
- (setq records (list (car records))))
-
- (unless bbdb-read-only
- (bbdb-editable)
- (dolist (record records)
- (run-hook-with-args 'bbdb-notice-record-hook record)))
-
- records))
-
-(defun bbdb-query-create ()
- "Interactive query used by `bbdb-update-records'.
-Return t if the record should be created or `nil' otherwise.
-Honor previous answers such as `!'."
- (let ((task bbdb-offer-to-create))
- ;; If we have remembered what the user typed previously,
- ;; `bbdb-offer-to-create' holds a character, i.e., a number.
- ;; -- Right now, we only remember "!".
- (when (not (integerp task))
- (let ((prompt (format "%s is not in BBDB; add? (y,!,n,s,q,?) "
- (or (nth 0 bbdb-update-records-address)
- (nth 1 bbdb-update-records-address))))
- event)
- (while (not event)
- (setq event (read-key-sequence prompt))
- (setq event (if (stringp event) (aref event 0))))
- (setq task event)
- (message ""))) ; clear the message buffer
-
- (cond ((eq task ?y)
- t)
- ((eq task ?!)
- (setq bbdb-offer-to-create task)
- t)
- ((or (eq task ?n)
- (eq task ?\s))
- (throw 'done 'next))
- ((or (eq task ?q)
- (eq task ?\a)) ; ?\a = C-g
- (throw 'done 'quit))
- ((eq task ?s)
- (setq bbdb-update-records-p 'search)
- (throw 'done 'next))
- (t ; any other key sequence
- (save-window-excursion
- (let* ((buffer (get-buffer-create " *BBDB Help*"))
- (window (or (get-buffer-window buffer)
- (split-window (get-lru-window)))))
- (with-current-buffer buffer
- (special-mode)
- (let (buffer-read-only)
- (erase-buffer)
- (insert
- "Your answer controls how BBDB updates/searches for records.
-
-Type ? for this help.
-Type y to add the current record.
-Type ! to add all remaining records.
-Type n to skip the current record. (You might also type space)
-Type s to switch from annotate to search mode.
-Type q to quit updating records. No more search or annotation is done.")
- (set-buffer-modified-p nil)
- (goto-char (point-min)))
- (set-window-buffer window buffer)
- (fit-window-to-buffer window)))
- ;; Try again!
- (bbdb-query-create))))))
-
-
-
-(defun bbdb-annotate-message (address &optional update-p)
- "Fill the records for message ADDRESS with as much info as possible.
-If a record for ADDRESS does not yet exist, UPDATE-P controls whether
-a new record is created for ADDRESS. UPDATE-P may take the values:
- update or nil Update existing records, never create a new record.
- query Query interactively whether to create a new record.
- create or t Create a new record.
- a function This functions will be called with no arguments.
- It should return one of the above values.
-Return the records matching ADDRESS or nil."
- (let* ((mail (nth 1 address)) ; possibly nil
- (name (unless (equal mail (car address))
- (car address)))
- (records (bbdb-message-search name mail))
- created-p new-records)
- (if (and (not records) (functionp update-p))
- (setq update-p (funcall update-p)))
- (cond ((eq t update-p) (setq update-p 'create))
- ((not update-p) (setq update-p 'update)))
-
- ;; Create a new record if nothing else fits.
- ;; In this way, we can fill the slots of the new record with
- ;; the same code that updates the slots of existing records.
- (unless (or records
- (eq update-p 'update)
- (not (or name mail)))
- ;; If there is no name, try to use the mail address as name
- (if (and bbdb-message-mail-as-name mail
- (or (null name)
- (string= "" name)))
- (setq name (funcall bbdb-message-clean-name-function mail)))
- (if (or (eq update-p 'create)
- (and (eq update-p 'query)
- (y-or-n-p (format "%s is not in the BBDB. Add? "
- (or name mail)))))
- (setq records (list (bbdb-empty-record))
- created-p t)))
-
- (dolist (record records)
- (let* ((old-name (bbdb-record-name record))
- (fullname (bbdb-divide-name (or name "")))
- (fname (car fullname))
- (lname (cdr fullname))
- (mail mail) ;; possibly changed below
- (created-p created-p)
- (update-p update-p)
- change-p add-mails add-name ignore-redundant)
-
- ;; Analyze the name part of the record.
- (cond ((or (not name)
- ;; The following tests can differ for more complicated names
- (bbdb-string= name old-name)
- (and (equal fname (bbdb-record-firstname record)) ; possibly
- (equal lname (bbdb-record-lastname record))) ; nil
- (member-ignore-case name (bbdb-record-aka record)))) ; do nothing
-
- (created-p ; new record
- (bbdb-record-set-field record 'name (cons fname lname)))
-
- ((not (setq add-name (bbdb-add-job bbdb-add-name record name)))) ; do nothing
-
- ((numberp add-name)
- (unless bbdb-silent
- (message "name mismatch: \"%s\" changed to \"%s\""
- old-name name)
- (sit-for add-name)))
-
- ((bbdb-eval-spec add-name
- (if old-name
- (format "Change name \"%s\" to \"%s\"? "
- old-name name)
- (format "Assign name \"%s\" to address \"%s\"? "
- name (car (bbdb-record-mail record)))))
- ;; Keep old-name as AKA?
- (when (and old-name
- (not (member-ignore-case old-name (bbdb-record-aka record))))
- (if (bbdb-eval-spec (bbdb-add-job bbdb-add-aka record old-name)
- (format "Keep name \"%s\" as an AKA? " old-name))
- (bbdb-record-set-field
- record 'aka (cons old-name (bbdb-record-aka record)))
- (bbdb-remhash old-name record)))
- (bbdb-record-set-field record 'name (cons fname lname))
- (setq change-p 'name))
-
- ;; make new name an AKA?
- ((and old-name
- (not (member-ignore-case name (bbdb-record-aka record)))
- (bbdb-eval-spec (bbdb-add-job bbdb-add-aka record name)
- (format "Make \"%s\" an alternate for \"%s\"? "
- name old-name)))
- (bbdb-record-set-field
- record 'aka (cons name (bbdb-record-aka record)))
- (setq change-p 'name)))
-
- ;; Is MAIL redundant compared with the mail addresses
- ;; that are already known for RECORD?
- (if (and mail
- (setq ignore-redundant
- (bbdb-add-job bbdb-ignore-redundant-mails record mail)))
- (let ((mails (bbdb-record-mail-canon record))
- (case-fold-search t) redundant ml re)
- (while (setq ml (pop mails))
- (if (and (setq re (bbdb-mail-redundant-re ml))
- (string-match re mail))
- (setq redundant ml mails nil)))
- (if redundant
- (cond ((numberp ignore-redundant)
- (unless bbdb-silent
- (message "%s: redundant mail `%s'"
- (bbdb-record-name record) mail)
- (sit-for ignore-redundant)))
- ((or (eq t ignore-redundant)
- bbdb-silent
- (y-or-n-p (format "Ignore redundant mail %s?" mail)))
- (setq mail redundant))))))
-
- ;; Analyze the mail part of the new records
- (cond ((or (not mail) (equal mail "???")
- (member-ignore-case mail (bbdb-record-mail-canon record)))) ; do nothing
-
- (created-p ; new record
- (bbdb-record-set-field record 'mail (list mail)))
-
- ((not (setq add-mails (bbdb-add-job bbdb-add-mails record mail)))) ; do nothing
-
- ((numberp add-mails)
- (unless bbdb-silent
- (message "%s: new address `%s'"
- (bbdb-record-name record) mail)
- (sit-for add-mails)))
-
- ((or (eq add-mails t) ; add it automatically
- bbdb-silent
- (y-or-n-p (format "Add address \"%s\" to %s? " mail
- (bbdb-record-name record)))
- (and (or (and (functionp update-p)
- (progn (setq update-p (funcall update-p)) nil))
- (memq update-p '(t create))
- (and (eq update-p 'query)
- (y-or-n-p
- (format "Create a new record for %s? "
- (bbdb-record-name record)))))
- (progn
- (setq record (bbdb-empty-record))
- (bbdb-record-set-name record fname lname)
- (setq created-p t))))
-
- (let ((mails (bbdb-record-mail record)))
- (if ignore-redundant
- ;; Does the new address MAIL make an old address redundant?
- (let ((mail-re (bbdb-mail-redundant-re mail))
- (case-fold-search t) okay redundant)
- (dolist (ml mails)
- (if (string-match mail-re ml) ; redundant mail address
- (push ml redundant)
- (push ml okay)))
- (let ((form (format "redundant mail%s %s"
- (if (< 1 (length redundant)) "s" "")
- (bbdb-concat 'mail (nreverse redundant))))
- (name (bbdb-record-name record)))
- (if redundant
- (cond ((numberp ignore-redundant)
- (unless bbdb-silent
- (message "%s: %s" name form)
- (sit-for ignore-redundant)))
- ((or (eq t ignore-redundant)
- bbdb-silent
- (y-or-n-p (format "Delete %s: " form)))
- (if (eq t ignore-redundant)
- (message "%s: deleting %s" name form))
- (setq mails okay)))))))
-
- ;; then modify RECORD
- (bbdb-record-set-field
- record 'mail
- (if (and mails
- (bbdb-eval-spec (bbdb-add-job bbdb-new-mails-primary
- record mail)
- (format "Make \"%s\" the primary address? " mail)))
- (cons mail mails)
- (nconc mails (list mail))))
- (unless change-p (setq change-p t)))))
-
- (cond (created-p
- (unless bbdb-silent
- (if (bbdb-record-name record)
- (message "created %s's record with address \"%s\""
- (bbdb-record-name record) mail)
- (message "created record with naked address \"%s\"" mail)))
- (bbdb-change-record record))
-
- (change-p
- (unless bbdb-silent
- (cond ((eq change-p 'name)
- (message "noticed \"%s\"" (bbdb-record-name record)))
- ((bbdb-record-name record)
- (message "noticed %s's address \"%s\""
- (bbdb-record-name record) mail))
- (t
- (message "noticed naked address \"%s\"" mail))))
- (bbdb-change-record record)))
-
- (run-hook-with-args 'bbdb-notice-mail-hook record)
- (push record new-records)))
-
- (nreverse new-records)))
-
-(defun bbdb-mua-update-records (&optional header-class update-p sort)
- "Wrapper for `bbdb-update-records'.
-HEADER-CLASS is defined in `bbdb-message-headers'. If it is nil,
-use all classes in `bbdb-message-headers'.
-UPDATE-P is defined in `bbdb-update-records'.
-If SORT is non-nil, sort records according to `bbdb-record-lessp'."
- (let ((mua (bbdb-mua)))
- (save-current-buffer
- (cond ;; VM
- ((eq mua 'vm)
- (vm-select-folder-buffer)
- (vm-check-for-killed-summary)
- (vm-error-if-folder-empty)
- (let ((enable-local-variables t)) ; ...or vm bind this to nil.
- (bbdb-update-records (bbdb-get-address-components header-class)
- update-p sort)))
- ;; Gnus
- ((eq mua 'gnus)
- (set-buffer gnus-article-buffer)
- (bbdb-update-records (bbdb-get-address-components header-class)
- update-p sort))
- ;; MH-E
- ((eq mua 'mh)
- (if mh-show-buffer (set-buffer mh-show-buffer))
- (bbdb-update-records (bbdb-get-address-components header-class)
- update-p sort))
- ;; Rmail
- ((eq mua 'rmail)
- (set-buffer rmail-buffer)
- (bbdb-update-records (bbdb-get-address-components header-class)
- update-p sort))
- ;; mu4e
- ((eq mua 'mu4e)
- (set-buffer mu4e~view-buffer-name)
- (bbdb-update-records (bbdb-get-address-components header-class)
- update-p sort))
- ;; Wanderlust
- ((eq mua 'wl)
- (bbdb-update-records (bbdb-get-address-components header-class)
- update-p sort))
- ;; Message and Mail
- ((memq mua '(message mail))
- (bbdb-update-records (bbdb-get-address-components header-class)
- update-p sort))))))
-
-(defmacro bbdb-mua-wrapper (&rest body)
- "Perform BODY in a MUA buffer."
- (declare (debug t))
- `(let ((mua (bbdb-mua)))
- ;; Here we replicate BODY multiple times which gets clumsy
- ;; for a larger BODY!
- (cond ((eq mua 'gnus)
- ;; This fails in *Article* buffers, where
- ;; `gnus-article-read-summary-keys' provides an additional wrapper
- (save-current-buffer
- (gnus-summary-select-article) ; sets buffer `gnus-summary-buffer'
- ,@body))
- ((memq mua '(mail message rmail mh vm mu4e wl))
- (cond ((eq mua 'vm) (vm-follow-summary-cursor))
- ((eq mua 'mh) (mh-show)))
- ;; rmail, mail, message, mu4e and wl do not require any wrapper
- ,@body))))
-
-(defun bbdb-mua-update-interactive-p ()
- "Interactive spec for arg UPDATE-P of `bbdb-mua-display-records' and friends.
-If these commands are called without a prefix, the value of their arg
-UPDATE-P is the car of the variable `bbdb-mua-update-interactive-p'.
-Called with a prefix, the value of UPDATE-P is the cdr of this variable."
- (let ((update-p (if current-prefix-arg
- (cdr bbdb-mua-update-interactive-p)
- (car bbdb-mua-update-interactive-p))))
- (if (eq update-p 'read)
- (let ((str (completing-read "Action: " '((query) (search) (create))
- nil t)))
- (unless (string= "" str) (intern str))) ; nil otherwise
- update-p)))
-
-(defun bbdb-mua-window-p ()
- "Return lambda function matching the MUA window.
-This return value can be used as arg HORIZ-P of `bbdb-display-records'."
- (let ((mm-alist bbdb-mua-mode-alist)
- elt fun)
- (while (setq elt (cdr (pop mm-alist)))
- (if (memq major-mode elt)
- (setq fun `(lambda (window)
- (with-current-buffer (window-buffer window)
- (memq major-mode ',elt)))
- mm-alist nil)))
- fun))
-
-;;;###autoload
-(defun bbdb-mua-display-records (&optional header-class update-p all)
- "Display the BBDB record(s) for the addresses in this message.
-This looks into the headers of a message according to HEADER-CLASS.
-Then for the mail addresses found the corresponding BBDB records are displayed.
-UPDATE-P determines whether only existing BBDB records are displayed
-or whether also new records are created for these mail addresses.
-
-HEADER-CLASS is defined in `bbdb-message-headers'. If it is nil,
-use all classes in `bbdb-message-headers'.
-UPDATE-P may take the same values as `bbdb-update-records-p'.
-For interactive calls, see function `bbdb-mua-update-interactive-p'.
-If ALL is non-nil, bind `bbdb-message-all-addresses' to ALL."
- (interactive (list nil (bbdb-mua-update-interactive-p)))
- (let ((bbdb-pop-up-window-size bbdb-mua-pop-up-window-size)
- (bbdb-message-all-addresses (or all bbdb-message-all-addresses))
- records)
- (bbdb-mua-wrapper
- (setq records (bbdb-mua-update-records header-class update-p t)))
- (if records (bbdb-display-records records nil nil nil (bbdb-mua-window-p)))
- records))
-
-;; The following commands are some frontends for `bbdb-mua-display-records',
-;; which is always doing the real work. In your init file, you can further
-;; modify or adapt these simple commands to your liking.
-
-;;;###autoload
-(defun bbdb-mua-display-sender (&optional update-p)
- "Display the BBDB record(s) for the sender of this message.
-UPDATE-P may take the same values as `bbdb-update-records-p'.
-For interactive calls, see function `bbdb-mua-update-interactive-p'."
- (interactive (list (bbdb-mua-update-interactive-p)))
- (bbdb-mua-display-records 'sender update-p))
-
-;;;###autoload
-(defun bbdb-mua-display-recipients (&optional update-p)
- "Display the BBDB record(s) for the recipients of this message.
-UPDATE-P may take the same values as `bbdb-update-records-p'.
-For interactive calls, see function `bbdb-mua-update-interactive-p'."
- (interactive (list (bbdb-mua-update-interactive-p)))
- (bbdb-mua-display-records 'recipients update-p))
-
-;;;###autoload
-(defun bbdb-mua-display-all-records (&optional update-p)
- "Display the BBDB record(s) for all addresses in this message.
-UPDATE-P may take the same values as `bbdb-update-records-p'.
-For interactive calls, see function `bbdb-mua-update-interactive-p'."
- (interactive (list (bbdb-mua-update-interactive-p)))
- (bbdb-mua-display-records nil update-p t))
-
-;;;###autoload
-(defun bbdb-mua-display-all-recipients (&optional update-p)
- "Display BBDB records for all recipients of this message.
-UPDATE-P may take the same values as `bbdb-update-records-p'.
-For interactive calls, see function `bbdb-mua-update-interactive-p'."
- (interactive (list (bbdb-mua-update-interactive-p)))
- (bbdb-mua-display-records 'recipients update-p t))
-
-;; The commands `bbdb-annotate-record' and `bbdb-mua-edit-field'
-;; have kind of similar goals, yet they use rather different strategies.
-;; `bbdb-annotate-record' is less obtrusive. It does not display
-;; the records it operates on, nor does it display the content
-;; of the field before or after adding or replacing the annotation.
-;; Hence the user needs to know what she is doing.
-;; `bbdb-mua-edit-field' is more explicit: It displays the records
-;; as well as the current content of the field that gets edited.
-
-;; In principle, this function can be used not only with MUAs.
-(defun bbdb-annotate-record (record annotation &optional field replace)
- "In RECORD add an ANNOTATION to field FIELD.
-FIELD defaults to `bbdb-annotate-field'.
-If REPLACE is non-nil, ANNOTATION replaces the content of FIELD.
-If ANNOTATION is an empty string and REPLACE is non-nil, delete FIELD."
- (if (memq field '(name firstname lastname phone address xfields))
- (error "Field `%s' illegal" field))
- (setq annotation (bbdb-string-trim annotation))
- (cond ((memq field '(affix organization mail aka))
- (setq annotation (list annotation)))
- ((not field) (setq field bbdb-annotate-field)))
- (bbdb-record-set-field record field annotation (not replace))
- (bbdb-change-record record))
-
-;; FIXME: For interactive calls of the following commands, the arg UPDATE-P
-;; should have the same meaning as for `bbdb-mua-display-records',
-;; that is, it should use `bbdb-mua-update-interactive-p'.
-;; But here the prefix arg is already used in a different way.
-;; We could possibly solve this problem if all `bbdb-mua-*' commands
-;; used another prefix arg that is consistently used only for
-;; `bbdb-mua-update-interactive-p'.
-;; Yet this prefix arg must be defined within the key space of the MUA(s).
-;; This results in lots of conflicts...
-;;
-;; Current workaround:
-;; These commands use merely the car of `bbdb-mua-update-interactive-p'.
-;; If one day someone proposes a smart solution to this problem (suggestions
-;; welcome!), this solution will hopefully include the current workaround
-;; as a subset of all its features.
-
-(defun bbdb-mua-annotate-field-interactive ()
- "Interactive specification for `bbdb-mua-annotate-sender' and friends."
- (bbdb-editable)
- (let ((field (if (eq 'all-fields bbdb-annotate-field)
- (intern (completing-read
- "Field: "
- (mapcar 'symbol-name
- (append '(affix organization mail aka)
- bbdb-xfield-label-list))))
- bbdb-annotate-field)))
- (list (read-string (format "Annotate `%s': " field))
- field current-prefix-arg
- (car bbdb-mua-update-interactive-p))))
-
-;;;###autoload
-(defun bbdb-mua-annotate-sender (annotation &optional field replace update-p)
- "Add ANNOTATION to field FIELD of the BBDB record(s) of message sender(s).
-FIELD defaults to `bbdb-annotate-field'.
-If REPLACE is non-nil, ANNOTATION replaces the content of FIELD.
-UPDATE-P may take the same values as `bbdb-update-records-p'.
-For interactive calls, use car of `bbdb-mua-update-interactive-p'."
- (interactive (bbdb-mua-annotate-field-interactive))
- (bbdb-mua-wrapper
- (dolist (record (bbdb-mua-update-records 'sender update-p))
- (bbdb-annotate-record record annotation field replace))))
-
-;;;###autoload
-(defun bbdb-mua-annotate-recipients (annotation &optional field replace
- update-p)
- "Add ANNOTATION to field FIELD of the BBDB records of message recipients.
-FIELD defaults to `bbdb-annotate-field'.
-If REPLACE is non-nil, ANNOTATION replaces the content of FIELD.
-UPDATE-P may take the same values as `bbdb-update-records-p'.
-For interactive calls, use car of `bbdb-mua-update-interactive-p'."
- (interactive (bbdb-mua-annotate-field-interactive))
- (bbdb-mua-wrapper
- (dolist (record (bbdb-mua-update-records 'recipients update-p))
- (bbdb-annotate-record record annotation field replace))))
-
-(defun bbdb-mua-edit-field-interactive ()
- "Interactive specification for command `bbdb-mua-edit-field' and friends."
- (bbdb-editable)
- (list (if (eq 'all-fields bbdb-mua-edit-field)
- (intern (completing-read
- "Field: "
- (mapcar 'symbol-name
- (append '(name affix organization aka mail)
- bbdb-xfield-label-list))))
- bbdb-mua-edit-field)
- (bbdb-mua-update-interactive-p)))
-
-;;;###autoload
-(defun bbdb-mua-edit-field (&optional field update-p header-class)
- "Edit FIELD of the BBDB record(s) of message sender(s) or recipients.
-FIELD defaults to value of variable `bbdb-mua-edit-field'.
-UPDATE-P may take the same values as `bbdb-update-records-p'.
-For interactive calls, see function `bbdb-mua-update-interactive-p'.
-HEADER-CLASS is defined in `bbdb-message-headers'. If it is nil,
-use all classes in `bbdb-message-headers'."
- (interactive (bbdb-mua-edit-field-interactive))
- (cond ((memq field '(firstname lastname address phone xfields))
- (error "Field `%s' not editable this way" field))
- ((not field)
- (setq field bbdb-mua-edit-field)))
- (bbdb-mua-wrapper
- (let ((records (bbdb-mua-update-records header-class update-p))
- (bbdb-pop-up-window-size bbdb-mua-pop-up-window-size))
- (when records
- (bbdb-display-records records nil nil nil (bbdb-mua-window-p))
- (dolist (record records)
- (bbdb-edit-field record field))))))
-
-;;;###autoload
-(defun bbdb-mua-edit-field-sender (&optional field update-p)
- "Edit FIELD of record corresponding to sender of this message.
-FIELD defaults to value of variable `bbdb-mua-edit-field'.
-UPDATE-P may take the same values as `bbdb-update-records-p'.
-For interactive calls, see function `bbdb-mua-update-interactive-p'."
- (interactive (bbdb-mua-edit-field-interactive))
- (bbdb-mua-edit-field field update-p 'sender))
-
-;;;###autoload
-(defun bbdb-mua-edit-field-recipients (&optional field update-p)
- "Edit FIELD of record corresponding to recipient of this message.
-FIELD defaults to value of variable `bbdb-mua-edit-field'.
-UPDATE-P may take the same values as `bbdb-update-records-p'.
-For interactive calls, see function `bbdb-mua-update-interactive-p'."
- (interactive (bbdb-mua-edit-field-interactive))
- (bbdb-mua-edit-field field update-p 'recipients))
-
-;; Functions for noninteractive use in MUA hooks
-
-;;;###autoload
-(defun bbdb-mua-auto-update (&optional header-class update-p)
- "Update BBDB automatically based on incoming and outgoing messages.
-This looks into the headers of a message according to HEADER-CLASS.
-Then for the mail addresses found the corresponding BBDB records are updated.
-UPDATE-P determines whether only existing BBDB records are taken
-or whether also new records are created for these mail addresses.
-Return matching records.
-
-HEADER-CLASS is defined in `bbdb-message-headers'. If it is nil,
-use all classes in `bbdb-message-headers'.
-UPDATE-P may take the same values as `bbdb-mua-auto-update-p'.
-If UPDATE-P is nil, use `bbdb-mua-auto-update-p' (which see).
-
-If `bbdb-mua-pop-up' is non-nil, BBDB pops up the *BBDB* buffer
-along with the MUA window(s), displaying the matching records
-using `bbdb-pop-up-layout'.
-If this is nil, BBDB is updated silently.
-
-This function is intended for noninteractive use via appropriate MUA hooks.
-Call `bbdb-mua-auto-update-init' in your init file to put this function
-into the respective MUA hooks.
-See `bbdb-mua-display-records' and friends for interactive commands."
- (let* ((bbdb-silent-internal t)
- (records (bbdb-mua-update-records header-class
- (or update-p
- bbdb-mua-auto-update-p)))
- (bbdb-pop-up-window-size bbdb-mua-pop-up-window-size))
- (if bbdb-mua-pop-up
- (if records
- (bbdb-display-records records bbdb-pop-up-layout
- nil nil (bbdb-mua-window-p))
- ;; If there are no records, empty the BBDB window.
- (bbdb-undisplay-records)))
- records))
-
-;; Should the following be replaced by a minor mode??
-;; Or should we make this function interactive in some other way?
-
-;;;###autoload
-(defun bbdb-mua-auto-update-init (&rest muas)
- "For MUAS add `bbdb-mua-auto-update' to their presentation hook.
-If a MUA is not an element of MUAS, `bbdb-mua-auto-update' is removed
-from the respective presentation hook.
-
-Call this function in your init file to use the auto update feature with MUAS.
-This function is separate from the general function `bbdb-initialize'
-as this allows one to initialize the auto update feature for some MUAs only,
-for example only for outgoing messages.
-
-See `bbdb-mua-auto-update' for details about the auto update feature."
- (dolist (mua '((message . message-send-hook)
- (mail . mail-send-hook)
- (rmail . rmail-show-message-hook)
- (gnus . gnus-article-prepare-hook)
- (mh . mh-show-hook)
- (vm . vm-select-message-hook)
- (wl . wl-message-redisplay-hook)))
- (if (memq (car mua) muas)
- (add-hook (cdr mua) 'bbdb-mua-auto-update)
- (remove-hook (cdr mua) 'bbdb-mua-auto-update))))
-
-;;;###autoload
-(defun bbdb-auto-notes (record)
- "Automatically annotate RECORD based on the headers of the current message.
-See the variables `bbdb-auto-notes-rules', `bbdb-auto-notes-ignore-messages'
-and `bbdb-auto-notes-ignore-headers'.
-For use as an element of `bbdb-notice-record-hook'."
- ;; This code re-evaluates the annotations each time a message is viewed.
- ;; It would be faster if we could somehow store (permanently?) that we
- ;; have already annotated a message.
- (let ((case-fold-search t))
- (unless (or bbdb-read-only
- ;; check the ignore-messages pattern
- (let ((ignore-messages bbdb-auto-notes-ignore-messages)
- ignore rule)
- (while (and (not ignore) (setq rule (pop ignore-messages)))
- (if (cond ((functionp rule)
- ;; RULE may use `bbdb-update-records-address'
- (funcall rule record))
- ((symbolp rule)
- (eq rule (nth 4 bbdb-update-records-address)))
- ((eq 1 (safe-length rule))
- (bbdb-message-header-re (car rule) (cdr rule)))
- ((eq 2 (safe-length rule))
- (and (eq (car rule) (nth 4 bbdb-update-records-address))
- (bbdb-message-header-re (nth 1 rule) (nth 2 rule)))))
- (setq ignore t)))
- ignore))
- (bbdb-editable)
-
- ;; For speed-up expanded rules are stored in `bbdb-auto-notes-rules-expanded'.
- (when (and bbdb-auto-notes-rules
- (not bbdb-auto-notes-rules-expanded))
- (let (expanded mua from-to header)
- (dolist (rule bbdb-auto-notes-rules)
- ;; Which MUA do we want?
- (if (or (stringp (car rule))
- (stringp (nth 1 rule)))
- (setq mua t)
- (setq mua (if (symbolp (car rule)) (listp (car rule)) (car rule))
- rule (cdr rule)))
- ;; Which FROM-TO headers do we want?
- (if (stringp (car rule))
- (setq from-to t)
- (setq from-to (car rule)
- rule (cdr rule)))
- (setq header (car rule))
- (let (string field replace elt-e)
- (dolist (elt (cdr rule))
- (if (consp (setq string (cdr elt)))
- (setq field (car string) ; (REGEXP FIELD-NAME STRING REPLACE)
- replace (nth 2 string) ; perhaps nil
- string (nth 1 string))
- ;; else it's simple (REGEXP . STRING)
- (setq field bbdb-default-xfield
- replace nil))
- (push (list (car elt) field string replace) elt-e))
- (push (append (list mua from-to header) (nreverse elt-e)) expanded)))
- (setq bbdb-auto-notes-rules-expanded (nreverse expanded))))
-
- (dolist (rule bbdb-auto-notes-rules-expanded)
- (let ((mua (car rule)) (from-to (nth 1 rule)) (header (nth 2 rule))
- hd-val string annotation)
- (when (and (or (eq mua t)
- (memq (nth 4 bbdb-update-records-address) mua))
- (or (eq from-to t)
- (member-ignore-case
- (nth 2 bbdb-update-records-address) from-to)
- (memq (nth 3 bbdb-update-records-address) from-to))
- (setq hd-val (bbdb-message-header header)))
- (dolist (elt (nthcdr 3 rule))
- (when (and (string-match (car elt) hd-val)
- (let ((ignore (cdr (assoc-string
- header
- bbdb-auto-notes-ignore-headers t))))
- (not (and ignore (string-match ignore hd-val)))))
- (setq string (nth 2 elt)
- annotation
- (cond ((integerp string)
- (match-string string hd-val))
- ((stringp string)
- (replace-match string nil nil hd-val))
- ((functionp string)
- (funcall string hd-val))
- (t (error "Illegal value: %s" string))))
- (bbdb-annotate-record record annotation
- (nth 1 elt) (nth 3 elt))))))))))
-
-;;; Mark BBDB records in the MUA summary buffer
-
-(defun bbdb-mua-summary-unify (address)
- "Unify mail ADDRESS displayed for a message in the MUA Summary buffer.
-Typically ADDRESS refers to the value of the From header of a message.
-If ADDRESS matches a record in BBDB display a unified name instead of ADDRESS
-in the MUA Summary buffer.
-
-Unification uses `bbdb-mua-summary-unification-list' (see there).
-The first match in this list becomes the text string displayed
-for a message in the MUA Summary buffer instead of ADDRESS.
-If variable `bbdb-mua-summary-mark' is non-nil use it to precede known addresses.
-Return the unified mail address.
-
-Currently this works with Gnus and VM. It requires the BBDB insinuation
-of these MUAs. Also, the MUA Summary format string must use
-`bbdb-mua-summary-unify-format-letter' (see there)."
- ;; ADDRESS is analyzed as in `bbdb-get-address-components'.
- (let* ((data (bbdb-extract-address-components address))
- (name (car data))
- (mail (cadr data))
- (record (car (bbdb-message-search name mail)))
- (u-list bbdb-mua-summary-unification-list)
- elt val)
- (while (setq elt (pop u-list))
- (setq val (cond ((eq elt 'message-name) name)
- ((eq elt 'message-mail) mail)
- ((eq elt 'message-address) address)
- (record (let ((result (bbdb-record-field record elt)))
- (if (stringp result) result
- (car result)))))) ; RESULT is list.
- (if val (setq u-list nil)))
- (format "%s%s"
- (cond ((not bbdb-mua-summary-mark) "")
- ((not record) " ")
- ((functionp bbdb-mua-summary-mark-field)
- (funcall bbdb-mua-summary-mark-field record))
- ((bbdb-record-xfield record bbdb-mua-summary-mark-field))
- (t bbdb-mua-summary-mark))
- (or val name mail address "**UNKNOWN**"))))
-
-(defun bbdb-mua-summary-mark (address)
- "In the MUA Summary buffer mark messages matching a BBDB record.
-ADDRESS typically refers to the value of the From header of a message.
-If ADDRESS matches a record in BBDB return a mark, \" \" otherwise.
-The mark itself is the value of the xfield `bbdb-mua-summary-mark-field'
-if this xfield is in the poster's record, and `bbdb-mua-summary-mark' otherwise."
- (if (not bbdb-mua-summary-mark)
- "" ; for consistency
- ;; ADDRESS is analyzed as in `bbdb-get-address-components'.
- (let* ((data (bbdb-extract-address-components address))
- (record (car (bbdb-message-search (car data) (cadr data)))))
- (if record
- (or (when (functionp bbdb-mua-summary-mark-field)
- (funcall bbdb-mua-summary-mark-field record)
- t)
- (bbdb-record-xfield record bbdb-mua-summary-mark-field)
- bbdb-mua-summary-mark)
- " "))))
-
-(provide 'bbdb-mua)
-
-;;; bbdb-mua.el ends here