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