Newer
Older
;;; eudc.el --- Emacs Unified Directory Client - CCSO PH/QI Support
;; Copyright (C) 1998 Free Software Foundation, Inc.
;; Author: Oscar Figueiredo <Oscar.Figueiredo@epfl.ch>
;; Maintainer: Oscar Figueiredo <Oscar.Figueiredo@epfl.ch>
;; Created: Feb 1998
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
;; Keywords: help
;; This file is part of XEmacs
;; XEmacs 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.
;; XEmacs 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 XEmacs; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;; This library provides specific CCSO PH/QI protocol support for the
;; Emacs Unified Directory Client library.
;;; Installation:
;; Install EUDC first. See EUDC documentation.
;; This library runs under XEmacs 20 and under Emacs 19.34 and above
;;; Usage:
;;; Code:
(require 'eudc)
;;{{{ Package customization variables
(defgroup eudc-ph nil
"Emacs Unified Directory Client - CCSO PH/QI Support"
:group 'comm)
(defcustom eudc-ph-bbdb-conversion-alist
'((name . name)
(net . email)
(address . (eudc-bbdbify-address address "Address"))
(phone . ((eudc-bbdbify-phone phone "Phone")
(eudc-bbdbify-phone office_phone "Office Phone"))))
"*A mapping from BBDB to PH/QI fields.
This is a list of cons cells (BBDB-FIELD . SPEC-OR-LIST) where
BBDB-FIELD is the name of a field that must be defined in your BBDB
environment (standard field names are `name', `company', `net', `phone',
`address' and `notes'). SPEC-OR-LIST is either a single SPEC or a list
of SPECs. Lists of specs are valid only for the `phone' and `address'
BBDB fields. SPECs are sexps which are evaluated:
a string evaluates to itself,
a symbol evaluates to the symbol value. Symbols naming PH/QI fields
present in the record evaluate to the value of the field in the record,
a form is evaluated as a function. The argument list may contain PH/QI
field names which eval to the corresponding values in the
record. The form evaluation should return something appropriate for
the particular BBDB-FIELD (see `bbdb-create-internal').
`eudc-bbdbify-phone' and `eudc-bbdbify-address' are provided as convenience
functions to parse phones and addresses."
:tag "BBDB to CCSO Field Name Mapping"
:type '(repeat (cons :tag "Field Name"
(symbol :tag "BBDB Field")
(sexp :tag "Conversion Spec")))
:group 'eudc-ph)
;;}}}
;;{{{ Internal cooking
(defconst eudc-ph-protocol-locals
'((cons eudc-bbdb-conversion-alist . eudc-ph-bbdb-conversion-alist)
(eudc-query-function . eudc-ph-query-internal)
(eudc-list-attributes-function . eudc-ph-get-field-list))
"CCSO PH/QI protocol specific values of EUDC variables.
This should be an alist of the form (EUDC-VAR . PH-VAR) where
EUDC-VAR is the name of a EUDC variable and PH-VAR is the
corresponding PH-specific variable.")
(defvar eudc-ph-process-buffer nil)
(defvar eudc-ph-read-point)
(defconst eudc-ph-default-server-port 105
"Default TCP port for CCSO PH/QI directory services.")
(defun eudc-ph-query-internal (query &optional return-fields)
"Query the PH/QI server with QUERY.
QUERY can be a string NAME or a list made of strings NAME
and/or cons cells (KEY . VALUE) where KEYs should be valid
CCSO database keys. NAME is equivalent to (DEFAULT . NAME),
where DEFAULT is the default key of the database.
RETURN-FIELDS is a list of database fields to return,
defaulting to `eudc-default-return-attributes'."
(let (request)
(if (null return-fields)
(setq return-fields eudc-default-return-attributes))
(setq request
(concat "query "
(if (stringp query)
query
(mapconcat (function (lambda (elt)
(if (stringp elt) elt)
(format "%s=%s" (car elt) (cdr elt))))
query
" "))
(if return-fields
(concat " return " (mapconcat 'symbol-name return-fields " ")))))
(and (> (length request) 6)
(eudc-ph-do-request request)
(eudc-ph-parse-query-result return-fields))))
(defun eudc-ph-get-field-list (full-records)
"Return a list of valid field names for the current server.
If FULL-RECORDS is non-nil, full records including field description
are returned"
(if full-records
(eudc-ph-parse-query-result)
(mapcar 'eudc-caar (eudc-ph-parse-query-result))))
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
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
(defun eudc-ph-parse-query-result (&optional fields)
"Return a list of alists of key/values from in `eudc-ph-process-buffer'.
Fields not in FIELDS are discarded."
(let (record
records
line-regexp
current-key
key
value
ignore)
(save-excursion
(message "Parsing results...")
(set-buffer eudc-ph-process-buffer)
(goto-char (point-min))
(while (re-search-forward "^\\(-[0-9]+\\):\\([0-9]+\\):" nil t)
(catch 'ignore
(setq line-regexp (concat "^\\(-[0-9]+\\):" (match-string 2) ":[ \t]*\\([-a-zA-Z_]*\\)?:[ \t]*\\(.*\\)$"))
(beginning-of-line)
(setq record nil
ignore nil
current-key nil)
(while (re-search-forward line-regexp nil t)
(catch 'skip-line
(if (string= "-508" (match-string 1))
;; A field is missing in this entry. Skip it or skip the
;; whole record (see `eudc-strict-return-matches')
(if (not eudc-strict-return-matches)
(throw 'skip-line t)
(while (re-search-forward line-regexp nil t))
(setq ignore t)
(throw 'ignore t)))
(setq key (and (not (string= (match-string 2) ""))
(intern (match-string 2)))
value (match-string 3))
(if (and current-key
(eq key current-key))
(setq key nil)
(setq current-key key))
(if (or (null fields)
(memq 'all fields)
(memq current-key fields))
(if key
(setq record (cons (cons key value) record)) ; New key
(setcdr (car record) (if (listp (eudc-cdar record))
(append (eudc-cdar record) (list value))
(list (eudc-cdar record) value))))))))
(and (not ignore)
(or (null fields)
(memq 'all fields)
(setq record (nreverse record)))
(setq record (if (not (eq 'list eudc-duplicate-attribute-handling-method))
(eudc-filter-duplicate-attributes record)
(list record)))
(setq records (append record records))))
)
(message "Done")
records)
)
(defun eudc-ph-do-request (request)
"Send REQUEST to the server.
Wait for response and return the buffer containing it."
(let (process
buffer)
(unwind-protect
(progn
(message "Contacting server...")
(setq process (eudc-ph-open-session))
(if process
(save-excursion
(set-buffer (setq buffer (process-buffer process)))
(eudc-ph-send-command process request)
(message "Request sent, waiting for reply...")
(eudc-ph-read-response process))))
(if process
(eudc-ph-close-session process)))
buffer))
(defun eudc-ph-open-session (&optional server)
"Open a connection to the given CCSO/QI SERVER.
SERVER is either a string naming the server or a list (NAME PORT)."
(let (process
host
port)
(catch 'done
(if (null server)
(setq server (or eudc-server
(call-interactively 'eudc-ph-set-server))))
(string-match "\\(.*\\)\\(:\\(.*\\)\\)?" server)
(setq host (match-string 1 server))
(setq port (or (match-string 3 server)
eudc-ph-default-server-port))
(setq eudc-ph-process-buffer (get-buffer-create (format " *PH-%s*" host)))
(save-excursion
(set-buffer eudc-ph-process-buffer)
(erase-buffer)
(setq eudc-ph-read-point (point))
(and eudc-xemacs-mule-p
(set-buffer-file-coding-system 'binary t)))
(setq process (open-network-stream "ph" eudc-ph-process-buffer host port))
(if (null process)
(throw 'done nil))
(process-kill-without-query process)
process)))
(defun eudc-ph-close-session (process)
(save-excursion
(set-buffer (process-buffer process))
(eudc-ph-send-command process "quit")
(eudc-ph-read-response process)
(if (fboundp 'add-async-timeout)
(add-async-timeout 10 'delete-process process)
(run-at-time 2 nil 'delete-process process))))
(defun eudc-ph-send-command (process command)
(goto-char (point-max))
(process-send-string process command)
(process-send-string process "\r\n")
)
(defun eudc-ph-read-response (process &optional return-response)
"Read a response from the PH/QI query process PROCESS.
Returns nil if response starts with an error code. If the
response is successful the return code or the reponse itself is returned
depending on RETURN-RESPONSE."
(let ((case-fold-search nil)
return-code
match-end)
(goto-char eudc-ph-read-point)
;; CCSO protocol : response complete if status >= 200
(while (not (re-search-forward "^\\(^[2-5].*\\):.*\n" nil t))
(accept-process-output process)
(goto-char eudc-ph-read-point))
(setq match-end (point))
(goto-char eudc-ph-read-point)
(if (and (setq return-code (match-string 1))
(setq return-code (string-to-number return-code))
(>= (abs return-code) 300))
(progn (setq eudc-ph-read-point match-end) nil)
(setq eudc-ph-read-point match-end)
(if return-response
(buffer-substring (point) match-end)
return-code))))
;;}}}
;;{{{ High-level interfaces (interactive functions)
(defun eudc-ph-customize ()
"Customize the EUDC PH support."
(interactive)
(customize-group 'eudc-ph))
(defun eudc-ph-set-server (server)
"Set the PH server to SERVER."
(interactive "sNew PH/QI Server: ")
(message "Selected PH/QI server is now %s" server)
(eudc-set-server server 'ph))
;;}}}
(eudc-register-protocol 'ph eudc-ph-protocol-locals)
(provide 'eudc-ph)
;;; eudc-ph.el ends here