(rmail-reply-regexp): Also ignore mailing list
[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,
af435184 4;; 2001, 2002, 2003, 2004, 2005, 2006 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.
af435184
BW
321
322You can have your message initiate an \"ftp\" transfer when the
323recipient reads the message. You are prompted for the remote HOST
324and FILENAME, the media TYPE, and the content DESCRIPTION.
bdcfe844 325
0c47b17c 326See also \\[mh-mh-to-mime]."
c26cf6c8 327 (interactive (list
c3d9274a
BW
328 (read-string "Remote host: ")
329 (read-string "Remote filename: ")
0c47b17c
BW
330 (mh-minibuffer-read-type "DUMMY-FILENAME")
331 (mml-minibuffer-read-description)))
332 (mh-mh-compose-external-type "anon-ftp" host filename
333 type description))
c26cf6c8 334
c3d9274a 335;;;###mh-autoload
0c47b17c
BW
336(defun mh-mh-compose-external-compressed-tar (host filename description)
337 "Add tag to include anonymous ftp reference to a compressed tar file.
af435184 338
2dcf34f9 339In addition to retrieving the file via anonymous \"ftp\" as per
af435184 340the command \\[mh-mh-compose-anon-ftp], the file will also be
2dcf34f9
BW
341uncompressed and untarred. You are prompted for the remote HOST
342and FILENAME and the content DESCRIPTION.
0c47b17c
BW
343
344See also \\[mh-mh-to-mime]."
c26cf6c8 345 (interactive (list
c3d9274a
BW
346 (read-string "Remote host: ")
347 (read-string "Remote filename: ")
0c47b17c
BW
348 (mml-minibuffer-read-description)))
349 (mh-mh-compose-external-type "anon-ftp" host filename
350 "application/octet-stream"
351 description
352 "type=tar; conversions=x-compress"
353 "mode=image"))
c26cf6c8 354
f0d73c14 355;;;###mh-autoload
0c47b17c
BW
356(defun mh-mh-compose-external-type (access-type host filename type
357 &optional description
358 attributes parameters
359 comment)
360 "Add tag to refer to a remote file.
af435184 361
2dcf34f9
BW
362This command is a general utility for referencing external files.
363In fact, all of the other commands that insert directives to
364access external files call this command. You are prompted for the
365ACCESS-TYPE, remote HOST and FILENAME, and content TYPE. If you
366provide a prefix argument, you are also prompted for a content
367DESCRIPTION, ATTRIBUTES, PARAMETERS, and a COMMENT.
0c47b17c
BW
368
369See also \\[mh-mh-to-mime]."
f0d73c14 370 (interactive (list
0c47b17c 371 (completing-read "Access type: " mh-access-types)
f0d73c14 372 (read-string "Remote host: ")
0c47b17c
BW
373 (read-string "Remote filename: ")
374 (mh-minibuffer-read-type "DUMMY-FILENAME")
375 (if current-prefix-arg (mml-minibuffer-read-description))
f0d73c14 376 (if current-prefix-arg (read-string "Attributes: "))
0c47b17c 377 (if current-prefix-arg (read-string "Parameters: "))
f0d73c14 378 (if current-prefix-arg (read-string "Comment: "))))
c26cf6c8
RS
379 (beginning-of-line)
380 (insert "#@" type)
381 (and attributes
382 (insert "; " attributes))
383 (and comment
384 (insert " (" comment ") "))
385 (insert " [")
386 (and description
387 (insert description))
388 (insert "] ")
389 (insert "access-type=" access-type "; ")
390 (insert "site=" host)
3fda54a2 391 (insert "; name=" (file-name-nondirectory filename))
f0d73c14
BW
392 (let ((directory (file-name-directory filename)))
393 (and directory
394 (insert "; directory=\"" directory "\"")))
0c47b17c
BW
395 (and parameters
396 (insert "; " parameters))
c26cf6c8
RS
397 (insert "\n"))
398
c3d9274a 399;;;###mh-autoload
0c47b17c
BW
400(defun mh-mh-forward-message (&optional description folder messages)
401 "Add tag to forward a message.
2dcf34f9
BW
402You are prompted for a content DESCRIPTION, the name of the
403FOLDER in which the messages to forward are located, and the
404MESSAGES' numbers.
bdcfe844 405
0c47b17c 406See also \\[mh-mh-to-mime]."
c26cf6c8 407 (interactive (list
0c47b17c 408 (mml-minibuffer-read-description)
c3d9274a 409 (mh-prompt-for-folder "Message from" mh-sent-from-folder nil)
d88a70a0 410 (read-string (concat "Messages"
f0d73c14 411 (if (numberp mh-sent-from-msg)
0c47b17c
BW
412 (format " (default %d): "
413 mh-sent-from-msg)
d88a70a0 414 ": ")))))
c26cf6c8
RS
415 (beginning-of-line)
416 (insert "#forw [")
417 (and description
418 (not (string= description ""))
419 (insert description))
420 (insert "]")
421 (and folder
422 (not (string= folder ""))
423 (insert " " folder))
847b8219 424 (if (and messages
c3d9274a 425 (not (string= messages "")))
c26cf6c8 426 (let ((start (point)))
c3d9274a
BW
427 (insert " " messages)
428 (subst-char-in-region start (point) ?, ? ))
f0d73c14 429 (if (numberp mh-sent-from-msg)
c3d9274a 430 (insert " " (int-to-string mh-sent-from-msg))))
c26cf6c8
RS
431 (insert "\n"))
432
c3d9274a 433;;;###mh-autoload
0c47b17c
BW
434(defun mh-mh-to-mime (&optional extra-args)
435 "Compose MIME message from MH-style directives.
d1699462 436
2dcf34f9
BW
437Typically, you send a message with attachments just like any other
438message. However, you may take a sneak preview of the MIME encoding if
439you wish by running this command.
0c47b17c 440
af435184
BW
441If you wish to pass additional arguments to \"mhbuild\" (\"mhn\")
442to affect how it builds your message, use the option
443`mh-mh-to-mime-args'. For example, you can build a consistency
444check into the message by setting `mh-mh-to-mime-args' to
445\"-check\". The recipient of your message can then run \"mhbuild
446-check\" on the message--\"mhbuild\" (\"mhn\") will complain if
447the message has been corrupted on the way. This command only
448consults this option when given a prefix argument EXTRA-ARGS.
0c47b17c 449
d1699462
BW
450The hook `mh-mh-to-mime-hook' is called after the message has been
451formatted.
0c47b17c 452
2dcf34f9
BW
453The effects of this command can be undone by running
454\\[mh-mh-to-mime-undo]."
847b8219 455 (interactive "*P")
0c47b17c 456 (mh-mh-quote-unescaped-sharp)
c26cf6c8 457 (save-buffer)
0c47b17c 458 (message "Running %s..." (if (mh-variant-p 'nmh) "mhbuild" "mhn"))
a1b4049d 459 (cond
f0d73c14 460 ((mh-variant-p 'nmh)
a1b4049d 461 (mh-exec-cmd-error nil
0c47b17c
BW
462 "mhbuild"
463 (if extra-args mh-mh-to-mime-args)
464 buffer-file-name))
a1b4049d
BW
465 (t
466 (mh-exec-cmd-error (format "mhdraft=%s" buffer-file-name)
0c47b17c
BW
467 "mhn"
468 (if extra-args mh-mh-to-mime-args)
469 buffer-file-name)))
c26cf6c8 470 (revert-buffer t t)
0c47b17c
BW
471 (message "Running %s...done" (if (mh-variant-p 'nmh) "mhbuild" "mhn"))
472 (run-hooks 'mh-mh-to-mime-hook))
c26cf6c8 473
0c47b17c 474(defun mh-mh-quote-unescaped-sharp ()
5a4aad03
BW
475 "Quote \"#\" characters that haven't been quoted for \"mhbuild\".
476If the \"#\" character is present in the first column, but it isn't
2dcf34f9
BW
477part of a MH-style directive then \"mhbuild\" gives an error.
478This function will quote all such characters."
f0d73c14
BW
479 (save-excursion
480 (goto-char (point-min))
481 (while (re-search-forward "^#" nil t)
482 (beginning-of-line)
0c47b17c 483 (unless (mh-mh-directive-present-p (point) (line-end-position))
f0d73c14
BW
484 (insert "#"))
485 (goto-char (line-end-position)))))
486
c3d9274a 487;;;###mh-autoload
0c47b17c
BW
488(defun mh-mh-to-mime-undo (noconfirm)
489 "Undo effects of \\[mh-mh-to-mime].
af435184
BW
490
491It does this by reverting to a backup file. You are prompted to
492confirm this action, but you can avoid the confirmation by adding
493a prefix argument NOCONFIRM."
c26cf6c8
RS
494 (interactive "*P")
495 (if (null buffer-file-name)
496 (error "Buffer does not seem to be associated with any file"))
497 (let ((backup-strings '("," "#"))
c3d9274a 498 backup-file)
c26cf6c8 499 (while (and backup-strings
c3d9274a
BW
500 (not (file-exists-p
501 (setq backup-file
502 (concat (file-name-directory buffer-file-name)
503 (car backup-strings)
504 (file-name-nondirectory buffer-file-name)
505 ".orig")))))
c26cf6c8
RS
506 (setq backup-strings (cdr backup-strings)))
507 (or backup-strings
f9c53c97 508 (error "Backup file for %s no longer exists" buffer-file-name))
c26cf6c8 509 (or noconfirm
c3d9274a
BW
510 (yes-or-no-p (format "Revert buffer from file %s? "
511 backup-file))
512 (error "Revert not confirmed"))
c26cf6c8
RS
513 (let ((buffer-read-only nil))
514 (erase-buffer)
515 (insert-file-contents backup-file))
516 (after-find-file nil)))
60370d40 517
924df208 518;;;###mh-autoload
0c47b17c
BW
519(defun mh-mh-directive-present-p (&optional begin end)
520 "Check if the text between BEGIN and END might be a MH-style directive.
2dcf34f9
BW
521The optional argument BEGIN defaults to the beginning of the
522buffer, while END defaults to the the end of the buffer."
f0d73c14
BW
523 (unless begin (setq begin (point-min)))
524 (unless end (setq end (point-max)))
924df208 525 (save-excursion
0c47b17c 526 (block 'search-for-mh-directive
f0d73c14
BW
527 (goto-char begin)
528 (while (re-search-forward "^#" end t)
924df208
BW
529 (let ((s (buffer-substring-no-properties (point) (line-end-position))))
530 (cond ((equal s ""))
531 ((string-match "^forw[ \t\n]+" s)
0c47b17c 532 (return-from 'search-for-mh-directive t))
924df208 533 (t (let ((first-token (car (split-string s "[ \t;@]"))))
f0d73c14
BW
534 (when (and first-token
535 (string-match mh-media-type-regexp
536 first-token))
0c47b17c 537 (return-from 'search-for-mh-directive t)))))))
924df208
BW
538 nil)))
539
bdcfe844
BW
540\f
541
542;;; MIME composition functions
543
c3d9274a 544;;;###mh-autoload
bdcfe844 545(defun mh-mml-to-mime ()
0c47b17c 546 "Compose MIME message from MML tags.
2dcf34f9
BW
547
548Typically, you send a message with attachments just like any
549other message. However, you may take a sneak preview of the MIME
550encoding if you wish by running this command.
0c47b17c
BW
551
552This action can be undone by running \\[undo]."
bdcfe844 553 (interactive)
a66894d8 554 (require 'message)
0c47b17c 555 (when mh-pgp-support-flag ;; This is only needed for PGP
bdcfe844 556 (message-options-set-recipient))
f0d73c14
BW
557 (let ((saved-text (buffer-string))
558 (buffer (current-buffer))
559 (modified-flag (buffer-modified-p)))
560 (condition-case err (mml-to-mime)
561 (error
562 (with-current-buffer buffer
563 (delete-region (point-min) (point-max))
564 (insert saved-text)
565 (set-buffer-modified-p modified-flag))
566 (error (error-message-string err))))))
bdcfe844 567
c3d9274a 568;;;###mh-autoload
bdcfe844
BW
569(defun mh-mml-forward-message (description folder message)
570 "Forward a message as attachment.
2dcf34f9
BW
571
572The function will prompt the user for a DESCRIPTION, a FOLDER and
573MESSAGE number."
f0d73c14 574 (let ((msg (if (and (equal message "") (numberp mh-sent-from-msg))
bdcfe844
BW
575 mh-sent-from-msg
576 (car (read-from-string message)))))
577 (cond ((integerp msg)
578 (if (string= "" description)
579 ;; Rationale: mml-attach-file constructs a malformed composition
580 ;; if the description string is empty. This fixes SF #625168.
581 (mml-attach-file (format "%s%s/%d"
582 mh-user-path (substring folder 1) msg)
583 "message/rfc822")
584 (mml-attach-file (format "%s%s/%d"
585 mh-user-path (substring folder 1) msg)
586 "message/rfc822"
924df208 587 description)))
f9c53c97 588 (t (error "The message number, %s, is not a integer" msg)))))
bdcfe844 589
f0d73c14
BW
590(defvar mh-mml-cryptographic-method-history ())
591
592;;;###mh-autoload
593(defun mh-mml-query-cryptographic-method ()
594 "Read the cryptographic method to use."
595 (if current-prefix-arg
596 (let ((def (or (car mh-mml-cryptographic-method-history)
597 mh-mml-method-default)))
0c47b17c 598 (completing-read (format "Method (default %s): " def)
f0d73c14
BW
599 '(("pgp") ("pgpmime") ("smime"))
600 nil t nil 'mh-mml-cryptographic-method-history def))
601 mh-mml-method-default))
602
c3d9274a 603;;;###mh-autoload
bdcfe844 604(defun mh-mml-attach-file (&optional disposition)
0c47b17c 605 "Add a tag to insert a MIME message part from a file.
2dcf34f9
BW
606
607You are prompted for the filename containing the object, the
608media type if it cannot be determined automatically, a content
609description and the DISPOSITION of the attachment.
bdcfe844 610
0c47b17c 611This is basically `mml-attach-file' from Gnus, modified such that a prefix
5a4aad03 612argument yields an \"inline\" disposition and Content-Type is determined
bdcfe844
BW
613automatically."
614 (let* ((file (mml-minibuffer-read-file "Attach file: "))
0c47b17c 615 (type (mh-minibuffer-read-type file))
bdcfe844
BW
616 (description (mml-minibuffer-read-description))
617 (dispos (or disposition
0c47b17c 618 (mml-minibuffer-read-disposition type))))
bdcfe844 619 (mml-insert-empty-tag 'part 'type type 'filename file
924df208 620 'disposition dispos 'description description)))
bdcfe844 621
eccf9613
BW
622(defvar mh-identity-pgg-default-user-id)
623
f0d73c14 624(defun mh-secure-message (method mode &optional identity)
0c47b17c 625 "Add tag to encrypt or sign message.
2dcf34f9 626
f0d73c14
BW
627METHOD should be one of: \"pgpmime\", \"pgp\", \"smime\".
628MODE should be one of: \"sign\", \"encrypt\", \"signencrypt\", \"none\".
629IDENTITY is optionally the default-user-id to use."
0c47b17c
BW
630 (if (not mh-pgp-support-flag)
631 (error "Your version of Gnus does not support PGP/GPG")
f0d73c14
BW
632 ;; Check the arguments
633 (let ((valid-methods (list "pgpmime" "pgp" "smime"))
634 (valid-modes (list "sign" "encrypt" "signencrypt" "none")))
635 (if (not (member method valid-methods))
f9c53c97 636 (error "Method %s is invalid" method))
f0d73c14 637 (if (not (member mode valid-modes))
f9c53c97 638 (error "Mode %s is invalid" mode))
f0d73c14
BW
639 (mml-unsecure-message)
640 (if (not (string= mode "none"))
641 (save-excursion
642 (goto-char (point-min))
643 (mh-goto-header-end 1)
644 (if mh-identity-pgg-default-user-id
645 (mml-insert-tag 'secure 'method method 'mode mode
646 'sender mh-identity-pgg-default-user-id)
647 (mml-insert-tag 'secure 'method method 'mode mode)))))))
bdcfe844 648
c3d9274a 649;;;###mh-autoload
285d1e0c
MB
650(defun mh-mml-unsecure-message ()
651 "Remove any secure message tags."
652 (interactive)
0c47b17c
BW
653 (if (not mh-pgp-support-flag)
654 (error "Your version of Gnus does not support PGP/GPG")
f0d73c14
BW
655 (mml-unsecure-message)))
656
657;;;###mh-autoload
658(defun mh-mml-secure-message-sign (method)
0c47b17c 659 "Add tag to sign the message.
2dcf34f9
BW
660
661A proper multipart message is created for you when you send the
af435184 662message. Use the command \\[mh-mml-unsecure-message] to remove
2dcf34f9 663this tag. Use a prefix argument METHOD to be prompted for one of
4023e353 664the possible security methods (see `mh-mml-method-default')."
f0d73c14
BW
665 (interactive (list (mh-mml-query-cryptographic-method)))
666 (mh-secure-message method "sign" mh-identity-pgg-default-user-id))
667
668;;;###mh-autoload
669(defun mh-mml-secure-message-encrypt (method)
0c47b17c 670 "Add tag to encrypt the message.
2dcf34f9
BW
671
672A proper multipart message is created for you when you send the
af435184 673message. Use the command \\[mh-mml-unsecure-message] to remove
2dcf34f9 674this tag. Use a prefix argument METHOD to be prompted for one of
4023e353 675the possible security methods (see `mh-mml-method-default')."
f0d73c14
BW
676 (interactive (list (mh-mml-query-cryptographic-method)))
677 (mh-secure-message method "encrypt" mh-identity-pgg-default-user-id))
678
679;;;###mh-autoload
680(defun mh-mml-secure-message-signencrypt (method)
0c47b17c 681 "Add tag to encrypt and sign the message.
2dcf34f9
BW
682
683A proper multipart message is created for you when you send the
af435184 684message. Use the command \\[mh-mml-unsecure-message] to remove
2dcf34f9 685this tag. Use a prefix argument METHOD to be prompted for one of
4023e353 686the possible security methods (see `mh-mml-method-default')."
f0d73c14
BW
687 (interactive (list (mh-mml-query-cryptographic-method)))
688 (mh-secure-message method "signencrypt" mh-identity-pgg-default-user-id))
924df208
BW
689
690;;;###mh-autoload
0c47b17c
BW
691(defun mh-mml-tag-present-p ()
692 "Check if the current buffer has text which may be a MML tag."
924df208
BW
693 (save-excursion
694 (goto-char (point-min))
695 (re-search-forward
98b7b7ed
BW
696 (concat
697 "\\(<#\\(mml\\|part\\)\\(.\\|\n\\)*>[ \n\t]*<#/\\(mml\\|part\\)>\\|"
698 "^<#secure.+>$\\)")
924df208 699 nil t)))
bdcfe844
BW
700
701\f
702
bdcfe844
BW
703;;; MIME cleanup
704
c3d9274a 705;;;###mh-autoload
bdcfe844
BW
706(defun mh-mime-cleanup ()
707 "Free the decoded MIME parts."
708 (let ((mime-data (gethash (current-buffer) mh-globals-hash)))
709 ;; This is for Emacs, what about XEmacs?
924df208 710 (mh-funcall-if-exists remove-images (point-min) (point-max))
bdcfe844
BW
711 (when mime-data
712 (mm-destroy-parts (mh-mime-handles mime-data))
713 (remhash (current-buffer) mh-globals-hash))))
714
c3d9274a 715;;;###mh-autoload
bdcfe844 716(defun mh-destroy-postponed-handles ()
0c47b17c 717 "Free MIME data for externally displayed MIME parts."
bdcfe844
BW
718 (let ((mime-data (mh-buffer-data)))
719 (when mime-data
720 (mm-destroy-parts (mh-mime-handles mime-data)))
721 (remhash (current-buffer) mh-globals-hash)))
722
723(defun mh-handle-set-external-undisplayer (folder handle function)
724 "Replacement for `mm-handle-set-external-undisplayer'.
2dcf34f9
BW
725
726This is only called in recent versions of Gnus. The MIME handles
727are stored in data structures corresponding to MH-E folder buffer
728FOLDER instead of in Gnus (as in the original). The MIME part,
729HANDLE is associated with the undisplayer FUNCTION."
bdcfe844
BW
730 (if (mm-keep-viewer-alive-p handle)
731 (let ((new-handle (copy-sequence handle)))
c3d9274a
BW
732 (mm-handle-set-undisplayer new-handle function)
733 (mm-handle-set-undisplayer handle nil)
bdcfe844
BW
734 (save-excursion
735 (set-buffer folder)
736 (push new-handle (mh-mime-handles (mh-buffer-data)))))
737 (mm-handle-set-undisplayer handle function)))
738
739\f
740
741;;; MIME transformations
c3d9274a 742(eval-when-compile (require 'font-lock))
bdcfe844 743
c3d9274a 744;;;###mh-autoload
bdcfe844
BW
745(defun mh-add-missing-mime-version-header ()
746 "Some mail programs don't put a MIME-Version header.
2dcf34f9
BW
747I have seen this only in spam, so maybe we shouldn't fix
748this ;-)"
bdcfe844
BW
749 (save-excursion
750 (goto-char (point-min))
a66894d8
BW
751 (re-search-forward "\n\n" nil t)
752 (save-restriction
753 (narrow-to-region (point-min) (point))
754 (when (and (message-fetch-field "content-type")
755 (not (message-fetch-field "mime-version")))
756 (goto-char (point-min))
bdcfe844
BW
757 (insert "MIME-Version: 1.0\n")))))
758
a66894d8
BW
759(defun mh-small-show-buffer-p ()
760 "Check if show buffer is small.
2dcf34f9
BW
761This is used to decide if smileys and graphical emphasis will be
762displayed."
a66894d8
BW
763 (let ((max nil))
764 (when (and (boundp 'font-lock-maximum-size) font-lock-maximum-size)
765 (cond ((numberp font-lock-maximum-size)
766 (setq max font-lock-maximum-size))
767 ((listp font-lock-maximum-size)
768 (setq max (cdr (or (assoc 'mh-show-mode font-lock-maximum-size)
769 (assoc t font-lock-maximum-size)))))))
770 (or (not (numberp max)) (>= (/ max 8) (buffer-size)))))
771
c3d9274a 772;;;###mh-autoload
bdcfe844 773(defun mh-display-smileys ()
0c47b17c 774 "Display smileys."
a66894d8
BW
775 (when (and mh-graphical-smileys-flag (mh-small-show-buffer-p))
776 (mh-funcall-if-exists smiley-region (point-min) (point-max))))
bdcfe844 777
c3d9274a 778;;;###mh-autoload
bdcfe844 779(defun mh-display-emphasis ()
0c47b17c 780 "Display graphical emphasis."
a66894d8 781 (when (and mh-graphical-emphasis-flag (mh-small-show-buffer-p))
c3d9274a 782 (flet ((article-goto-body ())) ; shadow this function to do nothing
bdcfe844
BW
783 (save-excursion
784 (goto-char (point-min))
785 (article-emphasize)))))
786
787;; Copied from gnus-art.el (should be checked for other cool things that can
788;; be added to the buttons)
789(defvar mh-mime-button-commands
790 '((mh-press-button "\r" "Toggle Display")))
791(defvar mh-mime-button-map
792 (let ((map (make-sparse-keymap)))
793 (unless (>= (string-to-number emacs-version) 21)
794 ;; XEmacs doesn't care.
795 (set-keymap-parent map mh-show-mode-map))
924df208
BW
796 (mh-do-in-gnu-emacs
797 (define-key map [mouse-2] 'mh-push-button))
798 (mh-do-in-xemacs
799 (define-key map '(button2) 'mh-push-button))
bdcfe844
BW
800 (dolist (c mh-mime-button-commands)
801 (define-key map (cadr c) (car c)))
802 map))
803(defvar mh-mime-button-line-format-alist
804 '((?T long-type ?s)
805 (?d description ?s)
806 (?p index ?s)
807 (?e dots ?s)))
808(defvar mh-mime-button-line-format "%{%([%p. %d%T]%)%}%e\n")
809(defvar mh-mime-security-button-pressed nil)
810(defvar mh-mime-security-button-line-format "%{%([[%t:%i]%D]%)%}\n")
811(defvar mh-mime-security-button-end-line-format "%{%([[End of %t]%D]%)%}\n")
812(defvar mh-mime-security-button-line-format-alist
813 '((?t type ?s)
814 (?i info ?s)
815 (?d details ?s)
816 (?D pressed-details ?s)))
817(defvar mh-mime-security-button-map
818 (let ((map (make-sparse-keymap)))
819 (unless (>= (string-to-number emacs-version) 21)
820 (set-keymap-parent map mh-show-mode-map))
821 (define-key map "\r" 'mh-press-button)
924df208
BW
822 (mh-do-in-gnu-emacs
823 (define-key map [mouse-2] 'mh-push-button))
824 (mh-do-in-xemacs
825 (define-key map '(button2) 'mh-push-button))
bdcfe844
BW
826 map))
827
828(defvar mh-mime-save-parts-directory nil
829 "Default to use for `mh-mime-save-parts-default-directory'.
830Set from last use.")
831
c3d9274a 832;;;###mh-autoload
553fb735
BW
833(defun mh-mime-save-parts (prompt)
834 "Save attachments.
835
2dcf34f9
BW
836You can save all of the attachments at once with this command.
837The attachments are saved in the directory specified by the
838option `mh-mime-save-parts-default-directory' unless you use a
839prefix argument PROMPT in which case you are prompted for the
840directory. These directories may be superseded by MH profile
841components, since this function calls on \"mhstore\" (\"mhn\") to
842do the work."
bdcfe844
BW
843 (interactive "P")
844 (let ((msg (if (eq major-mode 'mh-show-mode)
845 (mh-show-buffer-message-number)
846 (mh-get-msg-num t)))
847 (folder (if (eq major-mode 'mh-show-mode)
848 mh-show-folder-buffer
849 mh-current-folder))
f0d73c14 850 (command (if (mh-variant-p 'nmh) "mhstore" "mhn"))
bdcfe844
BW
851 (directory
852 (cond
553fb735 853 ((and (or prompt
bdcfe844
BW
854 (equal nil mh-mime-save-parts-default-directory)
855 (equal t mh-mime-save-parts-default-directory))
856 (not mh-mime-save-parts-directory))
f0d73c14 857 (read-file-name "Store in directory: " nil nil t nil))
553fb735 858 ((and (or prompt
bdcfe844
BW
859 (equal t mh-mime-save-parts-default-directory))
860 mh-mime-save-parts-directory)
861 (read-file-name (format
078cb314 862 "Store in directory (default %s): "
bdcfe844
BW
863 mh-mime-save-parts-directory)
864 "" mh-mime-save-parts-directory t ""))
865 ((stringp mh-mime-save-parts-default-directory)
866 mh-mime-save-parts-default-directory)
867 (t
868 mh-mime-save-parts-directory))))
869 (if (and (equal directory "") mh-mime-save-parts-directory)
870 (setq directory mh-mime-save-parts-directory))
871 (if (not (file-directory-p directory))
f0d73c14 872 (message "No directory specified")
bdcfe844
BW
873 (if (equal nil mh-mime-save-parts-default-directory)
874 (setq mh-mime-save-parts-directory directory))
875 (save-excursion
924df208 876 (set-buffer (get-buffer-create mh-log-buffer))
bdcfe844
BW
877 (cd directory)
878 (setq mh-mime-save-parts-directory directory)
924df208
BW
879 (let ((initial-size (mh-truncate-log-buffer)))
880 (apply 'call-process
881 (expand-file-name command mh-progs) nil t nil
882 (mh-list-to-string (list folder msg "-auto")))
883 (if (> (buffer-size) initial-size)
884 (save-window-excursion
885 (switch-to-buffer-other-window mh-log-buffer)
886 (sit-for 3))))))))
bdcfe844
BW
887
888;; Avoid errors if gnus-sum isn't loaded yet...
889(defvar gnus-newsgroup-charset nil)
890(defvar gnus-newsgroup-name nil)
891
924df208
BW
892(defun mh-decode-message-body ()
893 "Decode message based on charset.
894If message has been encoded for transfer take that into account."
a66894d8
BW
895 (let (ct charset cte)
896 (goto-char (point-min))
897 (re-search-forward "\n\n" nil t)
898 (save-restriction
899 (narrow-to-region (point-min) (point))
900 (setq ct (ignore-errors (mail-header-parse-content-type
901 (message-fetch-field "Content-Type" t)))
902 charset (mail-content-type-get ct 'charset)
903 cte (message-fetch-field "Content-Transfer-Encoding")))
924df208
BW
904 (when (stringp cte) (setq cte (mail-header-strip cte)))
905 (when (or (not ct) (equal (car ct) "text/plain"))
906 (save-restriction
907 (narrow-to-region (min (1+ (mh-mail-header-end)) (point-max))
908 (point-max))
909 (mm-decode-body charset
910 (and cte (intern (downcase
911 (gnus-strip-whitespace cte))))
912 (car ct))))))
913
d103d8b3
BW
914;;;###mh-autoload
915(defun mh-toggle-mh-decode-mime-flag ()
553fb735 916 "Toggle the value of `mh-decode-mime-flag'."
eccf9613 917 (interactive)
d103d8b3
BW
918 (setq mh-decode-mime-flag (not mh-decode-mime-flag))
919 (mh-show nil t)
553fb735
BW
920 (message "%s" (if mh-decode-mime-flag
921 "Processing attachments normally"
922 "Displaying raw message")))
d103d8b3 923
924df208
BW
924;;;###mh-autoload
925(defun mh-decode-message-header ()
926 "Decode RFC2047 encoded message header fields."
927 (when mh-decode-mime-flag
928 (let ((buffer-read-only nil))
929 (rfc2047-decode-region (point-min) (mh-mail-header-end)))))
930
c3d9274a 931;;;###mh-autoload
bdcfe844
BW
932(defun mh-mime-display (&optional pre-dissected-handles)
933 "Display (and possibly decode) MIME handles.
2dcf34f9
BW
934Optional argument, PRE-DISSECTED-HANDLES is a list of MIME
935handles. If present they are displayed otherwise the buffer is
936parsed and then displayed."
bdcfe844 937 (let ((handles ())
924df208
BW
938 (folder mh-show-folder-buffer)
939 (raw-message-data (buffer-string)))
c3d9274a
BW
940 (flet ((mm-handle-set-external-undisplayer
941 (handle function)
942 (mh-handle-set-external-undisplayer folder handle function)))
924df208
BW
943 (goto-char (point-min))
944 (unless (search-forward "\n\n" nil t)
945 (goto-char (point-max))
946 (insert "\n\n"))
947
948 (condition-case err
949 (progn
950 ;; If needed dissect the current buffer
951 (if pre-dissected-handles
952 (setq handles pre-dissected-handles)
953 (setq handles (or (mm-dissect-buffer nil) (mm-uu-dissect)))
954 (setf (mh-mime-handles (mh-buffer-data))
955 (mm-merge-handles handles
956 (mh-mime-handles (mh-buffer-data))))
957 (unless handles (mh-decode-message-body)))
958
f0d73c14
BW
959 (cond ((and handles
960 (or (not (stringp (car handles))) (cdr handles)))
961 ;; Goto start of message body
962 (goto-char (point-min))
963 (or (search-forward "\n\n" nil t) (goto-char (point-max)))
bdcfe844 964
f0d73c14
BW
965 ;; Delete the body
966 (delete-region (point) (point-max))
bdcfe844 967
f0d73c14
BW
968 ;; Display the MIME handles
969 (mh-mime-display-part handles))
970 (t (mh-signature-highlight))))
924df208 971 (error
f9c53c97 972 (message "Please report this error:\n %s"
924df208
BW
973 (error-message-string err))
974 (delete-region (point-min) (point-max))
975 (insert raw-message-data))))))
bdcfe844
BW
976
977(defun mh-mime-display-part (handle)
978 "Decides the viewer to call based on the type of HANDLE."
979 (cond ((null handle) nil)
980 ((not (stringp (car handle)))
981 (mh-mime-display-single handle))
982 ((equal (car handle) "multipart/alternative")
983 (mh-mime-display-alternative (cdr handle)))
0c47b17c 984 ((and mh-pgp-support-flag
bdcfe844
BW
985 (or (equal (car handle) "multipart/signed")
986 (equal (car handle) "multipart/encrypted")))
987 (mh-mime-display-security handle))
988 (t (mh-mime-display-mixed (cdr handle)))))
989
990(defun mh-mime-display-alternative (handles)
991 "Choose among the alternatives, HANDLES the part that will be displayed.
992If no part is preferred then all the parts are displayed."
a66894d8
BW
993 (let* ((preferred (mm-preferred-alternative handles))
994 (others (loop for x in handles unless (eq x preferred) collect x)))
bdcfe844 995 (cond ((and preferred (stringp (car preferred)))
a66894d8
BW
996 (mh-mime-display-part preferred)
997 (mh-mime-maybe-display-alternatives others))
bdcfe844
BW
998 (preferred
999 (save-restriction
1000 (narrow-to-region (point) (if (eobp) (point) (1+ (point))))
c3d9274a 1001 (mh-mime-display-single preferred)
a66894d8 1002 (mh-mime-maybe-display-alternatives others)
bdcfe844
BW
1003 (goto-char (point-max))))
1004 (t (mh-mime-display-mixed handles)))))
1005
a66894d8
BW
1006(defun mh-mime-maybe-display-alternatives (alternatives)
1007 "Show buttons for ALTERNATIVES.
2dcf34f9
BW
1008If `mh-mime-display-alternatives-flag' is non-nil then display
1009buttons for alternative parts that are usually suppressed."
a66894d8
BW
1010 (when (and mh-display-buttons-for-alternatives-flag alternatives)
1011 (insert "\n----------------------------------------------------\n")
1012 (insert "Alternatives:\n")
1013 (dolist (x alternatives)
1014 (insert "\n")
1015 (mh-insert-mime-button x (mh-mime-part-index x) nil))
1016 (insert "\n----------------------------------------------------\n")))
1017
bdcfe844
BW
1018(defun mh-mime-display-mixed (handles)
1019 "Display the list of MIME parts, HANDLES recursively."
1020 (mapcar #'mh-mime-display-part handles))
1021
1022(defun mh-mime-part-index (handle)
1023 "Generate the button number for MIME part, HANDLE.
2dcf34f9
BW
1024Notice that a hash table is used to display the same number when
1025buttons need to be displayed multiple times (for instance when
1026nested messages are opened)."
bdcfe844
BW
1027 (or (gethash handle (mh-mime-part-index-hash (mh-buffer-data)))
1028 (setf (gethash handle (mh-mime-part-index-hash (mh-buffer-data)))
1029 (incf (mh-mime-parts-count (mh-buffer-data))))))
1030
bdcfe844
BW
1031(defun mh-small-image-p (handle)
1032 "Decide whether HANDLE is a \"small\" image that can be displayed inline.
1033This is only useful if a Content-Disposition header is not present."
1034 (let ((media-test (caddr (assoc (car (mm-handle-type handle))
1035 mh-mm-inline-media-tests)))
1036 (mm-inline-large-images t))
1037 (and media-test
1038 (equal (mm-handle-media-supertype handle) "image")
c3d9274a
BW
1039 (funcall media-test handle) ; Since mm-inline-large-images is T,
1040 ; this only tells us if the image is
1041 ; something that emacs can display
bdcfe844 1042 (let* ((image (mm-get-image handle)))
a66894d8
BW
1043 (or (mh-do-in-xemacs
1044 (and (mh-funcall-if-exists glyphp image)
1045 (< (glyph-width image)
1046 (or mh-max-inline-image-width (window-pixel-width)))
1047 (< (glyph-height image)
1048 (or mh-max-inline-image-height
1049 (window-pixel-height)))))
1050 (mh-do-in-gnu-emacs
1051 (let ((size (mh-funcall-if-exists image-size image)))
1052 (and size
1053 (< (cdr size) (or mh-max-inline-image-height
1054 (1- (window-height))))
1055 (< (car size) (or mh-max-inline-image-width
1056 (window-width)))))))))))
bdcfe844 1057
c3d9274a
BW
1058(defun mh-inline-vcard-p (handle)
1059 "Decide if HANDLE is a vcard that must be displayed inline."
1060 (let ((type (mm-handle-type handle)))
924df208
BW
1061 (and (or (featurep 'vcard) (fboundp 'vcard-pretty-print))
1062 (consp type)
c3d9274a
BW
1063 (equal (car type) "text/x-vcard")
1064 (save-excursion
1065 (save-restriction
1066 (widen)
1067 (goto-char (point-min))
f0d73c14 1068 (not (mh-signature-separator-p)))))))
c3d9274a 1069
bdcfe844
BW
1070(defun mh-mime-display-single (handle)
1071 "Display a leaf node, HANDLE in the MIME tree."
1072 (let* ((type (mm-handle-media-type handle))
1073 (small-image-flag (mh-small-image-p handle))
1074 (attachmentp (equal (car (mm-handle-disposition handle))
1075 "attachment"))
1076 (inlinep (and (equal (car (mm-handle-disposition handle)) "inline")
1077 (mm-inlinable-p handle)
1078 (mm-inlined-p handle)))
c3d9274a
BW
1079 (displayp (or inlinep ; show if inline OR
1080 (mh-inline-vcard-p handle); inline vcard OR
1081 (and (not attachmentp) ; if not an attachment
1082 (or small-image-flag ; and small image
1083 ; and user wants inline
bdcfe844
BW
1084 (and (not (equal
1085 (mm-handle-media-supertype handle)
1086 "image"))
1087 (mm-inlinable-p handle)
1088 (mm-inlined-p handle)))))))
1089 (save-restriction
1090 (narrow-to-region (point) (if (eobp) (point) (1+ (point))))
0c47b17c 1091 (cond ((and mh-pgp-support-flag
bdcfe844 1092 (equal type "application/pgp-signature"))
c3d9274a 1093 nil) ; skip signatures as they are already handled...
bdcfe844
BW
1094 ((not displayp)
1095 (insert "\n")
1096 (mh-insert-mime-button handle (mh-mime-part-index handle) nil))
1097 ((and displayp (not mh-display-buttons-for-inline-parts-flag))
f0d73c14
BW
1098 (or (mm-display-part handle) (mm-display-part handle))
1099 (mh-signature-highlight handle))
bdcfe844
BW
1100 ((and displayp mh-display-buttons-for-inline-parts-flag)
1101 (insert "\n")
1102 (mh-insert-mime-button handle (mh-mime-part-index handle) nil)
1103 (forward-line -1)
1104 (mh-mm-display-part handle)))
1105 (goto-char (point-max)))))
1106
f0d73c14
BW
1107(defun mh-signature-highlight (&optional handle)
1108 "Highlight message signature in HANDLE.
2dcf34f9
BW
1109The optional argument, HANDLE is a MIME handle if the function is
1110being used to highlight the signature in a MIME part."
f0d73c14
BW
1111 (let ((regexp
1112 (cond ((not handle) "^-- $")
1113 ((not (and (equal (mm-handle-media-supertype handle) "text")
1114 (equal (mm-handle-media-subtype handle) "html")))
1115 "^-- $")
1116 ((eq (mh-mm-text-html-renderer) 'lynx) "^ --$")
1117 (t "^--$"))))
1118 (save-excursion
1119 (goto-char (point-max))
1120 (when (re-search-backward regexp nil t)
1121 (mh-do-in-gnu-emacs
1122 (let ((ov (make-overlay (point) (point-max))))
44d55491 1123 (overlay-put ov 'face 'mh-show-signature)
f0d73c14
BW
1124 (overlay-put ov 'evaporate t)))
1125 (mh-do-in-xemacs
1126 (set-extent-property (make-extent (point) (point-max))
44d55491 1127 'face 'mh-show-signature))))))
f0d73c14 1128
924df208
BW
1129(mh-do-in-xemacs
1130 (defvar dots)
1131 (defvar type))
1132
bdcfe844
BW
1133(defun mh-insert-mime-button (handle index displayed)
1134 "Insert MIME button for HANDLE.
2dcf34f9
BW
1135INDEX is the part number that will be DISPLAYED. It is also used
1136by commands like \"K v\" which operate on individual MIME parts."
bdcfe844
BW
1137 ;; The button could be displayed by a previous decode. In that case
1138 ;; undisplay it if we need a hidden button.
1139 (when (and (mm-handle-displayed-p handle) (not displayed))
1140 (mm-display-part handle))
1141 (let ((name (or (mail-content-type-get (mm-handle-type handle) 'name)
1142 (mail-content-type-get (mm-handle-disposition handle)
1143 'filename)
1144 (mail-content-type-get (mm-handle-type handle) 'url)
1145 ""))
1146 (type (mm-handle-media-type handle))
1147 (description (mail-decode-encoded-word-string
1148 (or (mm-handle-description handle) "")))
1149 (dots (if (or displayed (mm-handle-displayed-p handle)) " " "..."))
1150 long-type begin end)
1151 (if (string-match ".*/" name) (setq name (substring name (match-end 0))))
1152 (setq long-type (concat type (and (not (equal name ""))
1153 (concat "; " name))))
1154 (unless (equal description "")
1155 (setq long-type (concat " --- " long-type)))
1156 (unless (bolp) (insert "\n"))
1157 (setq begin (point))
1158 (gnus-eval-format
1159 mh-mime-button-line-format mh-mime-button-line-format-alist
1160 `(,@(gnus-local-map-property mh-mime-button-map)
c3d9274a
BW
1161 mh-callback mh-mm-display-part
1162 mh-part ,index
1163 mh-data ,handle))
bdcfe844
BW
1164 (setq end (point))
1165 (widget-convert-button
1166 'link begin end
1167 :mime-handle handle
1168 :action 'mh-widget-press-button
1169 :button-keymap mh-mime-button-map
1170 :help-echo
f0d73c14
BW
1171 "Mouse-2 click or press RET (in show buffer) to toggle display")
1172 (dolist (ov (mh-funcall-if-exists overlays-in begin end))
1173 (mh-funcall-if-exists overlay-put ov 'evaporate t))))
bdcfe844
BW
1174
1175;; There is a bug in Gnus inline image display due to which an extra line
1176;; gets inserted every time it is viewed. To work around that problem we are
1177;; using an extra property 'mh-region to remember the region that is added
1178;; when the button is clicked. The region is then deleted to make sure that
1179;; no extra lines get inserted.
1180(defun mh-mm-display-part (handle)
1181 "Toggle display of button for MIME part, HANDLE."
1182 (beginning-of-line)
1183 (let ((id (get-text-property (point) 'mh-part))
1184 (point (point))
1185 (window (selected-window))
1186 (mail-parse-charset 'nil)
1187 (mail-parse-ignored-charsets nil)
1188 region buffer-read-only)
1189 (save-excursion
1190 (unwind-protect
1191 (let ((win (get-buffer-window (current-buffer) t)))
1192 (when win
1193 (select-window win))
1194 (goto-char point)
1195
1196 (if (mm-handle-displayed-p handle)
1197 ;; This will remove the part.
1198 (progn
1199 ;; Delete the button and displayed part (if any)
1200 (let ((region (get-text-property point 'mh-region)))
a66894d8 1201 (when region
924df208
BW
1202 (mh-funcall-if-exists
1203 remove-images (car region) (cdr region)))
bdcfe844
BW
1204 (mm-display-part handle)
1205 (when region
1206 (delete-region (car region) (cdr region))))
1207 ;; Delete button (if it still remains). This happens for
1208 ;; externally displayed parts where the previous step does
1209 ;; nothing.
1210 (unless (eolp)
1211 (delete-region (point) (progn (forward-line) (point)))))
1212 (save-restriction
1213 (delete-region (point) (progn (forward-line 1) (point)))
1214 (narrow-to-region (point) (point))
1215 ;; Maybe we need another unwind-protect here.
1216 (when (equal (mm-handle-media-supertype handle) "image")
1217 (insert "\n"))
1218 (when (and (not (eq (ignore-errors (mm-display-part handle))
1219 'inline))
1220 (equal (mm-handle-media-supertype handle)
1221 "image"))
1222 (goto-char (point-min))
1223 (delete-char 1))
1224 (when (equal (mm-handle-media-supertype handle) "text")
553fb735 1225 (when (eq mh-highlight-citation-style 'gnus)
bdcfe844
BW
1226 (mh-gnus-article-highlight-citation))
1227 (mh-display-smileys)
f0d73c14
BW
1228 (mh-display-emphasis)
1229 (mh-signature-highlight handle))
bdcfe844
BW
1230 (setq region (cons (progn (goto-char (point-min))
1231 (point-marker))
1232 (progn (goto-char (point-max))
1233 (point-marker)))))))
1234 (when (window-live-p window)
1235 (select-window window))
1236 (goto-char point)
1237 (beginning-of-line)
1238 (mh-insert-mime-button handle id (mm-handle-displayed-p handle))
1239 (goto-char point)
1240 (when region
1241 (add-text-properties (line-beginning-position) (line-end-position)
1242 `(mh-region ,region)))))))
1243
c3d9274a 1244;;;###mh-autoload
bdcfe844 1245(defun mh-press-button ()
553fb735
BW
1246 "View contents of button.
1247
2dcf34f9
BW
1248This command is a toggle so if you use it again on the same
1249attachment, the attachment is hidden."
bdcfe844
BW
1250 (interactive)
1251 (let ((mm-inline-media-tests mh-mm-inline-media-tests)
1252 (data (get-text-property (point) 'mh-data))
1253 (function (get-text-property (point) 'mh-callback))
1254 (buffer-read-only nil)
1255 (folder mh-show-folder-buffer))
c3d9274a
BW
1256 (flet ((mm-handle-set-external-undisplayer
1257 (handle function)
1258 (mh-handle-set-external-undisplayer folder handle function)))
bdcfe844
BW
1259 (when (and function (eolp))
1260 (backward-char))
1261 (unwind-protect (and function (funcall function data))
1262 (set-buffer-modified-p nil)))))
1263
c3d9274a 1264;;;###mh-autoload
bdcfe844
BW
1265(defun mh-push-button (event)
1266 "Click MIME button for EVENT.
2dcf34f9
BW
1267
1268If the MIME part is visible then it is removed. Otherwise the
1269part is displayed. This function is called when the mouse is used
1270to click the MIME button."
bdcfe844 1271 (interactive "e")
a66894d8
BW
1272 (mh-do-at-event-location event
1273 (let ((folder mh-show-folder-buffer)
1274 (mm-inline-media-tests mh-mm-inline-media-tests)
1275 (data (get-text-property (point) 'mh-data))
1276 (function (get-text-property (point) 'mh-callback)))
1277 (flet ((mm-handle-set-external-undisplayer (handle func)
1278 (mh-handle-set-external-undisplayer folder handle func)))
1279 (and function (funcall function data))))))
bdcfe844 1280
c3d9274a 1281;;;###mh-autoload
bdcfe844
BW
1282(defun mh-mime-save-part ()
1283 "Save MIME part at point."
1284 (interactive)
1285 (let ((data (get-text-property (point) 'mh-data)))
1286 (when data
a66894d8
BW
1287 (let ((mm-default-directory
1288 (file-name-as-directory (or mh-mime-save-parts-directory
1289 default-directory))))
c3d9274a
BW
1290 (mh-mm-save-part data)
1291 (setq mh-mime-save-parts-directory mm-default-directory)))))
bdcfe844 1292
c3d9274a 1293;;;###mh-autoload
bdcfe844
BW
1294(defun mh-mime-inline-part ()
1295 "Toggle display of the raw MIME part."
1296 (interactive)
1297 (let* ((buffer-read-only nil)
1298 (data (get-text-property (point) 'mh-data))
1299 (inserted-flag (get-text-property (point) 'mh-mime-inserted))
1300 (displayed-flag (mm-handle-displayed-p data))
1301 (point (point))
1302 start end)
1303 (cond ((and data (not inserted-flag) (not displayed-flag))
1304 (let ((contents (mm-get-part data)))
1305 (add-text-properties (line-beginning-position) (line-end-position)
1306 '(mh-mime-inserted t))
1307 (setq start (point-marker))
1308 (forward-line 1)
1309 (mm-insert-inline data contents)
1310 (setq end (point-marker))
1311 (add-text-properties
1312 start (progn (goto-char start) (line-end-position))
1313 `(mh-region (,start . ,end)))))
1314 ((and data (or inserted-flag displayed-flag))
1315 (mh-press-button)
1316 (message "MIME part already inserted")))
1317 (goto-char point)
1318 (set-buffer-modified-p nil)))
1319
f0d73c14
BW
1320;;;###mh-autoload
1321(defun mh-display-with-external-viewer (part-index)
553fb735
BW
1322 "View attachment externally.
1323
2dcf34f9
BW
1324If Emacs does not know how to view an attachment, you could save
1325it into a file and then run some program to open it. It is
1326easier, however, to launch the program directly from MH-E with
1327this command. While you'll most likely use this to view
1328spreadsheets and documents, it is also useful to use your browser
1329to view HTML attachments with higher fidelity than what Emacs can
1330provide.
1331
1332This command displays the attachment associated with the button
1333under the cursor. If the cursor is not located over a button,
1334then the cursor first moves to the next button, wrapping to the
1335beginning of the message if necessary. You can provide a numeric
1336prefix argument PART-INDEX to view the attachment labeled with
1337that number.
1338
1339This command tries to provide a reasonable default for the viewer
1340by calling the Emacs function `mailcap-mime-info'. This function
1341usually reads the file \"/etc/mailcap\"."
f0d73c14
BW
1342 (interactive "P")
1343 (when (consp part-index) (setq part-index (car part-index)))
1344 (mh-folder-mime-action
1345 part-index
1346 #'(lambda ()
1347 (let* ((part (get-text-property (point) 'mh-data))
1348 (type (mm-handle-media-type part))
1349 (methods (mapcar (lambda (x) (list (cdr (assoc 'viewer x))))
1350 (mailcap-mime-info type 'all)))
1351 (def (caar methods))
553fb735
BW
1352 (prompt (format "Viewer%s: " (if def
1353 (format " (default %s)" def)
1354 "")))
f0d73c14
BW
1355 (method (completing-read prompt methods nil nil nil nil def))
1356 (folder mh-show-folder-buffer)
1357 (buffer-read-only nil))
1358 (when (string-match "^[^% \t]+$" method)
1359 (setq method (concat method " %s")))
1360 (flet ((mm-handle-set-external-undisplayer (handle function)
1361 (mh-handle-set-external-undisplayer folder handle function)))
1362 (unwind-protect (mm-display-external part method)
1363 (set-buffer-modified-p nil)))))
1364 nil))
1365
bdcfe844
BW
1366(defun mh-widget-press-button (widget el)
1367 "Callback for widget, WIDGET.
1368Parameter EL is unused."
1369 (goto-char (widget-get widget :from))
1370 (mh-press-button))
1371
1372(defun mh-mime-display-security (handle)
1373 "Display PGP encrypted/signed message, HANDLE."
bdcfe844
BW
1374 (save-restriction
1375 (narrow-to-region (point) (point))
f0d73c14 1376 (insert "\n")
bdcfe844
BW
1377 (mh-insert-mime-security-button handle)
1378 (mh-mime-display-mixed (cdr handle))
1379 (insert "\n")
1380 (let ((mh-mime-security-button-line-format
c3d9274a 1381 mh-mime-security-button-end-line-format))
bdcfe844
BW
1382 (mh-insert-mime-security-button handle))
1383 (mm-set-handle-multipart-parameter
f0d73c14 1384 handle 'mh-region (cons (point-min-marker) (point-max-marker)))))
bdcfe844 1385
cee9f5c6
BW
1386;; I rewrote the security part because Gnus doesn't seem to ever minimize
1387;; the button. That is once the mime-security button is pressed there seems
1388;; to be no way of getting rid of the inserted text.
bdcfe844
BW
1389(defun mh-mime-security-show-details (handle)
1390 "Toggle display of detailed security info for HANDLE."
1391 (let ((details (mm-handle-multipart-ctl-parameter handle 'gnus-details)))
1392 (when details
1393 (let ((mh-mime-security-button-pressed
c3d9274a 1394 (not (get-text-property (point) 'mh-button-pressed)))
bdcfe844 1395 (mh-mime-security-button-line-format
c3d9274a 1396 (get-text-property (point) 'mh-line-format)))
bdcfe844
BW
1397 (forward-char -1)
1398 (while (eq (get-text-property (point) 'mh-line-format)
1399 mh-mime-security-button-line-format)
1400 (forward-char -1))
1401 (forward-char)
1402 (save-restriction
1403 (narrow-to-region (point) (point))
1404 (mh-insert-mime-security-button handle))
1405 (delete-region
1406 (point)
1407 (or (text-property-not-all
1408 (point) (point-max)
1409 'mh-line-format mh-mime-security-button-line-format)
1410 (point-max)))
1411 (forward-line -1)))))
1412
e495eaec
BW
1413(defun mh-mime-security-button-face (info)
1414 "Return the button face to use for encrypted/signed mail based on INFO."
1415 (cond ((string-match "OK" info) ;Decrypted mail
d49ed7d4 1416 'mh-show-pgg-good)
e495eaec 1417 ((string-match "Failed" info) ;Decryption failed or signature invalid
d49ed7d4 1418 'mh-show-pgg-bad)
e495eaec 1419 ((string-match "Undecided" info);Unprocessed mail
d49ed7d4 1420 'mh-show-pgg-unknown)
e495eaec 1421 ((string-match "Untrusted" info);Key not trusted
d49ed7d4
BW
1422 'mh-show-pgg-unknown)
1423 (t
1424 'mh-show-pgg-good)))
e495eaec 1425
bdcfe844
BW
1426(defun mh-mime-security-press-button (handle)
1427 "Callback from security button for part HANDLE."
f0d73c14
BW
1428 (if (mm-handle-multipart-ctl-parameter handle 'gnus-info)
1429 (mh-mime-security-show-details handle)
1430 (let ((region (mm-handle-multipart-ctl-parameter handle 'mh-region))
1431 point)
1432 (setq point (point))
1433 (goto-char (car region))
1434 (delete-region (car region) (cdr region))
1435 (with-current-buffer (mm-handle-multipart-ctl-parameter handle 'buffer)
1436 (let* ((mm-verify-option 'known)
1437 (mm-decrypt-option 'known)
1438 (new (mm-possibly-verify-or-decrypt (cdr handle) handle)))
1439 (unless (eq new (cdr handle))
1440 (mm-destroy-parts (cdr handle))
1441 (setcdr handle new))))
1442 (mh-mime-display-security handle)
1443 (goto-char point))))
bdcfe844
BW
1444
1445;; These variables should already be initialized in mm-decode.el if we have a
1446;; recent enough Gnus. The defvars are here to avoid compiler warnings.
1447(defvar mm-verify-function-alist nil)
1448(defvar mm-decrypt-function-alist nil)
1449
1450(defvar pressed-details)
1451
1452(defun mh-insert-mime-security-button (handle)
1453 "Display buttons for PGP message, HANDLE."
1454 (let* ((protocol (mm-handle-multipart-ctl-parameter handle 'protocol))
1455 (crypto-type (or (nth 2 (assoc protocol mm-verify-function-alist))
1456 (nth 2 (assoc protocol mm-decrypt-function-alist))
1457 "Unknown"))
1458 (type (concat crypto-type
1459 (if (equal (car handle) "multipart/signed")
1460 " Signed" " Encrypted")
1461 " Part"))
1462 (info (or (mm-handle-multipart-ctl-parameter handle 'gnus-info)
1463 "Undecided"))
1464 (details (mm-handle-multipart-ctl-parameter handle 'gnus-details))
e495eaec 1465 pressed-details begin end face)
bdcfe844
BW
1466 (setq details (if details (concat "\n" details) ""))
1467 (setq pressed-details (if mh-mime-security-button-pressed details ""))
e495eaec 1468 (setq face (mh-mime-security-button-face info))
bdcfe844
BW
1469 (unless (bolp) (insert "\n"))
1470 (setq begin (point))
1471 (gnus-eval-format
1472 mh-mime-security-button-line-format
1473 mh-mime-security-button-line-format-alist
1474 `(,@(gnus-local-map-property mh-mime-security-button-map)
c3d9274a
BW
1475 mh-button-pressed ,mh-mime-security-button-pressed
1476 mh-callback mh-mime-security-press-button
1477 mh-line-format ,mh-mime-security-button-line-format
1478 mh-data ,handle))
bdcfe844
BW
1479 (setq end (point))
1480 (widget-convert-button 'link begin end
1481 :mime-handle handle
1482 :action 'mh-widget-press-button
1483 :button-keymap mh-mime-security-button-map
e495eaec 1484 :button-face face
bdcfe844 1485 :help-echo "Mouse-2 click or press RET (in show buffer) to see security details.")
f0d73c14
BW
1486 (dolist (ov (mh-funcall-if-exists overlays-in begin end))
1487 (mh-funcall-if-exists overlay-put ov 'evaporate t))
bdcfe844
BW
1488 (when (equal info "Failed")
1489 (let* ((type (if (equal (car handle) "multipart/signed")
1490 "verification" "decryption"))
1491 (warning (if (equal type "decryption")
1492 "(passphrase may be incorrect)" "")))
1493 (message "%s %s failed %s" crypto-type type warning)))))
1494
1495(defun mh-mm-inline-message (handle)
1496 "Display message, HANDLE.
2dcf34f9
BW
1497The function decodes the message and displays it. It avoids
1498decoding the same message multiple times."
bdcfe844 1499 (let ((b (point))
bdcfe844 1500 (clean-message-header mh-clean-message-header-flag)
f0d73c14
BW
1501 (invisible-headers mh-invisible-header-fields-compiled)
1502 (visible-headers nil))
bdcfe844
BW
1503 (save-excursion
1504 (save-restriction
1505 (narrow-to-region b b)
1506 (mm-insert-part handle)
1507 (mh-mime-display
1508 (or (gethash handle (mh-mime-handles-cache (mh-buffer-data)))
1509 (setf (gethash handle (mh-mime-handles-cache (mh-buffer-data)))
1510 (let ((handles (or (mm-dissect-buffer nil)
1511 (mm-uu-dissect))))
1512 (setf (mh-mime-handles (mh-buffer-data))
1513 (mm-merge-handles
1514 handles (mh-mime-handles (mh-buffer-data))))
1515 handles))))
1516
1517 (goto-char (point-min))
924df208 1518 (mh-show-xface)
bdcfe844
BW
1519 (cond (clean-message-header
1520 (mh-clean-msg-header (point-min)
1521 invisible-headers
1522 visible-headers)
1523 (goto-char (point-min)))
1524 (t
1525 (mh-start-of-uncleaned-message)))
924df208 1526 (mh-decode-message-header)
bdcfe844
BW
1527 (mh-show-addr)
1528 ;; The other highlighting types don't need anything special
553fb735 1529 (when (eq mh-highlight-citation-style 'gnus)
bdcfe844
BW
1530 (mh-gnus-article-highlight-citation))
1531 (goto-char (point-min))
1532 (insert "\n------- Forwarded Message\n\n")
1533 (mh-display-smileys)
1534 (mh-display-emphasis)
1535 (mm-handle-set-undisplayer
1536 handle
1537 `(lambda ()
1538 (let (buffer-read-only)
1539 (if (fboundp 'remove-specifier)
1540 ;; This is only valid on XEmacs.
1541 (mapcar (lambda (prop)
1542 (remove-specifier
1543 (face-property 'default prop) (current-buffer)))
1544 '(background background-pixmap foreground)))
1545 (delete-region ,(point-min-marker) ,(point-max-marker)))))))))
1546
1547(provide 'mh-mime)
1548
cee9f5c6
BW
1549;; Local Variables:
1550;; indent-tabs-mode: nil
1551;; sentence-end-double-space: nil
1552;; End:
bdcfe844 1553
cee9f5c6 1554;; arch-tag: 0dd36518-1b64-4a84-8f4e-59f422d3f002
60370d40 1555;;; mh-mime.el ends here