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