tm 7.34.
[elisp/tm.git] / tm-edit.el
1 ;;;
2 ;;; tm-edit.el --- Simple MIME Composer for GNU Emacs
3 ;;;
4
5 ;; Copyright (C) 1993 UMEDA Masanobu
6 ;; Copyright (C) 1994,1995 MORIOKA Tomohiko
7
8 ;; Author: UMEDA Masanobu <umerin@mse.kyutech.ac.jp>
9 ;;         MORIOKA Tomohiko <morioka@jaist.ac.jp>
10 ;; Version: $Revision: 7.34 $
11 ;; Keywords: mail, news, MIME, multimedia, multilingual
12
13 ;; This file is not part of GNU Emacs.
14
15 ;; GNU Emacs is free software; you can redistribute it and/or modify
16 ;; it under the terms of the GNU General Public License as published by
17 ;; the Free Software Foundation; either version 2, or (at your option)
18 ;; any later version.
19
20 ;; GNU Emacs is distributed in the hope that it will be useful,
21 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
22 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23 ;; GNU General Public License for more details.
24
25 ;; You should have received a copy of the GNU General Public License
26 ;; along with GNU Emacs; see the file COPYING.  If not, write to
27 ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
28
29 ;;; Commentary:
30
31 ;; This is an Emacs minor mode for editing Internet multimedia
32 ;; messages formatted in MIME (RFC 1521 and RFC 1522). All messages in
33 ;; this mode are composed in the tagged MIME format, that are
34 ;; described in the following examples. The messages composed in the
35 ;; tagged MIME format are automatically translated into a MIME
36 ;; compliant message when exiting the mode.
37
38 ;; Mule (a multilingual extension to Emacs 18 and 19) has a capability
39 ;; of handling multilingual text in limited ISO-2022 manner that is
40 ;; based on early experiences in Japanese Internet community and
41 ;; resulted in RFC 1468 (ISO-2022-JP charset for MIME). In order to
42 ;; enable multilingual capability in single text message in MIME,
43 ;; charset of multilingual text written in Mule is declared as either
44 ;; `ISO-2022-JP-2' [RFC 1554] or `ISO-2022-INT-1'. Mule is required
45 ;; for reading the such messages.
46
47 ;; This MIME composer can work with Mail mode, mh-e letter Mode, and
48 ;; News mode.  First of all, you need the following autoload
49 ;; definition to load mime/editor-mode automatically:
50 ;;
51 ;; (autoload 'mime/editor-mode "tm-edit"
52 ;;      "Minor mode for editing MIME message." t)
53 ;;
54 ;; In case of Mail mode (includes VM mode), you need the following
55 ;; hook definition:
56 ;;
57 ;; (add-hook 'mail-mode-hook 'mime/editor-mode)
58 ;; (add-hook 'mail-send-hook 'mime-editor/maybe-translate)
59 ;;
60 ;; In case of MH-E, you need the following hook definition:
61 ;;
62 ;; (add-hook 'mh-letter-mode-hook
63 ;;           (function
64 ;;            (lambda ()
65 ;;              (mime/editor-mode)
66 ;;              (make-local-variable 'mail-header-separator)
67 ;;              (setq mail-header-separator "--------")
68 ;;              ))))
69 ;; (add-hook 'mh-before-send-letter-hook 'mime-editor/maybe-translate)
70 ;;
71 ;; In case of News mode, you need the following hook definition:
72 ;;
73 ;; (add-hook 'news-reply-mode-hook 'mime/editor-mode)
74 ;; (add-hook 'news-inews-hook 'mime-editor/maybe-translate)
75 ;;
76 ;; In case of Emacs 19, it is possible to emphasize the message tags
77 ;; using font-lock mode as follows:
78 ;;
79 ;; (add-hook 'mime/editor-mode-hook
80 ;;           (function
81 ;;            (lambda ()
82 ;;              (font-lock-mode 1)
83 ;;              (setq font-lock-keywords (list mime-editor/tag-regexp))
84 ;;              ))))
85
86 ;; The message tag looks like:
87 ;;
88 ;;      --[[TYPE/SUBTYPE;PARAMETERS][ENCODING]]
89 ;;
90 ;; The tagged MIME message examples:
91 ;;
92 ;; This is a conventional plain text.  It should be translated into
93 ;; text/plain.
94 ;; 
95 ;;--[[text/plain]]
96 ;; This is also a plain text.  But, it is explicitly specified as is.
97 ;;
98 ;;--[[text/plain; charset=ISO-2022-JP]]
99 ;; \e$B$3$l$O\e(B charset \e$B$r\e(B ISO-2022-JP \e$B$K;XDj$7$?F|K\8l$N\e(B plain \e$B%F%-%9%H$G$9\e(B.
100 ;;
101 ;;--[[text/richtext]]
102 ;; <center>This is a richtext.</center>
103 ;;
104 ;;--[[image/gif][base64]]^M...image encoded in base64 comes here...
105 ;;
106 ;;--[[audio/basic][base64]]^M...audio encoded in base64 comes here...
107
108 ;; LCD Archive Entry:
109 ;; mime|Masanobu UMEDA|umerin@mse.kyutech.ac.jp|
110 ;; Simple MIME Composer|
111 ;; $Date: 1995/12/14 15:49:00 $|$Revision: 7.34 $|~/misc/mime.el.Z|
112
113 ;;; Code:
114
115 (require 'sendmail)
116 (require 'mail-utils)
117 (require 'mel)
118 (require 'tl-822)
119 (require 'tl-list)
120 (require 'tm-view)
121 (require 'tm-ew-e)
122 (require 'signature)
123
124
125 ;;; @ version
126 ;;;
127
128 (defconst mime-editor/RCS-ID
129   "$Id: tm-edit.el,v 7.34 1995/12/14 15:49:00 morioka Exp $")
130
131 (defconst mime-editor/version (get-version-string mime-editor/RCS-ID))
132
133
134 ;;; @ variables
135 ;;;
136
137 (defvar mime-prefix "\C-c\C-x"
138   "*Keymap prefix for MIME commands.")
139
140 (defvar mime-signature-file "~/.signature.rtf"
141   "*Signature file to be included as a part of a multipart message.")
142
143 (defvar mime-ignore-preceding-spaces nil
144   "*Ignore preceding white spaces if non-nil.")
145
146 (defvar mime-ignore-trailing-spaces nil
147   "*Ignore trailing white spaces if non-nil.")
148
149 (defvar mime-ignore-same-text-tag t
150   "*Ignore preceding text content-type tag that is same with new one.
151 If non-nil, the text tag is not inserted unless something different.")
152
153 (defvar mime-auto-hide-body t
154   "*Hide non-textual body encoded in base64 after insertion if non-nil.")
155
156 (defvar mime-body-charset-chooser
157   (cond ((boundp 'NEMACS)
158          (function mime-body-charset-chooser-for-nemacs))
159         ((featurep 'mule)
160          (function mime-body-charset-chooser-for-mule))
161         ((string-match "^19\\." emacs-version)
162          (function mime-body-charset-chooser-for-emacs19))
163         (t                              ;ASCII only emacs
164          (function mime-body-charset-chooser-for-emacs18)))
165   "*Function to identify charset and encoding of a text in a given region.
166 The value is a form of (CHARSET . ENCODING), where ENCODING must be a
167 full name, such as base64.")
168
169 (defvar mime-string-encoder
170   (cond ((boundp 'NEMACS)
171          (function mime-string-encoder-for-nemacs))
172         ((featurep 'mule)
173          (function mime-string-encoder-for-mule))
174         ((string-match "^19\\." emacs-version)
175          (function mime-string-encoder-for-emacs19))
176         (t                              ;ASCII only emacs
177          (function mime-string-encoder-for-emacs18)))
178   "*Function to encode a string for given encoding method.
179 The method is a form of (CHARSET . ENCODING).")
180
181 (defvar mime-voice-recorder
182   (function mime-voice-recorder-for-sun)
183   "*Function to record a voice message and return a buffer that contains it.")
184
185 (defvar mime/editor-mode-hook nil
186   "*Hook called when enter MIME mode.")
187
188 (defvar mime-editor/translate-hook nil
189   "*Hook called before translating into a MIME compliant message.
190 To insert a signature file specified by mime-signature-file
191 (`.signature.rtf' by default) automatically, call the function
192 `mime-editor/insert-signature' from this hook.")
193
194 (defvar mime-editor/exit-hook nil
195   "*Hook called when exit MIME mode.")
196
197 (defvar mime-content-types
198   '(("text"
199      ;; Charset parameter need not to be specified, since it is
200      ;; defined automatically while translation.
201      ("plain"
202       ;;("charset" "" "ISO-2022-JP" "US-ASCII" "ISO-8859-1" "ISO-8859-8")
203       )
204      ("richtext"
205       ;;("charset" "" "ISO-2022-JP" "US-ASCII" "ISO-8859-1" "ISO-8859-8")
206       )
207      ("enriched"
208       ;;("charset" "" "ISO-2022-JP" "US-ASCII" "ISO-8859-1" "ISO-8859-8")
209       )
210      ("x-latex"
211       ;;("charset" "" "ISO-2022-JP" "US-ASCII" "ISO-8859-1" "ISO-8859-8")
212       )
213      ("html"
214       ;;("charset" "" "ISO-2022-JP" "US-ASCII" "ISO-8859-1" "ISO-8859-8")
215       )
216      ("x-rot13-47")
217      )
218     ("message"
219      ("external-body"
220       ("access-type"
221        ("anon-ftp"
222         ("site" "ftp.jaist.ac.jp" "wnoc-fuk.wide.ad.jp" "nic.karrn.ad.jp")
223         ("directory" "/pub/GNU/elisp/mime")
224         ("name")
225         ("mode" "image" "ascii" "local8"))
226        ("ftp"
227         ("site")
228         ("directory")
229         ("name")
230         ("mode" "image" "ascii" "local8"))
231        ("tftp"        ("site") ("name"))
232        ("afs"         ("site") ("name"))
233        ("local-file"  ("site") ("name"))
234        ("mail-server" ("server" "ftpmail@nic.karrn.ad.jp"))
235        ))
236      ("rfc822")
237      )
238     ("application"
239      ("octet-stream"
240       ("name")
241       ("type" "" "tar" "shar")
242       ("conversions"))
243      ("postscript")
244      ("x-kiss" ("x-cnf")))
245     ("image"
246      ("gif")
247      ("jpeg")
248      ("tiff")
249      ("x-pic")
250      ("x-xwd")
251      ("x-xbm")
252      )
253     ("audio" ("basic"))
254     ("video" ("mpeg"))
255     )
256   "*Alist of content-type, subtype, parameters and its values.")
257
258 (defvar mime-file-types
259   '(("\\.rtf$"
260      "text"     "richtext"      nil     nil)
261     ("\\.html$"
262      "text"     "html"          nil     nil)
263     ("\\.ps$"
264      "application" "postscript" nil     "quoted-printable")
265     ("\\.gif$"
266      "image"    "gif"           nil     "base64"
267      (("Content-Description" . file))
268      )
269     ("\\.jpg$"
270      "image"    "jpeg"          nil     "base64")
271     ("\\.xwd$"
272      "image"    "x-xwd"         nil     "base64")
273     ("\\.xbm$"
274      "image"    "x-xbm"         nil     "base64")
275     ("\\.pic$"
276      "image"    "x-pic"         nil     "base64"
277      (("Content-Description" . file))
278      )
279     ("\\.tiff$"
280      "image"    "tiff"          nil     "base64")
281     ("\\.au$"
282      "audio"    "basic"         nil     "base64")
283     ("\\.mpg$"
284      "video"    "mpeg"          nil     "base64")
285     ("\\.el$"
286      "application" "octet-stream" (("name" . file)
287                                    ("type" . "emacs-lisp")) "7bit")
288     ("\\.lsp$"
289      "application" "octet-stream" (("name" . file)
290                                    ("type" . "common-lisp")) "7bit")
291     ("\\.tar.gz$"
292      "application" "octet-stream" (("name" . file)
293                                    ("type" . "tar")
294                                    ("conversions" . "gzip")) nil)
295     ("\\.diff$"
296      "application" "octet-stream" (("name" . file)
297                                    ("type" . "patch")) nil)
298     ("\\.signature"
299      "text"     "plain"         nil     nil)
300     (".*"       nil             nil     nil     nil)
301     )
302   "*Alist of file name, types, parameters, and default encoding.
303 If encoding is nil, it is determined from its contents.")
304
305 ;;; @@ about message inserting
306 ;;;
307
308 (defvar mime-editor/yank-ignored-field-list
309   '("Received" "Sender" "Approved" "Path" "Status" "X-VM-.*" "X-UIDL")
310   "Delete these fields from original message when it is inserted
311 as message/rfc822 part.
312 Each elements are regexp of field-name. [tm-edit.el]")
313
314 (defvar mime-editor/yank-ignored-field-regexp
315   (concat "^"
316           (apply (function regexp-or) mime-editor/yank-ignored-field-list)
317           ":"))
318
319 (defvar mime-editor/message-inserter-alist nil)
320 (defvar mime-editor/mail-inserter-alist nil)
321
322 ;;; @@ about message splitting
323 ;;;
324
325 (defvar mime-editor/split-message t
326   "*Split large message if it is non-nil. [tm-edit.el]")
327
328 (defvar mime-editor/message-default-max-length 1000
329   "*Default maximum size of a message. [tm-edit.el]")
330
331 (defvar mime-editor/message-max-length-alist
332   '((news-reply-mode . 500)))
333
334 (defconst mime-editor/split-ignored-field-regexp
335   "\\(^Content-\\|^Subject:\\|^Mime-Version:\\)")
336
337 (defvar mime-editor/split-blind-field-regexp
338   "\\(^[BDFbdf]cc:\\|^cc:[ \t]*$\\)")
339
340 (defvar mime-editor/message-default-sender-alist
341   '((mail-mode . mail-send-and-exit)
342     (mh-letter-mode . mh-send-letter)
343     (news-reply-mode . gnus-inews-news)
344     ))
345
346 (defvar mime-editor/split-message-sender-alist
347   '((mail-mode
348      . (lambda ()
349          (interactive)
350          (sendmail-send-it)
351          ))
352     (mh-letter-mode
353      . (lambda (&optional arg)
354          (interactive "P")
355          (write-region (point-min) (point-max)
356                        mime-editor/draft-file-name nil 'no-message)
357          (cond (arg
358                 (pop-to-buffer "MH mail delivery")
359                 (erase-buffer)
360                 (mh-exec-cmd-output mh-send-prog t "-watch" "-nopush"
361                                     "-nodraftfolder"
362                                     mh-send-args mime-editor/draft-file-name)
363                 (goto-char (point-max)) ; show the interesting part
364                 (recenter -1)
365                 (sit-for 1))
366                (t
367                 (apply 'mh-exec-cmd-quiet t mh-send-prog 
368                        (mh-list-to-string
369                         (list "-nopush" "-nodraftfolder"
370                               "-noverbose" "-nowatch"
371                               mh-send-args mime-editor/draft-file-name)))))
372          ))
373     ))
374
375 (defvar mime-editor/window-config-alist
376   '((mail-mode       . nil)
377     (mh-letter-mode  . mh-previous-window-config)
378     (news-reply-mode . (cond ((boundp 'gnus-winconf-post-news)
379                               (prog1
380                                   gnus-winconf-post-news
381                                 (setq gnus-winconf-post-news nil)
382                                 ))
383                              ((boundp 'gnus-prev-winconf)
384                               (prog1
385                                   gnus-prev-winconf
386                                 (setq gnus-prev-winconf nil)
387                                 ))
388                              ))
389     ))
390
391 (defvar mime-editor/news-reply-mode-server-running nil)
392
393
394 ;;; @@ about PGP
395 ;;;
396
397 (defvar mime-editor/signing-type nil
398   "*PGP signing type (pgp-elkins, pgp-kazu or nil). [tm-edit.el]")
399
400 (defvar mime-editor/encrypting-type nil
401   "*PGP encrypting type (pgp-elkins, pgp-kazu or nil).
402 Sorry `pgp-elkins' is not supported yet. [tm-edit.el]")
403
404 (if (or mime-editor/signing-type mime-editor/encrypting-type)
405     (require 'mailcrypt)
406   )
407
408
409 ;;; @@ about tag
410 ;;;
411
412 (defconst mime-editor/single-part-tag-regexp
413   "^--[[][[]\\([^]]*\\)]\\([[]\\([^]]*\\)]\\|\\)]"
414   "*Regexp of MIME tag in the form of [[CONTENT-TYPE][ENCODING]].")
415
416 (defconst mime-editor/multipart-beginning-regexp "^--<<\\([^<>]+\\)>>-{\n")
417
418 (defconst mime-editor/multipart-end-regexp "^--}-<<\\([^<>]+\\)>>\n")
419
420 (defconst mime-editor/beginning-tag-regexp
421   (regexp-or mime-editor/single-part-tag-regexp
422              mime-editor/multipart-beginning-regexp))
423
424 (defconst mime-editor/end-tag-regexp
425   (regexp-or mime-editor/single-part-tag-regexp
426              mime-editor/multipart-end-regexp))
427
428 (defconst mime-editor/tag-regexp
429   (regexp-or mime-editor/single-part-tag-regexp
430              mime-editor/multipart-beginning-regexp
431              mime-editor/multipart-end-regexp))
432
433 (defvar mime-tag-format "--[[%s]]"
434   "*Control-string making a MIME tag.")
435
436 (defvar mime-tag-format-with-encoding "--[[%s][%s]]"
437   "*Control-string making a MIME tag with encoding.")
438
439 ;;; @@ multipart boundary
440 ;;;
441
442 (defvar mime-multipart-boundary "Multipart"
443   "*Boundary of a multipart message.")
444
445
446 ;;; @@ buffer local variables
447 ;;;
448
449 (defvar mime/editor-mode-old-local-map nil)
450 (defvar mime/editor-mode-old-selective-display nil)
451 (defvar mime/editing-buffer nil)
452
453 \f
454 ;;; @ constants
455 ;;;
456
457 (defconst mime-tspecials-regexp "[][()<>@,;:\\\"/?.= \t]"
458   "*Specify MIME tspecials.
459 Tspecials means any character that matches with it in header must be quoted.")
460
461 (defconst mime-editor/mime-version-value
462   (format "1.0 (generated by tm-edit %s)" mime-editor/version)
463   "MIME version number.")
464
465
466 ;;; @ keymap and menu
467 ;;;
468
469 (defvar mime/editor-mode-flag nil)
470 (make-variable-buffer-local 'mime/editor-mode-flag)
471
472 (set-alist 'minor-mode-alist 'mime/editor-mode-flag '(" MIME-Edit"))
473
474 (defun mime-editor/define-keymap (keymap)
475   "Add mime-editor commands to KEYMAP."
476   (if (not (keymapp keymap))
477       nil
478     (define-key keymap "\C-t" 'mime-editor/insert-text)
479     (define-key keymap "\C-i" 'mime-editor/insert-file)
480     (define-key keymap "\C-e" 'mime-editor/insert-external)
481     (define-key keymap "\C-v" 'mime-editor/insert-voice)
482     (define-key keymap "\C-y" 'mime-editor/insert-message)
483     (define-key keymap "\C-m" 'mime-editor/insert-mail)
484     (define-key keymap "\C-w" 'mime-editor/insert-signature)
485     (define-key keymap "\C-s" 'mime-editor/insert-signature)
486     (define-key keymap "\C-k" 'mime-editor/insert-key)
487     (define-key keymap "t"    'mime-editor/insert-tag)
488     (define-key keymap "a"    'mime-editor/enclose-alternative-region)
489     (define-key keymap "p"    'mime-editor/enclose-parallel-region)
490     (define-key keymap "m"    'mime-editor/enclose-mixed-region)
491     (define-key keymap "d"    'mime-editor/enclose-digest-region)
492     (define-key keymap "s"    'mime-editor/enclose-signed-region)
493     (define-key keymap "e"    'mime-editor/enclose-encrypted-region)
494     (define-key keymap "\C-p" 'mime-editor/preview-message)
495     (define-key keymap "\C-z" 'mime-editor/exit)
496     (define-key keymap "?"    'mime-editor/help)
497     ))
498
499 (defconst mime-editor/menu-title "MIME-Edit")
500
501 (defconst mime-editor/menu-list
502   (nconc
503    '((mime-help "Describe MIME editor mode" mime-editor/help)
504      (file      "Insert File"           mime-editor/insert-file)
505      (external  "Insert External"       mime-editor/insert-external)
506      (voice     "Insert Voice"          mime-editor/insert-voice)
507      (message   "Insert Message"        mime-editor/insert-message)
508      (mail      "Insert Mail"           mime-editor/insert-mail)
509      (signature "Insert Signature"      mime-editor/insert-signature)
510      (text      "Insert Text"           mime-editor/insert-text)
511      (tag       "Insert Tag"            mime-editor/insert-tag)
512      (alternative "Enclose as alternative"
513                   mime-editor/enclose-alternative-region)
514      (parallel  "Enclose as parallel"   mime-editor/enclose-parallel-region)
515      (mixed     "Enclose as serial"     mime-editor/enclose-mixed-region)
516      (digest    "Enclose as digest"     mime-editor/enclose-digest-region)
517      (signed    "Enclose as signed"     mime-editor/enclose-signed-region)
518      (encrypted "Enclose as encrypted"  mime-editor/enclose-encrypted-region)
519      (key       "Insert Public Key"     mime-editor/insert-key)
520      (split     "About split"           mime-editor/set-split)
521      )
522    (if (and (featurep 'mailcrypt)
523             (not (or mime-editor/signing-type mime-editor/encrypting-type)))
524        '((sign    "About sign"       mime-editor/set-sign)
525          (encrypt "About encryption" mime-editor/set-encrypt)
526          ))
527    '((preview   "Preview Message"       mime-editor/preview-message))
528    )
529   "MIME-edit menubar entry.")
530
531 (defun mime-editor/define-menu-for-emacs19 ()
532   "Define menu for Emacs 19."
533   (define-key (current-local-map) [menu-bar mime-edit]
534     (cons mime-editor/menu-title
535           (make-sparse-keymap mime-editor/menu-title)))
536   (mapcar (function
537            (lambda (item)
538              (define-key (current-local-map)
539                (vector 'menu-bar 'mime-edit (car item))
540                (cons (nth 1 item)(nth 2 item))
541                )
542              ))
543           (reverse mime-editor/menu-list)
544           ))
545
546 ;;; modified by Pekka Marjola <pema@niksula.hut.fi>
547 ;;;     1995/9/5 (c.f. [tm-en:69])
548 (defun mime-editor/define-menu-for-xemacs ()
549   "Define menu for Emacs 19."
550   (cond ((featurep 'menubar)
551          (make-local-variable 'current-menubar)
552          (set-buffer-menubar current-menubar)
553          (add-submenu nil
554                       (cons mime-editor/menu-title
555                             (mapcar (function
556                                      (lambda (item)
557                                        (vector (nth 1 item)(nth 2 item)
558                                                mime/editor-mode-flag)
559                                        ))
560                                     mime-editor/menu-list)))
561          )))
562
563 ;;; modified by Steven L. Baur <steve@miranova.com>
564 ;;;     1995/12/6 (c.f. [tm-en:209])
565 (if (and (string-match "XEmacs\\|Lucid" emacs-version)
566          (not (boundp 'mime-editor/popup-menu-for-xemacs)))
567     (setq mime-editor/popup-menu-for-xemacs
568           (append '("MIME Commands" "---")
569                   (mapcar (function (lambda (item)
570                                       (vector (nth 1 item)
571                                               (nth 2 item)
572                                               t)))
573                           mime-editor/menu-list)))
574   )
575 ;;; end
576
577
578 ;;; @ functions
579 ;;;
580
581 ;;;###autoload
582 (defun mime/editor-mode ()
583   "MIME minor mode for editing the tagged MIME message.
584
585 In this mode, basically, the message is composed in the tagged MIME
586 format.  The message tag looks like:
587
588         `--[[text/plain; charset=ISO-2022-JP][7bit]]'.
589
590 The tag specifies the MIME content type, subtype, optional parameters
591 and transfer encoding of the message following the tag.  Messages
592 without any tag are treated as `text/plain' by default.  Charset and
593 transfer encoding are automatically defined unless explicitly
594 specified.  Binary messages such as audio and image are usually hidden
595 using selective-display facility.  The messages in the tagged MIME
596 format are automatically translated into a MIME compliant message when
597 exiting this mode.
598
599 Available charsets depend on Emacs version being used.  The following
600 lists the available charsets of each emacs.
601
602 Emacs18:        US-ASCII is only available.
603 NEmacs:         US-ASCII and ISO-2022-JP are available.
604 Emacs19:        US-ASCII and ISO-8859-1 are available.
605 Mule:           US-ASCII, ISO-8859-* (except for ISO-8859-6),
606                 ISO-2022-JP, ISO-2022-JP-2 and ISO-2022-INT-1 are available.
607
608 ISO-2022-JP-2 and ISO-2022-INT-1 charsets used in Mule is expected to
609 be used to represent multilingual text in intermixed manner.  Any
610 languages that has no registered charset are represented as either
611 ISO-2022-JP-2 or ISO-2022-INT-1 in Mule.
612
613 Following commands are available in addition to major mode commands:
614 \\[mime-editor/insert-text]     insert a text message.
615 \\[mime-editor/insert-file]     insert a (binary) file.
616 \\[mime-editor/insert-external] insert a reference to external body.
617 \\[mime-editor/insert-voice]    insert a voice message.
618 \\[mime-editor/insert-message]  insert a mail or news message.
619 \\[mime-editor/insert-mail]     insert a mail message.
620 \\[mime-editor/insert-signature]        insert a signature file at end.
621 \\[mime-editor/insert-tag]      insert a new MIME tag.
622 \\[mime-editor/enclose-alternative-region]      enclose as multipart/alternative.
623 \\[mime-editor/enclose-parallel-region] enclose as multipart/parallel.
624 \\[mime-editor/enclose-mixed-region]    enclose as multipart/mixed.
625 \\[mime-editor/enclose-digest-region]   enclose as multipart/digest.
626 \\[mime-editor/preview-message] preview editing MIME message.
627 \\[mime-editor/exit]    exit and translate into a MIME compliant message.
628 \\[mime-editor/maybe-translate] exit, translate and run the original command.
629 \\[mime-editor/help]    show this help.
630
631 Additional commands are available in some major modes:
632 C-c C-c         exit, translate and run the original command.
633 C-c C-s         exit, translate and run the original command.
634
635 The following is a message example written in the tagged MIME format.
636 TABs at the beginning of the line are not a part of the message:
637
638         This is a conventional plain text.  It should be translated
639         into text/plain.
640         --[[text/plain]]
641         This is also a plain text.  But, it is explicitly specified as
642         is.
643         --[[text/plain; charset=ISO-2022-JP]]
644         \e$B$3$l$O\e(B charset \e$B$r\e(B ISO-2022-JP \e$B$K;XDj$7$?F|K\8l$N\e(B plain \e$B%F%-%9\e(B
645         \e$B%H$G$9\e(B.
646         --[[text/richtext]]
647         <center>This is a richtext.</center>
648         --[[image/gif][base64]]^M...image encoded in base64 here...
649         --[[audio/basic][base64]]^M...audio encoded in base64 here...
650
651 User customizable variables (not documented all of them):
652  mime-prefix
653     Specifies a key prefix for MIME minor mode commands.
654
655  mime-signature-file
656     Specifies a signature file to be included as part of a multipart
657     message.
658
659  mime-ignore-preceding-spaces
660     Preceding white spaces in a message body are ignored if non-nil.
661
662  mime-ignore-trailing-spaces
663     Trailing white spaces in a message body are ignored if non-nil.
664
665  mime-auto-fill-header
666     Fill header fields that contain encoded-words if non-nil.
667
668  mime-auto-hide-body
669     Hide a non-textual body message encoded in base64 after insertion
670     if non-nil.
671
672  mime-body-charset-chooser
673     Specifies a function to identify charset and encoding of a text in
674     a given region.  The value is a form of (CHARSET . ENCODING),
675     where ENCODING must be a full name, such as base64.
676
677  mime-string-encoder
678     Specifies a function to encode a string for given encoding method.
679     The method is a form of (CHARSET . ENCODING).
680
681  mime-voice-recorder
682     Specifies a function to record a voice message and return a buffer
683     that contains it.  The function mime-voice-recorder-for-sun is for
684     Sun SparcStations.
685
686  mime/editor-mode-hook
687     Turning on MIME mode calls the value of mime/editor-mode-hook, if
688     it is non-nil.
689
690  mime-editor/translate-hook
691     The value of mime-editor/translate-hook is called just before translating
692     the tagged MIME format into a MIME compliant message if it is
693     non-nil.  If the hook call the function mime-editor/insert-signature,
694     the signature file will be inserted automatically.
695
696  mime-editor/exit-hook
697     Turning off MIME mode calls the value of mime-editor/exit-hook, if it is
698     non-nil."
699   (interactive)
700   (if mime/editor-mode-flag
701       (error "You are already editing a MIME message.")
702     (setq mime/editor-mode-flag t)
703     ;; Remember old key bindings.
704     (make-local-variable 'mime/editor-mode-old-local-map)
705     (setq mime/editor-mode-old-local-map (current-local-map))
706     ;; Add MIME commands to current local map.
707     (use-local-map (copy-keymap (current-local-map)))
708     (if (not (lookup-key (current-local-map) mime-prefix))
709         (define-key (current-local-map) mime-prefix (make-sparse-keymap)))
710     (mime-editor/define-keymap (lookup-key (current-local-map) mime-prefix))
711     
712     ;; Define menu.  Menus for other emacs implementations are
713     ;; welcome.
714     ;;  modified by Pekka Marjola <pema@niksula.hut.fi>
715     ;;          1995/9/5 (c.f. [tm-eng:69])
716     (cond ((string-match "XEmacs\\|Lucid" emacs-version)
717            (mime-editor/define-menu-for-xemacs))
718           ((string-match "^19\\." emacs-version)
719            (mime-editor/define-menu-for-emacs19)
720            ))
721     ;; end
722     
723     ;; Remember old selective-display.
724     (make-local-variable 'mime/editor-mode-old-selective-display)
725     (setq mime/editor-mode-old-selective-display selective-display)
726     (setq selective-display t)
727     ;; I don't care about saving these.
728     (setq paragraph-start
729           (concat mime-editor/single-part-tag-regexp "\\|" paragraph-start))
730     (setq paragraph-separate
731           (concat mime-editor/single-part-tag-regexp "\\|" paragraph-separate))
732     (run-hooks 'mime/editor-mode-hook)
733     (message
734      (substitute-command-keys
735       "Type \\[mime-editor/exit] to exit MIME mode, and type \\[mime-editor/help] to get help."))
736     ))
737
738 ;;;###autoload
739 (defalias 'edit-mime 'mime/editor-mode)         ; for convenience
740 (defalias 'mime-mode 'mime/editor-mode)         ; for convenience
741
742 (defun mime-editor/exit (&optional nomime no-error)
743   "Translate the tagged MIME message into a MIME compliant message.
744 With no argument encode a message in the buffer into MIME, otherwise
745 just return to previous mode."
746   (interactive "P")
747   (if (not mime/editor-mode-flag)
748       (if (null no-error)
749           (error "You aren't editing a MIME message.")
750         )
751     (if (not nomime)
752         (progn
753           (run-hooks 'mime-editor/translate-hook)
754           (mime-editor/translate-buffer)))
755     ;; Restore previous state.
756     (setq mime/editor-mode-flag nil)
757     (use-local-map mime/editor-mode-old-local-map)
758     
759     ;; modified by Pekka Marjola <pema@niksula.hut.fi>
760     ;;  1995/9/5 (c.f. [tm-eng:69])
761     (if (string-match "XEmacs\\|Lucid" emacs-version)
762         (progn
763           (delete-menu-item (list mime-editor/menu-title))
764                                         ; should rather be const
765           ;; (while mime-editor/xemacs-old-bindings
766           ;;   (eval (pop mime-editor/xemacs-old-bindings)))
767           (local-unset-key mime-prefix)))
768     ;; end
769     
770     (setq selective-display mime/editor-mode-old-selective-display)
771     (set-buffer-modified-p (buffer-modified-p))
772     (run-hooks 'mime-editor/exit-hook)
773     (message "Exit MIME editor mode.")
774     ))
775
776 (defun mime-editor/maybe-translate ()
777   (interactive)
778   (mime-editor/exit nil t)
779   (call-interactively 'mime-editor/maybe-split-and-send)
780   )
781
782 (defun mime-editor/help ()
783   "Show help message about MIME mode."
784   (interactive)
785   (with-output-to-temp-buffer "*Help*"
786     (princ "MIME editor mode:\n")
787     (princ (documentation 'mime/editor-mode))
788     (print-help-return-message)))
789
790 (defun mime-editor/insert-text ()
791   "Insert a text message.
792 Charset is automatically obtained from the mime-body-charset-chooser."
793   (interactive)
794   (if (and (mime-editor/insert-tag "text" nil nil)
795            (looking-at mime-editor/single-part-tag-regexp))
796       (progn
797         ;; Make a space between the following message.
798         (insert "\n")
799         (forward-char -1)
800         )))
801
802 (defun mime-editor/insert-file (file)
803   "Insert a message from a file."
804   (interactive "fInsert file as MIME message: ")
805   (let*  ((guess (mime-find-file-type file))
806           (pritype (nth 0 guess))
807           (subtype (nth 1 guess))
808           (parameters (nth 2 guess))
809           (default (nth 3 guess))       ;Guess encoding from its file name.
810           (fields (nth 4 guess))
811           (encoding
812            (if (not (interactive-p))
813                default
814              (completing-read
815               (concat "What transfer encoding"
816                       (if default
817                           (concat " (default "
818                                   (if (string-equal default "")
819                                       "\"\""
820                                     default)
821                                   ")"
822                                   ))
823                       ": ")
824               mime-encoding-method-alist nil t nil))))
825     (if (string-equal encoding "")
826         (setq encoding default))
827     (if (or (consp parameters) (consp fields))
828         (let ((rest parameters) cell attribute value)
829           (setq parameters "")
830           (while rest
831             (setq cell (car rest))
832             (setq attribute (car cell))
833             (setq value (cdr cell))
834             (if (eq value 'file)
835                 (setq value (file-name-nondirectory file))
836               )
837             (setq parameters (concat parameters "; " attribute "=" value))
838             (setq rest (cdr rest))
839             )
840           (setq rest fields)
841           (while rest
842             (setq cell (car rest))
843             (setq attribute (car cell))
844             (setq value (cdr cell))
845             (if (eq value 'file)
846                 (setq value (file-name-nondirectory file))
847               )
848             (setq parameters (concat parameters "\n" attribute ": " value))
849             (setq rest (cdr rest))
850             )
851           ))
852     (mime-editor/insert-tag pritype subtype parameters)
853     (mime-editor/insert-binary-file file encoding)
854     ))
855
856 (defun mime-editor/insert-external ()
857   "Insert a reference to external body."
858   (interactive)
859   (mime-editor/insert-tag "message" "external-body" nil ";\n\t")
860   ;;(forward-char -1)
861   ;;(insert "Content-Description: " (read-string "Content-Description: ") "\n")
862   ;;(forward-line 1)
863   (let* ((pritype (mime-prompt-for-type))
864          (subtype (mime-prompt-for-subtype pritype))
865          (parameters (mime-prompt-for-parameters pritype subtype ";\n\t")))
866     (and pritype
867          subtype
868          (insert "Content-Type: "
869                  pritype "/" subtype (or parameters "") "\n")))
870   (if (and (not (eobp))
871            (not (looking-at mime-editor/single-part-tag-regexp)))
872       (insert (mime-make-text-tag) "\n")))
873
874 (defun mime-editor/insert-voice ()
875   "Insert a voice message."
876   (interactive)
877   (mime-editor/insert-tag "audio" "basic" nil)
878   (let ((buffer (funcall mime-voice-recorder)))
879     (unwind-protect
880         (mime-editor/insert-binary-buffer buffer "base64")
881       (kill-buffer buffer)
882       )))
883
884 (defun mime-editor/insert-signature (&optional arg)
885   "Insert a signature file specified by mime-signature-file."
886   (interactive "P")
887   (let ((signature
888          (expand-file-name
889           (if arg
890               (read-file-name "Insert your signature: "
891                               (concat signature-file-name "-")
892                               signature-file-name
893                               nil)
894             (signature/get-signature-file-name))))
895         )
896     (if signature-insert-at-eof
897         (goto-char (point-max))
898       )
899     (apply (function mime-editor/insert-tag)
900            (mime-find-file-type signature))
901     (if (file-readable-p signature)
902         (progn
903           (goto-char (point-max))
904           (if (not (bolp))
905               (insert "\n"))
906           (delete-blank-lines)
907           (insert-file-contents signature)
908           (set-buffer-modified-p (buffer-modified-p))
909                                         ; force mode line update
910           ))))
911
912 \f
913 ;; Insert a new tag around a point.
914
915 (defun mime-editor/insert-tag (&optional pritype subtype parameters delimiter)
916   "Insert new MIME tag and return a list of PRITYPE, SUBTYPE, and PARAMETERS.
917 If nothing is inserted, return nil."
918   (interactive)
919   (let ((oldtag nil)
920         (newtag nil)
921         (current (point))
922         exist-prev-tag exist-next-tag)
923     (setq pritype
924           (or pritype
925               (mime-prompt-for-type)))
926     (setq subtype
927           (or subtype
928               (mime-prompt-for-subtype pritype)))
929     (setq parameters
930           (or parameters
931               (mime-prompt-for-parameters pritype subtype delimiter)))
932     ;; Make a new MIME tag.
933     (setq newtag (mime-make-tag pritype subtype parameters))
934     ;; Find an current MIME tag.
935     (setq oldtag
936           (save-excursion
937             (if (mime-editor/goto-tag)
938                 (progn
939                   (if (eq current (match-beginning 0))
940                       (setq exist-next-tag t)
941                     (setq exist-prev-tag t)
942                     )
943                   (buffer-substring (match-beginning 0) (match-end 0))
944                   )
945               ;; Assume content type is 'text/plan'.
946               (mime-make-tag "text" "plain")
947               )))
948     ;; We are only interested in TEXT.
949     (if (and oldtag
950              (not (mime-test-content-type
951                    (mime-editor/get-contype oldtag) "text")))
952         (setq oldtag nil))
953     (cond (exist-prev-tag (insert "\n"))
954           (exist-next-tag (save-excursion
955                             (insert "\n")
956                             )))
957     ;; (beginning-of-line)
958     ;; (cond ((and oldtag                  ;Text
959     ;;             (not (eobp))
960     ;;             (save-excursion
961     ;;               (forward-line -1)
962     ;;               (looking-at mime-editor/beginning-tag-regexp)
963     ;;               )
964     ;;             (or mime-ignore-same-text-tag
965     ;;                 (not (string-equal oldtag newtag))))
966     ;;        ;; If point is at the next of current tag, move to the
967     ;;        ;; beginning of the tag to disable insertion of extra tag.
968     ;;        (forward-line -1))
969     ;;       ((and oldtag                  ;Text
970     ;;             (not (eobp))
971     ;;             (not (looking-at mime-editor/tag-regexp))
972     ;;             (or mime-ignore-same-text-tag
973     ;;                 (not (string-equal oldtag newtag))))
974     ;;        ;; Copy current tag to break a text into two.
975     ;;        (save-excursion
976     ;;          (insert oldtag "\n")))
977     ;;       ((and (null oldtag)           ;Not text
978     ;;             (not (looking-at mime-editor/tag-regexp)))
979     ;;        ;; Adjust insertion point.  In the middle of text, it is
980     ;;        ;; okay to break the text into two.  However, it should not
981     ;;        ;; be broken into two, if otherwise.
982     ;;        (goto-char (mime-editor/content-end))
983     ;;        (if (eolp)
984     ;;            (forward-line 1))
985     ;;        (if (not (bolp))
986     ;;            (insert "\n"))
987     ;;        ))
988     (if (not (bolp))
989         (if exist-prev-tag
990             (forward-line 1)
991           (insert "\n")
992           ))
993     ;; Make a new tag.
994     (if (or (not oldtag)                ;Not text
995             (or mime-ignore-same-text-tag
996                 (not (string-equal oldtag newtag))))
997         (progn
998           ;; Mark the beginning of the tag for convenience.
999           (push-mark (point) 'nomsg)
1000           (insert newtag "\n")
1001           (list pritype subtype parameters) ;New tag is created.
1002           )
1003       ;; Restore previous point.
1004       (goto-char current)
1005       nil                               ;Nothing is created.
1006       )
1007     ))
1008
1009 ;; Insert the binary content after MIME tag.
1010 ;;      modified by MORITA Masahiro <hiro@isl.ntt.JP>
1011 ;;      for x-uue
1012 (defun mime-editor/insert-binary-file (file &optional encoding)
1013   "Insert binary FILE at point.
1014 Optional argument ENCODING specifies an encoding method such as base64."
1015   (let ((tmpbuf (get-buffer-create " *MIME insert*")))
1016     (save-excursion
1017       (set-buffer tmpbuf)
1018       (erase-buffer)
1019       (let ((mc-flag nil)               ;Mule
1020             (file-coding-system-for-read
1021              (if (featurep 'mule) *noconv*))
1022             (kanji-flag nil)            ;NEmacs
1023             (emx-binary-mode t)         ;Stop CRLF to LF conversion in OS/2
1024             )
1025         (let (jka-compr-compression-info-list
1026               jam-zcat-filename-list)
1027           (insert-file-contents file))))
1028     (prog1
1029         (if (and (stringp encoding)
1030                  (string-equal (downcase encoding) "x-uue"))
1031             (progn
1032               (require 'mel-u)
1033               (let ((uuencode-external-encoder
1034                      (cons (car uuencode-external-encoder)
1035                            (list (file-name-nondirectory file))
1036                            )))
1037                 (mime-editor/insert-binary-buffer tmpbuf encoding)
1038                 ))
1039           (mime-editor/insert-binary-buffer tmpbuf encoding))
1040       (kill-buffer tmpbuf))))
1041
1042 ;; Insert the binary content after MIME tag.
1043 ;;      modified by MORITA Masahiro <hiro@isl.ntt.JP>
1044 ;;      for x-uue
1045 (defun mime-editor/insert-binary-buffer (buffer &optional encoding)
1046   "Insert binary BUFFER at point.
1047 Optional argument ENCODING specifies an encoding method such as base64."
1048   (let* ((tagend (1- (point)))          ;End of the tag
1049          (hide-p (and mime-auto-hide-body
1050                       (stringp encoding)
1051                       (let ((en (downcase encoding)))
1052                         (or (string-equal en "base64")
1053                             (string-equal en "x-uue")
1054                             ))))
1055          )
1056     (save-restriction
1057       (narrow-to-region (1- (point)) (point))
1058       (let ((start (point))
1059             (emx-binary-mode t))        ;Stop LF to CRLF conversion in OS/2
1060         (insert-buffer-substring buffer)
1061         ;; Encode binary message if necessary.
1062         (if encoding
1063             (mime-encode-region encoding start (point-max))))
1064       (if hide-p
1065           (progn
1066             (mime-flag-region (point-min) (1- (point-max)) ?\^M)
1067             (goto-char (point-max)))
1068         ))
1069     ;; Define encoding even if it is 7bit.
1070     (if (stringp encoding)
1071         (save-excursion
1072           (goto-char tagend)            ;Make sure which line the tag is on.
1073           (mime-editor/define-encoding encoding)))
1074     ))
1075
1076 \f
1077 ;; Commands work on a current message flagment.
1078
1079 (defun mime-editor/goto-tag ()
1080   "Search for the beginning of the tagged MIME message."
1081   (let ((current (point)) multipart)
1082     (if (looking-at mime-editor/tag-regexp)
1083         t
1084       ;; At first, go to the end.
1085       (cond ((re-search-forward mime-editor/beginning-tag-regexp nil t)
1086              (goto-char (match-beginning 0)) ;For multiline tag
1087              (forward-line -1)
1088              (end-of-line)
1089              )
1090             (t
1091              (goto-char (point-max))
1092              ))
1093       ;; Then search for the beginning. 
1094       (re-search-backward mime-editor/end-tag-regexp nil t)
1095       (beginning-of-line)
1096       (or (looking-at mime-editor/beginning-tag-regexp)
1097           ;; Restore previous point.
1098           (progn
1099             (goto-char current)
1100             nil
1101             ))
1102       )))
1103
1104 (defun mime-editor/content-beginning ()
1105   "Return the point of the beginning of content."
1106   (save-excursion
1107     (let ((beg (save-excursion
1108                  (beginning-of-line) (point))))
1109       (if (mime-editor/goto-tag)
1110           (let ((top (point)))
1111             (goto-char (match-end 0))
1112             (if (and (= beg top)
1113                      (= (following-char) ?\^M))
1114                 (point)
1115               (forward-line 1)
1116               (point)))
1117         ;; Default text/plain tag.
1118         (goto-char (point-min))
1119         (re-search-forward
1120          (concat "\n" (regexp-quote mail-header-separator)
1121                  (if mime-ignore-preceding-spaces
1122                      "[ \t\n]*\n" "\n")) nil 'move)
1123         (point))
1124       )))
1125
1126 (defun mime-editor/content-end ()
1127   "Return the point of the end of content."
1128   (save-excursion
1129     (let ((beg (save-excursion
1130                  (beginning-of-line) (point))))
1131       (if (mime-editor/goto-tag)
1132           (let ((top (point)))
1133             (goto-char (match-end 0))
1134             (if (and (= beg top)        ;Must be on the same line.
1135                      (= (following-char) ?\^M))
1136                 (progn
1137                   (end-of-line)
1138                   (point))
1139               ;; Move to the end of this text.
1140               (if (re-search-forward mime-editor/tag-regexp nil 'move)
1141                   ;; Don't forget a multiline tag.
1142                   (goto-char (match-beginning 0)))
1143               (point)
1144               ))
1145         ;; Assume the message begins with text/plain.
1146         (goto-char (mime-editor/content-beginning))
1147         (if (re-search-forward mime-editor/tag-regexp nil 'move)
1148             ;; Don't forget a multiline tag.
1149             (goto-char (match-beginning 0)))
1150         (point))
1151       )))
1152
1153 (defun mime-editor/define-charset (charset)
1154   "Set charset of current tag to CHARSET."
1155   (save-excursion
1156     (if (mime-editor/goto-tag)
1157         (let ((tag (buffer-substring (match-beginning 0) (match-end 0))))
1158           (delete-region (match-beginning 0) (match-end 0))
1159           (insert
1160            (mime-create-tag (mime-set-parameter
1161                              (mime-editor/get-contype tag) "charset" charset)
1162                             (mime-editor/get-encoding tag))))
1163       )))
1164
1165 (defun mime-editor/define-encoding (encoding)
1166   "Set encoding of current tag to ENCODING."
1167   (save-excursion
1168     (if (mime-editor/goto-tag)
1169         (let ((tag (buffer-substring (match-beginning 0) (match-end 0))))
1170           (delete-region (match-beginning 0) (match-end 0))
1171           (insert (mime-create-tag (mime-editor/get-contype tag) encoding)))
1172       )))
1173
1174 (defun mime-editor/choose-charset ()
1175   "Choose charset of a text following current point."
1176   (save-excursion
1177     (let* ((beg (point))
1178            (end (mime-editor/content-end)))
1179       (car (funcall mime-body-charset-chooser beg end)))))
1180
1181 (defun mime-editor/choose-encoding ()
1182   "Choose encoding of a text following current point."
1183   (save-excursion
1184     (let* ((beg (point))
1185            (end (mime-editor/content-end)))
1186       (cdr (funcall mime-body-charset-chooser beg end)))))
1187
1188 (defun mime-make-text-tag (&optional subtype)
1189   "Make a tag for a text after current point.
1190 Subtype of text type can be specified by an optional argument SUBTYPE.
1191 Otherwise, it is obtained from mime-content-types."
1192   (let* ((pritype "text")
1193          (subtype (or subtype
1194                       (car (car (cdr (assoc pritype mime-content-types)))))))
1195     ;; Charset should be defined later.
1196     (mime-make-tag pritype subtype)))
1197
1198 \f
1199 ;; Tag handling functions
1200
1201 (defun mime-make-tag (pritype subtype &optional parameters encoding)
1202   "Make a tag of MIME message of PRITYPE, SUBTYPE and optional PARAMETERS."
1203   (mime-create-tag (concat (or pritype "") "/" (or subtype "")
1204                            (or parameters ""))
1205                    encoding))
1206
1207 (defun mime-create-tag (contype &optional encoding)
1208   "Make a tag with CONTENT-TYPE and optional ENCODING."
1209   (format (if encoding mime-tag-format-with-encoding mime-tag-format)
1210           contype encoding))
1211
1212 (defun mime-editor/get-contype (tag)
1213   "Return Content-Type (including parameters) of TAG."
1214   (and (stringp tag)
1215        (or (string-match mime-editor/single-part-tag-regexp tag)
1216            (string-match mime-editor/multipart-beginning-regexp tag)
1217            (string-match mime-editor/multipart-end-regexp tag)
1218            )
1219        (substring tag (match-beginning 1) (match-end 1))
1220        ))
1221
1222 (defun mime-editor/get-encoding (tag)
1223   "Return encoding of TAG."
1224   (and (stringp tag)
1225        (string-match mime-editor/single-part-tag-regexp tag)
1226        (match-beginning 3)
1227        (not (= (match-beginning 3) (match-end 3)))
1228        (substring tag (match-beginning 3) (match-end 3))))
1229
1230 (defun mime-get-parameter (contype parameter)
1231   "For given CONTYPE return value for PARAMETER.
1232 Nil if no such parameter."
1233   (if (string-match
1234        (concat
1235         ";[ \t\n]*"
1236         (regexp-quote parameter)
1237         "[ \t\n]*=[ \t\n]*\\([^\" \t\n;]*\\|\"[^\"]*\"\\)\\([ \t\n]*;\\|$\\)")
1238        contype)
1239       (substring contype (match-beginning 1) (match-end 1))
1240     nil                                 ;No such parameter
1241     ))
1242
1243 (defun mime-set-parameter (contype parameter value)
1244   "For given CONTYPE set PARAMETER to VALUE."
1245   (if (string-match
1246        (concat
1247         ";[ \t\n]*\\("
1248         (regexp-quote parameter)
1249         "[ \t\n]*=[ \t\n]*\\([^\" \t\n;]*\\|\"[^\"]*\"\\)\\)[ \t\n]*\\(;\\|$\\)")
1250        contype)
1251       ;; Change value
1252       (concat (substring contype 0 (match-beginning 1))
1253               parameter "=" value
1254               (substring contype (match-end 1)))
1255     (concat contype "; " parameter "=" value)))
1256
1257 (defun mime-strip-parameters (contype)
1258   "Return primary content-type and subtype without parameters for CONTYPE."
1259   (if (string-match "^[ \t]*\\([^; \t\n]*\\)" contype)
1260       (substring contype (match-beginning 1) (match-end 1)) nil))
1261
1262 (defun mime-test-content-type (contype type &optional subtype)
1263   "Test if CONTYPE is a TYPE and an optional SUBTYPE."
1264   (and (stringp contype)
1265        (stringp type)
1266        (string-match
1267         (concat "^[ \t]*" (downcase type) "/" (downcase (or subtype "")))
1268         (downcase contype))))
1269
1270 \f
1271 ;; Basic functions
1272
1273 (defun mime-find-file-type (file)
1274   "Guess Content-Type, subtype, and parameters from FILE."
1275   (let ((guess nil)
1276         (guesses mime-file-types))
1277     (while (and (not guess) guesses)
1278       (if (string-match (car (car guesses)) file)
1279           (setq guess (cdr (car guesses))))
1280       (setq guesses (cdr guesses)))
1281     guess
1282     ))
1283
1284 (defun mime-prompt-for-type ()
1285   "Ask for Content-type."
1286   (let ((type ""))
1287     ;; Repeat until primary content type is specified.
1288     (while (string-equal type "")
1289       (setq type
1290             (completing-read "What content type: "
1291                              mime-content-types
1292                              nil
1293                              'require-match ;Type must be specified.
1294                              nil
1295                              ))
1296       (if (string-equal type "")
1297           (progn
1298             (message "Content type is required.")
1299             (beep)
1300             (sit-for 1)
1301             ))
1302       )
1303     type
1304     ))
1305
1306 (defun mime-prompt-for-subtype (pritype)
1307   "Ask for Content-type subtype of Content-Type PRITYPE."
1308   (let* ((default (car (car (cdr (assoc pritype mime-content-types)))))
1309          (answer
1310           (completing-read
1311            (if default
1312                (concat
1313                 "What content subtype: (default " default ") ")
1314              "What content subtype: ")
1315            (cdr (assoc pritype mime-content-types))
1316            nil
1317            'require-match               ;Subtype must be specified.
1318            nil
1319            )))
1320     (if (string-equal answer "") default answer)))
1321
1322 (defun mime-prompt-for-parameters (pritype subtype &optional delimiter)
1323   "Ask for Content-type parameters of Content-Type PRITYPE and SUBTYPE.
1324 Optional DELIMITER specifies parameter delimiter (';' by default)."
1325   (let* ((delimiter (or delimiter "; "))
1326          (parameters
1327           (mapconcat
1328            (function identity)
1329            (delq nil
1330                  (mime-prompt-for-parameters-1
1331                   (cdr (assoc subtype
1332                               (cdr (assoc pritype mime-content-types))))))
1333            delimiter
1334            )))
1335     (if (and (stringp parameters)
1336              (not (string-equal parameters "")))
1337         (concat delimiter parameters)
1338       ""                                ;"" if no parameters
1339       )))
1340
1341 (defun mime-prompt-for-parameters-1 (optlist)
1342   (apply (function append)
1343          (mapcar (function mime-prompt-for-parameter) optlist)))
1344
1345 (defun mime-prompt-for-parameter (parameter)
1346   "Ask for PARAMETER.
1347 Parameter must be '(PROMPT CHOICE1 (CHOISE2 ...))."
1348   (let* ((prompt (car parameter))
1349          (choices (mapcar (function
1350                            (lambda (e)
1351                              (if (consp e) e (list e))))
1352                           (cdr parameter)))
1353          (default (car (car choices)))
1354          (answer nil))
1355     (if choices
1356         (progn
1357           (setq answer
1358                 (completing-read
1359                  (concat "What " prompt
1360                          ": (default "
1361                          (if (string-equal default "") "\"\"" default)
1362                          ") ")
1363                  choices nil nil ""))
1364           ;; If nothing is selected, use default.
1365           (if (string-equal answer "")
1366               (setq answer default)))
1367       (setq answer
1368             (read-string (concat "What " prompt ": "))))
1369     (cons (if (and answer
1370                    (not (string-equal answer "")))
1371               (concat prompt "="
1372                       ;; Note: control characters ignored!
1373                       (if (string-match mime-tspecials-regexp answer)
1374                           (concat "\"" answer "\"") answer)))
1375           (mime-prompt-for-parameters-1 (cdr (assoc answer (cdr parameter)))))
1376     ))
1377
1378 (defun mime-encode-string (encoding string)
1379   "Using ENCODING encode a STRING.
1380 If the STRING is too long, the encoded string may be broken into
1381 several lines."
1382   (save-excursion
1383     (set-buffer (get-buffer-create " *MIME encoding*"))
1384     (erase-buffer)
1385     (insert string)
1386     (mime-encode-region encoding (point-min) (point-max))
1387     (prog1
1388         (buffer-substring (point-min) (point-max))
1389       (kill-buffer (current-buffer)))))
1390
1391 (defun mime-decode-string (encoding string)
1392   "Using ENCODING decode a STRING."
1393   (save-excursion
1394     (set-buffer (get-buffer-create " *MIME decoding*"))
1395     (erase-buffer)
1396     (insert string)
1397     (mime-decode-region encoding (point-min) (point-max))
1398     (prog1
1399         (buffer-substring (point-min) (point-max))
1400       (kill-buffer (current-buffer)))))
1401
1402 (defun mime-flag-region (from to flag)
1403   "Hides or shows lines from FROM to TO, according to FLAG.
1404 If FLAG is `\\n' (newline character) then text is shown,
1405 while if FLAG is `\\^M' (control-M) the text is hidden."
1406   (let ((buffer-read-only nil)          ;Okay even if write protected.
1407         (modp (buffer-modified-p)))
1408     (unwind-protect
1409         (subst-char-in-region from to
1410                               (if (= flag ?\n) ?\^M ?\n)
1411                               flag t)
1412       (set-buffer-modified-p modp))))
1413
1414 \f
1415 ;;; @ Translate the tagged MIME messages into a MIME compliant message.
1416 ;;;
1417
1418 (defun mime-editor/translate-buffer ()
1419   "Encode the tagged MIME message in current buffer in MIME compliant message."
1420   (interactive)
1421   (if (catch 'mime-editor/error
1422         (save-excursion
1423           (mime/encode-message-header)
1424           (mime-editor/translate-body)
1425           (mime-editor/pgp-processing)
1426           ))
1427       (progn
1428         (undo)
1429         (error "Translation error!")
1430         )))
1431
1432 (defun mime-editor/find-inmost ()
1433   (goto-char (point-min))
1434   (if (re-search-forward mime-editor/multipart-beginning-regexp nil t)
1435       (let ((bb (match-beginning 0))
1436             (be (match-end 0))
1437             (type (buffer-substring (match-beginning 1)(match-end 1)))
1438             end-exp eb ee)
1439         (setq end-exp (format "^--}-<<%s>>\n" type))
1440         (widen)
1441         (if (re-search-forward end-exp nil t)
1442             (progn
1443               (setq eb (match-beginning 0))
1444               (setq ee (match-end 0))
1445               )
1446           (setq eb (point-max))
1447           (setq ee (point-max))
1448           )
1449         (narrow-to-region be eb)
1450         (goto-char be)
1451         (if (re-search-forward mime-editor/multipart-beginning-regexp nil t)
1452             (let (ret)
1453               (narrow-to-region (match-beginning 0)(point-max))
1454               (mime-editor/find-inmost)
1455               )
1456           (widen)
1457           ;;(delete-region eb ee)
1458           (list type bb be eb)
1459           ))))
1460
1461 (defun mime-editor/process-multipart-1 (boundary)
1462   (let ((ret (mime-editor/find-inmost)))
1463     (if ret
1464         (let ((type (car ret))
1465               (bb (nth 1 ret))(be (nth 2 ret))
1466               (eb (nth 3 ret))
1467               )
1468           (narrow-to-region bb eb)
1469           (delete-region bb be)
1470           (setq bb (point-min))
1471           (setq eb (point-max))
1472           (widen)
1473           (goto-char eb)
1474           (if (looking-at mime-editor/multipart-end-regexp)
1475               (let ((beg (match-beginning 0))
1476                     (end (match-end 0))
1477                     )
1478                 (delete-region beg end)
1479                 (if (and (not (looking-at mime-editor/single-part-tag-regexp))
1480                          (not (eobp)))
1481                     (insert (concat (mime-make-text-tag) "\n"))
1482                   )))
1483           (cond ((string= type "signed")
1484                  (cond ((eq mime-editor/signing-type 'pgp-elkins)
1485                         (mime-editor/sign-pgp-elkins bb eb boundary)
1486                         )
1487                        ((eq mime-editor/signing-type 'pgp-kazu)
1488                         (mime-editor/process-pgp-kazu 'mc-sign
1489                                                       bb eb boundary)
1490                         )
1491                        ))
1492                 ((string= type "encrypted")
1493                  (cond ((eq mime-editor/signing-type 'pgp-elkins)
1494                         (message "Sorry, it is not supported yet")
1495                         )
1496                        ((eq mime-editor/signing-type 'pgp-kazu)
1497                         (mime-editor/process-pgp-kazu 'mc-encrypt
1498                                                       bb eb boundary)
1499                         )))
1500                 (t
1501                  (setq boundary
1502                        (nth 2 (mime-editor/translate-region bb eb
1503                                                             boundary t)))
1504                  (goto-char bb)
1505                  (insert
1506                   (format "--[[multipart/%s;
1507  boundary=\"%s\"][7bit]]\n"
1508                           type boundary))
1509                  ))
1510           boundary))))
1511
1512 (defun mc-tmpgp-generic-parser (result)
1513   (if (or (not (eq result 0))
1514           (mc-message "^\aError: +Bad pass phrase\\.$" (current-buffer))
1515           )
1516       (progn
1517         (mc-deactivate-passwd t)
1518         nil)
1519     result))
1520
1521 (defvar mc-tmpgp-path "tmpgp")
1522
1523 (defun tm:mc-pgp-sign-region (start end &optional id unclear boundary)
1524   (if (not (boundp 'mc-pgp-user-id))
1525       (load "mc-pgp")
1526     )
1527   (let ((process-environment process-environment)
1528         (buffer (get-buffer-create mc-buffer-name))
1529         passwd args key parser pgp-path
1530         signature-file)
1531     (setq key (mc-pgp-lookup-key (or id mc-pgp-user-id)))
1532     (setq passwd
1533           (mc-activate-passwd
1534            (cdr key)
1535            (format "PGP passphrase for %s (%s): " (car key) (cdr key))))
1536     (setenv "PGPPASSFD" "0")
1537     (setq args
1538           (list "+verbose=1" "+language=en"
1539                 (format "+clearsig=%s" (if unclear "off" "on"))
1540                 "+batchmode" "-u" (cdr key)))
1541     (if boundary
1542         (progn
1543           (setq parser 'mc-tmpgp-generic-parser
1544                 pgp-path mc-tmpgp-path
1545                 signature-file (make-temp-name
1546                                 (expand-file-name "tm-sign" mime/tmp-dir))
1547                 args (cons "-fbst" args))
1548           (if mc-pgp-comment
1549               (setq args (cons (format "+comment=%s" mc-pgp-comment) args))
1550             )
1551           (setq args (cons signature-file args))
1552           )
1553       (setq parser 'mc-pgp-generic-parser
1554             pgp-path mc-pgp-path
1555             args (cons "-fast" args)
1556             )
1557       (if mc-pgp-comment
1558           (setq args (cons (format "+comment=%s" mc-pgp-comment) args))
1559         )
1560       )
1561     (message "Signing as %s ..." (car key))
1562     (if (mc-process-region start end passwd pgp-path args parser buffer)
1563         (progn
1564           (if boundary
1565               (progn
1566                 (goto-char (point-min))
1567                 (insert
1568                  (format "\
1569 --[[multipart/signed; protocol=\"application/pgp-signature\";
1570  boundary=\"%s\"; micalg=pgp-md5][7bit]]
1571 --%s\n" boundary boundary))
1572                 (goto-char (point-max))
1573                 (insert (format "\n--%s\n" boundary))
1574                 (insert "Content-Type: application/pgp-signature
1575 Content-Transfer-Encoding: base64
1576
1577 ")
1578                 (insert-file-contents signature-file)
1579                 (goto-char (point-max))
1580                 (insert (format "\n--%s--\n" boundary))
1581                 ))
1582           (delete-file signature-file)
1583           (message "Signing as %s ... Done." (car key))
1584           t)
1585       nil)))
1586
1587 (defun mime-editor/sign-pgp-elkins (beg end boundary)
1588   (save-excursion
1589     (save-restriction
1590       (narrow-to-region beg end)
1591       (let* ((ret
1592               (mime-editor/translate-region beg end boundary))
1593              (ctype    (car ret))
1594              (encoding (nth 1 ret))
1595              (parts    (nth 3 ret))
1596              (pgp-boundary (concat "pgp-sign-" boundary))
1597              )
1598         (goto-char beg)
1599         (insert (format "Content-Type: %s\n" ctype))
1600         (if encoding
1601             (insert (format "Content-Transfer-Encoding: %s\n" encoding))
1602           )
1603         (insert "\n")
1604         (or (tm:mc-pgp-sign-region (point-min)(point-max)
1605                                    nil nil pgp-boundary)
1606             (throw 'mime-editor/error 'pgp-error)
1607             )
1608         ))))
1609
1610 (defun mime-editor/process-pgp-kazu (type beg end boundary)
1611   (save-excursion
1612     (save-restriction
1613       (narrow-to-region beg end)
1614       (let* ((ret
1615               (mime-editor/translate-region beg end boundary))
1616              (ctype    (car ret))
1617              (encoding (nth 1 ret))
1618              (parts    (nth 3 ret))
1619              )
1620         (goto-char beg)
1621         (insert (format "Content-Type: %s\n" ctype))
1622         (if encoding
1623             (insert (format "Content-Transfer-Encoding: %s\n" encoding))
1624           )
1625         (insert "\n")
1626         (if (null
1627              (progn
1628                (goto-char beg)
1629                (insert "=\n")
1630                (prog1
1631                    (let ((mail-header-separator "="))
1632                      (call-interactively type)
1633                      )
1634                  (goto-char beg)
1635                  (and (search-forward "=\n")
1636                       (replace-match ""))
1637                  )))
1638             (throw 'mime-editor/error 'pgp-error)
1639           )
1640         (goto-char beg)
1641         (insert
1642          "--[[application/pgp; format=mime][7bit]]\n")
1643         ))
1644     ))
1645
1646 (defun mime-editor/translate-body ()
1647   "Encode the tagged MIME body in current buffer in MIME compliant message."
1648   (interactive)
1649   (save-excursion
1650     (let ((boundary
1651            (concat mime-multipart-boundary " " (current-time-string)))
1652           (i 1)
1653           (time (current-time-string))
1654           ret)
1655       (while (mime-editor/process-multipart-1
1656               (format "%s %s-%d" mime-multipart-boundary time i))
1657         (setq i (1+ i))
1658         )
1659       (save-restriction
1660         ;; We are interested in message body.
1661         (let* ((beg
1662                 (progn
1663                   (goto-char (point-min))
1664                   (re-search-forward
1665                    (concat "\n" (regexp-quote mail-header-separator)
1666                            (if mime-ignore-preceding-spaces
1667                                "[ \t\n]*\n" "\n")) nil 'move)
1668                   (point)))
1669                (end
1670                 (progn
1671                   (goto-char (point-max))
1672                   (and mime-ignore-trailing-spaces
1673                        (re-search-backward "[^ \t\n]\n" beg t)
1674                        (forward-char 1))
1675                   (point))))
1676           (setq ret (mime-editor/translate-region
1677                      beg end
1678                      (format "%s %s-%d" mime-multipart-boundary time i)))
1679           ))
1680       (let ((contype (car ret))         ;Content-Type
1681             (encoding (nth 1 ret))      ;Content-Transfer-Encoding
1682             )
1683         ;; Make primary MIME headers.
1684         (or (mail-position-on-field "Mime-Version")
1685             (insert mime-editor/mime-version-value))
1686         ;; Remove old Content-Type and other fields.
1687         (save-restriction
1688           (goto-char (point-min))
1689           (search-forward (concat "\n" mail-header-separator "\n") nil t)
1690           (narrow-to-region (point-min) (point))
1691           (goto-char (point-min))
1692           (mime-delete-field "Content-Type")
1693           (mime-delete-field "Content-Transfer-Encoding"))
1694         ;; Then, insert Content-Type and Content-Transfer-Encoding fields.
1695         (mail-position-on-field "Content-Type")
1696         (insert contype)
1697         (if encoding
1698             (progn
1699               (mail-position-on-field "Content-Transfer-Encoding")
1700               (insert encoding)))
1701         ))))
1702
1703 (defun mime-editor/translate-region (beg end &optional boundary multipart)
1704   (if (null boundary)
1705       (setq boundary
1706             (concat mime-multipart-boundary " " (current-time-string)))
1707     )
1708   (save-excursion
1709     (save-restriction
1710       (narrow-to-region beg end)
1711       (let ((tag nil)                   ;MIME tag
1712             (contype nil)               ;Content-Type
1713             (encoding nil)              ;Content-Transfer-Encoding
1714             (nparts 0))                 ;Number of body parts
1715         ;; Normalize the body part by inserting appropriate message
1716         ;; tags for every message contents.
1717         (mime-editor/normalize-body)
1718         ;; Counting the number of Content-Type.
1719         (goto-char (point-min))
1720         (while (re-search-forward mime-editor/single-part-tag-regexp nil t)
1721           (setq nparts (1+ nparts)))
1722         ;; Begin translation.
1723         (cond ((and (<= nparts 1)(not multipart))
1724                ;; It's a singular message.
1725                (goto-char (point-min))
1726                (while (re-search-forward
1727                        mime-editor/single-part-tag-regexp nil t)
1728                  (setq tag
1729                        (buffer-substring (match-beginning 0) (match-end 0)))
1730                  (delete-region (match-beginning 0) (1+ (match-end 0)))
1731                  (setq contype (mime-editor/get-contype tag))
1732                  (setq encoding (mime-editor/get-encoding tag))
1733                  ))
1734               (t
1735                ;; It's a multipart message.
1736                (goto-char (point-min))
1737                (while (re-search-forward
1738                        mime-editor/single-part-tag-regexp nil t)
1739                  (setq tag
1740                        (buffer-substring (match-beginning 0) (match-end 0)))
1741                  (delete-region (match-beginning 0) (match-end 0))
1742                  (setq contype (mime-editor/get-contype tag))
1743                  (setq encoding (mime-editor/get-encoding tag))
1744                  (insert "--" boundary "\n")
1745                  (insert "Content-Type: " contype "\n")
1746                  (if encoding
1747                      (insert "Content-Transfer-Encoding: " encoding "\n"))
1748                  )
1749                ;; Define Content-Type as "multipart/mixed".
1750                (setq contype
1751                      (concat "multipart/mixed;\n boundary=\"" boundary "\""))
1752                ;; Content-Transfer-Encoding must be "7bit".
1753                ;; The following encoding can be `nil', but is
1754                ;; specified as is since there is no way that a user
1755                ;; specifies it.
1756                (setq encoding "7bit")
1757                ;; Insert the trailer.
1758                (goto-char (point-max))
1759                (if multipart
1760                    (insert "--" boundary "--\n")
1761                  (insert "\n--" boundary "--\n")
1762                  )))
1763         (list contype encoding boundary nparts)
1764         ))))
1765
1766 (defun mime-editor/normalize-body ()
1767   "Normalize the body part by inserting appropriate message tags."
1768   ;; Insert the first MIME tags if necessary.
1769   (goto-char (point-min))
1770   (if (not (looking-at mime-editor/single-part-tag-regexp))
1771       (insert (mime-make-text-tag) "\n"))
1772   ;; Check each tag, and add new tag or correct it if necessary.
1773   (goto-char (point-min))
1774   (while (re-search-forward mime-editor/single-part-tag-regexp nil t)
1775     (let* ((tag (buffer-substring (match-beginning 0) (match-end 0)))
1776            (contype (mime-editor/get-contype tag))
1777            (charset (mime-get-parameter contype "charset"))
1778            (encoding (mime-editor/get-encoding tag)))
1779       ;; Remove extra whitespaces after the tag.
1780       (if (looking-at "[ \t]+$")
1781           (delete-region (match-beginning 0) (match-end 0)))
1782       (cond ((= (following-char) ?\^M)
1783              ;; It must be image, audio or video.
1784              (let ((beg (point))
1785                    (end (mime-editor/content-end)))
1786                ;; Insert explicit MIME tags after hidden messages.
1787                (forward-line 1)
1788                (if (and (not (eobp))
1789                         (not (looking-at mime-editor/single-part-tag-regexp)))
1790                    (progn
1791                      (insert (mime-make-text-tag) "\n")
1792                      (forward-line -1)  ;Process it again as text.
1793                      ))
1794                ;; Show a hidden message.  The point is not altered
1795                ;; after the conversion.
1796                (mime-flag-region beg end ?\n)))
1797             ((mime-test-content-type contype "message")
1798              ;; Content-type "message" should be sent as is.
1799              (forward-line 1))
1800             ((mime-test-content-type contype "text")
1801              ;; Define charset for text if necessary.
1802              (setq charset (or charset (mime-editor/choose-charset)))
1803              (mime-editor/define-charset charset)
1804              (if (string-equal contype "text/x-rot13-47")
1805                  (save-excursion
1806                    (forward-line)
1807                    (set-mark (point))
1808                    (goto-char (mime-editor/content-end))
1809                    (tm:caesar-region)
1810                    ))
1811              ;; Point is now on current tag.
1812              ;; Define encoding and encode text if necessary.
1813              (if (null encoding)        ;Encoding is not specified.
1814                  (let* ((encoding (mime-editor/choose-encoding))
1815                         (beg (mime-editor/content-beginning))
1816                         (end (mime-editor/content-end))
1817                         (body (buffer-substring beg end))
1818                         (encoded (funcall mime-string-encoder
1819                                           (cons charset encoding) body)))
1820                    (if (not (string-equal body encoded))
1821                        (progn
1822                          (goto-char beg)
1823                          (delete-region beg end)
1824                          (insert encoded)
1825                          (goto-char beg)))
1826                    (mime-editor/define-encoding encoding)))
1827              (forward-line 1))
1828             ((null encoding)            ;Encoding is not specified.
1829              ;; Application, image, audio, video, and any other
1830              ;; unknown content-type without encoding should be
1831              ;; encoded.
1832              (let* ((encoding "base64") ;Encode in BASE64 by default.
1833                     (beg (mime-editor/content-beginning))
1834                     (end (mime-editor/content-end))
1835                     (body (buffer-substring beg end))
1836                     (encoded (funcall mime-string-encoder
1837                                       (cons nil encoding) body)))
1838                (if (not (string-equal body encoded))
1839                    (progn
1840                      (goto-char beg)
1841                      (delete-region beg end)
1842                      (insert encoded)
1843                      (goto-char beg)))
1844                (mime-editor/define-encoding encoding))
1845              (forward-line 1))
1846             )
1847       )))
1848
1849 (defun mime-delete-field (field)
1850   "Delete header FIELD."
1851   (let ((regexp (format "^%s:[ \t]*" field)))
1852     (goto-char (point-min))
1853     (while (re-search-forward regexp nil t)
1854       (delete-region (match-beginning 0)
1855                      (progn (forward-line 1) (point)))
1856       )))
1857
1858 \f
1859 ;;;
1860 ;;; Platform dependent functions
1861 ;;;
1862
1863 ;; Emacs 18 implementations
1864
1865 (defun mime-body-charset-chooser-for-emacs18 (begin end)
1866   "Return a cons of charset and encoding of a message in a given region.
1867 Encoding name must be a canonical name, such as `base64'."
1868   '("US-ASCII" . nil)                   ;Default charset of MIME.
1869   )
1870
1871 (defun mime-string-encoder-for-emacs18 (method string)
1872   "For given METHOD that is a cons of charset and encoding, encode a STRING."
1873   (let ((charset (car method))
1874         (encoding (cdr method)))
1875     (cond ((stringp encoding)
1876            (mime-encode-string encoding string))
1877           ;; Return string without any encoding.
1878           (t string)
1879           )))
1880
1881 \f
1882 ;; Emacs 19 implementations
1883
1884 (defun mime-body-charset-chooser-for-emacs19 (begin end)
1885   "Return a cons of charset and encoding of a message in a given region.
1886 Encoding name must be a canonical name, such as `base64'.
1887 US-ASCII and ISO-8859-1 are supported on Emacs 19."
1888   (cond ((save-excursion
1889            (goto-char begin)
1890            (re-search-forward "[\200-\377]" end t))
1891          '("ISO-8859-1" . "quoted-printable"))
1892         (t
1893          '("US-ASCII" . nil))           ;Default charset of MIME.
1894         ))
1895
1896 (defun mime-string-encoder-for-emacs19 (method string)
1897   "For given METHOD that is a cons of charset and encoding, encode a STRING."
1898   (let ((charset (car method))
1899         (encoding (cdr method)))
1900     (cond ((stringp encoding)
1901            (mime-encode-string encoding string))
1902           ;; Return string without any encoding.
1903           (t string)
1904           )))
1905
1906 \f
1907 ;; NEmacs implementations
1908
1909 (defun mime-body-charset-chooser-for-nemacs (begin end)
1910   "Return a cons of charset and encoding of a message in a given region.
1911 Encoding name must be a canonical name, such as `base64'.
1912 US-ASCII and ISO-2022-JP are supported on NEmacs."
1913   (cond ((check-region-kanji-code begin end)
1914          ;; The following are safe encoding methods for use in
1915          ;; USENET News systems that strip off all ESCs.
1916          ;; '("ISO-2022-JP" . "quoted-printable")
1917          ;; '("ISO-2022-JP" . "base64")
1918          ;; The following expects transport systems are all MIME
1919          ;; compliants.  For instance, ESCs are never stripped off.
1920          '("ISO-2022-JP" . nil))
1921         (t
1922          '("US-ASCII" . nil))           ;Default charset of MIME.
1923         ))
1924
1925 (defun mime-string-encoder-for-nemacs (method string)
1926   "For given METHOD that is a cons of charset and encoding, encode a STRING.
1927 US-ASCII and ISO-2022-JP are supported on NEmacs."
1928   (let ((charset (car method))
1929         (encoding (cdr method)))
1930     (cond ((stringp encoding)
1931            (mime-encode-string encoding
1932                                ;; Convert internal (EUC) to JIS code.
1933                                (convert-string-kanji-code string 3 2)
1934                                ))
1935           ;; NEmacs can convert into ISO-2022-JP automatically,
1936           ;; but can do it myself as follows:
1937           ;;(t (convert-string-kanji-code string 3 2))
1938
1939           ;; Return string without any encoding.
1940           (t string)
1941           )))
1942
1943 \f
1944 ;; Mule implementations
1945 ;; Thanks to contributions by wkenji@flab.fujitsu.co.jp (Kenji
1946 ;; WAKAMIYA) and handa@etl.go.jp (Kenichi Handa).
1947
1948 (defun mime-body-charset-chooser-for-mule (begin end)
1949   "Return a cons of charset and encoding of a message in a given
1950 region.  Encoding name must be a canonical name, such as `base64'.
1951 US-ASCII, ISO-8859-* (except for ISO-8859-6), ISO-2022-JP,
1952 ISO-2022-JP-2 and ISO-2022-INT-1 are supported on Mule.  Either of
1953 charset ISO-2022-JP-2 or ISO-2022-INT-1 is used for multilingual text
1954 in Mule."
1955   (let ((lclist (find-charset-region begin end)))
1956     (cond ((null lclist)
1957            '("US-ASCII" . nil))         ;Default charset of MIME.
1958           ;; Multilingual capability requred.
1959           ((and (> (length lclist) 1)
1960                 (boundp '*iso-2022-int-1*))
1961            '("ISO-2022-INT-1" . nil))
1962           ((> (length lclist) 1)
1963            '("ISO-2022-JP-2" . nil))
1964           ;; Simple charset.
1965           ((memq lc-ltn1 lclist)
1966            '("ISO-8859-1" . "quoted-printable"))
1967           ((memq lc-ltn2 lclist)
1968            '("ISO-8859-2" . "quoted-printable"))
1969           ((memq lc-ltn3 lclist)
1970            '("ISO-8859-3" . "quoted-printable"))
1971           ((memq lc-ltn4 lclist)
1972            '("ISO-8859-4" . "quoted-printable"))
1973           ((memq lc-crl lclist)
1974            '("ISO-8859-5" . "quoted-printable"))
1975           ;;((memq lc-arb lclist)
1976           ;; '("ISO-8859-6" . "quoted-printable"))
1977           ((memq lc-grk lclist)
1978            '("ISO-8859-7" . "quoted-printable"))
1979           ((memq lc-hbw lclist)
1980            '("ISO-8859-8" . "quoted-printable"))
1981           ((memq lc-ltn5 lclist)
1982            '("ISO-8859-9" . "quoted-printable"))
1983           ((memq lc-jp lclist)
1984            '("ISO-2022-JP" . nil))
1985           ((memq lc-kr lclist)
1986            '("ISO-2022-KR" . nil))
1987           ;; Unknown charset.
1988           ((boundp '*iso-2022-int-1*)
1989            '("ISO-2022-INT-1" . nil))
1990           (t
1991            '("ISO-2022-JP-2" . nil))
1992           )))
1993
1994 (defun mime-string-encoder-for-mule (method string)
1995   "For given METHOD that is a cons of charset and encoding, encode a
1996 STRING.  US-ASCII, ISO-8859-* (except for ISO-8859-6), ISO-2022-JP,
1997 ISO-2022-JP-2 and ISO-2022-INT-1 are supported on Mule.  Either of
1998 charset ISO-2022-JP-2 or ISO-2022-INT-1 is used for multilingual
1999 text."
2000   (let* ((charset (car method))
2001          (encoding (cdr method))
2002          (coding-system
2003           (cdr (assoc (and (stringp charset) (upcase charset))
2004                       '(("ISO-8859-1" . *ctext*)
2005                         ("ISO-8859-2" . *iso-8859-2*)
2006                         ("ISO-8859-3" . *iso-8859-3*)
2007                         ("ISO-8859-4" . *iso-8859-4*)
2008                         ("ISO-8859-5" . *iso-8859-5*)
2009                         ;;("ISO-8859-6" . *iso-8859-6*)
2010                         ("ISO-8859-7" . *iso-8859-7*)
2011                         ("ISO-8859-8" . *iso-8859-8*)
2012                         ("ISO-8859-9" . *iso-8859-9*)
2013                         ("ISO-2022-JP" . *junet*)
2014                         ("ISO-2022-JP-2" . *iso-2022-ss2-7*)
2015                         ("ISO-2022-KR" . *korean-mail*)
2016                         ("ISO-2022-INT-1" . *iso-2022-int-1*)
2017                         )))))
2018     ;; In bilingual environment it may be unnecessary to convert the
2019     ;; coding system of the string unless transfer encoding is
2020     ;; required since such conversion may be performed by mule
2021     ;; automatically.
2022     (if (not (null coding-system))
2023         (setq string (code-convert-string string *internal* coding-system)))
2024     (if (stringp encoding)
2025         (setq string (mime-encode-string encoding string)))
2026     string
2027     ))
2028
2029 \f
2030 ;; Sun implementations
2031
2032 (defun mime-voice-recorder-for-sun ()
2033   "Record voice in a buffer using Sun audio device, and return the buffer.
2034 If the environment variable AUDIOHOST is defined, its value is used as
2035 a recording host instead of local host."
2036   (let ((buffer (get-buffer-create " *MIME audio*"))
2037         (host (getenv "AUDIOHOST")))
2038     (message "Start the recording on %s.  Type C-g to finish the recording..."
2039              (or host (system-name)))
2040     (save-excursion
2041       (set-buffer buffer)
2042       (erase-buffer)
2043       (condition-case errorcode
2044           (let ((selective-display nil) ;Disable ^M to nl translation.
2045                 (mc-flag nil)           ;Mule
2046                 (kanji-flag nil))       ;NEmacs
2047             ;; If AUDIOHOST is defined, use the value as recording host.
2048             (cond ((not (null host))
2049                    ;; Disable automatic conversion of coding system if Mule.
2050                    (if (featurep 'mule)
2051                        (define-program-coding-system nil "rsh" *noconv*))
2052                    (call-process "rsh"
2053                                  nil
2054                                  buffer
2055                                  nil
2056                                  host
2057                                  "cat"
2058                                  "/dev/audio"
2059                                  ))
2060                   (t
2061                    ;; Disable automatic conversion of coding system if Mule.
2062                    (if (featurep 'mule)
2063                        (define-program-coding-system nil "cat" *noconv*))
2064                    (call-process "cat"
2065                                  "/dev/audio"
2066                                  buffer
2067                                  nil
2068                                  ))))
2069         (quit (message "Type C-g to finish recording... done.")
2070               buffer                    ;Return the buffer
2071               )))))
2072
2073 \f
2074 ;;; @ Other useful commands.
2075 ;;;
2076
2077 ;; Message forwarding commands as content-type "message/rfc822".
2078
2079 (defun mime-editor/insert-message (&optional message)
2080   (interactive)
2081   (let ((inserter (assoc-value major-mode mime-editor/message-inserter-alist)))
2082     (if (and inserter (fboundp inserter))
2083         (progn
2084           (mime-editor/insert-tag "message" "rfc822")
2085           (funcall inserter message)
2086           )
2087       (message "Sorry, I don't have message inserter for your MUA.")
2088       )))
2089
2090 (defun mime-editor/insert-mail (&optional message)
2091   (interactive)
2092   (let ((inserter (assoc-value major-mode mime-editor/mail-inserter-alist)))
2093     (if (and inserter (fboundp inserter))
2094         (progn
2095           (mime-editor/insert-tag "message" "rfc822")
2096           (funcall inserter message)
2097           )
2098       (message "Sorry, I don't have mail inserter for your MUA.")
2099       )))
2100
2101 (defun mime-editor/inserted-message-filter ()
2102   (save-excursion
2103     (save-restriction
2104       (let ((header-start (point))
2105             (case-fold-search t)
2106             beg end)
2107         ;; for Emacs 18
2108         ;; (if (re-search-forward "^$" (marker-position (mark-marker)))
2109         (if (re-search-forward "^$" (mark t))
2110             (narrow-to-region header-start (match-beginning 0))
2111           )
2112         (goto-char header-start)
2113         (while (and (re-search-forward
2114                      mime-editor/yank-ignored-field-regexp nil t)
2115                     (setq beg (match-beginning 0))
2116                     (setq end (1+ (rfc822/field-end)))
2117                     )
2118           (delete-region beg end)
2119           )
2120         ))))
2121
2122
2123 ;;; @ multipart enclosure
2124 ;;;
2125
2126 (defun mime-editor/enclose-region (type beg end)
2127   (save-excursion
2128     (goto-char beg)
2129     (let ((current (point))
2130           exist-prev-tag)
2131       (save-excursion
2132         (if (mime-editor/goto-tag)
2133             (or (eq current (match-beginning 0))
2134                 (setq exist-prev-tag t)
2135                 )))
2136       (save-restriction
2137         (narrow-to-region beg end)
2138         (goto-char beg)
2139         (if exist-prev-tag
2140             (insert "\n")
2141           )
2142         (insert (format "--<<%s>>-{\n" type))
2143         (goto-char (point-max))
2144         (insert (format "\n--}-<<%s>>\n" type))
2145         (goto-char (point-max))
2146         )
2147       (if (and (not (looking-at mime-editor/single-part-tag-regexp))
2148                (not (eobp)))
2149           (insert (mime-make-text-tag) "\n")
2150         )
2151       )))
2152
2153 (defun mime-editor/enclose-mixed-region (beg end)
2154   (interactive "*r")
2155   (mime-editor/enclose-region "mixed" beg end)
2156   )
2157
2158 (defun mime-editor/enclose-parallel-region (beg end)
2159   (interactive "*r")
2160   (mime-editor/enclose-region "parallel" beg end)
2161   )
2162
2163 (defun mime-editor/enclose-digest-region (beg end)
2164   (interactive "*r")
2165   (mime-editor/enclose-region "digest" beg end)
2166   )
2167
2168 (defun mime-editor/enclose-alternative-region (beg end)
2169   (interactive "*r")
2170   (mime-editor/enclose-region "alternative" beg end)
2171   )
2172
2173 (defun mime-editor/enclose-signed-region (beg end)
2174   (interactive "*r")
2175   (if mime-editor/signing-type
2176       (mime-editor/enclose-region "signed" beg end)
2177     (message "Please specify signing type.")
2178     ))
2179
2180 (defun mime-editor/enclose-encrypted-region (beg end)
2181   (interactive "*r")
2182   (if mime-editor/signing-type
2183       (mime-editor/enclose-region "encrypted" beg end)
2184     (message "Please specify encrypting type.")
2185     ))
2186
2187 (defun mime-editor/insert-key (&optional arg)
2188   "Insert a pgp public key."
2189   (interactive "P")
2190   (mime-editor/insert-tag "application" "pgp-keys")
2191   (mime-editor/define-encoding "7bit")
2192   (mc-insert-public-key)
2193   )
2194
2195
2196 ;;; @ flag setting
2197 ;;;
2198
2199 (defun mime-editor/set-split (arg)
2200   (interactive
2201    (list
2202     (y-or-n-p "Do you want to enable split?")
2203     ))
2204   (setq mime-editor/split-message arg)
2205   (if arg
2206       (message "This message is enabled to split.")
2207     (message "This message is not enabled to split.")
2208     ))
2209
2210
2211 ;;; @ pgp
2212 ;;;
2213
2214 (defun mime-editor/set-sign (arg)
2215   (interactive
2216    (list
2217     (y-or-n-p "Do you want to sign?")
2218     ))
2219   (if arg
2220       (progn
2221         (setq mime-editor/pgp-processing 'sign)
2222         (message "This message will be signed.")
2223         )
2224     (if (eq mime-editor/pgp-processing 'sign)
2225         (setq mime-editor/pgp-processing nil)
2226       )
2227     (message "This message will not be signed.")
2228     ))
2229
2230 (defun mime-editor/set-encrypt (arg)
2231   (interactive
2232    (list
2233     (y-or-n-p "Do you want to encrypt?")
2234     ))
2235   (if arg
2236       (progn
2237         (setq mime-editor/pgp-processing 'encrypt)
2238         (message "This message will be encrypt.")
2239         )
2240     (if (eq mime-editor/pgp-processing 'encrypt)
2241         (setq mime-editor/pgp-processing nil)
2242       )
2243     (message "This message will not be encrypt.")
2244     ))
2245
2246 (defvar mime-editor/pgp-processing nil)
2247 (make-variable-buffer-local 'mime-editor/pgp-processing)
2248
2249 (defun mime-editor/call-mc (command)
2250   (let* ((header (rfc822/get-header-string-except
2251                   "^Content-Type:" mail-header-separator)))
2252     (goto-char (point-min))
2253     (if (search-forward mail-header-separator)
2254         (replace-match "")
2255       )
2256     (goto-char (point-min))
2257     (insert header)
2258     (insert "Content-Type: application/pgp; format=mime\n")
2259     (insert mail-header-separator)
2260     (insert "\n")
2261     (if (null (call-interactively command))
2262         (throw 'mime-editor/error 'pgp-error)
2263       )
2264     ))
2265
2266 (defun mime-editor/pgp-processing ()
2267   (let ((command
2268          (cdr (assq mime-editor/pgp-processing
2269                     '((sign    . mc-sign)
2270                       (encrypt . mc-encrypt)
2271                       )))))
2272     (and command
2273          (mime-editor/call-mc command)
2274          )))
2275
2276
2277 ;;; @ split
2278 ;;;
2279
2280 (defun mime-editor/insert-partial-header
2281   (fields subject id number total separator)
2282   (insert fields)
2283   (insert (format "Subject: %s (%d/%d)\n" subject number total))
2284   (insert (format "Mime-Version: 1.0 (split by tm-edit %s)\n"
2285                   mime-editor/version))
2286   (insert (format "\
2287 Content-Type: message/partial; id=%s; number=%d; total=%d\n%s\n"
2288                   id number total separator))
2289   )
2290
2291 (defun mime-editor/split-and-send
2292   (&optional cmd lines mime-editor/message-max-length)
2293   (interactive)
2294   (or lines
2295       (setq lines
2296             (count-lines (point-min) (point-max)))
2297       )
2298   (or mime-editor/message-max-length
2299       (setq mime-editor/message-max-length
2300             (or (cdr (assq major-mode mime-editor/message-max-length-alist))
2301                 mime-editor/message-default-max-length))
2302       )
2303   (let* ((mime-editor/draft-file-name 
2304           (or (buffer-file-name)
2305               (make-temp-name
2306                (expand-file-name "tm-draft" mime/tmp-dir))))
2307          (separator mail-header-separator)
2308          (config
2309           (eval (cdr (assq major-mode mime-editor/window-config-alist))))
2310          (id (concat "\""
2311                      (replace-space-with-underline (current-time-string))
2312                      "@" (system-name) "\"")))
2313     (run-hooks 'mime-editor/before-split-hook)
2314     (let* ((header (rfc822/get-header-string-except
2315                     mime-editor/split-ignored-field-regexp separator))
2316            (subject (mail-fetch-field "subject"))
2317            (total (+ (/ lines mime-editor/message-max-length)
2318                      (if (> (mod lines mime-editor/message-max-length) 0)
2319                          1)))
2320            (the-buf (current-buffer))
2321            (buf (get-buffer "*tmp-send*"))
2322            (command
2323             (or cmd
2324                 (cdr
2325                  (assq major-mode
2326                        mime-editor/split-message-sender-alist))
2327                 (cdr
2328                  (assq major-mode
2329                        mime-editor/message-default-sender-alist))
2330                 ))
2331            data)
2332       (goto-char (point-min))
2333       (if (re-search-forward (concat "^" (regexp-quote separator) "$")
2334                              nil t)
2335           (replace-match "")
2336         )
2337       (if buf
2338           (progn
2339             (switch-to-buffer buf)
2340             (erase-buffer)
2341             (switch-to-buffer the-buf)
2342             )
2343         (setq buf (get-buffer-create "*tmp-send*"))
2344         )
2345       (switch-to-buffer buf)
2346       (make-local-variable 'mail-header-separator)
2347       (setq mail-header-separator separator)
2348       (switch-to-buffer the-buf)
2349       (goto-char (point-min))
2350       (re-search-forward "^$" nil t)
2351       (let ((mime-editor/partial-number 1))
2352         (setq data (buffer-substring
2353                     (point-min)
2354                     (progn
2355                       (goto-line mime-editor/message-max-length)
2356                       (point))
2357                     ))
2358         (delete-region (point-min)(point))
2359         (switch-to-buffer buf)
2360         (mime-editor/insert-partial-header
2361          header subject id mime-editor/partial-number total separator)
2362         (insert data)
2363         (save-excursion
2364           (save-restriction
2365             (goto-char (point-min))
2366             (search-forward (concat "\n" mail-header-separator "\n"))
2367             (narrow-to-region
2368              (match-end 0)
2369              (if (re-search-forward "^$" nil t)
2370                  (match-beginning 0)
2371                (point-max)
2372                ))
2373             (goto-char (point-min))
2374             (while (re-search-forward
2375                     mime-editor/split-blind-field-regexp nil t)
2376               (delete-region (match-beginning 0)
2377                              (let ((e (rfc822/field-end)))
2378                                (if (< e (point-max))
2379                                    (1+ e)
2380                                  e)))
2381               )
2382             ))
2383         (save-excursion
2384           (message (format "Sending %d/%d..."
2385                            mime-editor/partial-number total))
2386           (call-interactively command)
2387           (message (format "Sending %d/%d... done"
2388                            mime-editor/partial-number total))
2389           )
2390         (erase-buffer)
2391         (switch-to-buffer the-buf)
2392         (setq mime-editor/partial-number 2)
2393         (while (< mime-editor/partial-number total)
2394           (setq data (buffer-substring
2395                       (point-min)
2396                       (progn
2397                         (goto-line mime-editor/message-max-length)
2398                         (point))
2399                       ))
2400           (delete-region (point-min)(point))
2401           (switch-to-buffer buf)
2402           (mime-editor/insert-partial-header
2403            header subject id mime-editor/partial-number total separator)
2404           (insert data)
2405           (save-excursion
2406             (message (format "Sending %d/%d..."
2407                              mime-editor/partial-number total))
2408             (call-interactively command)
2409             (message (format "Sending %d/%d... done"
2410                              mime-editor/partial-number total))
2411             )
2412           (erase-buffer)
2413           (switch-to-buffer the-buf)
2414           (setq mime-editor/partial-number
2415                 (1+ mime-editor/partial-number))
2416           )
2417         (goto-char (point-min))
2418         (mime-editor/insert-partial-header
2419          header subject id mime-editor/partial-number total separator)
2420         (message (format "Sending %d/%d..."
2421                          mime-editor/partial-number total))
2422         ))))
2423
2424 (defun mime-editor/maybe-split-and-send (&optional cmd)
2425   (interactive)
2426   (run-hooks 'mime-editor/before-send-hook)
2427   (let ((mime-editor/message-max-length
2428          (or (cdr (assq major-mode mime-editor/message-max-length-alist))
2429              mime-editor/message-default-max-length))
2430         (lines (count-lines (point-min) (point-max)))
2431         )
2432     (if (and (> lines mime-editor/message-max-length)
2433              mime-editor/split-message)
2434         (mime-editor/split-and-send cmd lines mime-editor/message-max-length)
2435       )))
2436
2437
2438 ;;; @ preview message
2439 ;;;
2440
2441 (defun mime-editor/preview-message ()
2442   "preview editing MIME message. [tm-edit.el]"
2443   (interactive)
2444   (let* ((str (buffer-string))
2445          (separator mail-header-separator)
2446          (the-buf (current-buffer))
2447          (buf-name (buffer-name))
2448          (temp-buf-name (concat "*temp-article:" buf-name "*"))
2449          (buf (get-buffer temp-buf-name))
2450          )
2451     (if buf
2452         (progn
2453           (switch-to-buffer buf)
2454           (erase-buffer)
2455           )
2456       (setq buf (get-buffer-create temp-buf-name))
2457       (switch-to-buffer buf)
2458       )
2459     (insert str)
2460     (setq major-mode 'mime/temporary-message-mode)
2461     (make-local-variable 'mail-header-separator)
2462     (setq mail-header-separator separator)
2463     (make-local-variable 'mime/editing-buffer)
2464     (setq mime/editing-buffer the-buf)
2465     
2466     (run-hooks 'mime-editor/translate-hook)
2467     (mime-editor/translate-buffer)
2468     (goto-char (point-min))
2469     (if (re-search-forward
2470          (concat "^" (regexp-quote separator) "$"))
2471         (replace-match "")
2472       )
2473     (mime/viewer-mode)
2474     ))
2475
2476 (defun mime-editor/quitting-method ()
2477   (let ((temp mime::preview/article-buffer)
2478         buf)
2479     (mime-viewer/kill-buffer)
2480     (set-buffer temp)
2481     (setq buf mime/editing-buffer)
2482     (kill-buffer temp)
2483     (switch-to-buffer buf)
2484     ))
2485
2486 (set-alist 'mime-viewer/quitting-method-alist
2487            'mime/temporary-message-mode
2488            (function mime-editor/quitting-method)
2489            )
2490
2491
2492 ;;; @ draft preview
2493 ;;; 
2494 ;; by "OKABE Yasuo <okabe@kudpc.kyoto-u.ac.jp>
2495 ;;       Mon, 10 Apr 1995 20:03:07 +0900
2496
2497 (defvar mime-editor/draft-header-separator-alist
2498   '((news-reply-mode . mail-header-separator)
2499     (mh-letter-mode . mail-header-separator)
2500     ))
2501
2502 (defvar mime::article/draft-header-separator nil)
2503
2504 (defun mime-editor/draft-preview ()
2505   (interactive)
2506   (let ((sep (cdr (assq major-mode mime-editor/draft-header-separator-alist))))
2507     (or (stringp sep) (setq sep (eval sep)))
2508     (make-variable-buffer-local 'mime::article/draft-header-separator)
2509     (goto-char (point-min))
2510     (re-search-forward
2511      (concat "^\\(" (regexp-quote sep) "\\)?$"))
2512     (setq mime::article/draft-header-separator
2513           (buffer-substring (match-beginning 0) (match-end 0)))
2514     (replace-match "")
2515     (mime/viewer-mode (current-buffer))
2516     (pop-to-buffer (current-buffer))
2517     ))
2518
2519 (defun mime-viewer::quitting-method/draft-preview ()
2520   (let ((mother mime::preview/mother-buffer))
2521     (save-excursion
2522       (switch-to-buffer mother)
2523       (goto-char (point-min))
2524       (if (and
2525            (re-search-forward
2526             (concat "^\\("
2527                     (regexp-quote mime::article/draft-header-separator)
2528                     "\\)?$") nil t)
2529            (bolp))
2530           (progn
2531             (insert mime::article/draft-header-separator)
2532             (set-buffer-modified-p (buffer-modified-p))
2533             )))
2534     (mime-viewer/kill-buffer)
2535     (pop-to-buffer mother)
2536     ))
2537
2538 (set-alist 'mime-viewer/quitting-method-alist
2539            'mh-letter-mode
2540            (function mime-viewer::quitting-method/draft-preview)
2541            )
2542
2543 (set-alist 'mime-viewer/quitting-method-alist
2544            'news-reply-mode
2545            (function mime-viewer::quitting-method/draft-preview)
2546            )
2547
2548
2549 ;;; @ etc
2550 ;;;
2551
2552 (defun replace-space-with-underline (str)
2553   (mapconcat (function
2554               (lambda (arg)
2555                 (char-to-string
2556                  (if (= arg 32)
2557                      ?_
2558                    arg)))) str "")
2559   )
2560
2561
2562 ;;; @ end
2563 ;;;
2564
2565 (provide 'tm-edit)
2566
2567 (run-hooks 'tm-edit-load-hook)
2568
2569 ;;; tm-edit.el ends here