Minor tweak.
[bpt/emacs.git] / lisp / mh-e / mh-mime.el
CommitLineData
bdcfe844 1;;; mh-mime.el --- MH-E support for composing MIME messages
c26cf6c8 2
e495eaec 3;; Copyright (C) 1993, 1995,
deceef67 4;; 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
a1b4049d
BW
5
6;; Author: Bill Wohler <wohler@newt.com>
7;; Maintainer: Bill Wohler <wohler@newt.com>
8;; Keywords: mail
9;; See: mh-e.el
c26cf6c8 10
60370d40 11;; This file is part of GNU Emacs.
c26cf6c8 12
9b7bc076 13;; GNU Emacs is free software; you can redistribute it and/or modify
c26cf6c8
RS
14;; it under the terms of the GNU General Public License as published by
15;; the Free Software Foundation; either version 2, or (at your option)
16;; any later version.
17
9b7bc076 18;; GNU Emacs is distributed in the hope that it will be useful,
c26cf6c8
RS
19;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21;; GNU General Public License for more details.
22
23;; You should have received a copy of the GNU General Public License
b578f267 24;; along with GNU Emacs; see the file COPYING. If not, write to the
3a35cf56
LK
25;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
26;; Boston, MA 02110-1301, USA.
c26cf6c8
RS
27
28;;; Commentary:
29
bdcfe844 30;; Internal support for MH-E package.
0c47b17c
BW
31;; Support for generating MH-style directives for mhn or mhbuild as well as
32;; MML (MIME Meta Language) tags. MH-style directives are supported by MH 6.8
33;; or later.
c26cf6c8 34
847b8219
KH
35;;; Change Log:
36
c26cf6c8
RS
37;;; Code:
38
f0d73c14 39(eval-when-compile (require 'mh-acros))
a66894d8
BW
40(mh-require-cl)
41(require 'mh-comp)
bdcfe844 42(require 'gnus-util)
a66894d8 43(require 'mh-gnus)
bdcfe844 44
bdcfe844 45(autoload 'article-emphasize "gnus-art")
0c47b17c 46(autoload 'gnus-article-goto-header "gnus-art")
bdcfe844 47(autoload 'gnus-eval-format "gnus-spec")
0c47b17c 48(autoload 'gnus-get-buffer-create "gnus")
bdcfe844 49(autoload 'message-options-set-recipient "message")
1bea9ec4 50(autoload 'mm-uu-dissect "mm-uu")
0c47b17c 51(autoload 'mml-unsecure-message "mml-sec")
924df208 52(autoload 'rfc2047-decode-region "rfc2047")
0c47b17c 53(autoload 'widget-convert-button "wid-edit")
bdcfe844 54
c3d9274a 55;;;###mh-autoload
bdcfe844 56(defun mh-compose-insertion (&optional inline)
0c47b17c 57 "Add tag to include a file such as an image or sound.
0c47b17c 58
2dcf34f9
BW
59You are prompted for the filename containing the object, the
60media type if it cannot be determined automatically, and a
61content description. If you're using MH-style directives, you
62will also be prompted for additional attributes.
63
64The option `mh-compose-insertion' controls what type of tags are
65inserted. Optional argument INLINE means make it an inline
66attachment."
bdcfe844 67 (interactive "P")
0c47b17c 68 (if (equal mh-compose-insertion 'mml)
bdcfe844
BW
69 (if inline
70 (mh-mml-attach-file "inline")
71 (mh-mml-attach-file))
0c47b17c 72 (call-interactively 'mh-mh-attach-file)))
bdcfe844 73
c3d9274a 74;;;###mh-autoload
a05fcb7d 75(defun mh-compose-forward (&optional description folder messages)
0c47b17c 76 "Add tag to forward a message.
2dcf34f9
BW
77
78You are prompted for a content DESCRIPTION, the name of the
79FOLDER in which the messages to forward are located, and the
80MESSAGES' numbers.
0c47b17c
BW
81
82The option `mh-compose-insertion' controls what type of tags are inserted."
a05fcb7d 83 (interactive (let*
0c47b17c 84 ((description (mml-minibuffer-read-description))
a05fcb7d
BW
85 (folder (mh-prompt-for-folder "Message from"
86 mh-sent-from-folder nil))
87 (messages (let ((default-message
88 (if (and (equal
89 folder mh-sent-from-folder)
90 (numberp mh-sent-from-msg))
91 mh-sent-from-msg
92 (nth 0 (mh-translate-range
93 folder "cur")))))
94 (if default-message
95 (read-string
96 (format "Messages (default %d): "
97 default-message)
98 nil nil
99 (number-to-string default-message))
100 (read-string (format "Messages: "))))))
101 (list description folder messages)))
102 (let
103 ((range))
104 (if (null messages)
105 (setq messages ""))
106 (setq range (mh-translate-range folder messages))
107 (if (null range)
108 (error "No messages in specified range"))
109 (dolist (message range)
0c47b17c 110 (if (equal mh-compose-insertion 'mml)
a05fcb7d 111 (mh-mml-forward-message description folder (format "%s" message))
0c47b17c 112 (mh-mh-forward-message description folder (format "%s" message))))))
c26cf6c8 113
c26cf6c8
RS
114;; To do:
115;; paragraph code should not fill # lines if MIME enabled.
0c47b17c
BW
116;; implement mh-auto-mh-to-mime (if non-nil, \\[mh-send-letter]
117;; invokes mh-mh-to-mime automatically before sending.)
118;; actually, instead of mh-auto-mh-to-mime,
c26cf6c8
RS
119;; should read automhnproc from profile
120;; MIME option to mh-forward
121;; command to move to content-description insertion point
122
0c47b17c
BW
123(defvar mh-mh-to-mime-args nil
124 "Extra arguments for \\[mh-mh-to-mime] to pass to the \"mhbuild\" command.
2dcf34f9
BW
125The arguments are passed to \"mhbuild\" if \\[mh-mh-to-mime] is
126given a prefix argument. Normally default arguments to
127\"mhbuild\" are specified in the MH profile.")
847b8219 128
bdcfe844
BW
129(defvar mh-media-type-regexp
130 (concat (regexp-opt '("text" "image" "audio" "video" "application"
131 "multipart" "message") t)
132 "/[-.+a-zA-Z0-9]+")
133 "Regexp matching valid media types used in MIME attachment compositions.")
134
135;; Just defvar the variable to avoid compiler warning... This doesn't bind
136;; the variable, so things should work exactly as before.
137(defvar mh-have-file-command)
847b8219 138
f0d73c14 139;;;###mh-autoload
a1b4049d
BW
140(defun mh-have-file-command ()
141 "Return t if 'file' command is on the system.
142'file -i' is used to get MIME type of composition insertion."
143 (when (not (boundp 'mh-have-file-command))
c3d9274a 144 (load "executable" t t) ; executable-find not autoloaded in emacs20
a1b4049d
BW
145 (setq mh-have-file-command
146 (and (fboundp 'executable-find)
147 (executable-find "file") ; file command exists
148 ; and accepts -i and -b args.
149 (zerop (call-process "file" nil nil nil "-i" "-b"
150 (expand-file-name "inc" mh-progs))))))
151 mh-have-file-command)
152
bdcfe844
BW
153(defvar mh-file-mime-type-substitutions
154 '(("application/msword" "\.xls" "application/ms-excel")
f0d73c14
BW
155 ("application/msword" "\.ppt" "application/ms-powerpoint")
156 ("text/plain" "\.vcf" "text/x-vcard"))
bdcfe844
BW
157 "Substitutions to make for Content-Type returned from file command.
158The first element is the Content-Type returned by the file command.
2dcf34f9
BW
159The second element is a regexp matching the file name, usually the
160extension.
bdcfe844
BW
161The third element is the Content-Type to replace with.")
162
163(defun mh-file-mime-type-substitute (content-type filename)
164 "Return possibly changed CONTENT-TYPE on the FILENAME.
2dcf34f9
BW
165Substitutions are made from the `mh-file-mime-type-substitutions'
166variable."
bdcfe844
BW
167 (let ((subst mh-file-mime-type-substitutions)
168 (type) (match) (answer content-type)
169 (case-fold-search t))
170 (while subst
171 (setq type (car (car subst))
172 match (elt (car subst) 1))
173 (if (and (string-equal content-type type)
174 (string-match match filename))
175 (setq answer (elt (car subst) 2)
176 subst nil)
177 (setq subst (cdr subst))))
178 answer))
179
f0d73c14 180;;;###mh-autoload
a1b4049d
BW
181(defun mh-file-mime-type (filename)
182 "Return MIME type of FILENAME from file command.
183Returns nil if file command not on system."
184 (cond
185 ((not (mh-have-file-command))
186 nil) ;No file command, exit now.
187 ((not (and (file-exists-p filename)(file-readable-p filename)))
188 nil)
189 (t
190 (save-excursion
191 (let ((tmp-buffer (get-buffer-create mh-temp-buffer)))
192 (set-buffer tmp-buffer)
193 (unwind-protect
194 (progn
195 (call-process "file" nil '(t nil) nil "-b" "-i"
196 (expand-file-name filename))
197 (goto-char (point-min))
198 (if (not (re-search-forward mh-media-type-regexp nil t))
199 nil
bdcfe844 200 (mh-file-mime-type-substitute (match-string 0) filename)))
a1b4049d
BW
201 (kill-buffer tmp-buffer)))))))
202
c26cf6c8 203(defvar mh-mime-content-types
a1b4049d
BW
204 '(("application/mac-binhex40") ("application/msword")
205 ("application/octet-stream") ("application/pdf") ("application/pgp-keys")
206 ("application/pgp-signature") ("application/pkcs7-signature")
207 ("application/postscript") ("application/rtf")
208 ("application/vnd.ms-excel") ("application/vnd.ms-powerpoint")
209 ("application/vnd.ms-project") ("application/vnd.ms-tnef")
210 ("application/wordperfect5.1") ("application/wordperfect6.0")
211 ("application/zip")
212
213 ("audio/basic") ("audio/mpeg")
214
215 ("image/gif") ("image/jpeg") ("image/png")
216
217 ("message/delivery-status")
218 ("message/external-body") ("message/partial") ("message/rfc822")
219
220 ("text/enriched") ("text/html") ("text/plain") ("text/rfc822-headers")
f0d73c14 221 ("text/richtext") ("text/x-vcard") ("text/xml")
a1b4049d
BW
222
223 ("video/mpeg") ("video/quicktime"))
0c47b17c
BW
224 "Valid MIME content types for Emacs 20.
225Obsolete; use `mailcap-mime-types'.
226
227See also \\[mh-mh-to-mime].")
228
cee9f5c6
BW
229;; Delete mh-minibuffer-read-type and mh-mime-content-types and use
230;; mml-minibuffer-read-type when Emacs20 is no longer supported unless we
231;; think (mh-file-mime-type) is better than (mm-default-file-encoding).
0c47b17c
BW
232
233(defun mh-minibuffer-read-type (filename &optional default)
234 "Return the content type associated with the given FILENAME.
2dcf34f9
BW
235If the \"file\" command exists and recognizes the given file,
236then its value is returned\; otherwise, the user is prompted for
237a type (see `mailcap-mime-types' and for Emacs 20,
238`mh-mime-content-types').
0c47b17c
BW
239Optional argument DEFAULT is returned if a type isn't entered."
240 (mailcap-parse-mimetypes)
241 (let* ((default (or default
242 (mm-default-file-encoding filename)
243 "application/octet-stream"))
244 (type (or (mh-file-mime-type filename)
245 (completing-read
246 (format "Content type (default %s): " default)
247 (if (fboundp 'mailcap-mime-types)
248 (mapcar 'list (mailcap-mime-types))
249 mh-mime-content-types)))))
250 (if (not (equal type ""))
251 type
252 default)))
a1b4049d 253
f0d73c14
BW
254;; RFC 2045 - Multipurpose Internet Mail Extensions (MIME) Part One:
255;; Format of Internet Message Bodies.
256;; RFC 2046 - Multipurpose Internet Mail Extensions (MIME) Part Two:
257;; Media Types.
258;; RFC 2049 - Multipurpose Internet Mail Extensions (MIME) Part Five:
259;; Conformance Criteria and Examples.
260;; RFC 2017 - Definition of the URL MIME External-Body Access-Type
261;; RFC 1738 - Uniform Resource Locators (URL)
262(defvar mh-access-types
263 '(("anon-ftp") ; RFC2046 Anonymous File Transfer Protocol
264 ("file") ; RFC1738 Host-specific file names
265 ("ftp") ; RFC2046 File Transfer Protocol
266 ("gopher") ; RFC1738 The Gopher Protocol
267 ("http") ; RFC1738 Hypertext Transfer Protocol
268 ("local-file") ; RFC2046 Local file access
269 ("mail-server") ; RFC2046 mail-server Electronic mail address
270 ("mailto") ; RFC1738 Electronic mail address
271 ("news") ; RFC1738 Usenet news
272 ("nntp") ; RFC1738 Usenet news using NNTP access
273 ("propspero") ; RFC1738 Prospero Directory Service
274 ("telnet") ; RFC1738 Telnet
275 ("tftp") ; RFC2046 Trivial File Transfer Protocol
276 ("url") ; RFC2017 URL scheme MIME access-type Protocol
277 ("wais")) ; RFC1738 Wide Area Information Servers
88a34f43 278 "Valid MIME access-type values.")
f0d73c14 279
c3d9274a 280;;;###mh-autoload
0c47b17c
BW
281(defun mh-mh-attach-file (filename type description attributes)
282 "Add a tag to insert a MIME message part from a file.
2dcf34f9
BW
283You are prompted for the FILENAME containing the object, the
284media TYPE if it cannot be determined automatically, and a
285content DESCRIPTION. In addition, you are also prompted for
286additional ATTRIBUTES.
0c47b17c
BW
287
288See also \\[mh-mh-to-mime]."
289 (interactive (let ((filename (mml-minibuffer-read-file "Attach file: ")))
c3d9274a
BW
290 (list
291 filename
0c47b17c
BW
292 (mh-minibuffer-read-type filename)
293 (mml-minibuffer-read-description)
294 (read-string "Attributes: "
c3d9274a
BW
295 (concat "name=\""
296 (file-name-nondirectory filename)
297 "\"")))))
0c47b17c 298 (mh-mh-compose-type filename type description attributes))
c26cf6c8 299
0c47b17c 300(defun mh-mh-compose-type (filename type
c3d9274a 301 &optional description attributes comment)
0c47b17c 302 "Insert an MH-style directive to insert a file.
2dcf34f9
BW
303The file specified by FILENAME is encoded as TYPE. An optional
304DESCRIPTION is used as the Content-Description field, optional
305set of ATTRIBUTES and an optional COMMENT can also be included."
c26cf6c8
RS
306 (beginning-of-line)
307 (insert "#" type)
308 (and attributes
309 (insert "; " attributes))
310 (and comment
311 (insert " (" comment ")"))
312 (insert " [")
313 (and description
314 (insert description))
3fda54a2 315 (insert "] " (expand-file-name filename))
c26cf6c8
RS
316 (insert "\n"))
317
c3d9274a 318;;;###mh-autoload
0c47b17c
BW
319(defun mh-mh-compose-anon-ftp (host filename type description)
320 "Add tag to include anonymous ftp reference to a file.
2dcf34f9
BW
321You can even have your message initiate an \"ftp\" transfer when
322the recipient reads the message. You are prompted for the remote
0c47b17c 323HOST and FILENAME, the media TYPE, and the content DESCRIPTION.
bdcfe844 324
0c47b17c 325See also \\[mh-mh-to-mime]."
c26cf6c8 326 (interactive (list
c3d9274a
BW
327 (read-string "Remote host: ")
328 (read-string "Remote filename: ")
0c47b17c
BW
329 (mh-minibuffer-read-type "DUMMY-FILENAME")
330 (mml-minibuffer-read-description)))
331 (mh-mh-compose-external-type "anon-ftp" host filename
332 type description))
c26cf6c8 333
c3d9274a 334;;;###mh-autoload
0c47b17c
BW
335(defun mh-mh-compose-external-compressed-tar (host filename description)
336 "Add tag to include anonymous ftp reference to a compressed tar file.
2dcf34f9
BW
337In addition to retrieving the file via anonymous \"ftp\" as per
338the \\[mh-mh-compose-anon-ftp] command, the file will also be
339uncompressed and untarred. You are prompted for the remote HOST
340and FILENAME and the content DESCRIPTION.
0c47b17c
BW
341
342See also \\[mh-mh-to-mime]."
c26cf6c8 343 (interactive (list
c3d9274a
BW
344 (read-string "Remote host: ")
345 (read-string "Remote filename: ")
0c47b17c
BW
346 (mml-minibuffer-read-description)))
347 (mh-mh-compose-external-type "anon-ftp" host filename
348 "application/octet-stream"
349 description
350 "type=tar; conversions=x-compress"
351 "mode=image"))
c26cf6c8 352
f0d73c14 353;;;###mh-autoload
0c47b17c
BW
354(defun mh-mh-compose-external-type (access-type host filename type
355 &optional description
356 attributes parameters
357 comment)
358 "Add tag to refer to a remote file.
2dcf34f9
BW
359This command is a general utility for referencing external files.
360In fact, all of the other commands that insert directives to
361access external files call this command. You are prompted for the
362ACCESS-TYPE, remote HOST and FILENAME, and content TYPE. If you
363provide a prefix argument, you are also prompted for a content
364DESCRIPTION, ATTRIBUTES, PARAMETERS, and a COMMENT.
0c47b17c
BW
365
366See also \\[mh-mh-to-mime]."
f0d73c14 367 (interactive (list
0c47b17c 368 (completing-read "Access type: " mh-access-types)
f0d73c14 369 (read-string "Remote host: ")
0c47b17c
BW
370 (read-string "Remote filename: ")
371 (mh-minibuffer-read-type "DUMMY-FILENAME")
372 (if current-prefix-arg (mml-minibuffer-read-description))
f0d73c14 373 (if current-prefix-arg (read-string "Attributes: "))
0c47b17c 374 (if current-prefix-arg (read-string "Parameters: "))
f0d73c14 375 (if current-prefix-arg (read-string "Comment: "))))
c26cf6c8
RS
376 (beginning-of-line)
377 (insert "#@" type)
378 (and attributes
379 (insert "; " attributes))
380 (and comment
381 (insert " (" comment ") "))
382 (insert " [")
383 (and description
384 (insert description))
385 (insert "] ")
386 (insert "access-type=" access-type "; ")
387 (insert "site=" host)
3fda54a2 388 (insert "; name=" (file-name-nondirectory filename))
f0d73c14
BW
389 (let ((directory (file-name-directory filename)))
390 (and directory
391 (insert "; directory=\"" directory "\"")))
0c47b17c
BW
392 (and parameters
393 (insert "; " parameters))
c26cf6c8
RS
394 (insert "\n"))
395
c3d9274a 396;;;###mh-autoload
0c47b17c
BW
397(defun mh-mh-forward-message (&optional description folder messages)
398 "Add tag to forward a message.
2dcf34f9
BW
399You are prompted for a content DESCRIPTION, the name of the
400FOLDER in which the messages to forward are located, and the
401MESSAGES' numbers.
bdcfe844 402
0c47b17c 403See also \\[mh-mh-to-mime]."
c26cf6c8 404 (interactive (list
0c47b17c 405 (mml-minibuffer-read-description)
c3d9274a 406 (mh-prompt-for-folder "Message from" mh-sent-from-folder nil)
d88a70a0 407 (read-string (concat "Messages"
f0d73c14 408 (if (numberp mh-sent-from-msg)
0c47b17c
BW
409 (format " (default %d): "
410 mh-sent-from-msg)
d88a70a0 411 ": ")))))
c26cf6c8
RS
412 (beginning-of-line)
413 (insert "#forw [")
414 (and description
415 (not (string= description ""))
416 (insert description))
417 (insert "]")
418 (and folder
419 (not (string= folder ""))
420 (insert " " folder))
847b8219 421 (if (and messages
c3d9274a 422 (not (string= messages "")))
c26cf6c8 423 (let ((start (point)))
c3d9274a
BW
424 (insert " " messages)
425 (subst-char-in-region start (point) ?, ? ))
f0d73c14 426 (if (numberp mh-sent-from-msg)
c3d9274a 427 (insert " " (int-to-string mh-sent-from-msg))))
c26cf6c8
RS
428 (insert "\n"))
429
c3d9274a 430;;;###mh-autoload
0c47b17c
BW
431(defun mh-mh-to-mime (&optional extra-args)
432 "Compose MIME message from MH-style directives.
d1699462 433
2dcf34f9
BW
434Typically, you send a message with attachments just like any other
435message. However, you may take a sneak preview of the MIME encoding if
436you wish by running this command.
0c47b17c 437
2dcf34f9
BW
438If you wish to pass additional arguments to \"mhbuild\" (\"mhn\") to
439affect how it builds your message, use the `mh-mh-to-mime-args'
440option. For example, you can build a consistency check into the
441message by setting `mh-mh-to-mime-args' to \"-check\". The recipient
442of your message can then run \"mhbuild -check\" on the
443message--\"mhbuild\" (\"mhn\") will complain if the message has been
444corrupted on the way. This command only consults this option when
445given a prefix argument EXTRA-ARGS.
0c47b17c 446
d1699462
BW
447The hook `mh-mh-to-mime-hook' is called after the message has been
448formatted.
0c47b17c 449
2dcf34f9
BW
450The effects of this command can be undone by running
451\\[mh-mh-to-mime-undo]."
847b8219 452 (interactive "*P")
0c47b17c 453 (mh-mh-quote-unescaped-sharp)
c26cf6c8 454 (save-buffer)
0c47b17c 455 (message "Running %s..." (if (mh-variant-p 'nmh) "mhbuild" "mhn"))
a1b4049d 456 (cond
f0d73c14 457 ((mh-variant-p 'nmh)
a1b4049d 458 (mh-exec-cmd-error nil
0c47b17c
BW
459 "mhbuild"
460 (if extra-args mh-mh-to-mime-args)
461 buffer-file-name))
a1b4049d
BW
462 (t
463 (mh-exec-cmd-error (format "mhdraft=%s" buffer-file-name)
0c47b17c
BW
464 "mhn"
465 (if extra-args mh-mh-to-mime-args)
466 buffer-file-name)))
c26cf6c8 467 (revert-buffer t t)
0c47b17c
BW
468 (message "Running %s...done" (if (mh-variant-p 'nmh) "mhbuild" "mhn"))
469 (run-hooks 'mh-mh-to-mime-hook))
c26cf6c8 470
0c47b17c 471(defun mh-mh-quote-unescaped-sharp ()
5a4aad03
BW
472 "Quote \"#\" characters that haven't been quoted for \"mhbuild\".
473If the \"#\" character is present in the first column, but it isn't
2dcf34f9
BW
474part of a MH-style directive then \"mhbuild\" gives an error.
475This function will quote all such characters."
f0d73c14
BW
476 (save-excursion
477 (goto-char (point-min))
478 (while (re-search-forward "^#" nil t)
479 (beginning-of-line)
0c47b17c 480 (unless (mh-mh-directive-present-p (point) (line-end-position))
f0d73c14
BW
481 (insert "#"))
482 (goto-char (line-end-position)))))
483
c3d9274a 484;;;###mh-autoload
0c47b17c
BW
485(defun mh-mh-to-mime-undo (noconfirm)
486 "Undo effects of \\[mh-mh-to-mime].
2dcf34f9
BW
487Optional non-nil argument NOCONFIRM means don't ask for
488confirmation."
c26cf6c8
RS
489 (interactive "*P")
490 (if (null buffer-file-name)
491 (error "Buffer does not seem to be associated with any file"))
492 (let ((backup-strings '("," "#"))
c3d9274a 493 backup-file)
c26cf6c8 494 (while (and backup-strings
c3d9274a
BW
495 (not (file-exists-p
496 (setq backup-file
497 (concat (file-name-directory buffer-file-name)
498 (car backup-strings)
499 (file-name-nondirectory buffer-file-name)
500 ".orig")))))
c26cf6c8
RS
501 (setq backup-strings (cdr backup-strings)))
502 (or backup-strings
c3d9274a 503 (error "Backup file for %s no longer exists!" buffer-file-name))
c26cf6c8 504 (or noconfirm
c3d9274a
BW
505 (yes-or-no-p (format "Revert buffer from file %s? "
506 backup-file))
507 (error "Revert not confirmed"))
c26cf6c8
RS
508 (let ((buffer-read-only nil))
509 (erase-buffer)
510 (insert-file-contents backup-file))
511 (after-find-file nil)))
60370d40 512
924df208 513;;;###mh-autoload
0c47b17c
BW
514(defun mh-mh-directive-present-p (&optional begin end)
515 "Check if the text between BEGIN and END might be a MH-style directive.
2dcf34f9
BW
516The optional argument BEGIN defaults to the beginning of the
517buffer, while END defaults to the the end of the buffer."
f0d73c14
BW
518 (unless begin (setq begin (point-min)))
519 (unless end (setq end (point-max)))
924df208 520 (save-excursion
0c47b17c 521 (block 'search-for-mh-directive
f0d73c14
BW
522 (goto-char begin)
523 (while (re-search-forward "^#" end t)
924df208
BW
524 (let ((s (buffer-substring-no-properties (point) (line-end-position))))
525 (cond ((equal s ""))
526 ((string-match "^forw[ \t\n]+" s)
0c47b17c 527 (return-from 'search-for-mh-directive t))
924df208 528 (t (let ((first-token (car (split-string s "[ \t;@]"))))
f0d73c14
BW
529 (when (and first-token
530 (string-match mh-media-type-regexp
531 first-token))
0c47b17c 532 (return-from 'search-for-mh-directive t)))))))
924df208
BW
533 nil)))
534
bdcfe844
BW
535\f
536
537;;; MIME composition functions
538
c3d9274a 539;;;###mh-autoload
bdcfe844 540(defun mh-mml-to-mime ()
0c47b17c 541 "Compose MIME message from MML tags.
2dcf34f9
BW
542
543Typically, you send a message with attachments just like any
544other message. However, you may take a sneak preview of the MIME
545encoding if you wish by running this command.
0c47b17c
BW
546
547This action can be undone by running \\[undo]."
bdcfe844 548 (interactive)
a66894d8 549 (require 'message)
0c47b17c 550 (when mh-pgp-support-flag ;; This is only needed for PGP
bdcfe844 551 (message-options-set-recipient))
f0d73c14
BW
552 (let ((saved-text (buffer-string))
553 (buffer (current-buffer))
554 (modified-flag (buffer-modified-p)))
555 (condition-case err (mml-to-mime)
556 (error
557 (with-current-buffer buffer
558 (delete-region (point-min) (point-max))
559 (insert saved-text)
560 (set-buffer-modified-p modified-flag))
561 (error (error-message-string err))))))
bdcfe844 562
c3d9274a 563;;;###mh-autoload
bdcfe844
BW
564(defun mh-mml-forward-message (description folder message)
565 "Forward a message as attachment.
2dcf34f9
BW
566
567The function will prompt the user for a DESCRIPTION, a FOLDER and
568MESSAGE number."
f0d73c14 569 (let ((msg (if (and (equal message "") (numberp mh-sent-from-msg))
bdcfe844
BW
570 mh-sent-from-msg
571 (car (read-from-string message)))))
572 (cond ((integerp msg)
573 (if (string= "" description)
574 ;; Rationale: mml-attach-file constructs a malformed composition
575 ;; if the description string is empty. This fixes SF #625168.
576 (mml-attach-file (format "%s%s/%d"
577 mh-user-path (substring folder 1) msg)
578 "message/rfc822")
579 (mml-attach-file (format "%s%s/%d"
580 mh-user-path (substring folder 1) msg)
581 "message/rfc822"
924df208 582 description)))
bdcfe844
BW
583 (t (error "The message number, %s is not a integer!" msg)))))
584
f0d73c14
BW
585(defvar mh-mml-cryptographic-method-history ())
586
587;;;###mh-autoload
588(defun mh-mml-query-cryptographic-method ()
589 "Read the cryptographic method to use."
590 (if current-prefix-arg
591 (let ((def (or (car mh-mml-cryptographic-method-history)
592 mh-mml-method-default)))
0c47b17c 593 (completing-read (format "Method (default %s): " def)
f0d73c14
BW
594 '(("pgp") ("pgpmime") ("smime"))
595 nil t nil 'mh-mml-cryptographic-method-history def))
596 mh-mml-method-default))
597
c3d9274a 598;;;###mh-autoload
bdcfe844 599(defun mh-mml-attach-file (&optional disposition)
0c47b17c 600 "Add a tag to insert a MIME message part from a file.
2dcf34f9
BW
601
602You are prompted for the filename containing the object, the
603media type if it cannot be determined automatically, a content
604description and the DISPOSITION of the attachment.
bdcfe844 605
0c47b17c 606This is basically `mml-attach-file' from Gnus, modified such that a prefix
5a4aad03 607argument yields an \"inline\" disposition and Content-Type is determined
bdcfe844
BW
608automatically."
609 (let* ((file (mml-minibuffer-read-file "Attach file: "))
0c47b17c 610 (type (mh-minibuffer-read-type file))
bdcfe844
BW
611 (description (mml-minibuffer-read-description))
612 (dispos (or disposition
0c47b17c 613 (mml-minibuffer-read-disposition type))))
bdcfe844 614 (mml-insert-empty-tag 'part 'type type 'filename file
924df208 615 'disposition dispos 'description description)))
bdcfe844 616
eccf9613
BW
617(defvar mh-identity-pgg-default-user-id)
618
f0d73c14 619(defun mh-secure-message (method mode &optional identity)
0c47b17c 620 "Add tag to encrypt or sign message.
2dcf34f9 621
f0d73c14
BW
622METHOD should be one of: \"pgpmime\", \"pgp\", \"smime\".
623MODE should be one of: \"sign\", \"encrypt\", \"signencrypt\", \"none\".
624IDENTITY is optionally the default-user-id to use."
0c47b17c
BW
625 (if (not mh-pgp-support-flag)
626 (error "Your version of Gnus does not support PGP/GPG")
f0d73c14
BW
627 ;; Check the arguments
628 (let ((valid-methods (list "pgpmime" "pgp" "smime"))
629 (valid-modes (list "sign" "encrypt" "signencrypt" "none")))
630 (if (not (member method valid-methods))
0c47b17c 631 (error "Method \"%s\" is invalid" method))
f0d73c14 632 (if (not (member mode valid-modes))
0c47b17c 633 (error "Mode \"%s\" is invalid" mode))
f0d73c14
BW
634 (mml-unsecure-message)
635 (if (not (string= mode "none"))
636 (save-excursion
637 (goto-char (point-min))
638 (mh-goto-header-end 1)
639 (if mh-identity-pgg-default-user-id
640 (mml-insert-tag 'secure 'method method 'mode mode
641 'sender mh-identity-pgg-default-user-id)
642 (mml-insert-tag 'secure 'method method 'mode mode)))))))
bdcfe844 643
c3d9274a 644;;;###mh-autoload
285d1e0c
MB
645(defun mh-mml-unsecure-message ()
646 "Remove any secure message tags."
647 (interactive)
0c47b17c
BW
648 (if (not mh-pgp-support-flag)
649 (error "Your version of Gnus does not support PGP/GPG")
f0d73c14
BW
650 (mml-unsecure-message)))
651
652;;;###mh-autoload
653(defun mh-mml-secure-message-sign (method)
0c47b17c 654 "Add tag to sign the message.
2dcf34f9
BW
655
656A proper multipart message is created for you when you send the
657message. Use the \\[mh-mml-unsecure-message] command to remove
658this tag. Use a prefix argument METHOD to be prompted for one of
4023e353 659the possible security methods (see `mh-mml-method-default')."
f0d73c14
BW
660 (interactive (list (mh-mml-query-cryptographic-method)))
661 (mh-secure-message method "sign" mh-identity-pgg-default-user-id))
662
663;;;###mh-autoload
664(defun mh-mml-secure-message-encrypt (method)
0c47b17c 665 "Add tag to encrypt the message.
2dcf34f9
BW
666
667A proper multipart message is created for you when you send the
668message. Use the \\[mh-mml-unsecure-message] command to remove
669this tag. Use a prefix argument METHOD to be prompted for one of
4023e353 670the possible security methods (see `mh-mml-method-default')."
f0d73c14
BW
671 (interactive (list (mh-mml-query-cryptographic-method)))
672 (mh-secure-message method "encrypt" mh-identity-pgg-default-user-id))
673
674;;;###mh-autoload
675(defun mh-mml-secure-message-signencrypt (method)
0c47b17c 676 "Add tag to encrypt and sign the message.
2dcf34f9
BW
677
678A proper multipart message is created for you when you send the
679message. Use the \\[mh-mml-unsecure-message] command to remove
680this tag. Use a prefix argument METHOD to be prompted for one of
4023e353 681the possible security methods (see `mh-mml-method-default')."
f0d73c14
BW
682 (interactive (list (mh-mml-query-cryptographic-method)))
683 (mh-secure-message method "signencrypt" mh-identity-pgg-default-user-id))
924df208
BW
684
685;;;###mh-autoload
0c47b17c
BW
686(defun mh-mml-tag-present-p ()
687 "Check if the current buffer has text which may be a MML tag."
924df208
BW
688 (save-excursion
689 (goto-char (point-min))
690 (re-search-forward
691 "\\(<#part\\(.\\|\n\\)*>[ \n\t]*<#/part>\\|^<#secure.+>$\\)"
692 nil t)))
bdcfe844
BW
693
694\f
695
bdcfe844
BW
696;;; MIME cleanup
697
c3d9274a 698;;;###mh-autoload
bdcfe844
BW
699(defun mh-mime-cleanup ()
700 "Free the decoded MIME parts."
701 (let ((mime-data (gethash (current-buffer) mh-globals-hash)))
702 ;; This is for Emacs, what about XEmacs?
924df208 703 (mh-funcall-if-exists remove-images (point-min) (point-max))
bdcfe844
BW
704 (when mime-data
705 (mm-destroy-parts (mh-mime-handles mime-data))
706 (remhash (current-buffer) mh-globals-hash))))
707
c3d9274a 708;;;###mh-autoload
bdcfe844 709(defun mh-destroy-postponed-handles ()
0c47b17c 710 "Free MIME data for externally displayed MIME parts."
bdcfe844
BW
711 (let ((mime-data (mh-buffer-data)))
712 (when mime-data
713 (mm-destroy-parts (mh-mime-handles mime-data)))
714 (remhash (current-buffer) mh-globals-hash)))
715
716(defun mh-handle-set-external-undisplayer (folder handle function)
717 "Replacement for `mm-handle-set-external-undisplayer'.
2dcf34f9
BW
718
719This is only called in recent versions of Gnus. The MIME handles
720are stored in data structures corresponding to MH-E folder buffer
721FOLDER instead of in Gnus (as in the original). The MIME part,
722HANDLE is associated with the undisplayer FUNCTION."
bdcfe844
BW
723 (if (mm-keep-viewer-alive-p handle)
724 (let ((new-handle (copy-sequence handle)))
c3d9274a
BW
725 (mm-handle-set-undisplayer new-handle function)
726 (mm-handle-set-undisplayer handle nil)
bdcfe844
BW
727 (save-excursion
728 (set-buffer folder)
729 (push new-handle (mh-mime-handles (mh-buffer-data)))))
730 (mm-handle-set-undisplayer handle function)))
731
732\f
733
734;;; MIME transformations
c3d9274a 735(eval-when-compile (require 'font-lock))
bdcfe844 736
c3d9274a 737;;;###mh-autoload
bdcfe844
BW
738(defun mh-add-missing-mime-version-header ()
739 "Some mail programs don't put a MIME-Version header.
2dcf34f9
BW
740I have seen this only in spam, so maybe we shouldn't fix
741this ;-)"
bdcfe844
BW
742 (save-excursion
743 (goto-char (point-min))
a66894d8
BW
744 (re-search-forward "\n\n" nil t)
745 (save-restriction
746 (narrow-to-region (point-min) (point))
747 (when (and (message-fetch-field "content-type")
748 (not (message-fetch-field "mime-version")))
749 (goto-char (point-min))
bdcfe844
BW
750 (insert "MIME-Version: 1.0\n")))))
751
a66894d8
BW
752(defun mh-small-show-buffer-p ()
753 "Check if show buffer is small.
2dcf34f9
BW
754This is used to decide if smileys and graphical emphasis will be
755displayed."
a66894d8
BW
756 (let ((max nil))
757 (when (and (boundp 'font-lock-maximum-size) font-lock-maximum-size)
758 (cond ((numberp font-lock-maximum-size)
759 (setq max font-lock-maximum-size))
760 ((listp font-lock-maximum-size)
761 (setq max (cdr (or (assoc 'mh-show-mode font-lock-maximum-size)
762 (assoc t font-lock-maximum-size)))))))
763 (or (not (numberp max)) (>= (/ max 8) (buffer-size)))))
764
c3d9274a 765;;;###mh-autoload
bdcfe844 766(defun mh-display-smileys ()
0c47b17c 767 "Display smileys."
a66894d8
BW
768 (when (and mh-graphical-smileys-flag (mh-small-show-buffer-p))
769 (mh-funcall-if-exists smiley-region (point-min) (point-max))))
bdcfe844 770
c3d9274a 771;;;###mh-autoload
bdcfe844 772(defun mh-display-emphasis ()
0c47b17c 773 "Display graphical emphasis."
a66894d8 774 (when (and mh-graphical-emphasis-flag (mh-small-show-buffer-p))
c3d9274a 775 (flet ((article-goto-body ())) ; shadow this function to do nothing
bdcfe844
BW
776 (save-excursion
777 (goto-char (point-min))
778 (article-emphasize)))))
779
780;; Copied from gnus-art.el (should be checked for other cool things that can
781;; be added to the buttons)
782(defvar mh-mime-button-commands
783 '((mh-press-button "\r" "Toggle Display")))
784(defvar mh-mime-button-map
785 (let ((map (make-sparse-keymap)))
786 (unless (>= (string-to-number emacs-version) 21)
787 ;; XEmacs doesn't care.
788 (set-keymap-parent map mh-show-mode-map))
924df208
BW
789 (mh-do-in-gnu-emacs
790 (define-key map [mouse-2] 'mh-push-button))
791 (mh-do-in-xemacs
792 (define-key map '(button2) 'mh-push-button))
bdcfe844
BW
793 (dolist (c mh-mime-button-commands)
794 (define-key map (cadr c) (car c)))
795 map))
796(defvar mh-mime-button-line-format-alist
797 '((?T long-type ?s)
798 (?d description ?s)
799 (?p index ?s)
800 (?e dots ?s)))
801(defvar mh-mime-button-line-format "%{%([%p. %d%T]%)%}%e\n")
802(defvar mh-mime-security-button-pressed nil)
803(defvar mh-mime-security-button-line-format "%{%([[%t:%i]%D]%)%}\n")
804(defvar mh-mime-security-button-end-line-format "%{%([[End of %t]%D]%)%}\n")
805(defvar mh-mime-security-button-line-format-alist
806 '((?t type ?s)
807 (?i info ?s)
808 (?d details ?s)
809 (?D pressed-details ?s)))
810(defvar mh-mime-security-button-map
811 (let ((map (make-sparse-keymap)))
812 (unless (>= (string-to-number emacs-version) 21)
813 (set-keymap-parent map mh-show-mode-map))
814 (define-key map "\r" 'mh-press-button)
924df208
BW
815 (mh-do-in-gnu-emacs
816 (define-key map [mouse-2] 'mh-push-button))
817 (mh-do-in-xemacs
818 (define-key map '(button2) 'mh-push-button))
bdcfe844
BW
819 map))
820
821(defvar mh-mime-save-parts-directory nil
822 "Default to use for `mh-mime-save-parts-default-directory'.
823Set from last use.")
824
c3d9274a 825;;;###mh-autoload
553fb735
BW
826(defun mh-mime-save-parts (prompt)
827 "Save attachments.
828
2dcf34f9
BW
829You can save all of the attachments at once with this command.
830The attachments are saved in the directory specified by the
831option `mh-mime-save-parts-default-directory' unless you use a
832prefix argument PROMPT in which case you are prompted for the
833directory. These directories may be superseded by MH profile
834components, since this function calls on \"mhstore\" (\"mhn\") to
835do the work."
bdcfe844
BW
836 (interactive "P")
837 (let ((msg (if (eq major-mode 'mh-show-mode)
838 (mh-show-buffer-message-number)
839 (mh-get-msg-num t)))
840 (folder (if (eq major-mode 'mh-show-mode)
841 mh-show-folder-buffer
842 mh-current-folder))
f0d73c14 843 (command (if (mh-variant-p 'nmh) "mhstore" "mhn"))
bdcfe844
BW
844 (directory
845 (cond
553fb735 846 ((and (or prompt
bdcfe844
BW
847 (equal nil mh-mime-save-parts-default-directory)
848 (equal t mh-mime-save-parts-default-directory))
849 (not mh-mime-save-parts-directory))
f0d73c14 850 (read-file-name "Store in directory: " nil nil t nil))
553fb735 851 ((and (or prompt
bdcfe844
BW
852 (equal t mh-mime-save-parts-default-directory))
853 mh-mime-save-parts-directory)
854 (read-file-name (format
f0d73c14 855 "Store in directory: [%s] "
bdcfe844
BW
856 mh-mime-save-parts-directory)
857 "" mh-mime-save-parts-directory t ""))
858 ((stringp mh-mime-save-parts-default-directory)
859 mh-mime-save-parts-default-directory)
860 (t
861 mh-mime-save-parts-directory))))
862 (if (and (equal directory "") mh-mime-save-parts-directory)
863 (setq directory mh-mime-save-parts-directory))
864 (if (not (file-directory-p directory))
f0d73c14 865 (message "No directory specified")
bdcfe844
BW
866 (if (equal nil mh-mime-save-parts-default-directory)
867 (setq mh-mime-save-parts-directory directory))
868 (save-excursion
924df208 869 (set-buffer (get-buffer-create mh-log-buffer))
bdcfe844
BW
870 (cd directory)
871 (setq mh-mime-save-parts-directory directory)
924df208
BW
872 (let ((initial-size (mh-truncate-log-buffer)))
873 (apply 'call-process
874 (expand-file-name command mh-progs) nil t nil
875 (mh-list-to-string (list folder msg "-auto")))
876 (if (> (buffer-size) initial-size)
877 (save-window-excursion
878 (switch-to-buffer-other-window mh-log-buffer)
879 (sit-for 3))))))))
bdcfe844
BW
880
881;; Avoid errors if gnus-sum isn't loaded yet...
882(defvar gnus-newsgroup-charset nil)
883(defvar gnus-newsgroup-name nil)
884
924df208
BW
885(defun mh-decode-message-body ()
886 "Decode message based on charset.
887If message has been encoded for transfer take that into account."
a66894d8
BW
888 (let (ct charset cte)
889 (goto-char (point-min))
890 (re-search-forward "\n\n" nil t)
891 (save-restriction
892 (narrow-to-region (point-min) (point))
893 (setq ct (ignore-errors (mail-header-parse-content-type
894 (message-fetch-field "Content-Type" t)))
895 charset (mail-content-type-get ct 'charset)
896 cte (message-fetch-field "Content-Transfer-Encoding")))
924df208
BW
897 (when (stringp cte) (setq cte (mail-header-strip cte)))
898 (when (or (not ct) (equal (car ct) "text/plain"))
899 (save-restriction
900 (narrow-to-region (min (1+ (mh-mail-header-end)) (point-max))
901 (point-max))
902 (mm-decode-body charset
903 (and cte (intern (downcase
904 (gnus-strip-whitespace cte))))
905 (car ct))))))
906
d103d8b3
BW
907;;;###mh-autoload
908(defun mh-toggle-mh-decode-mime-flag ()
553fb735 909 "Toggle the value of `mh-decode-mime-flag'."
eccf9613 910 (interactive)
d103d8b3
BW
911 (setq mh-decode-mime-flag (not mh-decode-mime-flag))
912 (mh-show nil t)
553fb735
BW
913 (message "%s" (if mh-decode-mime-flag
914 "Processing attachments normally"
915 "Displaying raw message")))
d103d8b3 916
924df208
BW
917;;;###mh-autoload
918(defun mh-decode-message-header ()
919 "Decode RFC2047 encoded message header fields."
920 (when mh-decode-mime-flag
921 (let ((buffer-read-only nil))
922 (rfc2047-decode-region (point-min) (mh-mail-header-end)))))
923
c3d9274a 924;;;###mh-autoload
bdcfe844
BW
925(defun mh-mime-display (&optional pre-dissected-handles)
926 "Display (and possibly decode) MIME handles.
2dcf34f9
BW
927Optional argument, PRE-DISSECTED-HANDLES is a list of MIME
928handles. If present they are displayed otherwise the buffer is
929parsed and then displayed."
bdcfe844 930 (let ((handles ())
924df208
BW
931 (folder mh-show-folder-buffer)
932 (raw-message-data (buffer-string)))
c3d9274a
BW
933 (flet ((mm-handle-set-external-undisplayer
934 (handle function)
935 (mh-handle-set-external-undisplayer folder handle function)))
924df208
BW
936 (goto-char (point-min))
937 (unless (search-forward "\n\n" nil t)
938 (goto-char (point-max))
939 (insert "\n\n"))
940
941 (condition-case err
942 (progn
943 ;; If needed dissect the current buffer
944 (if pre-dissected-handles
945 (setq handles pre-dissected-handles)
946 (setq handles (or (mm-dissect-buffer nil) (mm-uu-dissect)))
947 (setf (mh-mime-handles (mh-buffer-data))
948 (mm-merge-handles handles
949 (mh-mime-handles (mh-buffer-data))))
950 (unless handles (mh-decode-message-body)))
951
f0d73c14
BW
952 (cond ((and handles
953 (or (not (stringp (car handles))) (cdr handles)))
954 ;; Goto start of message body
955 (goto-char (point-min))
956 (or (search-forward "\n\n" nil t) (goto-char (point-max)))
bdcfe844 957
f0d73c14
BW
958 ;; Delete the body
959 (delete-region (point) (point-max))
bdcfe844 960
f0d73c14
BW
961 ;; Display the MIME handles
962 (mh-mime-display-part handles))
963 (t (mh-signature-highlight))))
924df208
BW
964 (error
965 (message "Please report this error. The error message is:\n %s"
966 (error-message-string err))
967 (delete-region (point-min) (point-max))
968 (insert raw-message-data))))))
bdcfe844
BW
969
970(defun mh-mime-display-part (handle)
971 "Decides the viewer to call based on the type of HANDLE."
972 (cond ((null handle) nil)
973 ((not (stringp (car handle)))
974 (mh-mime-display-single handle))
975 ((equal (car handle) "multipart/alternative")
976 (mh-mime-display-alternative (cdr handle)))
0c47b17c 977 ((and mh-pgp-support-flag
bdcfe844
BW
978 (or (equal (car handle) "multipart/signed")
979 (equal (car handle) "multipart/encrypted")))
980 (mh-mime-display-security handle))
981 (t (mh-mime-display-mixed (cdr handle)))))
982
983(defun mh-mime-display-alternative (handles)
984 "Choose among the alternatives, HANDLES the part that will be displayed.
985If no part is preferred then all the parts are displayed."
a66894d8
BW
986 (let* ((preferred (mm-preferred-alternative handles))
987 (others (loop for x in handles unless (eq x preferred) collect x)))
bdcfe844 988 (cond ((and preferred (stringp (car preferred)))
a66894d8
BW
989 (mh-mime-display-part preferred)
990 (mh-mime-maybe-display-alternatives others))
bdcfe844
BW
991 (preferred
992 (save-restriction
993 (narrow-to-region (point) (if (eobp) (point) (1+ (point))))
c3d9274a 994 (mh-mime-display-single preferred)
a66894d8 995 (mh-mime-maybe-display-alternatives others)
bdcfe844
BW
996 (goto-char (point-max))))
997 (t (mh-mime-display-mixed handles)))))
998
a66894d8
BW
999(defun mh-mime-maybe-display-alternatives (alternatives)
1000 "Show buttons for ALTERNATIVES.
2dcf34f9
BW
1001If `mh-mime-display-alternatives-flag' is non-nil then display
1002buttons for alternative parts that are usually suppressed."
a66894d8
BW
1003 (when (and mh-display-buttons-for-alternatives-flag alternatives)
1004 (insert "\n----------------------------------------------------\n")
1005 (insert "Alternatives:\n")
1006 (dolist (x alternatives)
1007 (insert "\n")
1008 (mh-insert-mime-button x (mh-mime-part-index x) nil))
1009 (insert "\n----------------------------------------------------\n")))
1010
bdcfe844
BW
1011(defun mh-mime-display-mixed (handles)
1012 "Display the list of MIME parts, HANDLES recursively."
1013 (mapcar #'mh-mime-display-part handles))
1014
1015(defun mh-mime-part-index (handle)
1016 "Generate the button number for MIME part, HANDLE.
2dcf34f9
BW
1017Notice that a hash table is used to display the same number when
1018buttons need to be displayed multiple times (for instance when
1019nested messages are opened)."
bdcfe844
BW
1020 (or (gethash handle (mh-mime-part-index-hash (mh-buffer-data)))
1021 (setf (gethash handle (mh-mime-part-index-hash (mh-buffer-data)))
1022 (incf (mh-mime-parts-count (mh-buffer-data))))))
1023
bdcfe844
BW
1024(defun mh-small-image-p (handle)
1025 "Decide whether HANDLE is a \"small\" image that can be displayed inline.
1026This is only useful if a Content-Disposition header is not present."
1027 (let ((media-test (caddr (assoc (car (mm-handle-type handle))
1028 mh-mm-inline-media-tests)))
1029 (mm-inline-large-images t))
1030 (and media-test
1031 (equal (mm-handle-media-supertype handle) "image")
c3d9274a
BW
1032 (funcall media-test handle) ; Since mm-inline-large-images is T,
1033 ; this only tells us if the image is
1034 ; something that emacs can display
bdcfe844 1035 (let* ((image (mm-get-image handle)))
a66894d8
BW
1036 (or (mh-do-in-xemacs
1037 (and (mh-funcall-if-exists glyphp image)
1038 (< (glyph-width image)
1039 (or mh-max-inline-image-width (window-pixel-width)))
1040 (< (glyph-height image)
1041 (or mh-max-inline-image-height
1042 (window-pixel-height)))))
1043 (mh-do-in-gnu-emacs
1044 (let ((size (mh-funcall-if-exists image-size image)))
1045 (and size
1046 (< (cdr size) (or mh-max-inline-image-height
1047 (1- (window-height))))
1048 (< (car size) (or mh-max-inline-image-width
1049 (window-width)))))))))))
bdcfe844 1050
c3d9274a
BW
1051(defun mh-inline-vcard-p (handle)
1052 "Decide if HANDLE is a vcard that must be displayed inline."
1053 (let ((type (mm-handle-type handle)))
924df208
BW
1054 (and (or (featurep 'vcard) (fboundp 'vcard-pretty-print))
1055 (consp type)
c3d9274a
BW
1056 (equal (car type) "text/x-vcard")
1057 (save-excursion
1058 (save-restriction
1059 (widen)
1060 (goto-char (point-min))
f0d73c14 1061 (not (mh-signature-separator-p)))))))
c3d9274a 1062
bdcfe844
BW
1063(defun mh-mime-display-single (handle)
1064 "Display a leaf node, HANDLE in the MIME tree."
1065 (let* ((type (mm-handle-media-type handle))
1066 (small-image-flag (mh-small-image-p handle))
1067 (attachmentp (equal (car (mm-handle-disposition handle))
1068 "attachment"))
1069 (inlinep (and (equal (car (mm-handle-disposition handle)) "inline")
1070 (mm-inlinable-p handle)
1071 (mm-inlined-p handle)))
c3d9274a
BW
1072 (displayp (or inlinep ; show if inline OR
1073 (mh-inline-vcard-p handle); inline vcard OR
1074 (and (not attachmentp) ; if not an attachment
1075 (or small-image-flag ; and small image
1076 ; and user wants inline
bdcfe844
BW
1077 (and (not (equal
1078 (mm-handle-media-supertype handle)
1079 "image"))
1080 (mm-inlinable-p handle)
1081 (mm-inlined-p handle)))))))
1082 (save-restriction
1083 (narrow-to-region (point) (if (eobp) (point) (1+ (point))))
0c47b17c 1084 (cond ((and mh-pgp-support-flag
bdcfe844 1085 (equal type "application/pgp-signature"))
c3d9274a 1086 nil) ; skip signatures as they are already handled...
bdcfe844
BW
1087 ((not displayp)
1088 (insert "\n")
1089 (mh-insert-mime-button handle (mh-mime-part-index handle) nil))
1090 ((and displayp (not mh-display-buttons-for-inline-parts-flag))
f0d73c14
BW
1091 (or (mm-display-part handle) (mm-display-part handle))
1092 (mh-signature-highlight handle))
bdcfe844
BW
1093 ((and displayp mh-display-buttons-for-inline-parts-flag)
1094 (insert "\n")
1095 (mh-insert-mime-button handle (mh-mime-part-index handle) nil)
1096 (forward-line -1)
1097 (mh-mm-display-part handle)))
1098 (goto-char (point-max)))))
1099
f0d73c14
BW
1100(defun mh-signature-highlight (&optional handle)
1101 "Highlight message signature in HANDLE.
2dcf34f9
BW
1102The optional argument, HANDLE is a MIME handle if the function is
1103being used to highlight the signature in a MIME part."
f0d73c14
BW
1104 (let ((regexp
1105 (cond ((not handle) "^-- $")
1106 ((not (and (equal (mm-handle-media-supertype handle) "text")
1107 (equal (mm-handle-media-subtype handle) "html")))
1108 "^-- $")
1109 ((eq (mh-mm-text-html-renderer) 'lynx) "^ --$")
1110 (t "^--$"))))
1111 (save-excursion
1112 (goto-char (point-max))
1113 (when (re-search-backward regexp nil t)
1114 (mh-do-in-gnu-emacs
1115 (let ((ov (make-overlay (point) (point-max))))
44d55491 1116 (overlay-put ov 'face 'mh-show-signature)
f0d73c14
BW
1117 (overlay-put ov 'evaporate t)))
1118 (mh-do-in-xemacs
1119 (set-extent-property (make-extent (point) (point-max))
44d55491 1120 'face 'mh-show-signature))))))
f0d73c14 1121
924df208
BW
1122(mh-do-in-xemacs
1123 (defvar dots)
1124 (defvar type))
1125
bdcfe844
BW
1126(defun mh-insert-mime-button (handle index displayed)
1127 "Insert MIME button for HANDLE.
2dcf34f9
BW
1128INDEX is the part number that will be DISPLAYED. It is also used
1129by commands like \"K v\" which operate on individual MIME parts."
bdcfe844
BW
1130 ;; The button could be displayed by a previous decode. In that case
1131 ;; undisplay it if we need a hidden button.
1132 (when (and (mm-handle-displayed-p handle) (not displayed))
1133 (mm-display-part handle))
1134 (let ((name (or (mail-content-type-get (mm-handle-type handle) 'name)
1135 (mail-content-type-get (mm-handle-disposition handle)
1136 'filename)
1137 (mail-content-type-get (mm-handle-type handle) 'url)
1138 ""))
1139 (type (mm-handle-media-type handle))
1140 (description (mail-decode-encoded-word-string
1141 (or (mm-handle-description handle) "")))
1142 (dots (if (or displayed (mm-handle-displayed-p handle)) " " "..."))
1143 long-type begin end)
1144 (if (string-match ".*/" name) (setq name (substring name (match-end 0))))
1145 (setq long-type (concat type (and (not (equal name ""))
1146 (concat "; " name))))
1147 (unless (equal description "")
1148 (setq long-type (concat " --- " long-type)))
1149 (unless (bolp) (insert "\n"))
1150 (setq begin (point))
1151 (gnus-eval-format
1152 mh-mime-button-line-format mh-mime-button-line-format-alist
1153 `(,@(gnus-local-map-property mh-mime-button-map)
c3d9274a
BW
1154 mh-callback mh-mm-display-part
1155 mh-part ,index
1156 mh-data ,handle))
bdcfe844
BW
1157 (setq end (point))
1158 (widget-convert-button
1159 'link begin end
1160 :mime-handle handle
1161 :action 'mh-widget-press-button
1162 :button-keymap mh-mime-button-map
1163 :help-echo
f0d73c14
BW
1164 "Mouse-2 click or press RET (in show buffer) to toggle display")
1165 (dolist (ov (mh-funcall-if-exists overlays-in begin end))
1166 (mh-funcall-if-exists overlay-put ov 'evaporate t))))
bdcfe844
BW
1167
1168;; There is a bug in Gnus inline image display due to which an extra line
1169;; gets inserted every time it is viewed. To work around that problem we are
1170;; using an extra property 'mh-region to remember the region that is added
1171;; when the button is clicked. The region is then deleted to make sure that
1172;; no extra lines get inserted.
1173(defun mh-mm-display-part (handle)
1174 "Toggle display of button for MIME part, HANDLE."
1175 (beginning-of-line)
1176 (let ((id (get-text-property (point) 'mh-part))
1177 (point (point))
1178 (window (selected-window))
1179 (mail-parse-charset 'nil)
1180 (mail-parse-ignored-charsets nil)
1181 region buffer-read-only)
1182 (save-excursion
1183 (unwind-protect
1184 (let ((win (get-buffer-window (current-buffer) t)))
1185 (when win
1186 (select-window win))
1187 (goto-char point)
1188
1189 (if (mm-handle-displayed-p handle)
1190 ;; This will remove the part.
1191 (progn
1192 ;; Delete the button and displayed part (if any)
1193 (let ((region (get-text-property point 'mh-region)))
a66894d8 1194 (when region
924df208
BW
1195 (mh-funcall-if-exists
1196 remove-images (car region) (cdr region)))
bdcfe844
BW
1197 (mm-display-part handle)
1198 (when region
1199 (delete-region (car region) (cdr region))))
1200 ;; Delete button (if it still remains). This happens for
1201 ;; externally displayed parts where the previous step does
1202 ;; nothing.
1203 (unless (eolp)
1204 (delete-region (point) (progn (forward-line) (point)))))
1205 (save-restriction
1206 (delete-region (point) (progn (forward-line 1) (point)))
1207 (narrow-to-region (point) (point))
1208 ;; Maybe we need another unwind-protect here.
1209 (when (equal (mm-handle-media-supertype handle) "image")
1210 (insert "\n"))
1211 (when (and (not (eq (ignore-errors (mm-display-part handle))
1212 'inline))
1213 (equal (mm-handle-media-supertype handle)
1214 "image"))
1215 (goto-char (point-min))
1216 (delete-char 1))
1217 (when (equal (mm-handle-media-supertype handle) "text")
553fb735 1218 (when (eq mh-highlight-citation-style 'gnus)
bdcfe844
BW
1219 (mh-gnus-article-highlight-citation))
1220 (mh-display-smileys)
f0d73c14
BW
1221 (mh-display-emphasis)
1222 (mh-signature-highlight handle))
bdcfe844
BW
1223 (setq region (cons (progn (goto-char (point-min))
1224 (point-marker))
1225 (progn (goto-char (point-max))
1226 (point-marker)))))))
1227 (when (window-live-p window)
1228 (select-window window))
1229 (goto-char point)
1230 (beginning-of-line)
1231 (mh-insert-mime-button handle id (mm-handle-displayed-p handle))
1232 (goto-char point)
1233 (when region
1234 (add-text-properties (line-beginning-position) (line-end-position)
1235 `(mh-region ,region)))))))
1236
c3d9274a 1237;;;###mh-autoload
bdcfe844 1238(defun mh-press-button ()
553fb735
BW
1239 "View contents of button.
1240
2dcf34f9
BW
1241This command is a toggle so if you use it again on the same
1242attachment, the attachment is hidden."
bdcfe844
BW
1243 (interactive)
1244 (let ((mm-inline-media-tests mh-mm-inline-media-tests)
1245 (data (get-text-property (point) 'mh-data))
1246 (function (get-text-property (point) 'mh-callback))
1247 (buffer-read-only nil)
1248 (folder mh-show-folder-buffer))
c3d9274a
BW
1249 (flet ((mm-handle-set-external-undisplayer
1250 (handle function)
1251 (mh-handle-set-external-undisplayer folder handle function)))
bdcfe844
BW
1252 (when (and function (eolp))
1253 (backward-char))
1254 (unwind-protect (and function (funcall function data))
1255 (set-buffer-modified-p nil)))))
1256
c3d9274a 1257;;;###mh-autoload
bdcfe844
BW
1258(defun mh-push-button (event)
1259 "Click MIME button for EVENT.
2dcf34f9
BW
1260
1261If the MIME part is visible then it is removed. Otherwise the
1262part is displayed. This function is called when the mouse is used
1263to click the MIME button."
bdcfe844 1264 (interactive "e")
a66894d8
BW
1265 (mh-do-at-event-location event
1266 (let ((folder mh-show-folder-buffer)
1267 (mm-inline-media-tests mh-mm-inline-media-tests)
1268 (data (get-text-property (point) 'mh-data))
1269 (function (get-text-property (point) 'mh-callback)))
1270 (flet ((mm-handle-set-external-undisplayer (handle func)
1271 (mh-handle-set-external-undisplayer folder handle func)))
1272 (and function (funcall function data))))))
bdcfe844 1273
c3d9274a 1274;;;###mh-autoload
bdcfe844
BW
1275(defun mh-mime-save-part ()
1276 "Save MIME part at point."
1277 (interactive)
1278 (let ((data (get-text-property (point) 'mh-data)))
1279 (when data
a66894d8
BW
1280 (let ((mm-default-directory
1281 (file-name-as-directory (or mh-mime-save-parts-directory
1282 default-directory))))
c3d9274a
BW
1283 (mh-mm-save-part data)
1284 (setq mh-mime-save-parts-directory mm-default-directory)))))
bdcfe844 1285
c3d9274a 1286;;;###mh-autoload
bdcfe844
BW
1287(defun mh-mime-inline-part ()
1288 "Toggle display of the raw MIME part."
1289 (interactive)
1290 (let* ((buffer-read-only nil)
1291 (data (get-text-property (point) 'mh-data))
1292 (inserted-flag (get-text-property (point) 'mh-mime-inserted))
1293 (displayed-flag (mm-handle-displayed-p data))
1294 (point (point))
1295 start end)
1296 (cond ((and data (not inserted-flag) (not displayed-flag))
1297 (let ((contents (mm-get-part data)))
1298 (add-text-properties (line-beginning-position) (line-end-position)
1299 '(mh-mime-inserted t))
1300 (setq start (point-marker))
1301 (forward-line 1)
1302 (mm-insert-inline data contents)
1303 (setq end (point-marker))
1304 (add-text-properties
1305 start (progn (goto-char start) (line-end-position))
1306 `(mh-region (,start . ,end)))))
1307 ((and data (or inserted-flag displayed-flag))
1308 (mh-press-button)
1309 (message "MIME part already inserted")))
1310 (goto-char point)
1311 (set-buffer-modified-p nil)))
1312
f0d73c14
BW
1313;;;###mh-autoload
1314(defun mh-display-with-external-viewer (part-index)
553fb735
BW
1315 "View attachment externally.
1316
2dcf34f9
BW
1317If Emacs does not know how to view an attachment, you could save
1318it into a file and then run some program to open it. It is
1319easier, however, to launch the program directly from MH-E with
1320this command. While you'll most likely use this to view
1321spreadsheets and documents, it is also useful to use your browser
1322to view HTML attachments with higher fidelity than what Emacs can
1323provide.
1324
1325This command displays the attachment associated with the button
1326under the cursor. If the cursor is not located over a button,
1327then the cursor first moves to the next button, wrapping to the
1328beginning of the message if necessary. You can provide a numeric
1329prefix argument PART-INDEX to view the attachment labeled with
1330that number.
1331
1332This command tries to provide a reasonable default for the viewer
1333by calling the Emacs function `mailcap-mime-info'. This function
1334usually reads the file \"/etc/mailcap\"."
f0d73c14
BW
1335 (interactive "P")
1336 (when (consp part-index) (setq part-index (car part-index)))
1337 (mh-folder-mime-action
1338 part-index
1339 #'(lambda ()
1340 (let* ((part (get-text-property (point) 'mh-data))
1341 (type (mm-handle-media-type part))
1342 (methods (mapcar (lambda (x) (list (cdr (assoc 'viewer x))))
1343 (mailcap-mime-info type 'all)))
1344 (def (caar methods))
553fb735
BW
1345 (prompt (format "Viewer%s: " (if def
1346 (format " (default %s)" def)
1347 "")))
f0d73c14
BW
1348 (method (completing-read prompt methods nil nil nil nil def))
1349 (folder mh-show-folder-buffer)
1350 (buffer-read-only nil))
1351 (when (string-match "^[^% \t]+$" method)
1352 (setq method (concat method " %s")))
1353 (flet ((mm-handle-set-external-undisplayer (handle function)
1354 (mh-handle-set-external-undisplayer folder handle function)))
1355 (unwind-protect (mm-display-external part method)
1356 (set-buffer-modified-p nil)))))
1357 nil))
1358
bdcfe844
BW
1359(defun mh-widget-press-button (widget el)
1360 "Callback for widget, WIDGET.
1361Parameter EL is unused."
1362 (goto-char (widget-get widget :from))
1363 (mh-press-button))
1364
1365(defun mh-mime-display-security (handle)
1366 "Display PGP encrypted/signed message, HANDLE."
bdcfe844
BW
1367 (save-restriction
1368 (narrow-to-region (point) (point))
f0d73c14 1369 (insert "\n")
bdcfe844
BW
1370 (mh-insert-mime-security-button handle)
1371 (mh-mime-display-mixed (cdr handle))
1372 (insert "\n")
1373 (let ((mh-mime-security-button-line-format
c3d9274a 1374 mh-mime-security-button-end-line-format))
bdcfe844
BW
1375 (mh-insert-mime-security-button handle))
1376 (mm-set-handle-multipart-parameter
f0d73c14 1377 handle 'mh-region (cons (point-min-marker) (point-max-marker)))))
bdcfe844 1378
cee9f5c6
BW
1379;; I rewrote the security part because Gnus doesn't seem to ever minimize
1380;; the button. That is once the mime-security button is pressed there seems
1381;; to be no way of getting rid of the inserted text.
bdcfe844
BW
1382(defun mh-mime-security-show-details (handle)
1383 "Toggle display of detailed security info for HANDLE."
1384 (let ((details (mm-handle-multipart-ctl-parameter handle 'gnus-details)))
1385 (when details
1386 (let ((mh-mime-security-button-pressed
c3d9274a 1387 (not (get-text-property (point) 'mh-button-pressed)))
bdcfe844 1388 (mh-mime-security-button-line-format
c3d9274a 1389 (get-text-property (point) 'mh-line-format)))
bdcfe844
BW
1390 (forward-char -1)
1391 (while (eq (get-text-property (point) 'mh-line-format)
1392 mh-mime-security-button-line-format)
1393 (forward-char -1))
1394 (forward-char)
1395 (save-restriction
1396 (narrow-to-region (point) (point))
1397 (mh-insert-mime-security-button handle))
1398 (delete-region
1399 (point)
1400 (or (text-property-not-all
1401 (point) (point-max)
1402 'mh-line-format mh-mime-security-button-line-format)
1403 (point-max)))
1404 (forward-line -1)))))
1405
e495eaec
BW
1406(defun mh-mime-security-button-face (info)
1407 "Return the button face to use for encrypted/signed mail based on INFO."
1408 (cond ((string-match "OK" info) ;Decrypted mail
d49ed7d4 1409 'mh-show-pgg-good)
e495eaec 1410 ((string-match "Failed" info) ;Decryption failed or signature invalid
d49ed7d4 1411 'mh-show-pgg-bad)
e495eaec 1412 ((string-match "Undecided" info);Unprocessed mail
d49ed7d4 1413 'mh-show-pgg-unknown)
e495eaec 1414 ((string-match "Untrusted" info);Key not trusted
d49ed7d4
BW
1415 'mh-show-pgg-unknown)
1416 (t
1417 'mh-show-pgg-good)))
e495eaec 1418
bdcfe844
BW
1419(defun mh-mime-security-press-button (handle)
1420 "Callback from security button for part HANDLE."
f0d73c14
BW
1421 (if (mm-handle-multipart-ctl-parameter handle 'gnus-info)
1422 (mh-mime-security-show-details handle)
1423 (let ((region (mm-handle-multipart-ctl-parameter handle 'mh-region))
1424 point)
1425 (setq point (point))
1426 (goto-char (car region))
1427 (delete-region (car region) (cdr region))
1428 (with-current-buffer (mm-handle-multipart-ctl-parameter handle 'buffer)
1429 (let* ((mm-verify-option 'known)
1430 (mm-decrypt-option 'known)
1431 (new (mm-possibly-verify-or-decrypt (cdr handle) handle)))
1432 (unless (eq new (cdr handle))
1433 (mm-destroy-parts (cdr handle))
1434 (setcdr handle new))))
1435 (mh-mime-display-security handle)
1436 (goto-char point))))
bdcfe844
BW
1437
1438;; These variables should already be initialized in mm-decode.el if we have a
1439;; recent enough Gnus. The defvars are here to avoid compiler warnings.
1440(defvar mm-verify-function-alist nil)
1441(defvar mm-decrypt-function-alist nil)
1442
1443(defvar pressed-details)
1444
1445(defun mh-insert-mime-security-button (handle)
1446 "Display buttons for PGP message, HANDLE."
1447 (let* ((protocol (mm-handle-multipart-ctl-parameter handle 'protocol))
1448 (crypto-type (or (nth 2 (assoc protocol mm-verify-function-alist))
1449 (nth 2 (assoc protocol mm-decrypt-function-alist))
1450 "Unknown"))
1451 (type (concat crypto-type
1452 (if (equal (car handle) "multipart/signed")
1453 " Signed" " Encrypted")
1454 " Part"))
1455 (info (or (mm-handle-multipart-ctl-parameter handle 'gnus-info)
1456 "Undecided"))
1457 (details (mm-handle-multipart-ctl-parameter handle 'gnus-details))
e495eaec 1458 pressed-details begin end face)
bdcfe844
BW
1459 (setq details (if details (concat "\n" details) ""))
1460 (setq pressed-details (if mh-mime-security-button-pressed details ""))
e495eaec 1461 (setq face (mh-mime-security-button-face info))
bdcfe844
BW
1462 (unless (bolp) (insert "\n"))
1463 (setq begin (point))
1464 (gnus-eval-format
1465 mh-mime-security-button-line-format
1466 mh-mime-security-button-line-format-alist
1467 `(,@(gnus-local-map-property mh-mime-security-button-map)
c3d9274a
BW
1468 mh-button-pressed ,mh-mime-security-button-pressed
1469 mh-callback mh-mime-security-press-button
1470 mh-line-format ,mh-mime-security-button-line-format
1471 mh-data ,handle))
bdcfe844
BW
1472 (setq end (point))
1473 (widget-convert-button 'link begin end
1474 :mime-handle handle
1475 :action 'mh-widget-press-button
1476 :button-keymap mh-mime-security-button-map
e495eaec 1477 :button-face face
bdcfe844 1478 :help-echo "Mouse-2 click or press RET (in show buffer) to see security details.")
f0d73c14
BW
1479 (dolist (ov (mh-funcall-if-exists overlays-in begin end))
1480 (mh-funcall-if-exists overlay-put ov 'evaporate t))
bdcfe844
BW
1481 (when (equal info "Failed")
1482 (let* ((type (if (equal (car handle) "multipart/signed")
1483 "verification" "decryption"))
1484 (warning (if (equal type "decryption")
1485 "(passphrase may be incorrect)" "")))
1486 (message "%s %s failed %s" crypto-type type warning)))))
1487
1488(defun mh-mm-inline-message (handle)
1489 "Display message, HANDLE.
2dcf34f9
BW
1490The function decodes the message and displays it. It avoids
1491decoding the same message multiple times."
bdcfe844 1492 (let ((b (point))
bdcfe844 1493 (clean-message-header mh-clean-message-header-flag)
f0d73c14
BW
1494 (invisible-headers mh-invisible-header-fields-compiled)
1495 (visible-headers nil))
bdcfe844
BW
1496 (save-excursion
1497 (save-restriction
1498 (narrow-to-region b b)
1499 (mm-insert-part handle)
1500 (mh-mime-display
1501 (or (gethash handle (mh-mime-handles-cache (mh-buffer-data)))
1502 (setf (gethash handle (mh-mime-handles-cache (mh-buffer-data)))
1503 (let ((handles (or (mm-dissect-buffer nil)
1504 (mm-uu-dissect))))
1505 (setf (mh-mime-handles (mh-buffer-data))
1506 (mm-merge-handles
1507 handles (mh-mime-handles (mh-buffer-data))))
1508 handles))))
1509
1510 (goto-char (point-min))
924df208 1511 (mh-show-xface)
bdcfe844
BW
1512 (cond (clean-message-header
1513 (mh-clean-msg-header (point-min)
1514 invisible-headers
1515 visible-headers)
1516 (goto-char (point-min)))
1517 (t
1518 (mh-start-of-uncleaned-message)))
924df208 1519 (mh-decode-message-header)
bdcfe844
BW
1520 (mh-show-addr)
1521 ;; The other highlighting types don't need anything special
553fb735 1522 (when (eq mh-highlight-citation-style 'gnus)
bdcfe844
BW
1523 (mh-gnus-article-highlight-citation))
1524 (goto-char (point-min))
1525 (insert "\n------- Forwarded Message\n\n")
1526 (mh-display-smileys)
1527 (mh-display-emphasis)
1528 (mm-handle-set-undisplayer
1529 handle
1530 `(lambda ()
1531 (let (buffer-read-only)
1532 (if (fboundp 'remove-specifier)
1533 ;; This is only valid on XEmacs.
1534 (mapcar (lambda (prop)
1535 (remove-specifier
1536 (face-property 'default prop) (current-buffer)))
1537 '(background background-pixmap foreground)))
1538 (delete-region ,(point-min-marker) ,(point-max-marker)))))))))
1539
1540(provide 'mh-mime)
1541
cee9f5c6
BW
1542;; Local Variables:
1543;; indent-tabs-mode: nil
1544;; sentence-end-double-space: nil
1545;; End:
bdcfe844 1546
cee9f5c6 1547;; arch-tag: 0dd36518-1b64-4a84-8f4e-59f422d3f002
60370d40 1548;;; mh-mime.el ends here