1 ;;; bbdb-whois.el -- Big Brother gets a little help from Big Brother
2 ;;; This file is part of the Insidious Big Brother Database (aka BBDB).
4 ;;; Copyright (C) 1992, 1993 Roland McGrath
6 ;;; This program is free software; you can redistribute it and/or modify
7 ;;; it under the terms of the GNU General Public License as published by
8 ;;; the Free Software Foundation; either version 2, or (at your option)
11 ;;; This program is distributed in the hope that it will be useful,
12 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ;;; GNU General Public License for more details.
16 ;;; A copy of the GNU General Public License can be obtained from this
17 ;;; program's author (send electronic mail to roland@gnu.ai.mit.edu) or
18 ;;; from the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA
21 ;;; Send bug reports to bbdb@waider.ie
25 (defmacro bbdb-add-to-field (record field text)
26 (let ((get (intern (concat "bbdb-record-" (symbol-name field))))
27 (set (intern (concat "bbdb-record-set-" (symbol-name field)))))
28 (` (let ((old ((, get) (, record)))
31 ((, set) (, record) (nconc old (list text))))))))
33 (defcustom bbdb-whois-server (or (and (boundp 'whois-server) whois-server)
34 "whois.geektools.com")
35 "*Server for \\[bbdb-whois] lookups."
36 :group 'bbdb-utilities
39 (defvar bbdb-whois-name nil
40 "Used to store the name during a whois call.")
41 (make-variable-buffer-local 'bbdb-whois-name)
42 (defvar bbdb-whois-record nil
43 "Used to store the record during a whois call.")
44 (make-variable-buffer-local 'bbdb-whois-record)
46 ;;; main entry point. it'd be nice if we could bbdb-whois an arbitrary
47 ;;; name and make a record from that directly.
50 (defun bbdb-whois (the-record &optional server)
51 (interactive (list (bbdb-get-record "BBDB Whois: ")
52 (and current-prefix-arg
53 (read-string "Query whois server: "
55 (or server (setq server bbdb-whois-server))
56 (if (or (bbdb-record-lastname the-record) (bbdb-record-firstname the-record))
57 ;; XXX we seem to get called with a vector of nils.
59 (set-buffer (generate-new-buffer " *bbdb-whois*"))
60 (set bbdb-whois-record the-record)
62 (if (bbdb-record-getprop the-record 'nic)
63 (concat "!" (bbdb-record-getprop the-record 'nic))
64 (concat (bbdb-record-lastname the-record) ", "
65 (bbdb-record-firstname the-record))))
66 (let ((proc (open-network-stream "whois" (current-buffer) server 43)))
67 (set-process-sentinel proc 'bbdb-whois-sentinel)
68 (process-send-string proc (concat bbdb-whois-name "\r\n"))))))
70 ;;; This function parses the results from the server.
71 (defun bbdb-whois-sentinel (proc status)
74 (set-buffer (process-buffer proc))
75 (setq rec bbdb-whois-record)
78 ;; check for multiple replies
79 ;; should maybe present a menu/completion buffer of multiples and do a
81 (if (not (re-search-forward "Record last updated" (point-max) t))
82 (if (re-search-forward "No match" (point-max) t)
83 (message "Can not find a whois record for `%s'" bbdb-whois-name)
84 (if (re-search-forward "Access Limit Exceeded" (point-max) t)
85 (message "Per-day access limit to %s exceeded."
86 bbdb-whois-server) ;; bah!
87 (message "%s is ambiguous to whois; try a different name"
90 ;; clean up & parse buffer, otherwise.
91 (replace-string "\r\n" "\n")
93 (if (re-search-forward
94 (concat (if (string-match "^!" bbdb-whois-name)
96 (regexp-quote (substring bbdb-whois-name 1))
98 (concat (regexp-quote bbdb-whois-name)
99 ".*(\\([A-Z0-9]+\\))"))
100 "\\s *\\(\\S +@\\S +\\)?$")
102 (let ((net (if (match-beginning 2)
103 (downcase (buffer-substring (match-beginning 2)
105 (nic (buffer-substring (match-beginning 1) (match-end 1)))
108 (bbdb-add-to-field rec net net))
109 (bbdb-record-putprop rec 'nic nic)
112 ;; not all nic records have companies, though.
114 (back-to-indentation)
115 (let ((company (buffer-substring (point) (progn (end-of-line)
117 (old (bbdb-record-company rec)))
119 (bbdb-record-set-company rec company))
120 ((string= old company)
123 (bbdb-record-putprop rec 'nic-organization company))))
125 ;; Read the address info into LINES.
126 (while (progn (forward-line 1)
127 (not (looking-at "^$")))
128 (back-to-indentation)
129 (setq lines (cons (buffer-substring (point)
134 ;; Snarf phone number.
135 ;; phone, fax are presented, it seems, as
136 ;; +country area prefix number +country area prefix number
137 ;; we can look for the " +" and split there, I guess.
139 (let ((phones (car lines))
142 (while (string-match "^\\(.+\\) \\+" phones)
144 (append phone-numbers
145 (list (substring phones 0 (match-end 1))))
146 phones (substring phones (+ 1 (match-end 1)))))
147 (setq phone-numbers (append phone-numbers
150 ;; now add each member of the list to the bbdb record
151 ;; it'd be nice if we could be smarter about this.
154 (if (not (bbdb-find-phone
155 p (bbdb-record-phones rec)))
157 (vector (format "nic-phone-%d" n) p)))
158 (bbdb-add-to-field rec phones p-n)
162 ;; throw away phones line from what we've snarfed
163 (setq lines (cdr lines))))
167 (let ((addr (make-vector bbdb-address-length nil))
174 (if (string-match "^[A-Z][A-Z]$" (car lines))
175 (setq country (car lines) ;; could convert from ISO...
178 ;; extract city, state, zip
179 ;; it would be nice if this could all use bbdb-snarf.
180 ;; or if NICs would hand out something machine
181 ;; readable, like <shudder> XML.
183 ;; note the zipcode check at the end of the regexp
184 ;; isn't really a zipcode check, because we don't do
185 ;; zipcode checks any more.
187 "\\([^,]+\\),\\s *\\(\\S +\\)\\s *\\(.+\\)"
189 (setq city (substring (car lines)
192 state (substring (car lines)
195 zip (substring (car lines)
199 ;; otherwise we just stuff everything into the
200 ;; streets list and let the user clean it up. This
201 ;; would be nice to do heuristically, if I knew
202 ;; enough about variable address formats.
203 ;; (bbdb-snarf-grok-address (ADDR)) would be neat.
206 (bbdb-address-set-location addr "nic-address")
207 (bbdb-address-set-city addr (or city ""))
208 (bbdb-address-set-state addr (or state ""))
209 (bbdb-address-set-zip addr (or zip ""))
210 (bbdb-address-set-country addr (or country ""))
211 (setq lines (nreverse lines))
212 (bbdb-address-set-streets addr lines)
214 ;; should probably overwrite existing nic-address field.
215 (bbdb-add-to-field rec addresses addr)))
217 ;; Snarf any random notes.
221 (back-to-indentation)
223 "$\\|Record last updated on")))
224 (if (looking-at "Alternate mailbox: \\(\\S +\\)$")
225 (bbdb-add-to-field rec net
226 (buffer-substring (match-beginning 1)
228 (setq lines (cons (buffer-substring (point)
233 (bbdb-record-putprop rec 'nic-notes
238 ;; Snarf the last-update date.
239 (if (re-search-forward "Record last updated on \\(\\S *\\)\\."
241 (bbdb-record-putprop rec 'nic-updated
242 (buffer-substring (match-beginning 1)
246 (set-buffer bbdb-buffer-name)
247 (bbdb-redisplay-one-record rec)))
248 (message "No whois information for %s" bbdb-whois-name)))
249 (delete-process proc)
250 (kill-buffer (current-buffer)))))
252 (defun bbdb-find-phone (string record)
253 "Return the vector entry if STRING is a phone number listed in RECORD."
256 (while (and record (not done))
257 (setq phone (car record))
258 (if (string= string (bbdb-phone-string phone))
260 (setq record (cdr record)))
263 (provide 'bbdb-whois)