ietf-drums.el 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. ;;; ietf-drums.el --- Functions for parsing RFC822bis headers
  2. ;; Copyright (C) 1998-2012 Free Software Foundation, Inc.
  3. ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
  4. ;; This file is part of GNU Emacs.
  5. ;; GNU Emacs is free software: you can redistribute it and/or modify
  6. ;; it under the terms of the GNU General Public License as published by
  7. ;; the Free Software Foundation, either version 3 of the License, or
  8. ;; (at your option) any later version.
  9. ;; GNU Emacs is distributed in the hope that it will be useful,
  10. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. ;; GNU General Public License for more details.
  13. ;; You should have received a copy of the GNU General Public License
  14. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  15. ;;; Commentary:
  16. ;; DRUMS is an IETF Working Group that works (or worked) on the
  17. ;; successor to RFC822, "Standard For The Format Of Arpa Internet Text
  18. ;; Messages". This library is based on
  19. ;; draft-ietf-drums-msg-fmt-05.txt, released on 1998-08-05.
  20. ;; Pending a real regression self test suite, Simon Josefsson added
  21. ;; various self test expressions snipped from bug reports, and their
  22. ;; expected value, below. I you believe it could be useful, please
  23. ;; add your own test cases, or write a real self test suite, or just
  24. ;; remove this.
  25. ;; <m3oekvfd50.fsf@whitebox.m5r.de>
  26. ;; (ietf-drums-parse-address "'foo' <foo@example.com>")
  27. ;; => ("foo@example.com" . "'foo'")
  28. ;;; Code:
  29. (eval-when-compile (require 'cl))
  30. (require 'mm-util)
  31. (defvar ietf-drums-no-ws-ctl-token "\001-\010\013\014\016-\037\177"
  32. "US-ASCII control characters excluding CR, LF and white space.")
  33. (defvar ietf-drums-text-token "\001-\011\013\014\016-\177"
  34. "US-ASCII characters excluding CR and LF.")
  35. (defvar ietf-drums-specials-token "()<>[]:;@\\,.\""
  36. "Special characters.")
  37. (defvar ietf-drums-quote-token "\\"
  38. "Quote character.")
  39. (defvar ietf-drums-wsp-token " \t"
  40. "White space.")
  41. (defvar ietf-drums-fws-regexp
  42. (concat "[" ietf-drums-wsp-token "]*\n[" ietf-drums-wsp-token "]+")
  43. "Folding white space.")
  44. (defvar ietf-drums-atext-token "-^a-zA-Z0-9!#$%&'*+/=?_`{|}~"
  45. "Textual token.")
  46. (defvar ietf-drums-dot-atext-token "-^a-zA-Z0-9!#$%&'*+/=?_`{|}~."
  47. "Textual token including full stop.")
  48. (defvar ietf-drums-qtext-token
  49. (concat ietf-drums-no-ws-ctl-token "\041\043-\133\135-\177")
  50. "Non-white-space control characters, plus the rest of ASCII excluding
  51. backslash and doublequote.")
  52. (defvar ietf-drums-tspecials "][()<>@,;:\\\"/?="
  53. "Tspecials.")
  54. (defvar ietf-drums-syntax-table
  55. (let ((table (copy-syntax-table emacs-lisp-mode-syntax-table)))
  56. (modify-syntax-entry ?\\ "/" table)
  57. (modify-syntax-entry ?< "(" table)
  58. (modify-syntax-entry ?> ")" table)
  59. (modify-syntax-entry ?@ "w" table)
  60. (modify-syntax-entry ?/ "w" table)
  61. (modify-syntax-entry ?* "_" table)
  62. (modify-syntax-entry ?\; "_" table)
  63. (modify-syntax-entry ?\' "_" table)
  64. (if (featurep 'xemacs)
  65. (let ((i 128))
  66. (while (< i 256)
  67. (modify-syntax-entry i "w" table)
  68. (setq i (1+ i)))))
  69. table))
  70. (defun ietf-drums-token-to-list (token)
  71. "Translate TOKEN into a list of characters."
  72. (let ((i 0)
  73. b e c out range)
  74. (while (< i (length token))
  75. (setq c (mm-char-int (aref token i)))
  76. (incf i)
  77. (cond
  78. ((eq c (mm-char-int ?-))
  79. (if b
  80. (setq range t)
  81. (push c out)))
  82. (range
  83. (while (<= b c)
  84. (push (make-char 'ascii b) out)
  85. (incf b))
  86. (setq range nil))
  87. ((= i (length token))
  88. (push (make-char 'ascii c) out))
  89. (t
  90. (when b
  91. (push (make-char 'ascii b) out))
  92. (setq b c))))
  93. (nreverse out)))
  94. (defsubst ietf-drums-init (string)
  95. (set-syntax-table ietf-drums-syntax-table)
  96. (insert string)
  97. (ietf-drums-unfold-fws)
  98. (goto-char (point-min)))
  99. (defun ietf-drums-remove-comments (string)
  100. "Remove comments from STRING."
  101. (with-temp-buffer
  102. (let (c)
  103. (ietf-drums-init string)
  104. (while (not (eobp))
  105. (setq c (char-after))
  106. (cond
  107. ((eq c ?\")
  108. (condition-case err
  109. (forward-sexp 1)
  110. (error (goto-char (point-max)))))
  111. ((eq c ?\()
  112. (delete-region
  113. (point)
  114. (condition-case nil
  115. (with-syntax-table (copy-syntax-table ietf-drums-syntax-table)
  116. (modify-syntax-entry ?\" "w")
  117. (forward-sexp 1)
  118. (point))
  119. (error (point-max)))))
  120. (t
  121. (forward-char 1))))
  122. (buffer-string))))
  123. (defun ietf-drums-remove-whitespace (string)
  124. "Remove whitespace from STRING."
  125. (with-temp-buffer
  126. (ietf-drums-init string)
  127. (let (c)
  128. (while (not (eobp))
  129. (setq c (char-after))
  130. (cond
  131. ((eq c ?\")
  132. (forward-sexp 1))
  133. ((eq c ?\()
  134. (forward-sexp 1))
  135. ((memq c '(?\ ?\t ?\n))
  136. (delete-char 1))
  137. (t
  138. (forward-char 1))))
  139. (buffer-string))))
  140. (defun ietf-drums-get-comment (string)
  141. "Return the first comment in STRING."
  142. (with-temp-buffer
  143. (ietf-drums-init string)
  144. (let (result c)
  145. (while (not (eobp))
  146. (setq c (char-after))
  147. (cond
  148. ((eq c ?\")
  149. (forward-sexp 1))
  150. ((eq c ?\()
  151. (setq result
  152. (buffer-substring
  153. (1+ (point))
  154. (progn (forward-sexp 1) (1- (point))))))
  155. (t
  156. (forward-char 1))))
  157. result)))
  158. (defun ietf-drums-strip (string)
  159. "Remove comments and whitespace from STRING."
  160. (ietf-drums-remove-whitespace (ietf-drums-remove-comments string)))
  161. (defun ietf-drums-parse-address (string)
  162. "Parse STRING and return a MAILBOX / DISPLAY-NAME pair."
  163. (with-temp-buffer
  164. (let (display-name mailbox c display-string)
  165. (ietf-drums-init string)
  166. (while (not (eobp))
  167. (setq c (char-after))
  168. (cond
  169. ((or (eq c ? )
  170. (eq c ?\t))
  171. (forward-char 1))
  172. ((eq c ?\()
  173. (forward-sexp 1))
  174. ((eq c ?\")
  175. (push (buffer-substring
  176. (1+ (point)) (progn (forward-sexp 1) (1- (point))))
  177. display-name))
  178. ((looking-at (concat "[" ietf-drums-atext-token "@" "]"))
  179. (push (buffer-substring (point) (progn (forward-sexp 1) (point)))
  180. display-name))
  181. ((eq c ?<)
  182. (setq mailbox
  183. (ietf-drums-remove-whitespace
  184. (ietf-drums-remove-comments
  185. (buffer-substring
  186. (1+ (point))
  187. (progn (forward-sexp 1) (1- (point))))))))
  188. (t
  189. (message "Unknown symbol: %c" c)
  190. (forward-char 1))))
  191. ;; If we found no display-name, then we look for comments.
  192. (if display-name
  193. (setq display-string
  194. (mapconcat 'identity (reverse display-name) " "))
  195. (setq display-string (ietf-drums-get-comment string)))
  196. (if (not mailbox)
  197. (when (string-match "@" display-string)
  198. (cons
  199. (mapconcat 'identity (nreverse display-name) "")
  200. (ietf-drums-get-comment string)))
  201. (cons mailbox display-string)))))
  202. (defun ietf-drums-parse-addresses (string &optional rawp)
  203. "Parse STRING and return a list of MAILBOX / DISPLAY-NAME pairs.
  204. If RAWP, don't actually parse the addresses, but instead return
  205. a list of address strings."
  206. (if (null string)
  207. nil
  208. (with-temp-buffer
  209. (ietf-drums-init string)
  210. (let ((beg (point))
  211. pairs c address)
  212. (while (not (eobp))
  213. (setq c (char-after))
  214. (cond
  215. ((memq c '(?\" ?< ?\())
  216. (condition-case nil
  217. (forward-sexp 1)
  218. (error
  219. (skip-chars-forward "^,"))))
  220. ((eq c ?,)
  221. (setq address
  222. (if rawp
  223. (buffer-substring beg (point))
  224. (condition-case nil
  225. (ietf-drums-parse-address
  226. (buffer-substring beg (point)))
  227. (error nil))))
  228. (if address (push address pairs))
  229. (forward-char 1)
  230. (setq beg (point)))
  231. (t
  232. (forward-char 1))))
  233. (setq address
  234. (if rawp
  235. (buffer-substring beg (point))
  236. (condition-case nil
  237. (ietf-drums-parse-address
  238. (buffer-substring beg (point)))
  239. (error nil))))
  240. (if address (push address pairs))
  241. (nreverse pairs)))))
  242. (defun ietf-drums-unfold-fws ()
  243. "Unfold folding white space in the current buffer."
  244. (goto-char (point-min))
  245. (while (re-search-forward ietf-drums-fws-regexp nil t)
  246. (replace-match " " t t))
  247. (goto-char (point-min)))
  248. (defun ietf-drums-parse-date (string)
  249. "Return an Emacs time spec from STRING."
  250. (apply 'encode-time (parse-time-string string)))
  251. (defun ietf-drums-narrow-to-header ()
  252. "Narrow to the header section in the current buffer."
  253. (narrow-to-region
  254. (goto-char (point-min))
  255. (if (re-search-forward "^\r?$" nil 1)
  256. (match-beginning 0)
  257. (point-max)))
  258. (goto-char (point-min)))
  259. (defun ietf-drums-quote-string (string)
  260. "Quote string if it needs quoting to be displayed in a header."
  261. (if (string-match (concat "[^" ietf-drums-atext-token "]") string)
  262. (concat "\"" string "\"")
  263. string))
  264. (defun ietf-drums-make-address (name address)
  265. (if name
  266. (concat (ietf-drums-quote-string name) " <" address ">")
  267. address))
  268. (provide 'ietf-drums)
  269. ;;; ietf-drums.el ends here