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