diff options
| author | Richard M. Stallman | 1990-01-17 00:48:36 +0000 |
|---|---|---|
| committer | Richard M. Stallman | 1990-01-17 00:48:36 +0000 |
| commit | cedaf3aa8426893a73e3d0f230d17ad2f3bf38f1 (patch) | |
| tree | a020c4f1597dfc886733b67b32667f222d223623 | |
| parent | 701570d7d6b2239163cd22150e8709dbf53fcaf1 (diff) | |
| download | emacs-cedaf3aa8426893a73e3d0f230d17ad2f3bf38f1.tar.gz emacs-cedaf3aa8426893a73e3d0f230d17ad2f3bf38f1.zip | |
Initial revision
| -rw-r--r-- | lisp/mail/rfc822.el | 301 |
1 files changed, 301 insertions, 0 deletions
diff --git a/lisp/mail/rfc822.el b/lisp/mail/rfc822.el new file mode 100644 index 00000000000..f0662c2f6f2 --- /dev/null +++ b/lisp/mail/rfc822.el | |||
| @@ -0,0 +1,301 @@ | |||
| 1 | ;; Hairy rfc822 parser for mail and news and suchlike | ||
| 2 | ;; Copyright (C) 1986, 1987 Free Software Foundation, Inc. | ||
| 3 | ;; Author Richard Mlynarik. | ||
| 4 | |||
| 5 | ;; This file is part of GNU Emacs. | ||
| 6 | |||
| 7 | ;; GNU Emacs is free software; you can redistribute it and/or modify | ||
| 8 | ;; it under the terms of the GNU General Public License as published by | ||
| 9 | ;; the Free Software Foundation; either version 1, or (at your option) | ||
| 10 | ;; any later version. | ||
| 11 | |||
| 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 | |||
| 17 | ;; You should have received a copy of the GNU General Public License | ||
| 18 | ;; along with GNU Emacs; see the file COPYING. If not, write to | ||
| 19 | ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 20 | |||
| 21 | (provide 'rfc822) | ||
| 22 | |||
| 23 | ;; uses address-start free, throws to address | ||
| 24 | (defun rfc822-bad-address (reason) | ||
| 25 | (save-restriction | ||
| 26 | (insert "_^_") | ||
| 27 | (narrow-to-region address-start | ||
| 28 | (if (re-search-forward "[,;]" nil t) | ||
| 29 | (max (point-min) (1- (point))) | ||
| 30 | (point-max))) | ||
| 31 | ;; make the error string be suitable for inclusion in (...) | ||
| 32 | (let ((losers '("\\" "(" ")" "\n"))) | ||
| 33 | (while losers | ||
| 34 | (goto-char (point-min)) | ||
| 35 | (while (search-forward (car losers) nil t) | ||
| 36 | (backward-char 1) | ||
| 37 | (insert ?\\) | ||
| 38 | (forward-char 1)) | ||
| 39 | (setq losers (cdr losers)))) | ||
| 40 | (goto-char (point-min)) (insert "(Unparsable address -- " | ||
| 41 | reason | ||
| 42 | ":\n\t \"") | ||
| 43 | (goto-char (point-max)) (insert "\")")) | ||
| 44 | (rfc822-nuke-whitespace) | ||
| 45 | (throw 'address (buffer-substring address-start (point)))) | ||
| 46 | |||
| 47 | (defun rfc822-nuke-whitespace (&optional leave-space) | ||
| 48 | (let (ch) | ||
| 49 | (while (cond ((eobp) | ||
| 50 | nil) | ||
| 51 | ((= (setq ch (following-char)) ?\() | ||
| 52 | (forward-char 1) | ||
| 53 | (while (if (eobp) | ||
| 54 | (rfc822-bad-address "Unbalanced comment (...)") | ||
| 55 | (/= (setq ch (following-char)) ?\))) | ||
| 56 | (cond ((looking-at "[^()\\]+") | ||
| 57 | (replace-match "")) | ||
| 58 | ((= ch ?\() | ||
| 59 | (rfc822-nuke-whitespace)) | ||
| 60 | ((< (point) (1- (point-max))) | ||
| 61 | (delete-char 2)) | ||
| 62 | (t | ||
| 63 | (rfc822-bad-address "orphaned backslash")))) | ||
| 64 | ;; delete remaining "()" | ||
| 65 | (forward-char -1) | ||
| 66 | (delete-char 2) | ||
| 67 | t) | ||
| 68 | ((memq ch '(?\ ?\t ?\n)) | ||
| 69 | (delete-region (point) | ||
| 70 | (progn (skip-chars-forward " \t\n") (point))) | ||
| 71 | t) | ||
| 72 | (t | ||
| 73 | nil))) | ||
| 74 | (or (not leave-space) | ||
| 75 | (eobp) | ||
| 76 | (bobp) | ||
| 77 | (= (preceding-char) ?\ ) | ||
| 78 | (insert ?\ )))) | ||
| 79 | |||
| 80 | (defun rfc822-looking-at (regex &optional leave-space) | ||
| 81 | (if (cond ((stringp regex) | ||
| 82 | (if (looking-at regex) | ||
| 83 | (progn (goto-char (match-end 0)) | ||
| 84 | t))) | ||
| 85 | (t | ||
| 86 | (if (and (not (eobp)) | ||
| 87 | (= (following-char) regex)) | ||
| 88 | (progn (forward-char 1) | ||
| 89 | t)))) | ||
| 90 | (let ((tem (match-data))) | ||
| 91 | (rfc822-nuke-whitespace leave-space) | ||
| 92 | (store-match-data tem) | ||
| 93 | t))) | ||
| 94 | |||
| 95 | (defun rfc822-snarf-word () | ||
| 96 | ;; word is atom | quoted-string | ||
| 97 | (cond ((= (following-char) ?\") | ||
| 98 | ;; quoted-string | ||
| 99 | (or (rfc822-looking-at "\"\\([^\"\\\n]\\|\\\\.\\|\\\\\n\\)*\"") | ||
| 100 | (rfc822-bad-address "Unterminated quoted string"))) | ||
| 101 | ((rfc822-looking-at "[^][\000-\037\177-\377 ()<>@,;:\\\".]+") | ||
| 102 | ;; atom | ||
| 103 | ) | ||
| 104 | (t | ||
| 105 | (rfc822-bad-address "Rubbish in address")))) | ||
| 106 | |||
| 107 | (defun rfc822-snarf-words () | ||
| 108 | (rfc822-snarf-word) | ||
| 109 | (while (rfc822-looking-at ?.) | ||
| 110 | (rfc822-snarf-word))) | ||
| 111 | |||
| 112 | (defun rfc822-snarf-subdomain () | ||
| 113 | ;; sub-domain is domain-ref | domain-literal | ||
| 114 | (cond ((= (following-char) ?\[) | ||
| 115 | ;; domain-ref | ||
| 116 | (or (rfc822-looking-at "\\[\\([^][\\\n]\\|\\\\.\\|\\\\\n\\)*\\]") | ||
| 117 | (rfc822-bad-address "Unterminated domain literal [...]"))) | ||
| 118 | ((rfc822-looking-at "[^][\000-\037\177-\377 ()<>@,;:\\\".]+") | ||
| 119 | ;; domain-literal = atom | ||
| 120 | ) | ||
| 121 | (t | ||
| 122 | (rfc822-bad-address "Rubbish in host/domain specification")))) | ||
| 123 | |||
| 124 | (defun rfc822-snarf-domain () | ||
| 125 | (rfc822-snarf-subdomain) | ||
| 126 | (while (rfc822-looking-at ?.) | ||
| 127 | (rfc822-snarf-subdomain))) | ||
| 128 | |||
| 129 | (defun rfc822-snarf-frob-list (name separator terminator snarfer | ||
| 130 | &optional return) | ||
| 131 | (let ((first t) | ||
| 132 | (list ()) | ||
| 133 | tem) | ||
| 134 | (while (cond ((eobp) | ||
| 135 | (rfc822-bad-address | ||
| 136 | (format "End of addresses in middle of %s" name))) | ||
| 137 | ((rfc822-looking-at terminator) | ||
| 138 | nil) | ||
| 139 | ((rfc822-looking-at separator) | ||
| 140 | ;; multiple separators are allowed and do nothing. | ||
| 141 | (while (rfc822-looking-at separator)) | ||
| 142 | t) | ||
| 143 | (first | ||
| 144 | t) | ||
| 145 | (t | ||
| 146 | (rfc822-bad-address | ||
| 147 | (format "Gubbish in middle of %s" name)))) | ||
| 148 | (setq tem (funcall snarfer) | ||
| 149 | first nil) | ||
| 150 | (and return tem | ||
| 151 | (setq list (if (listp tem) | ||
| 152 | (nconc (reverse tem) list) | ||
| 153 | (cons tem list))))) | ||
| 154 | (nreverse list))) | ||
| 155 | |||
| 156 | ;; return either an address (a string) or a list of addresses | ||
| 157 | (defun rfc822-addresses-1 (&optional allow-groups) | ||
| 158 | ;; Looking for an rfc822 `address' | ||
| 159 | ;; Either a group (1*word ":" [#mailbox] ";") | ||
| 160 | ;; or a mailbox (addr-spec | 1*word route-addr) | ||
| 161 | ;; addr-spec is (local-part "@" domain) | ||
| 162 | ;; route-addr is ("<" [1#("@" domain) ":"] addr-spec ">") | ||
| 163 | ;; local-part is (word *("." word)) | ||
| 164 | ;; word is (atom | quoted-string) | ||
| 165 | ;; quoted-string is ("\([^\"\\n]\|\\.\|\\\n\)") | ||
| 166 | ;; atom is [^\000-\037\177 ()<>@,;:\".[]]+ | ||
| 167 | ;; domain is sub-domain *("." sub-domain) | ||
| 168 | ;; sub-domain is domain-ref | domain-literal | ||
| 169 | ;; domain-literal is "[" *(dtext | quoted-pair) "]" | ||
| 170 | ;; dtext is "[^][\\n" | ||
| 171 | ;; domain-ref is atom | ||
| 172 | (let ((address-start (point)) | ||
| 173 | (n 0)) | ||
| 174 | (catch 'address | ||
| 175 | ;; optimize common cases: | ||
| 176 | ;; foo | ||
| 177 | ;; foo.bar@bar.zap | ||
| 178 | ;; followed by "\\'\\|,\\|([^()\\]*)\\'" | ||
| 179 | ;; other common cases are: | ||
| 180 | ;; foo bar <foo.bar@baz.zap> | ||
| 181 | ;; "foo bar" <foo.bar@baz.zap> | ||
| 182 | ;; those aren't hacked yet. | ||
| 183 | (if (and (rfc822-looking-at "[^][\000-\037\177-\377 ()<>@,;:\\\"]+\\(\\|@[^][\000-\037\177-\377 ()<>@,;:\\\"]+\\)" t) | ||
| 184 | (progn (or (eobp) | ||
| 185 | (rfc822-looking-at ?,)))) | ||
| 186 | (progn | ||
| 187 | ;; rfc822-looking-at may have inserted a space | ||
| 188 | (or (bobp) (/= (preceding-char) ?\ ) (delete-char -1)) | ||
| 189 | ;; relying on the fact that rfc822-looking-at <char> | ||
| 190 | ;; doesn't mung match-data | ||
| 191 | (throw 'address (buffer-substring address-start (match-end 0))))) | ||
| 192 | (goto-char address-start) | ||
| 193 | (while t | ||
| 194 | (cond ((and (= n 1) (rfc822-looking-at ?@)) | ||
| 195 | ;; local-part@domain | ||
| 196 | (rfc822-snarf-domain) | ||
| 197 | (throw 'address | ||
| 198 | (buffer-substring address-start (point)))) | ||
| 199 | ((rfc822-looking-at ?:) | ||
| 200 | (cond ((not allow-groups) | ||
| 201 | (rfc822-bad-address "A group name may not appear here")) | ||
| 202 | ((= n 0) | ||
| 203 | (rfc822-bad-address "No name for :...; group"))) | ||
| 204 | ;; group | ||
| 205 | (throw 'address | ||
| 206 | ;; return a list of addresses | ||
| 207 | (rfc822-snarf-frob-list ":...; group" ?\, ?\; | ||
| 208 | 'rfc822-addresses-1 t))) | ||
| 209 | ((rfc822-looking-at ?<) | ||
| 210 | (let ((start (point)) | ||
| 211 | (strip t)) | ||
| 212 | (cond ((rfc822-looking-at ?>) | ||
| 213 | ;; empty path | ||
| 214 | ()) | ||
| 215 | ((and (not (eobp)) (= (following-char) ?\@)) | ||
| 216 | ;; <@foo.bar,@baz:quux@abcd.efg> | ||
| 217 | (rfc822-snarf-frob-list "<...> address" ?\, ?\: | ||
| 218 | (function (lambda () | ||
| 219 | (if (rfc822-looking-at ?\@) | ||
| 220 | (rfc822-snarf-domain) | ||
| 221 | (rfc822-bad-address | ||
| 222 | "Gubbish in route-addr"))))) | ||
| 223 | (rfc822-snarf-words) | ||
| 224 | (or (rfc822-looking-at ?@) | ||
| 225 | (rfc822-bad-address "Malformed <..@..> address")) | ||
| 226 | (rfc822-snarf-domain) | ||
| 227 | (setq strip nil)) | ||
| 228 | ((progn (rfc822-snarf-words) (rfc822-looking-at ?@)) | ||
| 229 | ; allow <foo> (losing unix seems to do this) | ||
| 230 | (rfc822-snarf-domain))) | ||
| 231 | (let ((end (point))) | ||
| 232 | (if (rfc822-looking-at ?\>) | ||
| 233 | (throw 'address | ||
| 234 | (buffer-substring (if strip start (1- start)) | ||
| 235 | (if strip end (1+ end)))) | ||
| 236 | (rfc822-bad-address "Unterminated <...> address"))))) | ||
| 237 | ((looking-at "[^][\000-\037\177-\377 ()<>@,;:\\.]") | ||
| 238 | ;; this allows "." to be part of the words preceding | ||
| 239 | ;; an addr-spec, since many broken mailers output | ||
| 240 | ;; "Hern K. Herklemeyer III | ||
| 241 | ;; <yank@megadeath.dod.gods-own-country>" | ||
| 242 | (or (= n 0) | ||
| 243 | (= (preceding-char) ?\ ) | ||
| 244 | (insert ?\ )) | ||
| 245 | (rfc822-snarf-words) | ||
| 246 | (setq n (1+ n))) | ||
| 247 | ((= n 0) | ||
| 248 | (throw 'address nil)) | ||
| 249 | ((= n 1) ; allow "foo" (losing unix seems to do this) | ||
| 250 | (throw 'address | ||
| 251 | (buffer-substring address-start (point)))) | ||
| 252 | ((or (eobp) (looking-at ",")) | ||
| 253 | (rfc822-bad-address "Missing comma or route-spec")) | ||
| 254 | (t | ||
| 255 | (rfc822-bad-address "Strange character or missing comma"))))))) | ||
| 256 | |||
| 257 | |||
| 258 | (defun rfc822-addresses (header-text) | ||
| 259 | (if (string-match "\\`[ \t]*\\([^][\000-\037\177-\377 ()<>@,;:\\\".]+\\)[ \t]*\\'" | ||
| 260 | header-text) | ||
| 261 | ;; Make very simple case moderately fast. | ||
| 262 | (list (substring header-text (match-beginning 1) (match-end 1))) | ||
| 263 | (let ((buf (generate-new-buffer " rfc822"))) | ||
| 264 | (unwind-protect | ||
| 265 | (save-excursion | ||
| 266 | (set-buffer buf) | ||
| 267 | (make-local-variable 'case-fold-search) | ||
| 268 | (setq case-fold-search nil) ;For speed(?) | ||
| 269 | (insert header-text) | ||
| 270 | ;; unfold continuation lines | ||
| 271 | (goto-char (point-min)) | ||
| 272 | |||
| 273 | (while (re-search-forward "\\([^\\]\\(\\\\\\\\\\)*\\)\n[ \t]" nil t) | ||
| 274 | (replace-match "\\1 " t)) | ||
| 275 | |||
| 276 | (goto-char (point-min)) | ||
| 277 | (rfc822-nuke-whitespace) | ||
| 278 | (let ((list ()) | ||
| 279 | tem | ||
| 280 | address-start); this is for rfc822-bad-address | ||
| 281 | (while (not (eobp)) | ||
| 282 | (setq address-start (point)) | ||
| 283 | (setq tem | ||
| 284 | (catch 'address ; this is for rfc822-bad-address | ||
| 285 | (cond ((rfc822-looking-at ?\,) | ||
| 286 | nil) | ||
| 287 | ((looking-at "[][\000-\037\177-\377@;:\\.>]") | ||
| 288 | (forward-char) | ||
| 289 | (rfc822-bad-address | ||
| 290 | (format "Strange character \\%c found" | ||
| 291 | (preceding-char)))) | ||
| 292 | (t | ||
| 293 | (rfc822-addresses-1 t))))) | ||
| 294 | (cond ((null tem)) | ||
| 295 | ((stringp tem) | ||
| 296 | (setq list (cons tem list))) | ||
| 297 | (t | ||
| 298 | (setq list (nconc (nreverse tem) list))))) | ||
| 299 | (nreverse list))) | ||
| 300 | (and buf (kill-buffer buf)))))) | ||
| 301 | |||