1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
  
     | 
    
      ;;; eudcb-bbdb.el --- Emacs Unified Directory Client - BBDB Backend
;; Copyright (C) 1998-2020 Free Software Foundation, Inc.
;; Author: Oscar Figueiredo <oscar@cpe.fr>
;;         Pavel JanÃk <Pavel@Janik.cz>
;; Maintainer: Thomas Fitzsimmons <fitzsim@fitzsim.org>
;; Keywords: comm
;; Package: eudc
;; This file is part of GNU Emacs.
;; GNU Emacs 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 3 of the License, or
;; (at your option) any later version.
;; GNU Emacs 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.  If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;;    This library provides an interface to use BBDB as a backend of
;;    the Emacs Unified Directory Client.
;;; Code:
(require 'eudc)
;; Make it loadable on systems without bbdb.
(require 'bbdb nil t)
(require 'bbdb-com nil t)
;;{{{      Internal cooking
;; I don't like this but mapcar does not accept a parameter to the function and
;; I don't want to use mapcar*
(defvar eudc-bbdb-current-query nil)
(defvar eudc-bbdb-current-return-attributes nil)
(defun eudc-bbdb-field (field-symbol)
  "Convert FIELD-SYMBOL so that it is recognized by the current BBDB version.
BBDB < 3 used `net'; BBDB >= 3 uses `mail'."
  ;; This just-in-time translation permits upgrading from BBDB 2 to
  ;; BBDB 3 without restarting Emacs.
  (cond ((and (eq field-symbol 'net)
	      (eudc--using-bbdb-3-or-newer-p))
         'mail)
        ((and (eq field-symbol 'company)
	      (eudc--using-bbdb-3-or-newer-p))
         'organization)
        (t field-symbol)))
(defvar eudc-bbdb-attributes-translation-alist
  '((name . lastname)
    (email . net)
    (phone . phones))
  "Alist mapping EUDC attribute names to BBDB names.")
(eudc-protocol-set 'eudc-query-function 'eudc-bbdb-query-internal 'bbdb)
(eudc-protocol-set 'eudc-list-attributes-function nil 'bbdb)
(eudc-protocol-set 'eudc-protocol-attributes-translation-alist
		   'eudc-bbdb-attributes-translation-alist 'bbdb)
(eudc-protocol-set 'eudc-bbdb-conversion-alist nil 'bbdb)
(eudc-protocol-set 'eudc-protocol-has-default-query-attributes nil 'bbdb)
(defun eudc-bbdb-format-query (query)
  "Format a EUDC query alist into a list suitable to `bbdb-search'."
  (let* ((firstname (cdr (assq 'firstname query)))
	 (lastname (cdr (assq 'lastname query)))
	 (name (or (and firstname lastname
			(concat firstname " " lastname))
		   firstname
		   lastname))
	(company (cdr (assq 'company query)))
	(net (cdr (assq 'net query)))
	(notes (cdr (assq 'notes query)))
	(phone (cdr (assq 'phone query))))
    (list name company net notes phone)))
(defun eudc-bbdb-filter-non-matching-record (record)
  "Return RECORD if it matches `eudc-bbdb-current-query', nil otherwise."
  (require 'bbdb)
  (catch 'unmatch
    (progn
      (dolist (condition eudc-bbdb-current-query)
        (let ((attr (car condition))
              (val (cdr condition))
              (case-fold-search t)
              bbdb-val)
          (or (and (memq attr '(firstname lastname aka company phones
                                addresses net))
                   (progn
                     (setq bbdb-val
                           (eval (list (intern (concat "bbdb-record-"
                                                       (symbol-name
                                                        (eudc-bbdb-field
                                                         attr))))
                                       'record)))
                     (if (listp bbdb-val)
                         (if eudc-bbdb-enable-substring-matches
                             (eval `(or ,@(mapcar (lambda (subval)
                                                    (string-match val subval))
                                                  bbdb-val)))
                           (member (downcase val)
                                   (mapcar 'downcase bbdb-val)))
                       (if eudc-bbdb-enable-substring-matches
                           (string-match val bbdb-val)
                         (string-equal (downcase val) (downcase bbdb-val))))))
              (throw 'unmatch nil))))
      record)))
;; External.
(declare-function bbdb-phone-location   "ext:bbdb" t) ; via bbdb-defstruct
(declare-function bbdb-phone-string     "ext:bbdb" (phone))
(declare-function bbdb-record-phones    "ext:bbdb" t) ; via bbdb-defstruct
(declare-function bbdb-address-streets  "ext:bbdb" t) ; via bbdb-defstruct
(declare-function bbdb-address-city     "ext:bbdb" t) ; via bbdb-defstruct
(declare-function bbdb-address-state    "ext:bbdb" t) ; via bbdb-defstruct
(declare-function bbdb-address-zip      "ext:bbdb" t) ; via bbdb-defstruct
(declare-function bbdb-address-location "ext:bbdb" t) ; via bbdb-defstruct
(declare-function bbdb-record-addresses "ext:bbdb" t) ; via bbdb-defstruct
(declare-function bbdb-records          "ext:bbdb"
                  (&optional dont-check-disk already-in-db-buffer))
(declare-function bbdb-record-notes "ext:bbdb" t) ; via bbdb-defstruct
;; External, BBDB >= 3.
(declare-function bbdb-phone-label "ext:bbdb" t) ; via bbdb-defstruct
(declare-function bbdb-record-phone "ext:bbdb" t) ; via bbdb-defstruct
(declare-function bbdb-record-address "ext:bbdb" t) ; via bbdb-defstruct
(declare-function bbdb-record-xfield "ext:bbdb" t) ; via bbdb-defstruct
(defun eudc-bbdb-extract-phones (record)
  (require 'bbdb)
  (mapcar (function
	   (lambda (phone)
	     (if eudc-bbdb-use-locations-as-attribute-names
		 (cons (intern (if (eudc--using-bbdb-3-or-newer-p)
                                   (bbdb-phone-label phone)
                                 (bbdb-phone-location phone)))
		       (bbdb-phone-string phone))
	       (cons 'phones (format "%s: %s"
				     (if (eudc--using-bbdb-3-or-newer-p)
                                         (bbdb-phone-label phone)
                                       (bbdb-phone-location phone))
				     (bbdb-phone-string phone))))))
	  (if (eudc--using-bbdb-3-or-newer-p)
              (bbdb-record-phone record)
            (bbdb-record-phones record))))
(defun eudc-bbdb-extract-addresses (record)
  (require 'bbdb)
  (let (s c val)
    (mapcar (lambda (address)
              (setq c (bbdb-address-streets address))
              (dotimes (n 3)
                (unless (zerop (length (setq s (nth n c))))
                  (setq val (concat val s "\n"))))
              (setq c (bbdb-address-city address)
                    s (bbdb-address-state address))
              (setq val (concat val
                                (if (and (> (length c) 0) (> (length s) 0))
                                    (concat c ", " s)
                                  c)
                                " "
                                (bbdb-address-zip address)))
              (if eudc-bbdb-use-locations-as-attribute-names
                  (cons (intern (bbdb-address-location address)) val)
                (cons 'addresses (concat (bbdb-address-location address)
                                         "\n" val))))
            (if (eudc--using-bbdb-3-or-newer-p)
                (bbdb-record-address record)
              (bbdb-record-addresses record)))))
(defun eudc-bbdb-format-record-as-result (record)
  "Format the BBDB RECORD as a EUDC query result record.
The record is filtered according to `eudc-bbdb-current-return-attributes'."
  (require 'bbdb)
  (let ((attrs (or eudc-bbdb-current-return-attributes
		   '(firstname lastname aka company phones addresses net notes)))
	attr
	eudc-rec
	val)
    (while (prog1
	       (setq attr (car attrs))
	     (setq attrs (cdr attrs)))
      (cond
       ((eq attr 'phones)
	(setq val (eudc-bbdb-extract-phones record)))
       ((eq attr 'addresses)
	(setq val (eudc-bbdb-extract-addresses record)))
       ((eq attr 'notes)
        (if (eudc--using-bbdb-3-or-newer-p)
            (setq val (bbdb-record-xfield record 'notes))
          (setq val (bbdb-record-notes record))))
       ((memq attr '(firstname lastname aka company net))
	(setq val (eval
		   (list (intern
			  (concat "bbdb-record-"
				  (symbol-name (eudc-bbdb-field attr))))
			 'record))))
       (t
	(error "Unknown BBDB attribute")))
      (cond
       ((or (not val) (equal val ""))) ; do nothing
       ((memq attr '(phones addresses))
	(setq eudc-rec (append val eudc-rec)))
       ((and (listp val)
	     (= 1 (length val)))
	(setq eudc-rec (cons (cons attr (car val)) eudc-rec)))
       ((> (length val) 0)
	(setq eudc-rec (cons (cons attr val) eudc-rec)))
       (t
	(error "Unexpected attribute value"))))
    (nreverse eudc-rec)))
(defun eudc-bbdb-query-internal (query &optional return-attrs)
  "Query BBDB  with QUERY.
QUERY is a list of cons cells (ATTR . VALUE) where ATTRs should be valid
BBDB attribute names.
RETURN-ATTRS is a list of attributes to return, defaulting to
`eudc-default-return-attributes'."
  (require 'bbdb)
  (let ((eudc-bbdb-current-query query)
	(eudc-bbdb-current-return-attributes return-attrs)
	(query-attrs (eudc-bbdb-format-query query))
	bbdb-attrs
	(records (bbdb-records))
	result
	filtered)
    ;; BBDB ORs its query attributes while EUDC ANDs them, hence we need to
    ;; call bbdb-search iteratively on the returned records for each of the
    ;; requested attributes
    (while (and records (> (length query-attrs) 0))
      (setq bbdb-attrs (append bbdb-attrs (list (car query-attrs))))
      (if (car query-attrs)
	  (setq records (eval `(bbdb-search ,(quote records) ,@bbdb-attrs))))
      (setq query-attrs (cdr query-attrs)))
    (mapc (function
	   (lambda (record)
	     (setq filtered (eudc-filter-duplicate-attributes record))
	     ;; If there were duplicate attributes reverse the order of the
	     ;; record so the unique attributes appear first
	     (if (> (length filtered) 1)
		 (setq filtered (mapcar (function
					 (lambda (rec)
					   (reverse rec)))
					filtered)))
	     (setq result (append result filtered))))
	  (delq nil
		(mapcar 'eudc-bbdb-format-record-as-result
			(delq nil
			      (mapcar 'eudc-bbdb-filter-non-matching-record
				      records)))))
    result))
;;}}}
;;{{{      High-level interfaces (interactive functions)
(defun eudc-bbdb-set-server (dummy)
  "Set the EUDC server to BBDB."
  (interactive)
  (eudc-set-server dummy 'bbdb)
  (message "BBDB server selected"))
;;}}}
(eudc-register-protocol 'bbdb)
(provide 'eudcb-bbdb)
;;; eudcb-bbdb.el ends here
 
     |