Convert consecutive FSF copyright years to ranges.
[bpt/emacs.git] / lisp / mail / undigest.el
CommitLineData
d501f516
ER
1;;; undigest.el --- digest-cracking support for the RMAIL mail reader
2
73b0cd50 3;; Copyright (C) 1985-1986, 1994, 1996, 2001-2011 Free Software Foundation, Inc.
58142744 4
e5167999 5;; Maintainer: FSF
d7b4d18f 6;; Keywords: mail
e5167999 7
0d20f9a0
JB
8;; This file is part of GNU Emacs.
9
b1fc2b50 10;; GNU Emacs is free software: you can redistribute it and/or modify
0d20f9a0 11;; it under the terms of the GNU General Public License as published by
b1fc2b50
GM
12;; the Free Software Foundation, either version 3 of the License, or
13;; (at your option) any later version.
0d20f9a0
JB
14
15;; GNU Emacs is distributed in the hope that it will be useful,
16;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18;; GNU General Public License for more details.
19
20;; You should have received a copy of the GNU General Public License
b1fc2b50 21;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
0d20f9a0 22
e5167999
ER
23;;; Commentary:
24
7a613b67
GM
25;; See Internet RFC 934 and RFC 1153.
26;; Also limited support for MIME digest encapsulation.
e5167999
ER
27
28;;; Code:
0d20f9a0 29
4fe9b563
KH
30(require 'rmail)
31
a34804da
FP
32(defcustom rmail-forward-separator-regex
33 "^----.*\\([Ff]orwarded\\|[Oo]riginal\\).*[Mm]essage"
34 "*Regexp to match the string that introduces forwarded messages.
35This is not a header, but a string contained in the body of the message.
1acb4c26 36You may need to customize it for local needs."
a34804da
FP
37 :type 'regexp
38 :group 'rmail-headers)
39
47687ab8 40\f
3af9d2cf
FP
41(defconst rmail-digest-methods
42 '(rmail-digest-parse-mime
43 rmail-digest-parse-rfc1153strict
44 rmail-digest-parse-rfc1153sloppy
45 rmail-digest-parse-rfc934)
47687ab8 46 "List of digest parsing functions, first tried first.
3af9d2cf 47
47687ab8
FP
48These functions operate on the current narrowing, and take no argument.
49A function returns nil if it cannot parse the digest. If it can, it
3af9d2cf
FP
50returns a list of cons pairs containing the start and end positions of
51each undigestified message as markers.")
52
3af9d2cf
FP
53(defun rmail-digest-parse-mime ()
54 (goto-char (point-min))
55 (when (let ((head-end (progn (search-forward "\n\n" nil t) (point))))
56 (goto-char (point-min))
7a613b67 57 (and head-end ; FIXME always true
3af9d2cf
FP
58 (re-search-forward
59 (concat
60 "^Content-type: multipart/digest;"
61 "\\s-* boundary=\"?\\([^\";\n]+\\)[\";\n]") head-end t)
62 (search-forward (match-string 1) nil t)))
63 ;; Ok, prolog separator found
64 (let ((start (make-marker))
65 (end (make-marker))
66 (separator (concat "\n--" (match-string 0) "\n\n"))
67 result)
68 (while (search-forward separator nil t)
69 (move-marker start (match-beginning 0))
70 (move-marker end (match-end 0))
71 (add-to-list 'result (cons (copy-marker start) (copy-marker end t))))
72 ;; Return the list of marker pairs
73 (nreverse result))))
74
75(defun rmail-digest-parse-rfc1153strict ()
76 "Parse following strictly the method defined in RFC 1153.
77See rmail-digest-methods."
78 (rmail-digest-rfc1153
79 "^-\\{70\\}\n\n"
80 "^\n-\\{30\\}\n\n"
81 "^\n-\\{30\\}\n\nEnd of .* Digest.*\n\\*\\{15,\\}\n+\'"))
82
83(defun rmail-digest-parse-rfc1153sloppy ()
84 "Parse using the method defined in RFC 1153, allowing for some sloppiness.
85See rmail-digest-methods."
86 (rmail-digest-rfc1153
87 "^-\\{55,\\}\n\n"
88 "^\n-\\{27,\\}\n\n"
980d5def
GM
89 ;; GNU Mailman knowingly (see comment at line 353 of ToDigest.py in
90 ;; Mailman source) produces non-conformant rfc 1153 digests, in that
91 ;; the trailer contains a "digest footer" like this:
92 ;; _______________________________________________
93 ;; <one or more lines of list blurb>
94 ;;
95 ;; End of Foo Digest...
96 ;; **************************************
97 "^\nEnd of"))
3af9d2cf
FP
98
99(defun rmail-digest-rfc1153 (prolog-sep message-sep trailer-sep)
100 (goto-char (point-min))
101 (when (re-search-forward prolog-sep nil t)
102 ;; Ok, prolog separator found
103 (let ((start (make-marker))
104 (end (make-marker))
105 separator result)
106 (move-marker start (match-beginning 0))
107 (move-marker end (match-end 0))
980d5def 108 (setq result (list (cons (copy-marker start) (copy-marker end t))))
3af9d2cf
FP
109 (when (re-search-forward message-sep nil t)
110 ;; Ok, at least one message separator found
111 (setq separator (match-string 0))
112 (when (re-search-forward trailer-sep nil t)
113 ;; Wonderful, we found a trailer, too. Now, go on splitting
114 ;; the digest into separate rmail messages
115 (goto-char (cdar result))
116 (while (search-forward separator nil t)
117 (move-marker start (match-beginning 0))
118 (move-marker end (match-end 0))
119 (add-to-list 'result
120 (cons (copy-marker start) (copy-marker end t))))
121 ;; Undo masking of separators inside digestified messages
122 (goto-char (point-min))
123 (while (search-forward
124 (replace-regexp-in-string "\n-" "\n " separator) nil t)
125 (replace-match separator))
126 ;; Return the list of marker pairs
127 (nreverse result))))))
128
129(defun rmail-digest-parse-rfc934 ()
130 (goto-char (point-min))
131 (when (re-search-forward "^\n?-[^ ].*\n\n?" nil t)
132 ;; Message separator found
133 (let ((start (make-marker))
134 (end (make-marker))
135 (separator (match-string 0))
136 result)
137 (goto-char (point-min))
138 (while (search-forward separator nil t)
139 (move-marker start (match-beginning 0))
140 (move-marker end (match-end 0))
141 (add-to-list 'result (cons (copy-marker start) (copy-marker end t))))
142 ;; Undo masking of separators inside digestified messages
143 (goto-char (point-min))
144 (while (search-forward "\n- -" nil t)
145 (replace-match "\n-"))
146 ;; Return the list of marker pairs
147 (nreverse result))))
148
2b54af74
DN
149(declare-function rmail-update-summary "rmailsum" (&rest ignore))
150
372a91d7 151;;;###autoload
0d20f9a0
JB
152(defun undigestify-rmail-message ()
153 "Break up a digest message into its constituent messages.
154Leaves original message, deleted, before the undigestified messages."
155 (interactive)
7a613b67
GM
156 (set-buffer rmail-buffer)
157 (let ((buff (current-buffer))
158 (current rmail-current-message)
159 (msgbeg (rmail-msgbeg rmail-current-message))
160 (msgend (rmail-msgend rmail-current-message)))
161 (if (rmail-buffers-swapped-p) (set-buffer rmail-view-buffer))
add0c454 162 (widen)
add0c454
RS
163 (let ((error t)
164 (buffer-read-only nil))
7a613b67
GM
165 (goto-char msgend)
166 (let ((msg-copy (buffer-substring-no-properties msgbeg msgend)))
3af9d2cf 167 (narrow-to-region (point) (point))
7a613b67
GM
168 (insert "\n" msg-copy))
169 (goto-char (point-min))
add0c454
RS
170 (unwind-protect
171 (progn
7a613b67
GM
172 (let ((fill-prefix "")
173 (case-fold-search t)
174 digest-name fun-list sep-list start end)
175 (setq digest-name (mail-strip-quoted-names
176 (save-restriction
177 (search-forward "\n\n" nil 'move)
178 (narrow-to-region (point-min) (point))
179 (or (mail-fetch-field "Reply-To")
180 (mail-fetch-field "To")
181 (mail-fetch-field "Apparently-To")
182 (mail-fetch-field "From")))))
183 (unless digest-name
184 (error "Message is not a digest--bad header"))
185 (setq fun-list rmail-digest-methods)
186 (while (and fun-list
187 (null (setq sep-list (funcall (car fun-list)))))
188 (setq fun-list (cdr fun-list)))
189 (unless sep-list
190 (error "Message is not a digest--no messages found"))
191 ;; Split the digest into separate rmail messages.
192 (while sep-list
193 (setq start (caar sep-list)
194 end (cdar sep-list))
195 (delete-region start end)
196 (goto-char start)
197 (search-forward "\n\n" (caar (cdr sep-list)) 'move)
198 (save-restriction
199 (narrow-to-region end (point))
200 (goto-char (point-min))
201 (insert "\nFrom rmail@localhost " (current-time-string) "\n")
202 (save-excursion
203 (forward-line -1)
204 (rmail-add-mbox-headers))
205 (unless (mail-fetch-field "To")
206 (insert "To: " digest-name "\n")))
207 (set-marker start nil)
208 (set-marker end nil)
209 (setq sep-list (cdr sep-list))))
add0c454
RS
210 (setq error nil)
211 (message "Message successfully undigestified")
7a613b67
GM
212 (set-buffer buff)
213 (rmail-swap-buffers-maybe)
214 (goto-char (point-max))
35426db4 215 ;; FIXME use rmail-count-new-messages.
7a613b67
GM
216 (rmail-set-message-counters)
217 (set-buffer-modified-p t)
218 (rmail-show-message current)
219 (rmail-delete-forward)
220 (if (rmail-summary-exists)
221 (rmail-select-summary (rmail-update-summary))))
222 (when error
223 (delete-region (point-min) (point-max))
224 (set-buffer buff)
225 (rmail-show-message current))))))
47687ab8 226\f
372a91d7 227;;;###autoload
9f606031
KH
228(defun unforward-rmail-message ()
229 "Extract a forwarded message from the containing message.
230This puts the forwarded message into a separate rmail message
231following the containing message."
232 (interactive)
7a613b67
GM
233 (set-buffer rmail-buffer)
234 (let ((buff (current-buffer))
235 (current rmail-current-message)
236 (beg (rmail-msgbeg rmail-current-message))
237 (msgend (rmail-msgend rmail-current-message))
238 (error t))
239 (unwind-protect
240 (progn
241 (if (rmail-buffers-swapped-p) (set-buffer rmail-view-buffer))
47687ab8 242 (widen)
7a613b67
GM
243 (goto-char beg)
244 (search-forward "\n\n" msgend)
245 (narrow-to-region beg (point))
246 (let ((old-fwd-from (mail-fetch-field "Forwarded-From" nil nil t))
247 (old-fwd-date (mail-fetch-field "Forwarded-Date" nil nil t))
248 (fwd-from (mail-fetch-field "From"))
249 (fwd-date (mail-fetch-field "Date"))
250 (buffer-read-only nil)
251 prefix forward-msg end)
252 (widen)
253 (narrow-to-region beg msgend)
254 (cond ((re-search-forward rmail-forward-separator-regex nil t)
255 (forward-line 1)
256 (skip-chars-forward "\n")
257 (setq beg (point))
258 (setq end (if (re-search-forward "^----.*[^- \t\n]" nil t)
259 (match-beginning 0) (point-max)))
260 (setq forward-msg
261 (replace-regexp-in-string
262 "^- -" "-" (buffer-substring beg end))))
263 ((and (re-search-forward "^\\(> ?\\)[a-zA-Z-]+: .*\n" nil t)
264 (setq beg (match-beginning 0))
265 (setq prefix (match-string-no-properties 1))
266 (goto-char beg)
267 (looking-at (concat "\\(" prefix ".+\n\\)*"
268 prefix "Date: ."))
269 (looking-at (concat "\\(" prefix ".+\n\\)*"
270 prefix "From: .+\n"
271 "\\(" prefix ".+\n\\)*"
272 "\\(> ?\\)?\n" prefix)))
273 (re-search-forward "^[^>\n]" nil 'move)
274 (backward-char)
275 (skip-chars-backward " \t\n")
276 (forward-line 1)
277 (setq end (point))
278 (setq forward-msg
279 (replace-regexp-in-string
280 (if (string= prefix ">") "^>" "> ?")
281 "" (buffer-substring beg end))))
282 (t
283 (error "No forwarded message found")))
284 (widen)
285 (goto-char msgend)
286 ;; Insert a fake From line.
287 ;; FIXME we could construct one using the From and Date headers
288 ;; of the forwarded message - is it worth it?
289 (insert "\n\nFrom rmail@localhost " (current-time-string) "\n")
290 (setq beg (point)) ; start of header
291 (while old-fwd-from
292 (insert "Forwarded-From: " (car old-fwd-from) "\n")
293 (insert "Forwarded-Date: " (car old-fwd-date) "\n")
294 (setq old-fwd-from (cdr old-fwd-from))
295 (setq old-fwd-date (cdr old-fwd-date)))
296 (insert "Forwarded-From: " fwd-from "\n")
297 (insert "Forwarded-Date: " fwd-date "\n")
298 (insert forward-msg "\n")
299 (goto-char beg)
300 (re-search-forward "\n$" nil 'move) ; end of header
301 (narrow-to-region beg (point))
47687ab8
FP
302 (goto-char (point-min))
303 (while (not (eobp))
304 (unless (looking-at "^[a-zA-Z-]+: ")
305 (insert "\t"))
7a613b67
GM
306 (forward-line))
307 (widen)
308 (goto-char beg)
309 (forward-line -1)
310 (rmail-add-mbox-headers)) ; marks as unseen
311 (setq error nil)
312 (set-buffer buff)
313 (rmail-swap-buffers-maybe)
314 (goto-char (point-max))
35426db4 315 ;; FIXME use rmail-count-new-messages.
7a613b67
GM
316 (rmail-set-message-counters)
317 (set-buffer-modified-p t)
318 (rmail-show-message current)
319 (if (rmail-summary-exists)
320 (rmail-select-summary (rmail-update-summary))))
321 (when error
322 (set-buffer buff)
323 (rmail-show-message current)))))
9f606031 324
896546cd
RS
325(provide 'undigest)
326
35426db4
GM
327;; Local Variables:
328;; generated-autoload-file: "rmail.el"
329;; End:
330
d501f516 331;;; undigest.el ends here