* frame.el (msdos-mouse-p):
[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,
d7a0267c 4;; 2005, 2006, 2007 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
372a91d7 156;;;###autoload
0d20f9a0
JB
157(defun undigestify-rmail-message ()
158 "Break up a digest message into its constituent messages.
159Leaves original message, deleted, before the undigestified messages."
160 (interactive)
add0c454
RS
161 (with-current-buffer rmail-buffer
162 (widen)
add0c454
RS
163 (let ((error t)
164 (buffer-read-only nil))
3af9d2cf
FP
165 (goto-char (rmail-msgend rmail-current-message))
166 (let ((msg-copy (buffer-substring (rmail-msgbeg rmail-current-message)
167 (rmail-msgend rmail-current-message))))
168 (narrow-to-region (point) (point))
169 (insert msg-copy))
170 (narrow-to-region (point-min) (1- (point-max)))
add0c454
RS
171 (unwind-protect
172 (progn
173 (save-restriction
174 (goto-char (point-min))
175 (delete-region (point-min)
3af9d2cf 176 (progn (search-forward "\n*** EOOH ***\n" nil t)
add0c454 177 (point)))
47687ab8 178 (insert "\n" rmail-mail-separator)
add0c454
RS
179 (narrow-to-region (point)
180 (point-max))
3af9d2cf
FP
181 (let ((fill-prefix "")
182 (case-fold-search t)
183 digest-name type start end separator fun-list sep-list)
184 (setq digest-name (mail-strip-quoted-names
185 (save-restriction
186 (search-forward "\n\n" nil 'move)
187 (setq start (point))
188 (narrow-to-region (point-min) start)
189 (or (mail-fetch-field "Reply-To")
190 (mail-fetch-field "To")
191 (mail-fetch-field "Apparently-To")
192 (mail-fetch-field "From")))))
193 (unless digest-name
194 (error "Message is not a digest--bad header"))
195
196 (setq fun-list rmail-digest-methods)
197 (while (and fun-list
198 (null (setq sep-list (funcall (car fun-list)))))
199 (setq fun-list (cdr fun-list)))
200 (unless sep-list
201 (error "Message is not a digest--no messages found"))
202
203 ;;; Split the digest into separate rmail messages
204 (while sep-list
205 (let ((start (caar sep-list))
206 (end (cdar sep-list)))
207 (delete-region start end)
208 (goto-char start)
47687ab8 209 (insert rmail-mail-separator)
3af9d2cf
FP
210 (search-forward "\n\n" (caar (cdr sep-list)) 'move)
211 (save-restriction
212 (narrow-to-region end (point))
213 (unless (mail-fetch-field "To")
214 (goto-char start)
215 (insert "To: " digest-name "\n")))
216 (set-marker start nil)
217 (set-marker end nil))
218 (setq sep-list (cdr sep-list)))))
219
add0c454
RS
220 (setq error nil)
221 (message "Message successfully undigestified")
222 (let ((n rmail-current-message))
223 (rmail-forget-messages)
224 (rmail-show-message n)
225 (rmail-delete-forward)
226 (if (rmail-summary-exists)
227 (rmail-select-summary
228 (rmail-update-summary)))))
229 (cond (error
230 (narrow-to-region (point-min) (1+ (point-max)))
231 (delete-region (point-min) (point-max))
232 (rmail-show-message rmail-current-message)))))))
47687ab8 233\f
372a91d7 234;;;###autoload
9f606031
KH
235(defun unforward-rmail-message ()
236 "Extract a forwarded message from the containing message.
237This puts the forwarded message into a separate rmail message
238following the containing message."
239 (interactive)
add0c454 240 ;; If we are in a summary buffer, switch to the Rmail buffer.
47687ab8
FP
241 (unwind-protect
242 (with-current-buffer rmail-buffer
47687ab8 243 (goto-char (point-min))
89dac01f
FP
244 (narrow-to-region (point)
245 (save-excursion (search-forward "\n\n") (point)))
47687ab8 246 (let ((buffer-read-only nil)
89dac01f
FP
247 (old-fwd-from (mail-fetch-field "Forwarded-From" nil nil t))
248 (old-fwd-date (mail-fetch-field "Forwarded-Date" nil nil t))
249 (fwd-from (mail-fetch-field "From"))
250 (fwd-date (mail-fetch-field "Date"))
e49ba701 251 beg end prefix forward-msg)
89dac01f
FP
252 (narrow-to-region (rmail-msgbeg rmail-current-message)
253 (rmail-msgend rmail-current-message))
254 (goto-char (point-min))
a34804da 255 (cond ((re-search-forward rmail-forward-separator-regex nil t)
47687ab8 256 (forward-line 1)
59cb5b9b 257 (skip-chars-forward "\n")
47687ab8
FP
258 (setq beg (point))
259 (setq end (if (re-search-forward "^----.*[^- \t\n]" nil t)
260 (match-beginning 0) (point-max)))
261 (setq forward-msg
262 (replace-regexp-in-string
263 "^- -" "-" (buffer-substring beg end))))
85e94996 264 ((and (re-search-forward "^\\(> ?\\)[a-zA-Z-]+: .*\n" nil t)
47687ab8 265 (setq beg (match-beginning 0))
baad7c13 266 (setq prefix (match-string-no-properties 1))
85e94996 267 (goto-char beg)
47687ab8 268 (looking-at (concat "\\(" prefix ".+\n\\)*"
baad7c13 269 prefix "Date: ."))
85e94996
FP
270 (looking-at (concat "\\(" prefix ".+\n\\)*"
271 prefix "From: .+\n"
272 "\\(" prefix ".+\n\\)*"
47687ab8
FP
273 "\\(> ?\\)?\n" prefix)))
274 (re-search-forward "^[^>\n]" nil 'move)
275 (backward-char)
276 (skip-chars-backward " \t\n")
277 (forward-line 1)
278 (setq end (point))
279 (setq forward-msg
280 (replace-regexp-in-string
281 (if (string= prefix ">") "^>" "> ?")
282 "" (buffer-substring beg end))))
283 (t
284 (error "No forwarded message found")))
285 (widen)
286 (goto-char (rmail-msgend rmail-current-message))
287 (narrow-to-region (point) (point))
288 (insert rmail-mail-separator)
289 (narrow-to-region (point) (point))
89dac01f
FP
290 (while old-fwd-from
291 (insert "Forwarded-From: " (car old-fwd-from) "\n")
292 (insert "Forwarded-Date: " (car old-fwd-date) "\n")
293 (setq old-fwd-from (cdr old-fwd-from))
294 (setq old-fwd-date (cdr old-fwd-date)))
295 (insert "Forwarded-From: " fwd-from "\n")
296 (insert "Forwarded-Date: " fwd-date "\n")
47687ab8
FP
297 (insert forward-msg)
298 (save-restriction
299 (goto-char (point-min))
300 (re-search-forward "\n$" nil 'move)
301 (narrow-to-region (point-min) (point))
302 (goto-char (point-min))
303 (while (not (eobp))
304 (unless (looking-at "^[a-zA-Z-]+: ")
305 (insert "\t"))
306 (forward-line)))
307 (goto-char (point-min))))
e49ba701
KS
308 (let ((n rmail-current-message))
309 (rmail-forget-messages)
310 (rmail-show-message n))
47687ab8
FP
311 (if (rmail-summary-exists)
312 (rmail-select-summary
313 (rmail-update-summary)))))
314
9f606031 315
896546cd
RS
316(provide 'undigest)
317
ab5796a9 318;;; arch-tag: 3a28b9fb-c1f5-43ef-9278-285f3e4b874d
d501f516 319;;; undigest.el ends here