url-ldap.el 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. ;;; url-ldap.el --- LDAP Uniform Resource Locator retrieval code
  2. ;; Copyright (C) 1998-1999, 2004-2012 Free Software Foundation, Inc.
  3. ;; Keywords: comm, data, processes
  4. ;; This file is part of GNU Emacs.
  5. ;;
  6. ;; GNU Emacs 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 3 of the License, or
  9. ;; (at your option) any later version.
  10. ;; GNU Emacs is distributed in the hope that it will be useful,
  11. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ;; GNU General Public License for more details.
  14. ;; You should have received a copy of the GNU General Public License
  15. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  16. ;;; Commentary:
  17. ;;; Code:
  18. (require 'url-vars)
  19. (require 'url-parse)
  20. (require 'url-util)
  21. (require 'ldap)
  22. (autoload 'tls-certificate-information "tls")
  23. ;; This has been implemented from RFC2255 'The LDAP URL Format' (Dec 1997)
  24. ;;
  25. ;; basic format is: ldap://host:port/dn?attributes?scope?filter?extensions
  26. ;;
  27. ;; Test URLs:
  28. ;; ldap://ldap.itd.umich.edu/cn%3Dumbflabmanager%2C%20ou%3DUser%20Groups%2C%20ou%3DGroups%2C%20o%3DUniversity%20of%20Michigan%2C%20c%3DUS
  29. ;; ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,c=US
  30. ;;
  31. ;; For simple queries, I have verified compatibility with Netscape
  32. ;; Communicator v4.5 under GNU/Linux.
  33. ;;
  34. ;; For anything _useful_ though, like specifying the attributes,
  35. ;; scope, filter, or extensions, netscape claims the URL format is
  36. ;; unrecognized. So I don't think it supports anything other than the
  37. ;; defaults (scope=base,attributes=*,filter=(objectClass=*)
  38. (defconst url-ldap-default-port 389 "Default LDAP port.")
  39. (defalias 'url-ldap-expand-file-name 'url-default-expander)
  40. (defvar url-ldap-pretty-names
  41. '(("l" . "City")
  42. ("objectclass" . "Object Class")
  43. ("o" . "Organization")
  44. ("ou" . "Organizational Unit")
  45. ("cn" . "Name")
  46. ("sn" . "Last Name")
  47. ("givenname" . "First Name")
  48. ("mail" . "Email")
  49. ("title" . "Title")
  50. ("c" . "Country")
  51. ("postalcode" . "ZIP Code")
  52. ("telephonenumber" . "Phone Number")
  53. ("facsimiletelephonenumber" . "Fax")
  54. ("postaladdress" . "Mailing Address")
  55. ("description" . "Notes"))
  56. "*An assoc list mapping LDAP attribute names to pretty descriptions of them.")
  57. (defvar url-ldap-attribute-formatters
  58. '(("mail" . (lambda (x) (format "<a href='mailto:%s'>%s</a>" x x)))
  59. ("owner" . url-ldap-dn-formatter)
  60. ("creatorsname" . url-ldap-dn-formatter)
  61. ("jpegphoto" . url-ldap-image-formatter)
  62. ("usercertificate" . url-ldap-certificate-formatter)
  63. ("modifiersname" . url-ldap-dn-formatter)
  64. ("namingcontexts" . url-ldap-dn-formatter)
  65. ("defaultnamingcontext" . url-ldap-dn-formatter)
  66. ("member" . url-ldap-dn-formatter))
  67. "*An assoc list mapping LDAP attribute names to pretty formatters for them.")
  68. (defsubst url-ldap-attribute-pretty-name (n)
  69. (or (cdr-safe (assoc (downcase n) url-ldap-pretty-names)) n))
  70. (defsubst url-ldap-attribute-pretty-desc (n v)
  71. (if (string-match "^\\([^;]+\\);" n)
  72. (setq n (match-string 1 n)))
  73. (funcall (or (cdr-safe (assoc (downcase n) url-ldap-attribute-formatters)) 'identity) v))
  74. (defun url-ldap-dn-formatter (dn)
  75. (concat "<a href='/"
  76. (url-hexify-string dn)
  77. "'>" dn "</a>"))
  78. (defun url-ldap-certificate-formatter (data)
  79. (condition-case ()
  80. (require 'ssl)
  81. (error nil))
  82. (let ((vals (if (fboundp 'ssl-certificate-information)
  83. (ssl-certificate-information data)
  84. (tls-certificate-information data))))
  85. (if (not vals)
  86. "<b>Unable to parse certificate</b>"
  87. (concat "<table border=0>\n"
  88. (mapconcat
  89. (lambda (ava)
  90. (format "<tr><td>%s</td><td>%s</td></tr>\n" (car ava) (cdr ava)))
  91. vals "\n")
  92. "</table>\n"))))
  93. (defun url-ldap-image-formatter (data)
  94. (format "<img alt='JPEG Photo' src='data:image/jpeg;base64,%s'>"
  95. (url-hexify-string (base64-encode-string data))))
  96. ;;;###autoload
  97. (defun url-ldap (url)
  98. "Perform an LDAP search specified by URL.
  99. The return value is a buffer displaying the search results in HTML.
  100. URL can be a URL string, or a URL vector of the type returned by
  101. `url-generic-parse-url'."
  102. (if (stringp url)
  103. (setq url (url-generic-parse-url (url-unhex-string url)))
  104. (if (not (vectorp url))
  105. (error "Argument is not a valid URL")))
  106. (with-current-buffer (generate-new-buffer " *url-ldap*")
  107. (setq url-current-object url)
  108. (insert "Content-type: text/html\r\n\r\n")
  109. (if (not (fboundp 'ldap-search-internal))
  110. (insert "<html>\n"
  111. " <head>\n"
  112. " <title>LDAP Not Supported</title>\n"
  113. " <base href='" (url-recreate-url url) "'>\n"
  114. " </head>\n"
  115. " <body>\n"
  116. " <h1>LDAP Not Supported</h1>\n"
  117. " <p>\n"
  118. " This version of Emacs does not support LDAP.\n"
  119. " </p>\n"
  120. " </body>\n"
  121. "</html>\n")
  122. (let* ((binddn nil)
  123. (data (url-filename url))
  124. (host (url-host url))
  125. (port (url-port url))
  126. (base-object nil)
  127. (attributes nil)
  128. (scope nil)
  129. (filter nil)
  130. (extensions nil)
  131. (results nil))
  132. ;; Get rid of leading /
  133. (if (string-match "^/" data)
  134. (setq data (substring data 1)))
  135. (setq data (mapcar (lambda (x) (if (/= (length x) 0) x nil)) (split-string data "\\?"))
  136. base-object (nth 0 data)
  137. attributes (nth 1 data)
  138. scope (nth 2 data)
  139. filter (nth 3 data)
  140. extensions (nth 4 data))
  141. ;; fill in the defaults
  142. (setq base-object (url-unhex-string (or base-object ""))
  143. scope (intern (url-unhex-string (or scope "base")))
  144. filter (url-unhex-string (or filter "(objectClass=*)")))
  145. (if (not (memq scope '(base one sub)))
  146. (error "Malformed LDAP URL: Unknown scope: %S" scope))
  147. ;; Convert to the internal LDAP support scoping names.
  148. (setq scope (cdr (assq scope '((base . base) (one . onelevel) (sub . subtree)))))
  149. (if attributes
  150. (setq attributes (mapcar 'url-unhex-string (split-string attributes ","))))
  151. ;; Parse out the extensions.
  152. (if extensions
  153. (setq extensions (mapcar (lambda (ext)
  154. (if (string-match "\\([^=]*\\)=\\(.*\\)" ext)
  155. (cons (match-string 1 ext) (match-string 2 ext))
  156. (cons ext ext)))
  157. (split-string extensions ","))
  158. extensions (mapcar (lambda (ext)
  159. (cons (url-unhex-string (car ext))
  160. (url-unhex-string (cdr ext))))
  161. extensions)))
  162. (setq binddn (cdr-safe (or (assoc "bindname" extensions)
  163. (assoc "!bindname" extensions))))
  164. ;; Now, let's actually do something with it.
  165. (setq results (cdr (ldap-search-internal
  166. (list 'host (concat host ":" (number-to-string port))
  167. 'base base-object
  168. 'attributes attributes
  169. 'scope scope
  170. 'filter filter
  171. 'binddn binddn))))
  172. (insert "<html>\n"
  173. " <head>\n"
  174. " <title>LDAP Search Results</title>\n"
  175. " <base href='" (url-recreate-url url) "'>\n"
  176. " </head>\n"
  177. " <body>\n"
  178. " <h1>" (int-to-string (length results)) " matches</h1>\n")
  179. (mapc (lambda (obj)
  180. (insert " <hr>\n"
  181. " <table border=1>\n")
  182. (mapc (lambda (attr)
  183. (if (= (length (cdr attr)) 1)
  184. ;; single match, easy
  185. (insert " <tr><td>"
  186. (url-ldap-attribute-pretty-name (car attr))
  187. "</td><td>"
  188. (url-ldap-attribute-pretty-desc (car attr) (car (cdr attr)))
  189. "</td></tr>\n")
  190. ;; Multiple matches, slightly uglier
  191. (insert " <tr>\n"
  192. (format " <td valign=top>")
  193. (url-ldap-attribute-pretty-name (car attr)) "</td><td>"
  194. (mapconcat (lambda (x)
  195. (url-ldap-attribute-pretty-desc (car attr) x))
  196. (cdr attr)
  197. "<br>\n")
  198. "</td>"
  199. " </tr>\n")))
  200. obj)
  201. (insert " </table>\n"))
  202. results)
  203. (insert " <hr>\n"
  204. " </body>\n"
  205. "</html>\n")))
  206. (current-buffer)))
  207. (provide 'url-ldap)
  208. ;;; url-ldap.el ends here