diff options
| author | Stefan Monnier | 2015-01-23 17:20:19 -0500 |
|---|---|---|
| committer | Stefan Monnier | 2015-01-23 17:20:19 -0500 |
| commit | ac5475dacb20d240db27d56199910d8a6fcc90e8 (patch) | |
| tree | 2f18fd1d40f2ba65122636bf81730ccb614a166b | |
| parent | fd62486e819056bc9d0f00c09731a45a7f837997 (diff) | |
| parent | e56e1b924d23a358a14ab069237db35a1c76d6a9 (diff) | |
| download | emacs-ac5475dacb20d240db27d56199910d8a6fcc90e8.tar.gz emacs-ac5475dacb20d240db27d56199910d8a6fcc90e8.zip | |
lisp/net/{eudc,ldap}: Merge branch streamline-eudc-configuration
| -rw-r--r-- | doc/misc/ChangeLog | 9 | ||||
| -rw-r--r-- | doc/misc/eudc.texi | 130 | ||||
| -rw-r--r-- | lisp/ChangeLog | 85 | ||||
| -rw-r--r-- | lisp/net/eudc-vars.el | 97 | ||||
| -rw-r--r-- | lisp/net/eudc.el | 71 | ||||
| -rw-r--r-- | lisp/net/eudcb-ldap.el | 29 | ||||
| -rw-r--r-- | lisp/net/ldap.el | 136 |
7 files changed, 425 insertions, 132 deletions
diff --git a/doc/misc/ChangeLog b/doc/misc/ChangeLog index 2baa13cea8c..e75589f92ec 100644 --- a/doc/misc/ChangeLog +++ b/doc/misc/ChangeLog | |||
| @@ -1,3 +1,8 @@ | |||
| 1 | 2015-01-23 Thomas Fitzsimmons <fitzsim@fitzsim.org> | ||
| 2 | |||
| 3 | * eudc.texi (LDAP Configuration): Rename from LDAP Requirements | ||
| 4 | and provide configuration examples. | ||
| 5 | |||
| 1 | 2015-01-17 Stefan Monnier <monnier@iro.umontreal.ca> | 6 | 2015-01-17 Stefan Monnier <monnier@iro.umontreal.ca> |
| 2 | 7 | ||
| 3 | * eieio.texi (Slot Options): Document :protection as unsupported. | 8 | * eieio.texi (Slot Options): Document :protection as unsupported. |
| @@ -28,8 +33,8 @@ | |||
| 28 | 33 | ||
| 29 | 2014-12-18 Eric Abrahamsen <eric@ericabrahamsen.net> | 34 | 2014-12-18 Eric Abrahamsen <eric@ericabrahamsen.net> |
| 30 | 35 | ||
| 31 | * gnus.texi (Gnus Registry Setup): Explain pruning changes. Mention | 36 | * gnus.texi (Gnus Registry Setup): Explain pruning changes. |
| 32 | gnus-registry-prune-factor. Explain sorting changes and | 37 | Mention gnus-registry-prune-factor. Explain sorting changes and |
| 33 | gnus-registry-default-sort-function. Correct file extension. | 38 | gnus-registry-default-sort-function. Correct file extension. |
| 34 | 39 | ||
| 35 | 2014-12-17 Jay Belanger <jay.p.belanger@gmail.com> | 40 | 2014-12-17 Jay Belanger <jay.p.belanger@gmail.com> |
diff --git a/doc/misc/eudc.texi b/doc/misc/eudc.texi index b5a4e3aae40..9757c82fe7e 100644 --- a/doc/misc/eudc.texi +++ b/doc/misc/eudc.texi | |||
| @@ -137,7 +137,7 @@ location, etc@enddots{} More information about LDAP can be found at | |||
| 137 | @url{http://www.openldap.org/}. | 137 | @url{http://www.openldap.org/}. |
| 138 | 138 | ||
| 139 | EUDC requires external support to access LDAP directory servers | 139 | EUDC requires external support to access LDAP directory servers |
| 140 | (@pxref{LDAP Requirements}) | 140 | (@pxref{LDAP Configuration}) |
| 141 | 141 | ||
| 142 | 142 | ||
| 143 | @node CCSO PH/QI | 143 | @node CCSO PH/QI |
| @@ -213,17 +213,131 @@ email composition buffers (@pxref{Inline Query Expansion}) | |||
| 213 | @end lisp | 213 | @end lisp |
| 214 | 214 | ||
| 215 | @menu | 215 | @menu |
| 216 | * LDAP Requirements:: EUDC needs external support for LDAP | 216 | * LDAP Configuration:: EUDC needs external support for LDAP |
| 217 | @end menu | 217 | @end menu |
| 218 | 218 | ||
| 219 | @node LDAP Requirements | 219 | @node LDAP Configuration |
| 220 | @section LDAP Requirements | 220 | @section LDAP Configuration |
| 221 | 221 | ||
| 222 | LDAP support is added by means of @file{ldap.el}, which is part of Emacs. | 222 | LDAP support is added by means of @file{ldap.el}, which is part of |
| 223 | @file{ldap.el} needs an external command line utility named | 223 | Emacs. @file{ldap.el} needs an external command line utility named |
| 224 | @file{ldapsearch}, available as part of Open LDAP | 224 | @file{ldapsearch}, available as part of OpenLDAP |
| 225 | (@url{http://www.openldap.org/}). | 225 | (@url{http://www.openldap.org/}). The configurations in this section |
| 226 | were tested with OpenLDAP 2.4.23. | ||
| 226 | 227 | ||
| 228 | The following examples use a base of | ||
| 229 | @code{ou=people,dc=example,dc=com} and the host name | ||
| 230 | @code{directory.example.com}, a server that supports LDAP-over-SSL | ||
| 231 | (the @code{ldaps} protocol, with default port @code{636}) and which | ||
| 232 | requires authentication by the user @code{emacsuser} with password | ||
| 233 | @code{s3cr3t}. | ||
| 234 | |||
| 235 | These configurations are meant to be self-contained; that is, each | ||
| 236 | provides everything required for sensible TAB-completion of email | ||
| 237 | fields. BBDB lookups are attempted first; if a matching BBDB entry is | ||
| 238 | found then EUDC will not attempt any LDAP lookups. | ||
| 239 | |||
| 240 | Wildcard LDAP lookups are supported using the @code{*} character. For | ||
| 241 | example, attempting to TAB-complete the following: | ||
| 242 | |||
| 243 | @example | ||
| 244 | To: * Smith | ||
| 245 | @end example | ||
| 246 | |||
| 247 | will return all LDAP entries with surnames that begin with | ||
| 248 | @code{Smith}. In every LDAP query it makes, EUDC implicitly appends | ||
| 249 | the wildcard character to the end of the last word. | ||
| 250 | |||
| 251 | @subsection Emacs-only Configuration | ||
| 252 | |||
| 253 | Emacs can pass most required configuration options via the | ||
| 254 | @file{ldapsearch} command-line. One exception is certificate | ||
| 255 | configuration for LDAP-over-SSL, which must be specified in | ||
| 256 | @file{/etc/openldap/ldap.conf}. On systems that provide such | ||
| 257 | certificates as part of the @code{OpenLDAP} installation, this can be | ||
| 258 | as simple as one line: | ||
| 259 | |||
| 260 | @example | ||
| 261 | TLS_CACERTDIR /etc/openldap/certs | ||
| 262 | @end example | ||
| 263 | |||
| 264 | In @file{.emacs}, these expressions suffice to configure EUDC for | ||
| 265 | LDAP: | ||
| 266 | |||
| 267 | @lisp | ||
| 268 | (eval-after-load "message" | ||
| 269 | '(define-key message-mode-map (kbd "TAB") 'eudc-expand-inline)) | ||
| 270 | (customize-set-variable 'eudc-server-hotlist | ||
| 271 | '(("" . bbdb) | ||
| 272 | ("ldaps://directory.example.com" . ldap))) | ||
| 273 | (customize-set-variable 'ldap-host-parameters-alist | ||
| 274 | '(("ldaps://directory.example.com" | ||
| 275 | base "ou=people,dc=example,dc=com" | ||
| 276 | binddn "example\\emacsuser" | ||
| 277 | passwd ldap-password-read))) | ||
| 278 | @end lisp | ||
| 279 | |||
| 280 | Specifying the function @code{ldap-password-read} for @code{passwd} | ||
| 281 | will cause Emacs to prompt interactively for the password. The | ||
| 282 | password will then be validated and cached, unless | ||
| 283 | @code{password-cache} is nil. You can customize | ||
| 284 | @code{password-cache-expiry} to control the duration for which the | ||
| 285 | password is cached. If you want to clear the cache, call | ||
| 286 | @code{password-reset}. | ||
| 287 | |||
| 288 | @subsection External Configuration | ||
| 289 | |||
| 290 | Your system may already be configured for a default LDAP server. For | ||
| 291 | example, @file{/etc/openldap/ldap.conf} might contain: | ||
| 292 | |||
| 293 | @example | ||
| 294 | BASE ou=people,dc=example,dc=com | ||
| 295 | URI ldaps://directory.example.com | ||
| 296 | TLS_CACERTDIR /etc/openldap/certs | ||
| 297 | @end example | ||
| 298 | |||
| 299 | To authenticate, the @dfn{bind distinguished name (binddn)} is | ||
| 300 | required, in this case, @code{example\emacsuser}, along with the | ||
| 301 | password. These can be specified in @file{~/.authinfo.gpg} with the | ||
| 302 | following line: | ||
| 303 | |||
| 304 | @example | ||
| 305 | machine ldaps://directory.example.com binddn example\emacsuser password s3cr3t | ||
| 306 | @end example | ||
| 307 | |||
| 308 | Then in the @file{.emacs} init file, these expressions suffice to | ||
| 309 | configure EUDC for LDAP: | ||
| 310 | |||
| 311 | @lisp | ||
| 312 | (eval-after-load "message" | ||
| 313 | '(define-key message-mode-map (kbd "TAB") 'eudc-expand-inline)) | ||
| 314 | (customize-set-variable 'eudc-server-hotlist | ||
| 315 | '(("" . bbdb) | ||
| 316 | ("ldaps://directory.example.com" . ldap))) | ||
| 317 | (customize-set-variable 'ldap-host-parameters-alist | ||
| 318 | '(("ldaps://directory.example.com" | ||
| 319 | auth-source t))) | ||
| 320 | @end lisp | ||
| 321 | |||
| 322 | For this example where we only care about one server, the server name | ||
| 323 | can be omitted in @file{~/.authinfo.gpg} and @file{.emacs}, in which | ||
| 324 | case @file{ldapsearch} defaults to the host name in | ||
| 325 | @file{/etc/openldap/ldap.conf}. | ||
| 326 | |||
| 327 | The @file{~/.authinfo.gpg} line becomes: | ||
| 328 | |||
| 329 | @example | ||
| 330 | binddn example\emacsuser password s3cr3t | ||
| 331 | @end example | ||
| 332 | |||
| 333 | and the @file{.emacs} expressions become: | ||
| 334 | |||
| 335 | @lisp | ||
| 336 | (eval-after-load "message" | ||
| 337 | '(define-key message-mode-map (kbd "TAB") 'eudc-expand-inline)) | ||
| 338 | (customize-set-variable 'eudc-server-hotlist '(("" . bbdb) ("" . ldap))) | ||
| 339 | (customize-set-variable 'ldap-host-parameters-alist '(("" auth-source t))) | ||
| 340 | @end lisp | ||
| 227 | 341 | ||
| 228 | @node Usage | 342 | @node Usage |
| 229 | @chapter Usage | 343 | @chapter Usage |
diff --git a/lisp/ChangeLog b/lisp/ChangeLog index ed4e1ab1e15..15518a73eb1 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog | |||
| @@ -1,3 +1,88 @@ | |||
| 1 | 2015-01-23 Thomas Fitzsimmons <fitzsim@fitzsim.org> | ||
| 2 | |||
| 3 | * net/ldap.el (ldap-search-internal): Mention binddn in invalid | ||
| 4 | credentials error message. | ||
| 5 | |||
| 6 | 2015-01-23 Thomas Fitzsimmons <fitzsim@fitzsim.org> | ||
| 7 | |||
| 8 | * net/ldap.el (ldap-password-read): Validate password before | ||
| 9 | caching it. | ||
| 10 | (ldap-search-internal): Handle ldapsearch error conditions. | ||
| 11 | |||
| 12 | 2015-01-23 Thomas Fitzsimmons <fitzsim@fitzsim.org> | ||
| 13 | |||
| 14 | * net/ldap.el (ldap-password-read): Handle password-cache being nil. | ||
| 15 | |||
| 16 | 2015-01-23 Thomas Fitzsimmons <fitzsim@fitzsim.org> | ||
| 17 | |||
| 18 | * net/eudc.el (eudc-expand-inline): Always restore former server | ||
| 19 | and protocol. | ||
| 20 | |||
| 21 | 2015-01-23 Thomas Fitzsimmons <fitzsim@fitzsim.org> | ||
| 22 | |||
| 23 | * net/eudcb-ldap.el: Don't nag the user in case a default base is | ||
| 24 | provided by the LDAP system configuration file. | ||
| 25 | |||
| 26 | 2015-01-23 Thomas Fitzsimmons <fitzsim@fitzsim.org> | ||
| 27 | |||
| 28 | * net/eudc.el (eudc-format-query): Preserve the | ||
| 29 | eudc-inline-query-format ordering of attributes in the returned list. | ||
| 30 | * net/eudcb-ldap.el (eudc-ldap-format-query-as-rfc1558): | ||
| 31 | Append the LDAP wildcard character to the last attribute value. | ||
| 32 | |||
| 33 | 2015-01-23 Thomas Fitzsimmons <fitzsim@fitzsim.org> | ||
| 34 | |||
| 35 | * net/eudcb-ldap.el (eudc-ldap-cleanup-record-simple): | ||
| 36 | Downcase field names of LDAP results. | ||
| 37 | (eudc-ldap-cleanup-record-filtering-addresses): Likewise. | ||
| 38 | |||
| 39 | 2015-01-23 Thomas Fitzsimmons <fitzsim@fitzsim.org> | ||
| 40 | |||
| 41 | * net/ldap.el (ldap-ldapsearch-password-prompt): New defcustom. | ||
| 42 | (ldap-search-internal): Send password to ldapsearch through a pipe | ||
| 43 | instead of via the command line. | ||
| 44 | |||
| 45 | 2015-01-23 Thomas Fitzsimmons <fitzsim@fitzsim.org> | ||
| 46 | |||
| 47 | * net/ldap.el: Require password-cache. | ||
| 48 | (ldap-password-read): New function. | ||
| 49 | (ldap-search-internal): Call ldap-password-read when it is | ||
| 50 | configured to be called. | ||
| 51 | |||
| 52 | 2015-01-23 Thomas Fitzsimmons <fitzsim@fitzsim.org> | ||
| 53 | |||
| 54 | * net/eudc-vars.el (eudc-expansion-overwrites-query): | ||
| 55 | Change default to nil. | ||
| 56 | |||
| 57 | 2015-01-23 Thomas Fitzsimmons <fitzsim@fitzsim.org> | ||
| 58 | |||
| 59 | * net/eudc.el (eudc-expand-inline): Ignore text properties of | ||
| 60 | string-to-expand. | ||
| 61 | |||
| 62 | 2015-01-23 Thomas Fitzsimmons <fitzsim@fitzsim.org> | ||
| 63 | |||
| 64 | * net/eudc-vars.el (eudc-inline-expansion-format): Default to a | ||
| 65 | format that includes first name and surname. | ||
| 66 | |||
| 67 | 2015-01-23 Thomas Fitzsimmons <fitzsim@fitzsim.org> | ||
| 68 | |||
| 69 | * net/eudc-vars.el (eudc-inline-query-format): Change default to | ||
| 70 | query email and first name instead of surname. | ||
| 71 | |||
| 72 | 2015-01-23 Thomas Fitzsimmons <fitzsim@fitzsim.org> | ||
| 73 | |||
| 74 | * net/ldap.el (ldap-search-internal): Support new-style LDAP URIs. | ||
| 75 | |||
| 76 | 2015-01-23 Thomas Fitzsimmons <fitzsim@fitzsim.org> | ||
| 77 | |||
| 78 | * net/eudc-vars.el (eudc-server): Adjust docstring to mention | ||
| 79 | eudc-server-hotlist. | ||
| 80 | (eudc-server-hotlist): Move from eudc.el and make defcustom. | ||
| 81 | * net/eudc.el (eudc-server-hotlist): Move to eudc-vars.el. | ||
| 82 | (eudc-set-server): Allow setting protocol to nil. | ||
| 83 | (eudc-expand-inline): Support hotlist-only expansions when server | ||
| 84 | is not set. | ||
| 85 | |||
| 1 | 2015-01-23 Stefan Monnier <monnier@iro.umontreal.ca> | 86 | 2015-01-23 Stefan Monnier <monnier@iro.umontreal.ca> |
| 2 | 87 | ||
| 3 | * emacs-lisp/cl-generic.el (cl-no-primary-method): New fun and error. | 88 | * emacs-lisp/cl-generic.el (cl-no-primary-method): New fun and error. |
diff --git a/lisp/net/eudc-vars.el b/lisp/net/eudc-vars.el index 6bc0337f958..29ddf613376 100644 --- a/lisp/net/eudc-vars.el +++ b/lisp/net/eudc-vars.el | |||
| @@ -41,14 +41,36 @@ | |||
| 41 | "The name or IP address of the directory server. | 41 | "The name or IP address of the directory server. |
| 42 | A port number may be specified by appending a colon and a | 42 | A port number may be specified by appending a colon and a |
| 43 | number to the name of the server. Use `localhost' if the directory | 43 | number to the name of the server. Use `localhost' if the directory |
| 44 | server resides on your computer (BBDB backend)." | 44 | server resides on your computer (BBDB backend). |
| 45 | :type '(choice (string :tag "Server") (const :tag "None" nil)) | 45 | |
| 46 | :group 'eudc) | 46 | To specify multiple servers, customize eudc-server-hotlist |
| 47 | instead." | ||
| 48 | :type '(choice (string :tag "Server") (const :tag "None" nil))) | ||
| 47 | 49 | ||
| 48 | ;; Known protocols (used in completion) | 50 | ;; Known protocols (used in completion) |
| 49 | ;; Not to be mistaken with `eudc-supported-protocols' | 51 | ;; Not to be mistaken with `eudc-supported-protocols' |
| 50 | (defvar eudc-known-protocols '(bbdb ph ldap)) | 52 | (defvar eudc-known-protocols '(bbdb ph ldap)) |
| 51 | 53 | ||
| 54 | (defcustom eudc-server-hotlist nil | ||
| 55 | "Directory servers to query. | ||
| 56 | This is an alist of the form (SERVER . PROTOCOL). SERVER is the | ||
| 57 | host name or URI of the server, PROTOCOL is a symbol representing | ||
| 58 | the EUDC backend with which to access the server. | ||
| 59 | |||
| 60 | The BBDB backend ignores SERVER; `localhost' can be used as a | ||
| 61 | placeholder string." | ||
| 62 | :tag "Directory Servers to Query" | ||
| 63 | :type `(repeat (cons :tag "Directory Server" | ||
| 64 | (string :tag "Server Host Name or URI") | ||
| 65 | (choice :tag "Protocol" | ||
| 66 | :menu-tag "Protocol" | ||
| 67 | ,@(mapcar (lambda (s) | ||
| 68 | (list 'const | ||
| 69 | ':tag (symbol-name s) s)) | ||
| 70 | eudc-known-protocols) | ||
| 71 | (const :tag "None" nil)))) | ||
| 72 | :version "25.1") | ||
| 73 | |||
| 52 | (defvar eudc-supported-protocols nil | 74 | (defvar eudc-supported-protocols nil |
| 53 | "Protocols currently supported by EUDC. | 75 | "Protocols currently supported by EUDC. |
| 54 | This variable is updated when protocol-specific libraries | 76 | This variable is updated when protocol-specific libraries |
| @@ -61,15 +83,13 @@ Supported protocols are specified by `eudc-supported-protocols'." | |||
| 61 | ,@(mapcar (lambda (s) | 83 | ,@(mapcar (lambda (s) |
| 62 | (list 'const ':tag (symbol-name s) s)) | 84 | (list 'const ':tag (symbol-name s) s)) |
| 63 | eudc-known-protocols) | 85 | eudc-known-protocols) |
| 64 | (const :tag "None" nil)) | 86 | (const :tag "None" nil))) |
| 65 | :group 'eudc) | ||
| 66 | 87 | ||
| 67 | 88 | ||
| 68 | (defcustom eudc-strict-return-matches t | 89 | (defcustom eudc-strict-return-matches t |
| 69 | "Ignore or allow entries not containing all requested return attributes. | 90 | "Ignore or allow entries not containing all requested return attributes. |
| 70 | If non-nil, such entries are ignored." | 91 | If non-nil, such entries are ignored." |
| 71 | :type 'boolean | 92 | :type 'boolean) |
| 72 | :group 'eudc) | ||
| 73 | 93 | ||
| 74 | (defcustom eudc-default-return-attributes nil | 94 | (defcustom eudc-default-return-attributes nil |
| 75 | "A list of default attributes to extract from directory entries. | 95 | "A list of default attributes to extract from directory entries. |
| @@ -82,8 +102,7 @@ server." | |||
| 82 | (repeat :menu-tag "Attribute list" | 102 | (repeat :menu-tag "Attribute list" |
| 83 | :tag "Attribute name" | 103 | :tag "Attribute name" |
| 84 | :value (nil) | 104 | :value (nil) |
| 85 | (symbol :tag "Attribute name"))) | 105 | (symbol :tag "Attribute name")))) |
| 86 | :group 'eudc) | ||
| 87 | 106 | ||
| 88 | (defcustom eudc-multiple-match-handling-method 'select | 107 | (defcustom eudc-multiple-match-handling-method 'select |
| 89 | "What to do when multiple entries match an inline expansion query. | 108 | "What to do when multiple entries match an inline expansion query. |
| @@ -102,8 +121,7 @@ Possible values are: | |||
| 102 | (const :menu-tag "Abort Operation" | 121 | (const :menu-tag "Abort Operation" |
| 103 | :tag "Abort Operation" abort) | 122 | :tag "Abort Operation" abort) |
| 104 | (const :menu-tag "Default (Use First)" | 123 | (const :menu-tag "Default (Use First)" |
| 105 | :tag "Default (Use First)" nil)) | 124 | :tag "Default (Use First)" nil))) |
| 106 | :group 'eudc) | ||
| 107 | 125 | ||
| 108 | (defcustom eudc-duplicate-attribute-handling-method '((email . duplicate)) | 126 | (defcustom eudc-duplicate-attribute-handling-method '((email . duplicate)) |
| 109 | "A method to handle entries containing duplicate attributes. | 127 | "A method to handle entries containing duplicate attributes. |
| @@ -130,10 +148,10 @@ different values." | |||
| 130 | (const :menu-tag "List" list) | 148 | (const :menu-tag "List" list) |
| 131 | (const :menu-tag "First" first) | 149 | (const :menu-tag "First" first) |
| 132 | (const :menu-tag "Concat" concat) | 150 | (const :menu-tag "Concat" concat) |
| 133 | (const :menu-tag "Duplicate" duplicate))))) | 151 | (const :menu-tag "Duplicate" duplicate)))))) |
| 134 | :group 'eudc) | ||
| 135 | 152 | ||
| 136 | (defcustom eudc-inline-query-format '((name) | 153 | (defcustom eudc-inline-query-format '((email) |
| 154 | (firstname) | ||
| 137 | (firstname name)) | 155 | (firstname name)) |
| 138 | "Format of an inline expansion query. | 156 | "Format of an inline expansion query. |
| 139 | This is a list of FORMATs. A FORMAT is itself a list of one or more | 157 | This is a list of FORMATs. A FORMAT is itself a list of one or more |
| @@ -160,14 +178,16 @@ must be set in a protocol/server-local fashion, see `eudc-server-set' and | |||
| 160 | (const :menu-tag "Email Address" :tag "Email Address" email) | 178 | (const :menu-tag "Email Address" :tag "Email Address" email) |
| 161 | (const :menu-tag "Phone" :tag "Phone" phone) | 179 | (const :menu-tag "Phone" :tag "Phone" phone) |
| 162 | (symbol :menu-tag "Other" :tag "Attribute name")))) | 180 | (symbol :menu-tag "Other" :tag "Attribute name")))) |
| 163 | :group 'eudc) | 181 | :version "25.1") |
| 164 | 182 | ||
| 165 | (defcustom eudc-expansion-overwrites-query t | 183 | ;; Default to nil so that the most common use of eudc-expand-inline, |
| 184 | ;; where replace is nil, does not affect the kill ring. | ||
| 185 | (defcustom eudc-expansion-overwrites-query nil | ||
| 166 | "If non-nil, expanding a query overwrites the query string." | 186 | "If non-nil, expanding a query overwrites the query string." |
| 167 | :type 'boolean | 187 | :type 'boolean |
| 168 | :group 'eudc) | 188 | :version "25.1") |
| 169 | 189 | ||
| 170 | (defcustom eudc-inline-expansion-format '("%s" email) | 190 | (defcustom eudc-inline-expansion-format '("%s %s <%s>" firstname name email) |
| 171 | "A list specifying the format of the expansion of inline queries. | 191 | "A list specifying the format of the expansion of inline queries. |
| 172 | This variable controls what `eudc-expand-inline' actually inserts in | 192 | This variable controls what `eudc-expand-inline' actually inserts in |
| 173 | the buffer. First element is a string passed to `format'. Remaining | 193 | the buffer. First element is a string passed to `format'. Remaining |
| @@ -185,7 +205,7 @@ are passed as additional arguments to `format'." | |||
| 185 | (const :menu-tag "Phone" :tag "Phone" phone) | 205 | (const :menu-tag "Phone" :tag "Phone" phone) |
| 186 | (symbol :menu-tag "Other") | 206 | (symbol :menu-tag "Other") |
| 187 | (symbol :tag "Attribute name")))) | 207 | (symbol :tag "Attribute name")))) |
| 188 | :group 'eudc) | 208 | :version "25.1") |
| 189 | 209 | ||
| 190 | (defcustom eudc-inline-expansion-servers 'server-then-hotlist | 210 | (defcustom eudc-inline-expansion-servers 'server-then-hotlist |
| 191 | "Which servers to contact for the expansion of inline queries. | 211 | "Which servers to contact for the expansion of inline queries. |
| @@ -198,8 +218,7 @@ Possible values are: | |||
| 198 | :menu-tag "Servers" | 218 | :menu-tag "Servers" |
| 199 | (const :menu-tag "Current server" current-server) | 219 | (const :menu-tag "Current server" current-server) |
| 200 | (const :menu-tag "Servers in the hotlist" hotlist) | 220 | (const :menu-tag "Servers in the hotlist" hotlist) |
| 201 | (const :menu-tag "Current server then hotlist" server-then-hotlist)) | 221 | (const :menu-tag "Current server then hotlist" server-then-hotlist))) |
| 202 | :group 'eudc) | ||
| 203 | 222 | ||
| 204 | (defcustom eudc-max-servers-to-query nil | 223 | (defcustom eudc-max-servers-to-query nil |
| 205 | "Maximum number of servers to query for an inline expansion. | 224 | "Maximum number of servers to query for an inline expansion. |
| @@ -213,8 +232,7 @@ If nil, query all servers available from `eudc-inline-expansion-servers'." | |||
| 213 | (const :menu-tag "3" 3) | 232 | (const :menu-tag "3" 3) |
| 214 | (const :menu-tag "4" 4) | 233 | (const :menu-tag "4" 4) |
| 215 | (const :menu-tag "5" 5) | 234 | (const :menu-tag "5" 5) |
| 216 | (integer :menu-tag "Set")) | 235 | (integer :menu-tag "Set"))) |
| 217 | :group 'eudc) | ||
| 218 | 236 | ||
| 219 | (defcustom eudc-query-form-attributes '(name firstname email phone) | 237 | (defcustom eudc-query-form-attributes '(name firstname email phone) |
| 220 | "A list of attributes presented in the query form." | 238 | "A list of attributes presented in the query form." |
| @@ -226,8 +244,7 @@ If nil, query all servers available from `eudc-inline-expansion-servers'." | |||
| 226 | (const :menu-tag "Surname" :tag "Surname" name) | 244 | (const :menu-tag "Surname" :tag "Surname" name) |
| 227 | (const :menu-tag "Email Address" :tag "Email Address" email) | 245 | (const :menu-tag "Email Address" :tag "Email Address" email) |
| 228 | (const :menu-tag "Phone" :tag "Phone" phone) | 246 | (const :menu-tag "Phone" :tag "Phone" phone) |
| 229 | (symbol :menu-tag "Other" :tag "Attribute name"))) | 247 | (symbol :menu-tag "Other" :tag "Attribute name")))) |
| 230 | :group 'eudc) | ||
| 231 | 248 | ||
| 232 | (defcustom eudc-user-attribute-names-alist '((url . "URL") | 249 | (defcustom eudc-user-attribute-names-alist '((url . "URL") |
| 233 | (callsign . "HAM Call Sign") | 250 | (callsign . "HAM Call Sign") |
| @@ -257,15 +274,13 @@ at `_' characters and capitalizing the individual words." | |||
| 257 | :tag "User-defined Names of Directory Attributes" | 274 | :tag "User-defined Names of Directory Attributes" |
| 258 | :type '(repeat (cons :tag "Field" | 275 | :type '(repeat (cons :tag "Field" |
| 259 | (symbol :tag "Directory attribute") | 276 | (symbol :tag "Directory attribute") |
| 260 | (string :tag "User friendly name "))) | 277 | (string :tag "User friendly name ")))) |
| 261 | :group 'eudc) | ||
| 262 | 278 | ||
| 263 | (defcustom eudc-use-raw-directory-names nil | 279 | (defcustom eudc-use-raw-directory-names nil |
| 264 | "If non-nil, use attributes names as defined in the directory. | 280 | "If non-nil, use attributes names as defined in the directory. |
| 265 | Otherwise, directory query/response forms display the user attribute | 281 | Otherwise, directory query/response forms display the user attribute |
| 266 | names defined in `eudc-user-attribute-names-alist'." | 282 | names defined in `eudc-user-attribute-names-alist'." |
| 267 | :type 'boolean | 283 | :type 'boolean) |
| 268 | :group 'eudc) | ||
| 269 | 284 | ||
| 270 | (defcustom eudc-attribute-display-method-alist nil | 285 | (defcustom eudc-attribute-display-method-alist nil |
| 271 | "An alist specifying methods to display attribute values. | 286 | "An alist specifying methods to display attribute values. |
| @@ -277,8 +292,7 @@ attribute values for display." | |||
| 277 | :tag "Attribute Decoding Functions" | 292 | :tag "Attribute Decoding Functions" |
| 278 | :type '(repeat (cons :tag "Attribute" | 293 | :type '(repeat (cons :tag "Attribute" |
| 279 | (symbol :tag "Name") | 294 | (symbol :tag "Name") |
| 280 | (symbol :tag "Display Function"))) | 295 | (symbol :tag "Display Function")))) |
| 281 | :group 'eudc) | ||
| 282 | 296 | ||
| 283 | (defcustom eudc-external-viewers '(("ImageMagick" "display" "-") | 297 | (defcustom eudc-external-viewers '(("ImageMagick" "display" "-") |
| 284 | ("ShowAudio" "showaudio")) | 298 | ("ShowAudio" "showaudio")) |
| @@ -295,18 +309,15 @@ arguments that should be passed to the program." | |||
| 295 | (repeat | 309 | (repeat |
| 296 | :tag "Arguments" | 310 | :tag "Arguments" |
| 297 | :inline t | 311 | :inline t |
| 298 | (string :tag "Argument")))) | 312 | (string :tag "Argument"))))) |
| 299 | :group 'eudc) | ||
| 300 | 313 | ||
| 301 | (defcustom eudc-options-file "~/.eudc-options" | 314 | (defcustom eudc-options-file "~/.eudc-options" |
| 302 | "A file where the `servers' hotlist is stored." | 315 | "A file where the `servers' hotlist is stored." |
| 303 | :type '(file :Tag "File Name:") | 316 | :type '(file :Tag "File Name:")) |
| 304 | :group 'eudc) | ||
| 305 | 317 | ||
| 306 | (defcustom eudc-mode-hook nil | 318 | (defcustom eudc-mode-hook nil |
| 307 | "Normal hook run on entry to EUDC mode." | 319 | "Normal hook run on entry to EUDC mode." |
| 308 | :type '(repeat (sexp :tag "Hook definition")) | 320 | :type 'hook) |
| 309 | :group 'eudc) | ||
| 310 | 321 | ||
| 311 | ;;}}} | 322 | ;;}}} |
| 312 | 323 | ||
| @@ -341,8 +352,7 @@ BBDB fields. SPECs are sexps which are evaluated: | |||
| 341 | :tag "BBDB to PH Field Name Mapping" | 352 | :tag "BBDB to PH Field Name Mapping" |
| 342 | :type '(repeat (cons :tag "Field Name" | 353 | :type '(repeat (cons :tag "Field Name" |
| 343 | (symbol :tag "BBDB Field") | 354 | (symbol :tag "BBDB Field") |
| 344 | (sexp :tag "Conversion Spec"))) | 355 | (sexp :tag "Conversion Spec")))) |
| 345 | :group 'eudc-ph) | ||
| 346 | 356 | ||
| 347 | ;;}}} | 357 | ;;}}} |
| 348 | 358 | ||
| @@ -376,8 +386,7 @@ BBDB fields. SPECs are sexps which are evaluated: | |||
| 376 | :tag "BBDB to LDAP Attribute Names Mapping" | 386 | :tag "BBDB to LDAP Attribute Names Mapping" |
| 377 | :type '(repeat (cons :tag "Field Name" | 387 | :type '(repeat (cons :tag "Field Name" |
| 378 | (symbol :tag "BBDB Field") | 388 | (symbol :tag "BBDB Field") |
| 379 | (sexp :tag "Conversion Spec"))) | 389 | (sexp :tag "Conversion Spec")))) |
| 380 | :group 'eudc-ldap) | ||
| 381 | 390 | ||
| 382 | ;;}}} | 391 | ;;}}} |
| 383 | 392 | ||
| @@ -391,14 +400,12 @@ BBDB fields. SPECs are sexps which are evaluated: | |||
| 391 | "If non-nil, BBDB address and phone locations are used as attribute names. | 400 | "If non-nil, BBDB address and phone locations are used as attribute names. |
| 392 | This has no effect on queries (you can't search for a specific location) | 401 | This has no effect on queries (you can't search for a specific location) |
| 393 | but influences the way records are displayed." | 402 | but influences the way records are displayed." |
| 394 | :type 'boolean | 403 | :type 'boolean) |
| 395 | :group 'eudc-bbdb) | ||
| 396 | 404 | ||
| 397 | (defcustom eudc-bbdb-enable-substring-matches t | 405 | (defcustom eudc-bbdb-enable-substring-matches t |
| 398 | "If non-nil, authorize substring match in the same way BBDB does. | 406 | "If non-nil, authorize substring match in the same way BBDB does. |
| 399 | Otherwise records must match queries exactly." | 407 | Otherwise records must match queries exactly." |
| 400 | :type 'boolean | 408 | :type 'boolean) |
| 401 | :group 'eudc-bbdb) | ||
| 402 | 409 | ||
| 403 | ;;}}} | 410 | ;;}}} |
| 404 | 411 | ||
diff --git a/lisp/net/eudc.el b/lisp/net/eudc.el index 0f2fc0be7bd..4dd80972e3f 100644 --- a/lisp/net/eudc.el +++ b/lisp/net/eudc.el | |||
| @@ -76,10 +76,6 @@ | |||
| 76 | 76 | ||
| 77 | (defvar mode-popup-menu) | 77 | (defvar mode-popup-menu) |
| 78 | 78 | ||
| 79 | ;; List of known servers | ||
| 80 | ;; Alist of (SERVER . PROTOCOL) | ||
| 81 | (defvar eudc-server-hotlist nil) | ||
| 82 | |||
| 83 | ;; List of variables that have server- or protocol-local bindings | 79 | ;; List of variables that have server- or protocol-local bindings |
| 84 | (defvar eudc-local-vars nil) | 80 | (defvar eudc-local-vars nil) |
| 85 | 81 | ||
| @@ -688,7 +684,8 @@ server for future sessions." | |||
| 688 | (cons (symbol-name elt) | 684 | (cons (symbol-name elt) |
| 689 | elt)) | 685 | elt)) |
| 690 | eudc-known-protocols))))) | 686 | eudc-known-protocols))))) |
| 691 | (unless (or (member protocol | 687 | (unless (or (null protocol) |
| 688 | (member protocol | ||
| 692 | eudc-supported-protocols) | 689 | eudc-supported-protocols) |
| 693 | (load (concat "eudcb-" (symbol-name protocol)) t)) | 690 | (load (concat "eudcb-" (symbol-name protocol)) t)) |
| 694 | (error "Unsupported protocol: %s" protocol)) | 691 | (error "Unsupported protocol: %s" protocol)) |
| @@ -766,7 +763,6 @@ otherwise a list of symbols is returned." | |||
| 766 | format (cdr format))) | 763 | format (cdr format))) |
| 767 | ;; If the same attribute appears more than once, merge | 764 | ;; If the same attribute appears more than once, merge |
| 768 | ;; the corresponding values | 765 | ;; the corresponding values |
| 769 | (setq query-alist (nreverse query-alist)) | ||
| 770 | (while query-alist | 766 | (while query-alist |
| 771 | (setq key (eudc-caar query-alist) | 767 | (setq key (eudc-caar query-alist) |
| 772 | val (eudc-cdar query-alist) | 768 | val (eudc-cdar query-alist) |
| @@ -812,19 +808,29 @@ If REPLACE is non-nil, then this expansion replaces the name in the buffer. | |||
| 812 | Multiple servers can be tried with the same query until one finds a match, | 808 | Multiple servers can be tried with the same query until one finds a match, |
| 813 | see `eudc-inline-expansion-servers'" | 809 | see `eudc-inline-expansion-servers'" |
| 814 | (interactive) | 810 | (interactive) |
| 815 | (if (memq eudc-inline-expansion-servers | 811 | (cond |
| 816 | '(current-server server-then-hotlist)) | 812 | ((eq eudc-inline-expansion-servers 'current-server) |
| 817 | (or eudc-server | 813 | (or eudc-server |
| 818 | (call-interactively 'eudc-set-server)) | 814 | (call-interactively 'eudc-set-server))) |
| 815 | ((eq eudc-inline-expansion-servers 'server-then-hotlist) | ||
| 816 | (or eudc-server | ||
| 817 | ;; Allow server to be nil if hotlist is set. | ||
| 818 | eudc-server-hotlist | ||
| 819 | (call-interactively 'eudc-set-server))) | ||
| 820 | ((eq eudc-inline-expansion-servers 'hotlist) | ||
| 819 | (or eudc-server-hotlist | 821 | (or eudc-server-hotlist |
| 820 | (error "No server in the hotlist"))) | 822 | (error "No server in the hotlist"))) |
| 823 | (t | ||
| 824 | (error "Wrong value for `eudc-inline-expansion-servers': %S" | ||
| 825 | eudc-inline-expansion-servers))) | ||
| 821 | (let* ((end (point)) | 826 | (let* ((end (point)) |
| 822 | (beg (save-excursion | 827 | (beg (save-excursion |
| 823 | (if (re-search-backward "\\([:,]\\|^\\)[ \t]*" | 828 | (if (re-search-backward "\\([:,]\\|^\\)[ \t]*" |
| 824 | (point-at-bol) 'move) | 829 | (point-at-bol) 'move) |
| 825 | (goto-char (match-end 0))) | 830 | (goto-char (match-end 0))) |
| 826 | (point))) | 831 | (point))) |
| 827 | (query-words (split-string (buffer-substring beg end) "[ \t]+")) | 832 | (query-words (split-string (buffer-substring-no-properties beg end) |
| 833 | "[ \t]+")) | ||
| 828 | query-formats | 834 | query-formats |
| 829 | response | 835 | response |
| 830 | response-string | 836 | response-string |
| @@ -840,18 +846,17 @@ see `eudc-inline-expansion-servers'" | |||
| 840 | ((eq eudc-inline-expansion-servers 'hotlist) | 846 | ((eq eudc-inline-expansion-servers 'hotlist) |
| 841 | eudc-server-hotlist) | 847 | eudc-server-hotlist) |
| 842 | ((eq eudc-inline-expansion-servers 'server-then-hotlist) | 848 | ((eq eudc-inline-expansion-servers 'server-then-hotlist) |
| 843 | (cons (cons eudc-server eudc-protocol) | 849 | (if eudc-server |
| 844 | (delete (cons eudc-server eudc-protocol) servers))) | 850 | (cons (cons eudc-server eudc-protocol) |
| 851 | (delete (cons eudc-server eudc-protocol) servers)) | ||
| 852 | eudc-server-hotlist)) | ||
| 845 | ((eq eudc-inline-expansion-servers 'current-server) | 853 | ((eq eudc-inline-expansion-servers 'current-server) |
| 846 | (list (cons eudc-server eudc-protocol))) | 854 | (list (cons eudc-server eudc-protocol))))) |
| 847 | (t | ||
| 848 | (error "Wrong value for `eudc-inline-expansion-servers': %S" | ||
| 849 | eudc-inline-expansion-servers)))) | ||
| 850 | (if (and eudc-max-servers-to-query | 855 | (if (and eudc-max-servers-to-query |
| 851 | (> (length servers) eudc-max-servers-to-query)) | 856 | (> (length servers) eudc-max-servers-to-query)) |
| 852 | (setcdr (nthcdr (1- eudc-max-servers-to-query) servers) nil)) | 857 | (setcdr (nthcdr (1- eudc-max-servers-to-query) servers) nil)) |
| 853 | 858 | ||
| 854 | (condition-case signal | 859 | (unwind-protect |
| 855 | (progn | 860 | (progn |
| 856 | (setq response | 861 | (setq response |
| 857 | (catch 'found | 862 | (catch 'found |
| @@ -887,14 +892,15 @@ see `eudc-inline-expansion-servers'" | |||
| 887 | 892 | ||
| 888 | ;; Process response through eudc-inline-expansion-format | 893 | ;; Process response through eudc-inline-expansion-format |
| 889 | (while response | 894 | (while response |
| 890 | (setq response-string (apply 'format | 895 | (setq response-string |
| 891 | (car eudc-inline-expansion-format) | 896 | (apply 'format |
| 892 | (mapcar (function | 897 | (car eudc-inline-expansion-format) |
| 893 | (lambda (field) | 898 | (mapcar (function |
| 894 | (or (cdr (assq field (car response))) | 899 | (lambda (field) |
| 895 | ""))) | 900 | (or (cdr (assq field (car response))) |
| 896 | (eudc-translate-attribute-list | 901 | ""))) |
| 897 | (cdr eudc-inline-expansion-format))))) | 902 | (eudc-translate-attribute-list |
| 903 | (cdr eudc-inline-expansion-format))))) | ||
| 898 | (if (> (length response-string) 0) | 904 | (if (> (length response-string) 0) |
| 899 | (setq response-strings | 905 | (setq response-strings |
| 900 | (cons response-string response-strings))) | 906 | (cons response-string response-strings))) |
| @@ -916,15 +922,10 @@ see `eudc-inline-expansion-servers'" | |||
| 916 | (delete-region beg end) | 922 | (delete-region beg end) |
| 917 | (insert (mapconcat 'identity response-strings ", "))) | 923 | (insert (mapconcat 'identity response-strings ", "))) |
| 918 | ((eq eudc-multiple-match-handling-method 'abort) | 924 | ((eq eudc-multiple-match-handling-method 'abort) |
| 919 | (error "There is more than one match for the query")))) | 925 | (error "There is more than one match for the query"))))) |
| 920 | (or (and (equal eudc-server eudc-former-server) | 926 | (or (and (equal eudc-server eudc-former-server) |
| 921 | (equal eudc-protocol eudc-former-protocol)) | 927 | (equal eudc-protocol eudc-former-protocol)) |
| 922 | (eudc-set-server eudc-former-server eudc-former-protocol t))) | 928 | (eudc-set-server eudc-former-server eudc-former-protocol t))))) |
| 923 | (error | ||
| 924 | (or (and (equal eudc-server eudc-former-server) | ||
| 925 | (equal eudc-protocol eudc-former-protocol)) | ||
| 926 | (eudc-set-server eudc-former-server eudc-former-protocol t)) | ||
| 927 | (signal (car signal) (cdr signal)))))) | ||
| 928 | 929 | ||
| 929 | ;;;###autoload | 930 | ;;;###autoload |
| 930 | (defun eudc-query-form (&optional get-fields-from-server) | 931 | (defun eudc-query-form (&optional get-fields-from-server) |
diff --git a/lisp/net/eudcb-ldap.el b/lisp/net/eudcb-ldap.el index 4c9b2490ee3..92972c5f99e 100644 --- a/lisp/net/eudcb-ldap.el +++ b/lisp/net/eudcb-ldap.el | |||
| @@ -70,16 +70,17 @@ | |||
| 70 | ("mail" . eudc-display-mail) | 70 | ("mail" . eudc-display-mail) |
| 71 | ("url" . eudc-display-url)) | 71 | ("url" . eudc-display-url)) |
| 72 | 'ldap) | 72 | 'ldap) |
| 73 | (eudc-protocol-set 'eudc-switch-to-server-hook | ||
| 74 | '(eudc-ldap-check-base) | ||
| 75 | 'ldap) | ||
| 76 | 73 | ||
| 77 | (defun eudc-ldap-cleanup-record-simple (record) | 74 | (defun eudc-ldap-cleanup-record-simple (record) |
| 78 | "Do some cleanup in a RECORD to make it suitable for EUDC." | 75 | "Do some cleanup in a RECORD to make it suitable for EUDC." |
| 79 | (mapcar | 76 | (mapcar |
| 80 | (function | 77 | (function |
| 81 | (lambda (field) | 78 | (lambda (field) |
| 82 | (cons (intern (car field)) | 79 | ;; Some servers return case-sensitive names (e.g. givenName |
| 80 | ;; instead of givenname); downcase the field's name so that it | ||
| 81 | ;; can be matched against | ||
| 82 | ;; eudc-ldap-attributes-translation-alist. | ||
| 83 | (cons (intern (downcase (car field))) | ||
| 83 | (if (cdr (cdr field)) | 84 | (if (cdr (cdr field)) |
| 84 | (cdr field) | 85 | (cdr field) |
| 85 | (car (cdr field)))))) | 86 | (car (cdr field)))))) |
| @@ -95,7 +96,7 @@ | |||
| 95 | (mapcar | 96 | (mapcar |
| 96 | (function | 97 | (function |
| 97 | (lambda (field) | 98 | (lambda (field) |
| 98 | (let ((name (intern (car field))) | 99 | (let ((name (intern (downcase (car field)))) |
| 99 | (value (cdr field))) | 100 | (value (cdr field))) |
| 100 | (if (memq name '(postaladdress registeredaddress)) | 101 | (if (memq name '(postaladdress registeredaddress)) |
| 101 | (setq value (mapcar 'eudc-filter-$ value))) | 102 | (setq value (mapcar 'eudc-filter-$ value))) |
| @@ -170,14 +171,16 @@ attribute names are returned. Default to `person'" | |||
| 170 | 171 | ||
| 171 | (defun eudc-ldap-format-query-as-rfc1558 (query) | 172 | (defun eudc-ldap-format-query-as-rfc1558 (query) |
| 172 | "Format the EUDC QUERY list as a RFC1558 LDAP search filter." | 173 | "Format the EUDC QUERY list as a RFC1558 LDAP search filter." |
| 173 | (format "(&%s)" | 174 | (let ((formatter (lambda (item &optional wildcard) |
| 174 | (apply 'concat | 175 | (format "(%s=%s)" |
| 175 | (mapcar (lambda (item) | 176 | (car item) |
| 176 | (format "(%s=%s)" | 177 | (concat |
| 177 | (car item) | 178 | (eudc-ldap-escape-query-special-chars |
| 178 | (eudc-ldap-escape-query-special-chars (cdr item)))) | 179 | (cdr item)) (if wildcard "*" "")))))) |
| 179 | query)))) | 180 | (format "(&%s)" |
| 180 | 181 | (concat | |
| 182 | (mapconcat formatter (butlast query) "") | ||
| 183 | (funcall formatter (car (last query)) t))))) | ||
| 181 | 184 | ||
| 182 | ;;}}} | 185 | ;;}}} |
| 183 | 186 | ||
diff --git a/lisp/net/ldap.el b/lisp/net/ldap.el index eb1b7589b48..a77fc3c6514 100644 --- a/lisp/net/ldap.el +++ b/lisp/net/ldap.el | |||
| @@ -34,6 +34,7 @@ | |||
| 34 | ;;; Code: | 34 | ;;; Code: |
| 35 | 35 | ||
| 36 | (require 'custom) | 36 | (require 'custom) |
| 37 | (require 'password-cache) | ||
| 37 | 38 | ||
| 38 | (autoload 'auth-source-search "auth-source") | 39 | (autoload 'auth-source-search "auth-source") |
| 39 | 40 | ||
| @@ -47,15 +48,13 @@ | |||
| 47 | A TCP port number can be appended to that name using a colon as | 48 | A TCP port number can be appended to that name using a colon as |
| 48 | a separator." | 49 | a separator." |
| 49 | :type '(choice (string :tag "Host name") | 50 | :type '(choice (string :tag "Host name") |
| 50 | (const :tag "Use library default" nil)) | 51 | (const :tag "Use library default" nil))) |
| 51 | :group 'ldap) | ||
| 52 | 52 | ||
| 53 | (defcustom ldap-default-port nil | 53 | (defcustom ldap-default-port nil |
| 54 | "Default TCP port for LDAP connections. | 54 | "Default TCP port for LDAP connections. |
| 55 | Initialized from the LDAP library at build time. Default value is 389." | 55 | Initialized from the LDAP library at build time. Default value is 389." |
| 56 | :type '(choice (const :tag "Use library default" nil) | 56 | :type '(choice (const :tag "Use library default" nil) |
| 57 | (integer :tag "Port number")) | 57 | (integer :tag "Port number"))) |
| 58 | :group 'ldap) | ||
| 59 | 58 | ||
| 60 | (defcustom ldap-default-base nil | 59 | (defcustom ldap-default-base nil |
| 61 | "Default base for LDAP searches. | 60 | "Default base for LDAP searches. |
| @@ -63,8 +62,7 @@ This is a string using the syntax of RFC 1779. | |||
| 63 | For instance, \"o=ACME, c=US\" limits the search to the | 62 | For instance, \"o=ACME, c=US\" limits the search to the |
| 64 | Acme organization in the United States." | 63 | Acme organization in the United States." |
| 65 | :type '(choice (const :tag "Use library default" nil) | 64 | :type '(choice (const :tag "Use library default" nil) |
| 66 | (string :tag "Search base")) | 65 | (string :tag "Search base"))) |
| 67 | :group 'ldap) | ||
| 68 | 66 | ||
| 69 | 67 | ||
| 70 | (defcustom ldap-host-parameters-alist nil | 68 | (defcustom ldap-host-parameters-alist nil |
| @@ -144,35 +142,35 @@ Valid properties include: | |||
| 144 | :tag "Size Limit" | 142 | :tag "Size Limit" |
| 145 | :inline t | 143 | :inline t |
| 146 | (const :tag "Size Limit" sizelimit) | 144 | (const :tag "Size Limit" sizelimit) |
| 147 | (integer :tag "(number of records)"))))) | 145 | (integer :tag "(number of records)")))))) |
| 148 | :group 'ldap) | ||
| 149 | 146 | ||
| 150 | (defcustom ldap-ldapsearch-prog "ldapsearch" | 147 | (defcustom ldap-ldapsearch-prog "ldapsearch" |
| 151 | "The name of the ldapsearch command line program." | 148 | "The name of the ldapsearch command line program." |
| 152 | :type '(string :tag "`ldapsearch' Program") | 149 | :type '(string :tag "`ldapsearch' Program")) |
| 153 | :group 'ldap) | ||
| 154 | 150 | ||
| 155 | (defcustom ldap-ldapsearch-args '("-LL" "-tt") | 151 | (defcustom ldap-ldapsearch-args '("-LL" "-tt") |
| 156 | "A list of additional arguments to pass to `ldapsearch'." | 152 | "A list of additional arguments to pass to `ldapsearch'." |
| 157 | :type '(repeat :tag "`ldapsearch' Arguments" | 153 | :type '(repeat :tag "`ldapsearch' Arguments" |
| 158 | (string :tag "Argument")) | 154 | (string :tag "Argument"))) |
| 159 | :group 'ldap) | 155 | |
| 156 | (defcustom ldap-ldapsearch-password-prompt-regexp "Enter LDAP Password: " | ||
| 157 | "A regular expression used to recognize the `ldapsearch' | ||
| 158 | program's password prompt." | ||
| 159 | :type 'regexp | ||
| 160 | :version "25.1") | ||
| 160 | 161 | ||
| 161 | (defcustom ldap-ignore-attribute-codings nil | 162 | (defcustom ldap-ignore-attribute-codings nil |
| 162 | "If non-nil, do not encode/decode LDAP attribute values." | 163 | "If non-nil, do not encode/decode LDAP attribute values." |
| 163 | :type 'boolean | 164 | :type 'boolean) |
| 164 | :group 'ldap) | ||
| 165 | 165 | ||
| 166 | (defcustom ldap-default-attribute-decoder nil | 166 | (defcustom ldap-default-attribute-decoder nil |
| 167 | "Decoder function to use for attributes whose syntax is unknown." | 167 | "Decoder function to use for attributes whose syntax is unknown." |
| 168 | :type 'symbol | 168 | :type 'symbol) |
| 169 | :group 'ldap) | ||
| 170 | 169 | ||
| 171 | (defcustom ldap-coding-system 'utf-8 | 170 | (defcustom ldap-coding-system 'utf-8 |
| 172 | "Coding system of LDAP string values. | 171 | "Coding system of LDAP string values. |
| 173 | LDAP v3 specifies the coding system of strings to be UTF-8." | 172 | LDAP v3 specifies the coding system of strings to be UTF-8." |
| 174 | :type 'symbol | 173 | :type 'symbol) |
| 175 | :group 'ldap) | ||
| 176 | 174 | ||
| 177 | (defvar ldap-attribute-syntax-encoders | 175 | (defvar ldap-attribute-syntax-encoders |
| 178 | [nil ; 1 ACI Item N | 176 | [nil ; 1 ACI Item N |
| @@ -476,6 +474,47 @@ Additional search parameters can be specified through | |||
| 476 | (mapcar 'ldap-decode-attribute record)) | 474 | (mapcar 'ldap-decode-attribute record)) |
| 477 | result)))) | 475 | result)))) |
| 478 | 476 | ||
| 477 | (defun ldap-password-read (host) | ||
| 478 | "Read LDAP password for HOST. | ||
| 479 | If the password is cached, it is read from the cache, otherwise the user | ||
| 480 | is prompted for the password. If `password-cache' is non-nil the password | ||
| 481 | is verified and cached. The `password-cache-expiry' variable | ||
| 482 | controls for how long the password is cached. | ||
| 483 | |||
| 484 | This function can be specified for the `passwd' property in | ||
| 485 | `ldap-host-parameters-alist' when interactive password prompting | ||
| 486 | is desired for HOST." | ||
| 487 | ;; Add ldap: namespace to allow empty string for default host. | ||
| 488 | (let* ((host-key (concat "ldap:" host)) | ||
| 489 | (password (password-read | ||
| 490 | (format "Enter LDAP Password%s: " | ||
| 491 | (if (equal host "") | ||
| 492 | "" | ||
| 493 | (format " for %s" host))) | ||
| 494 | host-key))) | ||
| 495 | (when (and password-cache | ||
| 496 | (not (password-in-cache-p host-key)) | ||
| 497 | ;; Confirm the password is valid before adding it to | ||
| 498 | ;; the password cache. ldap-search-internal will throw | ||
| 499 | ;; an error if the password is invalid. | ||
| 500 | (not (ldap-search-internal | ||
| 501 | `(host ,host | ||
| 502 | ;; Specify an arbitrary filter that should | ||
| 503 | ;; produce no results, since only | ||
| 504 | ;; authentication success is of interest. | ||
| 505 | filter "emacs-test-password=" | ||
| 506 | attributes nil | ||
| 507 | attrsonly nil | ||
| 508 | withdn nil | ||
| 509 | ;; Preempt passwd ldap-password-read | ||
| 510 | ;; setting in ldap-host-parameters-alist. | ||
| 511 | passwd ,password | ||
| 512 | ,@(cdr | ||
| 513 | (assoc | ||
| 514 | host | ||
| 515 | ldap-host-parameters-alist)))))) | ||
| 516 | (password-cache-add host-key password)) | ||
| 517 | password)) | ||
| 479 | 518 | ||
| 480 | (defun ldap-search-internal (search-plist) | 519 | (defun ldap-search-internal (search-plist) |
| 481 | "Perform a search on a LDAP server. | 520 | "Perform a search on a LDAP server. |
| @@ -531,7 +570,11 @@ an alist of attribute/value pairs." | |||
| 531 | (passwd (or (plist-get search-plist 'passwd) | 570 | (passwd (or (plist-get search-plist 'passwd) |
| 532 | (plist-get asfound :secret))) | 571 | (plist-get asfound :secret))) |
| 533 | ;; convert the password from a function call if needed | 572 | ;; convert the password from a function call if needed |
| 534 | (passwd (if (functionp passwd) (funcall passwd) passwd)) | 573 | (passwd (if (functionp passwd) |
| 574 | (if (eq passwd 'ldap-password-read) | ||
| 575 | (funcall passwd host) | ||
| 576 | (funcall passwd)) | ||
| 577 | passwd)) | ||
| 535 | ;; get the binddn from the search-list or from the | 578 | ;; get the binddn from the search-list or from the |
| 536 | ;; auth-source user or binddn tokens | 579 | ;; auth-source user or binddn tokens |
| 537 | (binddn (or (plist-get search-plist 'binddn) | 580 | (binddn (or (plist-get search-plist 'binddn) |
| @@ -550,7 +593,7 @@ an alist of attribute/value pairs." | |||
| 550 | (sizelimit (plist-get search-plist 'sizelimit)) | 593 | (sizelimit (plist-get search-plist 'sizelimit)) |
| 551 | (withdn (plist-get search-plist 'withdn)) | 594 | (withdn (plist-get search-plist 'withdn)) |
| 552 | (numres 0) | 595 | (numres 0) |
| 553 | arglist dn name value record result) | 596 | arglist dn name value record result proc) |
| 554 | (if (or (null filter) | 597 | (if (or (null filter) |
| 555 | (equal "" filter)) | 598 | (equal "" filter)) |
| 556 | (error "No search filter")) | 599 | (error "No search filter")) |
| @@ -559,7 +602,13 @@ an alist of attribute/value pairs." | |||
| 559 | (erase-buffer) | 602 | (erase-buffer) |
| 560 | (if (and host | 603 | (if (and host |
| 561 | (not (equal "" host))) | 604 | (not (equal "" host))) |
| 562 | (setq arglist (nconc arglist (list (format "-h%s" host))))) | 605 | (setq arglist (nconc arglist |
| 606 | (list (format | ||
| 607 | ;; Use -H if host is a new-style LDAP URI. | ||
| 608 | (if (string-match "^[a-zA-Z]+://" host) | ||
| 609 | "-H%s" | ||
| 610 | "-h%s") | ||
| 611 | host))))) | ||
| 563 | (if (and attrsonly | 612 | (if (and attrsonly |
| 564 | (not (equal "" attrsonly))) | 613 | (not (equal "" attrsonly))) |
| 565 | (setq arglist (nconc arglist (list "-A")))) | 614 | (setq arglist (nconc arglist (list "-A")))) |
| @@ -575,9 +624,9 @@ an alist of attribute/value pairs." | |||
| 575 | (if (and auth | 624 | (if (and auth |
| 576 | (equal 'simple auth)) | 625 | (equal 'simple auth)) |
| 577 | (setq arglist (nconc arglist (list "-x")))) | 626 | (setq arglist (nconc arglist (list "-x")))) |
| 578 | (if (and passwd | 627 | ;; Allow passwd to be set to "", representing a blank password. |
| 579 | (not (equal "" passwd))) | 628 | (if passwd |
| 580 | (setq arglist (nconc arglist (list (format "-w%s" passwd))))) | 629 | (setq arglist (nconc arglist (list "-W")))) |
| 581 | (if (and deref | 630 | (if (and deref |
| 582 | (not (equal "" deref))) | 631 | (not (equal "" deref))) |
| 583 | (setq arglist (nconc arglist (list (format "-a%s" deref))))) | 632 | (setq arglist (nconc arglist (list (format "-a%s" deref))))) |
| @@ -587,14 +636,43 @@ an alist of attribute/value pairs." | |||
| 587 | (if (and sizelimit | 636 | (if (and sizelimit |
| 588 | (not (equal "" sizelimit))) | 637 | (not (equal "" sizelimit))) |
| 589 | (setq arglist (nconc arglist (list (format "-z%s" sizelimit))))) | 638 | (setq arglist (nconc arglist (list (format "-z%s" sizelimit))))) |
| 590 | (apply #'call-process ldap-ldapsearch-prog | 639 | (if passwd |
| 591 | ;; Ignore stderr, which can corrupt results | 640 | (let* ((process-connection-type nil) |
| 592 | nil (list buf nil) nil | 641 | (proc-args (append arglist ldap-ldapsearch-args |
| 593 | (append arglist ldap-ldapsearch-args filter)) | 642 | filter)) |
| 643 | (proc (apply #'start-process "ldapsearch" buf | ||
| 644 | ldap-ldapsearch-prog | ||
| 645 | proc-args))) | ||
| 646 | (while (null (progn | ||
| 647 | (goto-char (point-min)) | ||
| 648 | (re-search-forward | ||
| 649 | ldap-ldapsearch-password-prompt-regexp | ||
| 650 | (point-max) t))) | ||
| 651 | (accept-process-output proc 1)) | ||
| 652 | (process-send-string proc passwd) | ||
| 653 | (process-send-string proc "\n") | ||
| 654 | (while (not (memq (process-status proc) '(exit signal))) | ||
| 655 | (sit-for 0.1)) | ||
| 656 | (let ((status (process-exit-status proc))) | ||
| 657 | (when (not (eq status 0)) | ||
| 658 | ;; Handle invalid credentials exit status specially | ||
| 659 | ;; for ldap-password-read. | ||
| 660 | (if (eq status 49) | ||
| 661 | (error (concat "Incorrect LDAP password or" | ||
| 662 | " bind distinguished name (binddn)")) | ||
| 663 | (error "Failed ldapsearch invocation: %s \"%s\"" | ||
| 664 | ldap-ldapsearch-prog | ||
| 665 | (mapconcat 'identity proc-args "\" \"")))))) | ||
| 666 | (apply #'call-process ldap-ldapsearch-prog | ||
| 667 | ;; Ignore stderr, which can corrupt results | ||
| 668 | nil (list buf nil) nil | ||
| 669 | (append arglist ldap-ldapsearch-args filter))) | ||
| 594 | (insert "\n") | 670 | (insert "\n") |
| 595 | (goto-char (point-min)) | 671 | (goto-char (point-min)) |
| 596 | 672 | ||
| 597 | (while (re-search-forward "[\t\n\f]+ " nil t) | 673 | (while (re-search-forward (concat "[\t\n\f]+ \\|" |
| 674 | ldap-ldapsearch-password-prompt-regexp) | ||
| 675 | nil t) | ||
| 598 | (replace-match "" nil nil)) | 676 | (replace-match "" nil nil)) |
| 599 | (goto-char (point-min)) | 677 | (goto-char (point-min)) |
| 600 | 678 | ||