eudcb-bbdb.el 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. ;;; eudcb-bbdb.el --- Emacs Unified Directory Client - BBDB Backend
  2. ;; Copyright (C) 1998-2012 Free Software Foundation, Inc.
  3. ;; Author: Oscar Figueiredo <oscar@cpe.fr>
  4. ;; Maintainer: Pavel Janík <Pavel@Janik.cz>
  5. ;; Keywords: comm
  6. ;; Package: eudc
  7. ;; This file is part of GNU Emacs.
  8. ;; GNU Emacs is free software: you can redistribute it and/or modify
  9. ;; it under the terms of the GNU General Public License as published by
  10. ;; the Free Software Foundation, either version 3 of the License, or
  11. ;; (at your option) any later version.
  12. ;; GNU Emacs is distributed in the hope that it will be useful,
  13. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. ;; GNU General Public License for more details.
  16. ;; You should have received a copy of the GNU General Public License
  17. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  18. ;;; Commentary:
  19. ;; This library provides an interface to use BBDB as a backend of
  20. ;; the Emacs Unified Directory Client.
  21. ;;; Code:
  22. (require 'eudc)
  23. (if (not (featurep 'bbdb))
  24. (load-library "bbdb"))
  25. (if (not (featurep 'bbdb-com))
  26. (load-library "bbdb-com"))
  27. ;;{{{ Internal cooking
  28. ;; I don't like this but mapcar does not accept a parameter to the function and
  29. ;; I don't want to use mapcar*
  30. (defvar eudc-bbdb-current-query nil)
  31. (defvar eudc-bbdb-current-return-attributes nil)
  32. (defvar eudc-bbdb-attributes-translation-alist
  33. '((name . lastname)
  34. (email . net)
  35. (phone . phones))
  36. "Alist mapping EUDC attribute names to BBDB names.")
  37. (eudc-protocol-set 'eudc-query-function 'eudc-bbdb-query-internal 'bbdb)
  38. (eudc-protocol-set 'eudc-list-attributes-function nil 'bbdb)
  39. (eudc-protocol-set 'eudc-protocol-attributes-translation-alist
  40. 'eudc-bbdb-attributes-translation-alist 'bbdb)
  41. (eudc-protocol-set 'eudc-bbdb-conversion-alist nil 'bbdb)
  42. (eudc-protocol-set 'eudc-protocol-has-default-query-attributes nil 'bbdb)
  43. (defun eudc-bbdb-format-query (query)
  44. "Format a EUDC query alist into a list suitable to `bbdb-search'."
  45. (let* ((firstname (cdr (assq 'firstname query)))
  46. (lastname (cdr (assq 'lastname query)))
  47. (name (or (and firstname lastname
  48. (concat firstname " " lastname))
  49. firstname
  50. lastname))
  51. (company (cdr (assq 'company query)))
  52. (net (cdr (assq 'net query)))
  53. (notes (cdr (assq 'notes query)))
  54. (phone (cdr (assq 'phone query))))
  55. (list name company net notes phone)))
  56. (defun eudc-bbdb-filter-non-matching-record (record)
  57. "Return RECORD if it matches `eudc-bbdb-current-query', nil otherwise."
  58. (catch 'unmatch
  59. (progn
  60. (dolist (condition eudc-bbdb-current-query)
  61. (let ((attr (car condition))
  62. (val (cdr condition))
  63. (case-fold-search t)
  64. bbdb-val)
  65. (or (and (memq attr '(firstname lastname aka company phones
  66. addresses net))
  67. (progn
  68. (setq bbdb-val
  69. (eval (list (intern (concat "bbdb-record-"
  70. (symbol-name attr)))
  71. 'record)))
  72. (if (listp bbdb-val)
  73. (if eudc-bbdb-enable-substring-matches
  74. (eval `(or ,@(mapcar (lambda (subval)
  75. (string-match val subval))
  76. bbdb-val)))
  77. (member (downcase val)
  78. (mapcar 'downcase bbdb-val)))
  79. (if eudc-bbdb-enable-substring-matches
  80. (string-match val bbdb-val)
  81. (string-equal (downcase val) (downcase bbdb-val))))))
  82. (throw 'unmatch nil))))
  83. record)))
  84. ;; External.
  85. (declare-function bbdb-phone-location "ext:bbdb" t) ; via bbdb-defstruct
  86. (declare-function bbdb-phone-string "ext:bbdb" (phone))
  87. (declare-function bbdb-record-phones "ext:bbdb" t) ; via bbdb-defstruct
  88. (declare-function bbdb-address-streets "ext:bbdb" t) ; via bbdb-defstruct
  89. (declare-function bbdb-address-city "ext:bbdb" t) ; via bbdb-defstruct
  90. (declare-function bbdb-address-state "ext:bbdb" t) ; via bbdb-defstruct
  91. (declare-function bbdb-address-zip "ext:bbdb" t) ; via bbdb-defstruct
  92. (declare-function bbdb-address-location "ext:bbdb" t) ; via bbdb-defstruct
  93. (declare-function bbdb-record-addresses "ext:bbdb" t) ; via bbdb-defstruct
  94. (declare-function bbdb-records "ext:bbdb"
  95. (&optional dont-check-disk already-in-db-buffer))
  96. (defun eudc-bbdb-extract-phones (record)
  97. (mapcar (function
  98. (lambda (phone)
  99. (if eudc-bbdb-use-locations-as-attribute-names
  100. (cons (intern (bbdb-phone-location phone))
  101. (bbdb-phone-string phone))
  102. (cons 'phones (format "%s: %s"
  103. (bbdb-phone-location phone)
  104. (bbdb-phone-string phone))))))
  105. (bbdb-record-phones record)))
  106. (defun eudc-bbdb-extract-addresses (record)
  107. (let (s c val)
  108. (mapcar (lambda (address)
  109. (setq c (bbdb-address-streets address))
  110. (dotimes (n 3)
  111. (unless (zerop (length (setq s (nth n c))))
  112. (setq val (concat val s "\n"))))
  113. (setq c (bbdb-address-city address)
  114. s (bbdb-address-state address))
  115. (setq val (concat val
  116. (if (and (> (length c) 0) (> (length s) 0))
  117. (concat c ", " s)
  118. c)
  119. " "
  120. (bbdb-address-zip address)))
  121. (if eudc-bbdb-use-locations-as-attribute-names
  122. (cons (intern (bbdb-address-location address)) val)
  123. (cons 'addresses (concat (bbdb-address-location address)
  124. "\n" val))))
  125. (bbdb-record-addresses record))))
  126. (defun eudc-bbdb-format-record-as-result (record)
  127. "Format the BBDB RECORD as a EUDC query result record.
  128. The record is filtered according to `eudc-bbdb-current-return-attributes'"
  129. (let ((attrs (or eudc-bbdb-current-return-attributes
  130. '(firstname lastname aka company phones addresses net notes)))
  131. attr
  132. eudc-rec
  133. val)
  134. (while (prog1
  135. (setq attr (car attrs))
  136. (setq attrs (cdr attrs)))
  137. (cond
  138. ((eq attr 'phones)
  139. (setq val (eudc-bbdb-extract-phones record)))
  140. ((eq attr 'addresses)
  141. (setq val (eudc-bbdb-extract-addresses record)))
  142. ((memq attr '(firstname lastname aka company net notes))
  143. (setq val (eval
  144. (list (intern
  145. (concat "bbdb-record-"
  146. (symbol-name attr)))
  147. 'record))))
  148. (t
  149. (setq val "Unknown BBDB attribute")))
  150. (if val
  151. (cond
  152. ((memq attr '(phones addresses))
  153. (setq eudc-rec (append val eudc-rec)))
  154. ((and (listp val)
  155. (= 1 (length val)))
  156. (setq eudc-rec (cons (cons attr (car val)) eudc-rec)))
  157. ((> (length val) 0)
  158. (setq eudc-rec (cons (cons attr val) eudc-rec)))
  159. (t
  160. (error "Unexpected attribute value")))))
  161. (nreverse eudc-rec)))
  162. (defun eudc-bbdb-query-internal (query &optional return-attrs)
  163. "Query BBDB with QUERY.
  164. QUERY is a list of cons cells (ATTR . VALUE) where ATTRs should be valid
  165. BBDB attribute names.
  166. RETURN-ATTRS is a list of attributes to return, defaulting to
  167. `eudc-default-return-attributes'."
  168. (let ((eudc-bbdb-current-query query)
  169. (eudc-bbdb-current-return-attributes return-attrs)
  170. (query-attrs (eudc-bbdb-format-query query))
  171. bbdb-attrs
  172. (records (bbdb-records))
  173. result
  174. filtered)
  175. ;; BBDB ORs its query attributes while EUDC ANDs them, hence we need to
  176. ;; call bbdb-search iteratively on the returned records for each of the
  177. ;; requested attributes
  178. (while (and records (> (length query-attrs) 0))
  179. (setq bbdb-attrs (append bbdb-attrs (list (car query-attrs))))
  180. (if (car query-attrs)
  181. (setq records (eval `(bbdb-search ,(quote records) ,@bbdb-attrs))))
  182. (setq query-attrs (cdr query-attrs)))
  183. (mapc (function
  184. (lambda (record)
  185. (setq filtered (eudc-filter-duplicate-attributes record))
  186. ;; If there were duplicate attributes reverse the order of the
  187. ;; record so the unique attributes appear first
  188. (if (> (length filtered) 1)
  189. (setq filtered (mapcar (function
  190. (lambda (rec)
  191. (reverse rec)))
  192. filtered)))
  193. (setq result (append result filtered))))
  194. (delq nil
  195. (mapcar 'eudc-bbdb-format-record-as-result
  196. (delq nil
  197. (mapcar 'eudc-bbdb-filter-non-matching-record
  198. records)))))
  199. result))
  200. ;;}}}
  201. ;;{{{ High-level interfaces (interactive functions)
  202. (defun eudc-bbdb-set-server (dummy)
  203. "Set the EUDC server to BBDB."
  204. (interactive)
  205. (eudc-set-server dummy 'bbdb)
  206. (message "BBDB server selected"))
  207. ;;}}}
  208. (eudc-register-protocol 'bbdb)
  209. (provide 'eudcb-bbdb)
  210. ;;; eudcb-bbdb.el ends here