* elmo-util.el (elmo-file-field-primitive-condition-match): Fixed
[elisp/wanderlust.git] / utils / bbdb-wl.el
1 ;;; bbdb-wl.el -- BBDB interface to Wanderlust
2
3 ;; Copyright (C) 1998,1999,2000 Yuuichi Teranishi <teranisi@gohome.org>
4
5 ;; Author: Yuuichi Teranishi <teranisi@gohome.org>
6 ;; Keywords: mail, news, database
7
8 ;;; Commentary:
9 ;;
10 ;;  Insert the following lines in your ~/.wl
11 ;; 
12 ;;  (require 'bbdb-wl)
13 ;;  (bbdb-wl-setup)
14
15 ;;; Code:
16 ;;
17
18 ;; bbdb setup.
19 (eval-when-compile
20   (require 'mime-setup)
21   (require 'elmo-vars)
22   (require 'elmo-util)
23   (require 'wl-summary)
24   (require 'wl-message)
25   (require 'wl-draft)
26   (require 'wl-address)
27   (require 'bbdb-com)
28   (defvar bbdb-pop-up-elided-display nil))
29 ;;  (or (fboundp 'bbdb-wl-extract-field-value-internal)
30 ;;      (defun bbdb-wl-extract-field-value-internal (field))))
31 (require 'bbdb)
32
33 (defvar bbdb-wl-get-update-record-hook nil)
34
35 (defun bbdb-wl-setup ()
36   (add-hook 'wl-message-redisplay-hook 'bbdb-wl-get-update-record)
37   (add-hook 'wl-summary-exit-hook 'bbdb-wl-hide-bbdb-buffer)
38   (add-hook 'wl-message-window-deleted-hook 'bbdb-wl-hide-bbdb-buffer)
39   (add-hook 'wl-exit-hook 'bbdb-wl-exit)
40   (add-hook 'wl-summary-toggle-disp-off-hook 'bbdb-wl-hide-bbdb-buffer)
41   (add-hook 'wl-summary-toggle-disp-folder-on-hook 'bbdb-wl-hide-bbdb-buffer)
42   (add-hook 'wl-summary-toggle-disp-folder-off-hook 'bbdb-wl-hide-bbdb-buffer)
43   (add-hook 'wl-summary-toggle-disp-folder-message-resumed-hook
44             'bbdb-wl-show-bbdb-buffer)
45   (add-hook 'wl-summary-mode-hook
46             (function
47              (lambda ()
48                (define-key (current-local-map) ":" 'bbdb-wl-show-sender)
49                (define-key (current-local-map) ";" 'bbdb-wl-edit-notes))))
50   (add-hook 'wl-summary-exit-hook 'bbdb-flush-all-caches)
51   (add-hook 'wl-summary-exec-hook 'bbdb-flush-all-caches)
52   (add-hook 'wl-mail-setup-hook
53             (function
54              (lambda ()
55 ;;;            (local-set-key "\M-\t" 'bbdb-complete-name)
56                (define-key (current-local-map) "\M-\t" 'bbdb-complete-name)
57                ))))
58
59 (defun bbdb-wl-exit ()
60   (let (bbdb-buf)
61     (if (setq bbdb-buf (get-buffer bbdb-buffer-name))
62         (kill-buffer bbdb-buf)))
63   (bbdb-save-db t))
64
65 (defun bbdb-wl-get-update-record ()
66   (with-current-buffer (wl-message-get-original-buffer)
67     (bbdb-wl-update-record)
68     (run-hooks 'bbdb-wl-get-update-record-hook)))
69
70 (defun bbdb-wl-hide-bbdb-buffer ()
71   (let (bbdb-buf bbdb-win)
72     (if (setq bbdb-buf (get-buffer bbdb-buffer-name))
73         (if (setq bbdb-win (get-buffer-window bbdb-buf))
74             (delete-window bbdb-win)))))
75
76 (defun bbdb-wl-show-bbdb-buffer ()
77   (save-selected-window
78     (if (get-buffer-window bbdb-buffer-name)
79         nil
80       (let ((mes-win (get-buffer-window
81                       (save-excursion
82                         (if (buffer-live-p  wl-current-summary-buffer)
83                             (set-buffer wl-current-summary-buffer))
84                         wl-message-buf-name)))
85             (cur-win (selected-window))
86             (b (current-buffer)))
87         (and mes-win (select-window mes-win))
88         (let ((size (min
89                      (- (window-height mes-win)
90                         window-min-height 1)
91                      (- (window-height mes-win)
92                         (max window-min-height
93                              (1+ bbdb-pop-up-target-lines))))))
94           (split-window mes-win (if (> size 0) size window-min-height)))
95         ;; goto the bottom of the two...
96         (select-window (next-window))
97         ;; make it display *BBDB*...
98         (let ((pop-up-windows nil))
99           (switch-to-buffer (get-buffer-create bbdb-buffer-name)))))))
100
101 (defun bbdb-wl-get-petname (from)
102   "For `wl-summary-get-petname-func'."
103   (let* ((address (wl-address-header-extract-address from))
104          (record (bbdb-search-simple nil address)))
105     (and record
106          (or (bbdb-record-name record)
107              (car (bbdb-record-name record))))))
108
109 (defun bbdb-wl-from-func (string)
110   "A candidate From field STRING.  For `wl-summary-from-func'."
111   (let ((hit (bbdb-search-simple nil (wl-address-header-extract-address
112                                       string)))
113         first-name last-name from-str)
114     (if hit
115         (progn
116           (setq first-name (aref hit 0))
117           (setq last-name (aref hit 1))
118           (cond ((and (null first-name)
119                       (null last-name))
120                  (setq from-str string))
121                 ((and first-name last-name)
122                  (setq from-str (concat first-name " " last-name)))
123                 ((or first-name last-name)
124                  (setq from-str (or first-name last-name))))
125           from-str)
126       string)))
127
128 (defun bbdb-wl-get-addresses (&optional only-first-address)
129   "Return real name and email address of sender respectively recipients.
130 If an address matches `bbdb-user-mail-names' it will be ignored.
131 The headers to search can be configured by `bbdb-get-addresses-headers'."
132   (save-excursion
133     (save-restriction
134       (std11-narrow-to-header)
135       (let ((headers bbdb-get-addresses-headers)
136             (uninteresting-senders bbdb-user-mail-names)
137             addrlist header structures structure fn ad)
138         (while headers
139           (setq header (std11-fetch-field (car headers)))
140           (when header
141             (setq structures (std11-parse-addresses-string
142                               (std11-unfold-string header)))
143             (while (and (setq structure (car structures))
144                         (eq (car structure) 'mailbox))
145               (setq fn (std11-full-name-string structure)
146                     fn (and fn
147                             (with-temp-buffer ; to keep raw buffer unibyte.
148                               (elmo-set-buffer-multibyte
149                                default-enable-multibyte-characters)
150                               (eword-decode-string
151                                (decode-mime-charset-string
152                                 fn wl-mime-charset))))
153                     ad (std11-address-string structure))
154
155               ;; ignore uninteresting addresses, this is kinda gross!
156               (when (or (not (stringp uninteresting-senders))
157                         (not (or
158                               (and fn (string-match uninteresting-senders fn))
159                               (and ad (string-match uninteresting-senders ad)))))
160                 (add-to-list 'addrlist (list fn ad)))
161
162               (if (and only-first-address addrlist)
163                   (setq structures nil headers nil)
164                 (setq structures (cdr structures)))))
165           (setq headers (cdr headers)))
166         (nreverse addrlist)))))
167
168 (defun bbdb-wl-update-record (&optional offer-to-create)
169   "Returns the record corresponding to the current WL message,
170 creating or modifying it as necessary.  A record will be created if
171 bbdb/mail-auto-create-p is non-nil, or if OFFER-TO-CREATE is true and
172 the user confirms the creation."
173   (let* ((bbdb-get-only-first-address-p t)
174          (records (bbdb-wl-update-records offer-to-create)))
175     (if (and records (listp records))
176         (car records)
177       records)))
178
179 (defun bbdb-wl-update-records (&optional offer-to-create)
180   "Returns the records corresponding to the current WL message,
181 creating or modifying it as necessary.  A record will be created if
182 bbdb/mail-auto-create-p is non-nil, or if OFFER-TO-CREATE is true and
183 the user confirms the creation."
184   (save-excursion
185     (if bbdb-use-pop-up
186         (bbdb-wl-pop-up-bbdb-buffer offer-to-create)
187       (let ((key
188              (save-excursion
189                (set-buffer
190                 (save-excursion
191                   (if (buffer-live-p wl-current-summary-buffer)
192                       (set-buffer wl-current-summary-buffer))
193                   wl-message-buf-name))
194                (intern (format
195                         "%s-%d"
196                         wl-current-summary-buffer
197                         wl-message-buffer-cur-number))))
198             record)
199         (or (progn (setq record (bbdb-message-cache-lookup key))
200                    (if (listp record) (nth 1 record) record))
201             (static-if (not (fboundp 'bbdb-update-records))
202                 (let* ((from (or (std11-field-body "From") ""))
203                        (addr (and from
204                                   (nth 1 (std11-extract-address-components
205                                           from)))))
206                   (if (or (null from)
207                           (null addr)
208                           (string-match (bbdb-user-mail-names) addr))
209                       (setq from (or (std11-field-body "To") from)))
210                   (with-temp-buffer ; to keep raw buffer unibyte.
211                     (elmo-set-buffer-multibyte
212                      default-enable-multibyte-characters)
213                     (setq from (eword-decode-string
214                                 (decode-mime-charset-string
215                                  from
216                                  wl-mime-charset))))
217                   (if from
218                       (bbdb-encache-message
219                        key
220                        (bbdb-annotate-message-sender
221                         from t
222                         (or (bbdb-invoke-hook-for-value
223                              bbdb/mail-auto-create-p)
224                             offer-to-create)
225                         offer-to-create))))
226               (bbdb-encache-message
227                key
228                (bbdb-update-records (bbdb-wl-get-addresses
229                                      bbdb-get-only-first-address-p)
230                                     (or (bbdb-invoke-hook-for-value
231                                          bbdb/mail-auto-create-p)
232                                         offer-to-create)
233                                     offer-to-create))))))))
234
235 (defun bbdb-wl-annotate-sender (string)
236   "Add a line to the end of the Notes field of the BBDB record
237 corresponding to the sender of this message."
238   (interactive (list (if bbdb-readonly-p
239                          (error "The Insidious Big Brother Database is read-only")
240                        (read-string "Comments: "))))
241   (set-buffer (wl-message-get-original-buffer))
242   (bbdb-annotate-notes (bbdb-wl-update-record t) string))
243
244 (defun bbdb-wl-edit-notes (&optional arg)
245   "Edit the notes field or (with a prefix arg) a user-defined field
246 of the BBDB record corresponding to the sender of this message."
247   (interactive "P")
248   (wl-summary-set-message-buffer-or-redisplay)
249   (set-buffer (wl-message-get-original-buffer))
250   (let ((record (or (bbdb-wl-update-record t) (error ""))))
251     (bbdb-display-records (list record))
252     (if arg
253         (bbdb-record-edit-property record nil t)
254       (bbdb-record-edit-notes record t))))
255
256 (if (not (boundp 'bbdb-get-addresses-from-headers))
257     (defvar bbdb-get-addresses-from-headers
258       '("From" "Resent-From" "Reply-To")))
259
260 (if (not (boundp 'bbdb-get-addresses-to-headers))
261     (defvar bbdb-get-addresses-to-headers
262       '("Resent-To" "Resent-CC" "To" "CC" "BCC")))
263
264 (if (not (boundp 'bbdb-get-addresses-headers))
265     (defvar bbdb-get-addresses-headers
266       (append bbdb-get-addresses-from-headers bbdb-get-addresses-to-headers)))
267
268 (defun bbdb-wl-show-records (&optional headers)
269   "Display the contents of the BBDB for the sender of this message.
270 This buffer will be in `bbdb-mode', with associated keybindings."
271   (interactive)
272   (wl-summary-set-message-buffer-or-redisplay)
273   (set-buffer (wl-message-get-original-buffer))
274   (let ((bbdb-get-addresses-headers (or headers bbdb-get-addresses-headers))
275         (bbdb-update-records-mode 'annotating)
276         (bbdb-message-cache nil)
277         (bbdb-user-mail-names nil)
278         records bbdb-win)
279     (setq records (bbdb-wl-update-records t))
280     (if records
281         (progn
282           (bbdb-wl-pop-up-bbdb-buffer)
283           (bbdb-display-records (if (listp records) records
284                                   (list records))))
285       (bbdb-undisplay-records))
286     (setq bbdb-win (get-buffer-window (get-buffer bbdb-buffer-name)))
287     (and bbdb-win
288          (select-window bbdb-win))
289     records))
290
291 (defun bbdb-wl-show-all-recipients ()
292   "Show all recipients of this message. Counterpart to `bbdb/vm-show-sender'."
293   (interactive)
294   (bbdb-wl-show-records  bbdb-get-addresses-to-headers))
295
296 (defun bbdb-wl-show-sender (&optional show-recipients)
297   "Display the contents of the BBDB for the senders of this message.
298 With a prefix argument show the recipients instead,
299 with two prefix arguments show all records.
300 This buffer will be in `bbdb-mode', with associated keybindings."
301   (interactive "p")
302   (cond ((= 4 show-recipients)
303          (bbdb-wl-show-all-recipients))
304         ((= 16 show-recipients)
305          (bbdb-wl-show-records))
306         (t 
307          (if (null (bbdb-wl-show-records bbdb-get-addresses-from-headers))
308              (bbdb-wl-show-all-recipients)))))
309
310 (defun bbdb-wl-pop-up-bbdb-buffer (&optional offer-to-create)
311   "Make the *BBDB* buffer be displayed along with the WL window(s),
312 displaying the record corresponding to the sender of the current message."
313   (if (get-buffer-window bbdb-buffer-name)
314       nil
315     (let ((mes-win (get-buffer-window
316                     (save-excursion
317                       (if (buffer-live-p  wl-current-summary-buffer)
318                           (set-buffer wl-current-summary-buffer))
319                       wl-message-buf-name)))
320           (cur-win (selected-window))
321           (b (current-buffer)))
322       (and mes-win
323            (select-window mes-win))
324       (let ((size (min
325                    (- (window-height mes-win)
326                       window-min-height 1)
327                    (- (window-height mes-win)
328                       (max window-min-height
329                            (1+ bbdb-pop-up-target-lines))))))
330         (split-window mes-win (if (> size 0) size window-min-height)))
331       ;; goto the bottom of the two...
332       (select-window (next-window))
333       ;; make it display *BBDB*...
334       (let ((pop-up-windows nil))
335         (switch-to-buffer (get-buffer-create bbdb-buffer-name)))
336       ;; select the original window we were in...
337       (select-window cur-win)
338       ;; and make sure the current buffer is correct as well.
339       (set-buffer b)))
340   (let ((bbdb-gag-messages t)
341         (bbdb-use-pop-up nil)
342         (bbdb-electric-p nil))
343     (let ((records (static-if (fboundp 'bbdb-update-records)
344                        (bbdb-wl-update-records offer-to-create)
345                      (bbdb-wl-update-record offer-to-create)))
346           (bbdb-elided-display (bbdb-pop-up-elided-display))
347           (b (current-buffer)))
348       (bbdb-display-records (if (listp records) records
349                               (list records)))
350       (set-buffer b)
351       records)))
352
353 (defun bbdb-wl-send-mail-internal (&optional to subj records)
354   (unwind-protect
355       (wl-draft (wl-address-header-extract-address to) "" (or subj ""))
356     (condition-case nil (delete-other-windows) (error))))
357
358 ;;; @ bbdb-extract-field-value -- stolen from tm-bbdb.
359 ;;;
360 (and (not (fboundp 'bbdb-wl-extract-field-value-internal))
361 ;;;  (not (fboundp 'PLEASE_REPLACE_WITH_SEMI-BASED_MIME-BBDB)) ;; mime-bbdb
362     (progn
363       (if (and (string< bbdb-version "1.58")
364                ;; (not (fboundp 'bbdb-extract-field-value) ; defined as autoload
365                (not (fboundp 'bbdb-header-start)))
366           (load "bbdb-hooks")
367         (require 'bbdb-hooks))
368       (fset 'bbdb-wl-extract-field-value-internal
369             (cond
370              ((fboundp 'tm:bbdb-extract-field-value)
371               (symbol-function 'tm:bbdb-extract-field-value))
372              (t (symbol-function 'bbdb-extract-field-value))))
373       (defun bbdb-extract-field-value (field)
374         (let ((value (bbdb-wl-extract-field-value-internal field)))
375           (with-temp-buffer ; to keep raw buffer unibyte.
376             (elmo-set-buffer-multibyte
377              default-enable-multibyte-characters)
378             (and value
379                  (eword-decode-string value)))))
380       ))
381
382
383 (provide 'bbdb-wl)
384
385 ;;; bbdb-wl.el ends here