* lisp/gnus-bbdb.el (gnus-bbdb/pop-up-bbdb-buffer): Don't bind
[elisp/gnus.git-] / lisp / gnus-bbdb.el
index 4c274ac..119e3bf 100644 (file)
@@ -1,27 +1,50 @@
-;;; -*- Mode:Emacs-Lisp -*-
+;; gnus-bbdb.el --- Interface to T-gnus
 
-;;; This file is part of Semi-gnus.
-;;; copyright (c) 1991, 1992, 1993 Jamie Zawinski <jwz@netscape.com>.
-;;;               1998             Keiichi Suzuki <kei-suzu@mail.wbs.ne.jp>
+;; Copyright (c) 1991,1992,1993 Jamie Zawinski <jwz@netscape.com>.
+;; Copyright (C) 1995,1996,1997 Shuhei KOBAYASHI
+;; Copyright (C) 1997,1998 MORIOKA Tomohiko
+;; Copyright (C) 1998,1999 Keiichi Suzuki <keiichi@nanap.org>
 
-;;; The Insidious Big Brother Database 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 1, 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 GNU Emacs; see the file COPYING.  If not, write to
-;;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+;; Author: Keiichi Suzuki <keiichi@nanap.org>
+;; Author: Shuhei KOBAYASHI <shuhei-k@jaist.ac.jp>
+;; Keywords: BBDB, MIME, multimedia, multilingual, mail, news
+
+;; This file is part of T-gnus.
+
+;; This program 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 2, or (at
+;; your option) any later version.
+
+;; This program 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 GNU Emacs; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Code:
 
 (require 'bbdb)
+(require 'bbdb-com)
 (require 'gnus)
+(require 'std11)
 (eval-when-compile
-  (require 'gnus-win))
+  (defvar bbdb-pop-up-elided-display)  ; default unbound.
+  (require 'gnus-win)
+  (require 'cl))
+
+(defvar gnus-bbdb/decode-field-body-function 'nnheader-decode-field-body
+  "*Field body decoder.")
+
+(defmacro gnus-bbdb/decode-field-body (field-body field-name)
+  `(or (and (functionp gnus-bbdb/decode-field-body-function)
+           (funcall gnus-bbdb/decode-field-body-function
+                    ,field-body ,field-name))
+       ,field-body))
 
 ;;;###autoload
 (defun gnus-bbdb/update-record (&optional offer-to-create)
@@ -31,28 +54,34 @@ bbdb/news-auto-create-p is non-nil, or if OFFER-TO-CREATE is true and
 the user confirms the creation."
   (if bbdb-use-pop-up
       (gnus-bbdb/pop-up-bbdb-buffer offer-to-create)
-    (let (from)
-      (save-restriction
+    (save-excursion
+      (let (from)
        (set-buffer gnus-original-article-buffer)
-       (setq from (mail-header-from mime-message-structure))
-       (when (or (null from)
-                 (string-match (bbdb-user-mail-names)
-                               (mail-strip-quoted-names from)))
-         ;; if logged-in user sent this, use recipients.
+       (save-restriction
          (widen)
          (narrow-to-region (point-min)
                            (progn (goto-char (point-min))
                                   (or (search-forward "\n\n" nil t)
                                       (error "message unexists"))
                                   (- (point) 2)))
-         (let ((to (mail-fetch-field "to")))
-            (setq from (mime-decode-field-body to 'To 'unfolding)))))
-      (when from
-       (bbdb-annotate-message-sender from t
-                                     (or (bbdb-invoke-hook-for-value
-                                          bbdb/news-auto-create-p)
-                                         offer-to-create)
-                                     offer-to-create)))))
+         (when (setq from (mail-fetch-field "from"))
+           (setq from (gnus-bbdb/extract-address-components
+                       (gnus-bbdb/decode-field-body from 'From))))
+         (when (and (car (cdr from))
+                    (string-match (bbdb-user-mail-names)
+                                  (car (cdr from))))
+           ;; if logged-in user sent this, use recipients.
+           (let ((to (mail-fetch-field "to")))
+             (when to
+               (setq from
+                     (gnus-bbdb/extract-address-components
+                      (gnus-bbdb/decode-field-body to 'To))))))
+       (when from
+         (bbdb-annotate-message-sender from t
+                                       (or (bbdb-invoke-hook-for-value
+                                            bbdb/news-auto-create-p)
+                                           offer-to-create)
+                                       offer-to-create)))))) )
 
 ;;;###autoload
 (defun gnus-bbdb/annotate-sender (string &optional replace)
@@ -88,35 +117,115 @@ This buffer will be in bbdb-mode, with associated keybindings."
 (defun gnus-bbdb/pop-up-bbdb-buffer (&optional offer-to-create)
   "Make the *BBDB* buffer be displayed along with the GNUS windows,
 displaying the record corresponding to the sender of the current message."
-  (let ((bbdb-gag-messages t)
-       (bbdb-use-pop-up nil)
-       (bbdb-electric-p nil))
-    (let ((record (gnus-bbdb/update-record offer-to-create))
-         (bbdb-elided-display (bbdb-pop-up-elided-display))
-         (b (current-buffer)))
+  (let* ((bbdb-gag-messages t)
+        (bbdb-electric-p nil)
+        (record
+         (let (bbdb-use-pop-up)
+           (gnus-bbdb/update-record offer-to-create)))
+        (bbdb-elided-display (bbdb-pop-up-elided-display)))
+    (save-current-buffer
       ;; display the bbdb buffer iff there is a record for this article.
-      (cond (record
-            (bbdb-pop-up-bbdb-buffer
-             (function (lambda (w)
-                         (let ((b (current-buffer)))
-                           (set-buffer (window-buffer w))
-                           (prog1 (or (eq major-mode 'mime-veiw-mode)
-                                      (eq major-mode 'gnus-article-mode))
-                                  (set-buffer b))))))
-            (bbdb-display-records (list record)))
-           (t
-            (or bbdb-inside-electric-display
-                (not (get-buffer-window bbdb-buffer-name))
-                (let (w)
-                  (delete-other-windows)
-                  (if (assq 'article gnus-buffer-configuration)
-                      (gnus-configure-windows 'article)
-                    (gnus-configure-windows 'SelectArticle))
-                  (if (setq w (get-buffer-window gnus-summary-buffer))
-                      (select-window w))
-                  ))))
-      (set-buffer b)
-      record)))
+      (cond
+       (record
+       (bbdb-pop-up-bbdb-buffer
+        (function (lambda (w)
+                    (with-current-buffer (window-buffer w)
+                      (memq major-mode
+                            '(mime-view-mode gnus-article-mode))))))
+       (bbdb-display-records (list record)))
+       ((and (not bbdb-inside-electric-display)
+            (get-buffer-window bbdb-buffer-name))
+       (delete-other-windows)
+       (if (assq 'article gnus-buffer-configuration)
+           (gnus-configure-windows 'article)
+         (gnus-configure-windows 'SelectArticle))
+       (let ((w (get-buffer-window gnus-summary-buffer)))
+         (if w (select-window w))))))
+    record))
+
+;;;###autoload
+(defun gnus-bbdb/split-mail (header-field bbdb-field
+                                         &optional regexp group)
+  "Mail split method for `nnmail-split-fancy'.
+HEADER-FIELD is a regexp or list of regexps as mail header field name
+for gathering mail addresses.  If HEADER-FIELD is a string, then it's
+used for just matching pattern.  If HEADER-FIELD is a list of strings,
+then these strings have priorities in the order.
+
+BBDB-FIELD is field name of BBDB.
+Optional argument REGEXP is regexp string for matching BBDB-FIELD value.
+If REGEXP is nil or not specified, then all BBDB-FIELD value is matched.
+
+If GROUP is nil or not specified, then BBDB-FIELD value is returned as
+group name.  If GROUP is a symbol `&', then list of all matching group's
+BBDB-FIELD values is returned.  Otherwise, GROUP is returned."
+  (if (listp header-field)
+      (if (eq group '&)
+         (gnus-bbdb/split-mail (mapconcat 'identity header-field "\\|")
+                               bbdb-field regexp group)
+       (let (rest)
+         (while (and header-field
+                     (null (setq rest (gnus-bbdb/split-mail
+                                       (car header-field) bbdb-field
+                                       regexp group))))
+           (setq header-field (cdr header-field)))
+         rest))
+    (let ((pat (concat "^\\(" header-field "\\):[ \t]"))
+         header-values)
+      (goto-char (point-min))
+      (while (re-search-forward pat nil t)
+       (setq header-values (cons (buffer-substring (point)
+                                               (std11-field-end))
+                                 header-values)))
+      (let ((address-regexp
+            (with-temp-buffer
+              (let (lal)
+                (while header-values
+                  (setq lal (std11-parse-addresses-string
+                             (pop header-values)))
+                  (while lal
+                    (gnus-bbdb/insert-address-regexp (pop lal)))))
+              (buffer-string))))
+       (unless (zerop (length address-regexp))
+         (gnus-bbdb/split-mail-1 address-regexp bbdb-field regexp group))))))
+
+(defun gnus-bbdb/insert-address-regexp (address)
+  "Insert string of address part from parsed ADDRESS of RFC 822."
+  (cond ((eq (car address) 'group)
+        (setq address (cdr address))
+        (while address
+          (gnus-bbdb/insert-address-regexp (pop address))))
+       ((eq (car address) 'mailbox)
+        (unless (eq (point) (point-min))
+          (insert "\\|"))
+        (let ((addr (nth 1 address)))
+          (insert (std11-addr-to-string
+                   (if (eq (car addr) 'phrase-route-addr)
+                       (nth 2 addr)
+                     (cdr addr))))))))
+
+(defun gnus-bbdb/split-mail-1 (address-regexp bbdb-field regexp group)
+  (let ((records (bbdb-search (bbdb-records) nil nil address-regexp))
+       prop rest)
+    (or regexp (setq regexp ""))
+    (catch 'done
+      (cond
+       ((eq group '&)
+       (while records
+         (when (and (setq prop (bbdb-record-getprop (car records) bbdb-field))
+                    (string-match regexp prop)
+                    (not (member prop rest)))
+           (setq rest (cons prop rest)))
+         (setq records (cdr records)))
+       (throw 'done (when rest (cons '& rest))))
+       (t
+       (while records
+         (when (or (null bbdb-field) 
+                   (and (setq prop (bbdb-record-getprop (car records)
+                                                        bbdb-field))
+                        (string-match regexp prop)))
+           (throw 'done (or group prop)))
+         (setq records (cdr records))))))))
 
 ;;
 ;; Announcing BBDB entries in the summary buffer
@@ -224,7 +333,7 @@ strings.  In the future this should change."
         (data (and (or gnus-bbdb/summary-mark-known-posters
                        gnus-bbdb/summary-show-bbdb-names)
                    (condition-case ()
-                       (mail-extract-address-components from)
+                       (gnus-bbdb/extract-address-components from)
                      (error nil))))
         (name (car data))
         (net (car (cdr data)))
@@ -281,7 +390,7 @@ This function is meant to be used with the user function defined in
   (let* ((from (mail-header-from header))
         (data (and gnus-bbdb/summary-show-bbdb-names
                    (condition-case ()
-                       (mail-extract-address-components from)
+                       (gnus-bbdb/extract-address-components from)
                      (error nil))))
         (name (car data))
         (net (car (cdr data)))
@@ -317,7 +426,7 @@ This function is meant to be used with the user function defined in
   "Given a Gnus message header, returns a mark if the poster is in the BBDB, \" \" otherwise.  The mark itself is the value of the field indicated by `bbdb-message-marker-field' (`mark-char' by default) if the indicated field is in the poster's record, and `gnus-bbdb/summary-known-poster-mark' otherwise."
   (let* ((from (mail-header-from header))
         (data (condition-case ()
-                  (mail-extract-address-components from)
+                  (gnus-bbdb/extract-address-components from)
                 (error nil)))
         (name (car data))
         (net (cadr data))
@@ -438,9 +547,55 @@ beginning of the message headers."
   ;; exist only in the message.
   (let (value)
     (when (setq value (mail-fetch-field field-name))
-      (mime-decode-field-body value
-                             (intern (capitalize field-name))
-                             'unfolding))))
+      (gnus-bbdb/decode-field-body value field-name))))
+
+;;; @ mail-extr
+;;;
+
+(defvar gnus-bbdb/canonicalize-full-name-methods
+  '(gnus-bbdb/canonicalize-dots
+    gnus-bbdb/canonicalize-spaces))
+
+(defun gnus-bbdb/extract-address-components (str)
+  (let* ((ret     (std11-extract-address-components str))
+         (phrase  (car ret))
+         (address (car (cdr ret)))
+         (methods gnus-bbdb/canonicalize-full-name-methods))
+    (while (and phrase methods)
+      (setq phrase  (funcall (car methods) phrase)
+            methods (cdr methods)))
+    (if (string= address "") (setq address nil))
+    (if (string= phrase "") (setq phrase nil))
+    (when (or phrase address)
+      (list phrase address))
+    ))
+
+;;; @ full-name canonicalization methods
+;;;
+
+(defun gnus-bbdb/canonicalize-spaces (str)
+  (let (dest)
+    (while (string-match "\\s +" str)
+      (setq dest (cons (substring str 0 (match-beginning 0)) dest))
+      (setq str (substring str (match-end 0)))
+      )
+    (or (string= str "")
+        (setq dest (cons str dest)))
+    (setq dest (nreverse dest))
+    (mapconcat 'identity dest " ")
+    ))
+
+(defun gnus-bbdb/canonicalize-dots (str)
+  (let (dest)
+    (while (string-match "\\." str)
+      (setq dest (cons (substring str 0 (match-end 0)) dest))
+      (setq str (substring str (match-end 0)))
+      )
+    (or (string= str "")
+        (setq dest (cons str dest)))
+    (setq dest (nreverse dest))
+    (mapconcat 'identity dest " ")
+    ))
 
 ;;
 ;; Insinuation