]> git.notmuchmail.org Git - notmuch/blob - emacs/notmuch-address.el
emacs: functions to import sender or recipient into BBDB
[notmuch] / emacs / notmuch-address.el
1 ;; notmuch-address.el --- address completion with notmuch
2 ;;
3 ;; Copyright © David Edmondson
4 ;;
5 ;; This file is part of Notmuch.
6 ;;
7 ;; Notmuch is free software: you can redistribute it and/or modify it
8 ;; under the terms of the GNU General Public License as published by
9 ;; the Free Software Foundation, either version 3 of the License, or
10 ;; (at your option) any later version.
11 ;;
12 ;; Notmuch is distributed in the hope that it will be useful, but
13 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
14 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 ;; General Public License for more details.
16 ;;
17 ;; You should have received a copy of the GNU General Public License
18 ;; along with Notmuch.  If not, see <http://www.gnu.org/licenses/>.
19 ;;
20 ;; Authors: David Edmondson <dme@dme.org>
21
22 (require 'message)
23
24 ;;
25
26 (defcustom notmuch-address-command "notmuch-addresses"
27   "The command which generates possible addresses. It must take a
28 single argument and output a list of possible matches, one per
29 line."
30   :type 'string
31   :group 'notmuch-send
32   :group 'notmuch-external)
33
34 (defvar notmuch-address-message-alist-member
35   '("^\\(Resent-\\)?\\(To\\|B?Cc\\|Reply-To\\|From\\|Mail-Followup-To\\|Mail-Copies-To\\):"
36               . notmuch-address-expand-name))
37
38 (defvar notmuch-address-history nil)
39
40 (defun notmuch-address-message-insinuate ()
41   (unless (memq notmuch-address-message-alist-member message-completion-alist)
42     (setq message-completion-alist
43           (push notmuch-address-message-alist-member message-completion-alist))))
44
45 (defun notmuch-address-options (original)
46   (process-lines notmuch-address-command original))
47
48 (defun notmuch-address-expand-name ()
49   (let* ((end (point))
50          (beg (save-excursion
51                 (re-search-backward "\\(\\`\\|[\n:,]\\)[ \t]*")
52                 (goto-char (match-end 0))
53                 (point)))
54          (orig (buffer-substring-no-properties beg end))
55          (completion-ignore-case t)
56          (options (notmuch-address-options orig))
57          (num-options (length options))
58          (chosen (cond
59                   ((eq num-options 0)
60                    nil)
61                   ((eq num-options 1)
62                    (car options))
63                   (t
64                    (completing-read (format "Address (%s matches): " num-options)
65                                     (cdr options) nil nil (car options)
66                                     'notmuch-address-history)))))
67     (if chosen
68         (progn
69           (push chosen notmuch-address-history)
70           (delete-region beg end)
71           (insert chosen))
72       (message "No matches.")
73       (ding))))
74
75 ;; Copied from `w3m-which-command'.
76 (defun notmuch-address-locate-command (command)
77   "Return non-nil if `command' is an executable either on
78 `exec-path' or an absolute pathname."
79   (when (stringp command)
80     (if (and (file-name-absolute-p command)
81              (file-executable-p command))
82         command
83       (setq command (file-name-nondirectory command))
84       (catch 'found-command
85         (let (bin)
86           (dolist (dir exec-path)
87             (setq bin (expand-file-name command dir))
88             (when (or (and (file-executable-p bin)
89                            (not (file-directory-p bin)))
90                       (and (file-executable-p (setq bin (concat bin ".exe")))
91                            (not (file-directory-p bin))))
92               (throw 'found-command bin))))))))
93
94 ;; If we can find the program specified by `notmuch-address-command',
95 ;; insinuate ourselves into `message-mode'.
96 (when (notmuch-address-locate-command notmuch-address-command)
97   (notmuch-address-message-insinuate))
98
99 ;; functions to add sender / recipients to BBDB
100
101 (defun notmuch-bbdb/snarf-headers (headers)
102   ;; Helper function to avoid code duplication in the two below
103   ;; headers should have the same format as bbdb-get-addresses-headers
104
105   ;; bbdb-get-addresses reads these
106   ;; Ugh, pass-by-global
107   (let ((addrs (bbdb-get-addresses nil nil 'notmuch-bbdb/get-header-content))
108         (bbdb-get-addresses-headers headers) ; headers to read
109         (bbdb-gag-messages t)) ; suppress m/n processed message)
110     (bbdb-update-records addrs t t))
111
112   (defun notmuch-bbdb/snarf-from ()
113     "Import the sender of the current message into BBDB"
114     (interactive)
115     (notmuch-bbdb/snarf-headers
116      (list (assoc 'authors bbdb-get-addresses-headers))))
117
118 (defun notmuch-bbdb/snarf-to ()
119   "Import all recipients of the current message into BBDB"
120   (interactive)
121   (notmuch-bbdb/snarf-headers
122    (list (assoc 'recipients bbdb-get-addresses-headers))))
123
124 (defvar notmuch-bbdb/header-by-name
125   ;; both are case sensitive
126   '( ("From" . :From)
127      ("To" . :To)
128      ("CC" . :Cc)
129      ("BCC" . :Bcc)
130      ("Resent-From" . nil)
131      ("Reply-To" . nil)
132      ("Resent-To" . nil)
133      ("Resent-CC" . nil))
134   "Alist for dispatching header symbols as used by notmuch-show-get-header
135 from strings as used by bbdb-get-addresses")
136
137 (defun notmuch-bbdb/get-header-content (name)
138   (notmuch-show-get-header (cdr (assoc name notmuch-bbdb/header-by-name))))
139
140 ;;
141
142 (provide 'notmuch-address)