tm 7.32.
[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.32.1.2 $
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/10 15:06:34 $|$Revision: 7.32.1.2 $|~/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.32.1.2 1995/12/10 15:06:34 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).
399 Sorry `pgp-elkins' is not supported yet. [tm-edit.el]")
400
401 (defvar mime-editor/encrypting-type nil
402   "*PGP encrypting type (pgp-elkins, pgp-kazu or nil).
403 Sorry `pgp-elkins' is not supported yet. [tm-edit.el]")
404
405 (if (or mime-editor/signing-type mime-editor/encrypting-type)
406     (require 'mailcrypt)
407   )
408
409
410 ;;; @@ about tag
411 ;;;
412
413 (defconst mime-editor/single-part-tag-regexp
414   "^--[[][[]\\([^]]*\\)]\\([[]\\([^]]*\\)]\\|\\)]"
415   "*Regexp of MIME tag in the form of [[CONTENT-TYPE][ENCODING]].")
416
417 (defconst mime-editor/multipart-beginning-regexp "^--<<\\([^<>]+\\)>>-{\n")
418
419 (defconst mime-editor/multipart-end-regexp "^--}-<<\\([^<>]+\\)>>\n")
420
421 (defconst mime-editor/beginning-tag-regexp
422   (regexp-or mime-editor/single-part-tag-regexp
423              mime-editor/multipart-beginning-regexp))
424
425 (defconst mime-editor/end-tag-regexp
426   (regexp-or mime-editor/single-part-tag-regexp
427              mime-editor/multipart-end-regexp))
428
429 (defconst mime-editor/tag-regexp
430   (regexp-or mime-editor/single-part-tag-regexp
431              mime-editor/multipart-beginning-regexp
432              mime-editor/multipart-end-regexp))
433
434 (defvar mime-tag-format "--[[%s]]"
435   "*Control-string making a MIME tag.")
436
437 (defvar mime-tag-format-with-encoding "--[[%s][%s]]"
438   "*Control-string making a MIME tag with encoding.")
439
440 ;;; @@ multipart boundary
441 ;;;
442
443 (defvar mime-multipart-boundary "Multipart"
444   "*Boundary of a multipart message.")
445
446
447 ;;; @@ buffer local variables
448 ;;;
449
450 (defvar mime/editor-mode-old-local-map nil)
451 (defvar mime/editor-mode-old-selective-display nil)
452 (defvar mime/editing-buffer nil)
453
454 \f
455 ;;; @ constants
456 ;;;
457
458 (defconst mime-tspecials-regexp "[][()<>@,;:\\\"/?.= \t]"
459   "*Specify MIME tspecials.
460 Tspecials means any character that matches with it in header must be quoted.")
461
462 (defconst mime-editor/mime-version-value
463   (format "1.0 (generated by tm-edit %s)" mime-editor/version)
464   "MIME version number.")
465
466
467 ;;; @ keymap and menu
468 ;;;
469
470 (defvar mime/editor-mode-flag nil)
471 (make-variable-buffer-local 'mime/editor-mode-flag)
472
473 (set-alist 'minor-mode-alist 'mime/editor-mode-flag '(" MIME-Edit"))
474
475 (defun mime-editor/define-keymap (keymap)
476   "Add mime-editor commands to KEYMAP."
477   (if (not (keymapp keymap))
478       nil
479     (define-key keymap "\C-t" 'mime-editor/insert-text)
480     (define-key keymap "\C-i" 'mime-editor/insert-file)
481     (define-key keymap "\C-e" 'mime-editor/insert-external)
482     (define-key keymap "\C-v" 'mime-editor/insert-voice)
483     (define-key keymap "\C-y" 'mime-editor/insert-message)
484     (define-key keymap "\C-m" 'mime-editor/insert-mail)
485     (define-key keymap "\C-w" 'mime-editor/insert-signature)
486     (define-key keymap "\C-s" 'mime-editor/insert-signature)
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      (split     "About split"           mime-editor/set-split)
520      )
521    (if (and (featurep 'mailcrypt)
522             (not (or mime-editor/signing-type mime-editor/encrypting-type)))
523        '((sign    "About sign"       mime-editor/set-sign)
524          (encrypt "About encryption" mime-editor/set-encrypt)
525          ))
526    '((preview   "Preview Message"       mime-editor/preview-message))
527    )
528   "MIME-edit menubar entry.")
529
530 (defun mime-editor/define-menu-for-emacs19 ()
531   "Define menu for Emacs 19."
532   (define-key (current-local-map) [menu-bar mime-edit]
533     (cons mime-editor/menu-title
534           (make-sparse-keymap mime-editor/menu-title)))
535   (mapcar (function
536            (lambda (item)
537              (define-key (current-local-map)
538                (vector 'menu-bar 'mime-edit (car item))
539                (cons (nth 1 item)(nth 2 item))
540                )
541              ))
542           (reverse mime-editor/menu-list)
543           ))
544
545 ;;; modified by Pekka Marjola <pema@niksula.hut.fi>
546 ;;;     1995/9/5 (c.f. [tm-en:69])
547 (defun mime-editor/define-menu-for-xemacs ()
548   "Define menu for Emacs 19."
549   (cond ((featurep 'menubar)
550          (make-local-variable 'current-menubar)
551          (set-buffer-menubar current-menubar)
552          (add-submenu nil
553                       (cons mime-editor/menu-title
554                             (mapcar (function
555                                      (lambda (item)
556                                        (vector (nth 1 item)(nth 2 item)
557                                                mime/editor-mode-flag)
558                                        ))
559                                     mime-editor/menu-list)))
560          )))
561
562 ;;; modified by Steven L. Baur <steve@miranova.com>
563 ;;;     1995/12/6 (c.f. [tm-en:209])
564 (if (and (string-match "XEmacs\\|Lucid" emacs-version)
565          (not (boundp 'mime-editor/popup-menu-for-xemacs)))
566     (setq mime-editor/popup-menu-for-xemacs
567           (append '("MIME Commands" "---")
568                   (mapcar (function (lambda (item)
569                                       (vector (nth 1 item)
570                                               (nth 2 item)
571                                               t)))
572                           mime-editor/menu-list)))
573   )
574 ;;; end
575
576
577 ;;; @ functions
578 ;;;
579
580 ;;;###autoload
581 (defun mime/editor-mode ()
582   "MIME minor mode for editing the tagged MIME message.
583
584 In this mode, basically, the message is composed in the tagged MIME
585 format.  The message tag looks like:
586
587         `--[[text/plain; charset=ISO-2022-JP][7bit]]'.
588
589 The tag specifies the MIME content type, subtype, optional parameters
590 and transfer encoding of the message following the tag.  Messages
591 without any tag are treated as `text/plain' by default.  Charset and
592 transfer encoding are automatically defined unless explicitly
593 specified.  Binary messages such as audio and image are usually hidden
594 using selective-display facility.  The messages in the tagged MIME
595 format are automatically translated into a MIME compliant message when
596 exiting this mode.
597
598 Available charsets depend on Emacs version being used.  The following
599 lists the available charsets of each emacs.
600
601 Emacs18:        US-ASCII is only available.
602 NEmacs:         US-ASCII and ISO-2022-JP are available.
603 Emacs19:        US-ASCII and ISO-8859-1 are available.
604 Mule:           US-ASCII, ISO-8859-* (except for ISO-8859-6),
605                 ISO-2022-JP, ISO-2022-JP-2 and ISO-2022-INT-1 are available.
606
607 ISO-2022-JP-2 and ISO-2022-INT-1 charsets used in Mule is expected to
608 be used to represent multilingual text in intermixed manner.  Any
609 languages that has no registered charset are represented as either
610 ISO-2022-JP-2 or ISO-2022-INT-1 in Mule.
611
612 Following commands are available in addition to major mode commands:
613 \\[mime-editor/insert-text]     insert a text message.
614 \\[mime-editor/insert-file]     insert a (binary) file.
615 \\[mime-editor/insert-external] insert a reference to external body.
616 \\[mime-editor/insert-voice]    insert a voice message.
617 \\[mime-editor/insert-message]  insert a mail or news message.
618 \\[mime-editor/insert-mail]     insert a mail message.
619 \\[mime-editor/insert-signature]        insert a signature file at end.
620 \\[mime-editor/insert-tag]      insert a new MIME tag.
621 \\[mime-editor/enclose-alternative-region]      enclose as multipart/alternative.
622 \\[mime-editor/enclose-parallel-region] enclose as multipart/parallel.
623 \\[mime-editor/enclose-mixed-region]    enclose as multipart/mixed.
624 \\[mime-editor/enclose-digest-region]   enclose as multipart/digest.
625 \\[mime-editor/preview-message] preview editing MIME message.
626 \\[mime-editor/exit]    exit and translate into a MIME compliant message.
627 \\[mime-editor/maybe-translate] exit, translate and run the original command.
628 \\[mime-editor/help]    show this help.
629
630 Additional commands are available in some major modes:
631 C-c C-c         exit, translate and run the original command.
632 C-c C-s         exit, translate and run the original command.
633
634 The following is a message example written in the tagged MIME format.
635 TABs at the beginning of the line are not a part of the message:
636
637         This is a conventional plain text.  It should be translated
638         into text/plain.
639         --[[text/plain]]
640         This is also a plain text.  But, it is explicitly specified as
641         is.
642         --[[text/plain; charset=ISO-2022-JP]]
643         \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
644         \e$B%H$G$9\e(B.
645         --[[text/richtext]]
646         <center>This is a richtext.</center>
647         --[[image/gif][base64]]^M...image encoded in base64 here...
648         --[[audio/basic][base64]]^M...audio encoded in base64 here...
649
650 User customizable variables (not documented all of them):
651  mime-prefix
652     Specifies a key prefix for MIME minor mode commands.
653
654  mime-signature-file
655     Specifies a signature file to be included as part of a multipart
656     message.
657
658  mime-ignore-preceding-spaces
659     Preceding white spaces in a message body are ignored if non-nil.
660
661  mime-ignore-trailing-spaces
662     Trailing white spaces in a message body are ignored if non-nil.
663
664  mime-auto-fill-header
665     Fill header fields that contain encoded-words if non-nil.
666
667  mime-auto-hide-body
668     Hide a non-textual body message encoded in base64 after insertion
669     if non-nil.
670
671  mime-body-charset-chooser
672     Specifies a function to identify charset and encoding of a text in
673     a given region.  The value is a form of (CHARSET . ENCODING),
674     where ENCODING must be a full name, such as base64.
675
676  mime-string-encoder
677     Specifies a function to encode a string for given encoding method.
678     The method is a form of (CHARSET . ENCODING).
679
680  mime-voice-recorder
681     Specifies a function to record a voice message and return a buffer
682     that contains it.  The function mime-voice-recorder-for-sun is for
683     Sun SparcStations.
684
685  mime/editor-mode-hook
686     Turning on MIME mode calls the value of mime/editor-mode-hook, if
687     it is non-nil.
688
689  mime-editor/translate-hook
690     The value of mime-editor/translate-hook is called just before translating
691     the tagged MIME format into a MIME compliant message if it is
692     non-nil.  If the hook call the function mime-editor/insert-signature,
693     the signature file will be inserted automatically.
694
695  mime-editor/exit-hook
696     Turning off MIME mode calls the value of mime-editor/exit-hook, if it is
697     non-nil."
698   (interactive)
699   (if mime/editor-mode-flag
700       (error "You are already editing a MIME message.")
701     (setq mime/editor-mode-flag t)
702     ;; Remember old key bindings.
703     (make-local-variable 'mime/editor-mode-old-local-map)
704     (setq mime/editor-mode-old-local-map (current-local-map))
705     ;; Add MIME commands to current local map.
706     (use-local-map (copy-keymap (current-local-map)))
707     (if (not (lookup-key (current-local-map) mime-prefix))
708         (define-key (current-local-map) mime-prefix (make-sparse-keymap)))
709     (mime-editor/define-keymap (lookup-key (current-local-map) mime-prefix))
710     
711     ;; Define menu.  Menus for other emacs implementations are
712     ;; welcome.
713     ;;  modified by Pekka Marjola <pema@niksula.hut.fi>
714     ;;          1995/9/5 (c.f. [tm-eng:69])
715     (cond ((string-match "XEmacs\\|Lucid" emacs-version)
716            (mime-editor/define-menu-for-xemacs))
717           ((string-match "^19\\." emacs-version)
718            (mime-editor/define-menu-for-emacs19)
719            ))
720     ;; end
721     
722     ;; Remember old selective-display.
723     (make-local-variable 'mime/editor-mode-old-selective-display)
724     (setq mime/editor-mode-old-selective-display selective-display)
725     (setq selective-display t)
726     ;; I don't care about saving these.
727     (setq paragraph-start
728           (concat mime-editor/single-part-tag-regexp "\\|" paragraph-start))
729     (setq paragraph-separate
730           (concat mime-editor/single-part-tag-regexp "\\|" paragraph-separate))
731     (run-hooks 'mime/editor-mode-hook)
732     (message
733      (substitute-command-keys
734       "Type \\[mime-editor/exit] to exit MIME mode, and type \\[mime-editor/help] to get help."))
735     ))
736
737 ;;;###autoload
738 (defalias 'edit-mime 'mime/editor-mode)         ; for convenience
739 (defalias 'mime-mode 'mime/editor-mode)         ; for convenience
740
741 (defun mime-editor/exit (&optional nomime no-error)
742   "Translate the tagged MIME message into a MIME compliant message.
743 With no argument encode a message in the buffer into MIME, otherwise
744 just return to previous mode."
745   (interactive "P")
746   (if (not mime/editor-mode-flag)
747       (if (null no-error)
748           (error "You aren't editing a MIME message.")
749         )
750     (if (not nomime)
751         (progn
752           (run-hooks 'mime-editor/translate-hook)
753           (mime-editor/translate-buffer)))
754     ;; Restore previous state.
755     (setq mime/editor-mode-flag nil)
756     (use-local-map mime/editor-mode-old-local-map)
757     
758     ;; modified by Pekka Marjola <pema@niksula.hut.fi>
759     ;;  1995/9/5 (c.f. [tm-eng:69])
760     (if (string-match "XEmacs\\|Lucid" emacs-version)
761         (progn
762           (delete-menu-item (list mime-editor/menu-title))
763                                         ; should rather be const
764           ;; (while mime-editor/xemacs-old-bindings
765           ;;   (eval (pop mime-editor/xemacs-old-bindings)))
766           (local-unset-key mime-prefix)))
767     ;; end
768     
769     (setq selective-display mime/editor-mode-old-selective-display)
770     (set-buffer-modified-p (buffer-modified-p))
771     (run-hooks 'mime-editor/exit-hook)
772     (message "Exit MIME editor mode.")
773     ))
774
775 (defun mime-editor/maybe-translate ()
776   (interactive)
777   (mime-editor/exit nil t)
778   (call-interactively 'mime-editor/maybe-split-and-send)
779   )
780
781 (defun mime-editor/help ()
782   "Show help message about MIME mode."
783   (interactive)
784   (with-output-to-temp-buffer "*Help*"
785     (princ "MIME editor mode:\n")
786     (princ (documentation 'mime/editor-mode))
787     (print-help-return-message)))
788
789 (defun mime-editor/insert-text ()
790   "Insert a text message.
791 Charset is automatically obtained from the mime-body-charset-chooser."
792   (interactive)
793   (if (and (mime-editor/insert-tag "text" nil nil)
794            (looking-at mime-editor/single-part-tag-regexp))
795       (progn
796         ;; Make a space between the following message.
797         (insert "\n")
798         (forward-char -1)
799         )))
800
801 (defun mime-editor/insert-file (file)
802   "Insert a message from a file."
803   (interactive "fInsert file as MIME message: ")
804   (let*  ((guess (mime-find-file-type file))
805           (pritype (nth 0 guess))
806           (subtype (nth 1 guess))
807           (parameters (nth 2 guess))
808           (default (nth 3 guess))       ;Guess encoding from its file name.
809           (fields (nth 4 guess))
810           (encoding
811            (if (not (interactive-p))
812                default
813              (completing-read
814               (concat "What transfer encoding"
815                       (if default
816                           (concat " (default "
817                                   (if (string-equal default "")
818                                       "\"\""
819                                     default)
820                                   ")"
821                                   ))
822                       ": ")
823               mime-encoding-method-alist nil t nil))))
824     (if (string-equal encoding "")
825         (setq encoding default))
826     (if (or (consp parameters) (consp fields))
827         (let ((rest parameters) cell attribute value)
828           (setq parameters "")
829           (while rest
830             (setq cell (car rest))
831             (setq attribute (car cell))
832             (setq value (cdr cell))
833             (if (eq value 'file)
834                 (setq value (file-name-nondirectory file))
835               )
836             (setq parameters (concat parameters "; " attribute "=" value))
837             (setq rest (cdr rest))
838             )
839           (setq rest fields)
840           (while rest
841             (setq cell (car rest))
842             (setq attribute (car cell))
843             (setq value (cdr cell))
844             (if (eq value 'file)
845                 (setq value (file-name-nondirectory file))
846               )
847             (setq parameters (concat parameters "\n" attribute ": " value))
848             (setq rest (cdr rest))
849             )
850           ))
851     (mime-editor/insert-tag pritype subtype parameters)
852     (mime-editor/insert-binary-file file encoding)
853     ))
854
855 (defun mime-editor/insert-external ()
856   "Insert a reference to external body."
857   (interactive)
858   (mime-editor/insert-tag "message" "external-body" nil ";\n\t")
859   ;;(forward-char -1)
860   ;;(insert "Content-Description: " (read-string "Content-Description: ") "\n")
861   ;;(forward-line 1)
862   (let* ((pritype (mime-prompt-for-type))
863          (subtype (mime-prompt-for-subtype pritype))
864          (parameters (mime-prompt-for-parameters pritype subtype ";\n\t")))
865     (and pritype
866          subtype
867          (insert "Content-Type: "
868                  pritype "/" subtype (or parameters "") "\n")))
869   (if (and (not (eobp))
870            (not (looking-at mime-editor/single-part-tag-regexp)))
871       (insert (mime-make-text-tag) "\n")))
872
873 (defun mime-editor/insert-voice ()
874   "Insert a voice message."
875   (interactive)
876   (mime-editor/insert-tag "audio" "basic" nil)
877   (let ((buffer (funcall mime-voice-recorder)))
878     (unwind-protect
879         (mime-editor/insert-binary-buffer buffer "base64")
880       (kill-buffer buffer)
881       )))
882
883 (defun mime-editor/insert-signature (&optional arg)
884   "Insert a signature file specified by mime-signature-file."
885   (interactive "P")
886   (let ((signature
887          (expand-file-name
888           (if arg
889               (read-file-name "Insert your signature: "
890                               (concat signature-file-name "-")
891                               signature-file-name
892                               nil)
893             (signature/get-signature-file-name))))
894         )
895     (if signature-insert-at-eof
896         (goto-char (point-max))
897       )
898     (apply (function mime-editor/insert-tag)
899            (mime-find-file-type signature))
900     (if (file-readable-p signature)
901         (progn
902           (goto-char (point-max))
903           (if (not (bolp))
904               (insert "\n"))
905           (delete-blank-lines)
906           (insert-file-contents signature)
907           (set-buffer-modified-p (buffer-modified-p))
908                                         ; force mode line update
909           ))))
910
911 \f
912 ;; Insert a new tag around a point.
913
914 (defun mime-editor/insert-tag (&optional pritype subtype parameters delimiter)
915   "Insert new MIME tag and return a list of PRITYPE, SUBTYPE, and PARAMETERS.
916 If nothing is inserted, return nil."
917   (interactive)
918   (let ((oldtag nil)
919         (newtag nil)
920         (current (point))
921         exist-prev-tag exist-next-tag)
922     (setq pritype
923           (or pritype
924               (mime-prompt-for-type)))
925     (setq subtype
926           (or subtype
927               (mime-prompt-for-subtype pritype)))
928     (setq parameters
929           (or parameters
930               (mime-prompt-for-parameters pritype subtype delimiter)))
931     ;; Make a new MIME tag.
932     (setq newtag (mime-make-tag pritype subtype parameters))
933     ;; Find an current MIME tag.
934     (setq oldtag
935           (save-excursion
936             (if (mime-editor/goto-tag)
937                 (progn
938                   (if (eq current (match-beginning 0))
939                       (setq exist-next-tag t)
940                     (setq exist-prev-tag t)
941                     )
942                   (buffer-substring (match-beginning 0) (match-end 0))
943                   )
944               ;; Assume content type is 'text/plan'.
945               (mime-make-tag "text" "plain")
946               )))
947     ;; We are only interested in TEXT.
948     (if (and oldtag
949              (not (mime-test-content-type
950                    (mime-editor/get-contype oldtag) "text")))
951         (setq oldtag nil))
952     (cond (exist-prev-tag (insert "\n"))
953           (exist-next-tag (save-excursion
954                             (insert "\n")
955                             )))
956     ;; (beginning-of-line)
957     ;; (cond ((and oldtag                  ;Text
958     ;;             (not (eobp))
959     ;;             (save-excursion
960     ;;               (forward-line -1)
961     ;;               (looking-at mime-editor/beginning-tag-regexp)
962     ;;               )
963     ;;             (or mime-ignore-same-text-tag
964     ;;                 (not (string-equal oldtag newtag))))
965     ;;        ;; If point is at the next of current tag, move to the
966     ;;        ;; beginning of the tag to disable insertion of extra tag.
967     ;;        (forward-line -1))
968     ;;       ((and oldtag                  ;Text
969     ;;             (not (eobp))
970     ;;             (not (looking-at mime-editor/tag-regexp))
971     ;;             (or mime-ignore-same-text-tag
972     ;;                 (not (string-equal oldtag newtag))))
973     ;;        ;; Copy current tag to break a text into two.
974     ;;        (save-excursion
975     ;;          (insert oldtag "\n")))
976     ;;       ((and (null oldtag)           ;Not text
977     ;;             (not (looking-at mime-editor/tag-regexp)))
978     ;;        ;; Adjust insertion point.  In the middle of text, it is
979     ;;        ;; okay to break the text into two.  However, it should not
980     ;;        ;; be broken into two, if otherwise.
981     ;;        (goto-char (mime-editor/content-end))
982     ;;        (if (eolp)
983     ;;            (forward-line 1))
984     ;;        (if (not (bolp))
985     ;;            (insert "\n"))
986     ;;        ))
987     (if (not (bolp))
988         (if exist-prev-tag
989             (forward-line 1)
990           (insert "\n")
991           ))
992     ;; Make a new tag.
993     (if (or (not oldtag)                ;Not text
994             (or mime-ignore-same-text-tag
995                 (not (string-equal oldtag newtag))))
996         (progn
997           ;; Mark the beginning of the tag for convenience.
998           (push-mark (point) 'nomsg)
999           (insert newtag "\n")
1000           (list pritype subtype parameters) ;New tag is created.
1001           )
1002       ;; Restore previous point.
1003       (goto-char current)
1004       nil                               ;Nothing is created.
1005       )
1006     ))
1007
1008 ;; Insert the binary content after MIME tag.
1009 ;;      modified by MORITA Masahiro <hiro@isl.ntt.JP>
1010 ;;      for x-uue
1011 (defun mime-editor/insert-binary-file (file &optional encoding)
1012   "Insert binary FILE at point.
1013 Optional argument ENCODING specifies an encoding method such as base64."
1014   (let ((tmpbuf (get-buffer-create " *MIME insert*")))
1015     (save-excursion
1016       (set-buffer tmpbuf)
1017       (erase-buffer)
1018       (let ((mc-flag nil)               ;Mule
1019             (file-coding-system-for-read
1020              (if (featurep 'mule) *noconv*))
1021             (kanji-flag nil)            ;NEmacs
1022             (emx-binary-mode t)         ;Stop CRLF to LF conversion in OS/2
1023             )
1024         (let (jka-compr-compression-info-list
1025               jam-zcat-filename-list)
1026           (insert-file-contents file))))
1027     (prog1
1028         (if (and (stringp encoding)
1029                  (string-equal (downcase encoding) "x-uue"))
1030             (progn
1031               (require 'mel-u)
1032               (let ((uuencode-external-encoder
1033                      (cons (car uuencode-external-encoder)
1034                            (list (file-name-nondirectory file))
1035                            )))
1036                 (mime-editor/insert-binary-buffer tmpbuf encoding)
1037                 ))
1038           (mime-editor/insert-binary-buffer tmpbuf encoding))
1039       (kill-buffer tmpbuf))))
1040
1041 ;; Insert the binary content after MIME tag.
1042 ;;      modified by MORITA Masahiro <hiro@isl.ntt.JP>
1043 ;;      for x-uue
1044 (defun mime-editor/insert-binary-buffer (buffer &optional encoding)
1045   "Insert binary BUFFER at point.
1046 Optional argument ENCODING specifies an encoding method such as base64."
1047   (let* ((tagend (1- (point)))          ;End of the tag
1048          (hide-p (and mime-auto-hide-body
1049                       (stringp encoding)
1050                       (let ((en (downcase encoding)))
1051                         (or (string-equal en "base64")
1052                             (string-equal en "x-uue")
1053                             ))))
1054          )
1055     (save-restriction
1056       (narrow-to-region (1- (point)) (point))
1057       (let ((start (point))
1058             (emx-binary-mode t))        ;Stop LF to CRLF conversion in OS/2
1059         (insert-buffer-substring buffer)
1060         ;; Encode binary message if necessary.
1061         (if encoding
1062             (mime-encode-region encoding start (point-max))))
1063       (if hide-p
1064           (progn
1065             (mime-flag-region (point-min) (1- (point-max)) ?\^M)
1066             (goto-char (point-max)))
1067         ))
1068     ;; Define encoding even if it is 7bit.
1069     (if (stringp encoding)
1070         (save-excursion
1071           (goto-char tagend)            ;Make sure which line the tag is on.
1072           (mime-editor/define-encoding encoding)))
1073     ))
1074
1075 \f
1076 ;; Commands work on a current message flagment.
1077
1078 (defun mime-editor/goto-tag ()
1079   "Search for the beginning of the tagged MIME message."
1080   (let ((current (point)) multipart)
1081     (if (looking-at mime-editor/tag-regexp)
1082         t
1083       ;; At first, go to the end.
1084       (cond ((re-search-forward mime-editor/beginning-tag-regexp nil t)
1085              (goto-char (match-beginning 0)) ;For multiline tag
1086              (forward-line -1)
1087              (end-of-line)
1088              )
1089             (t
1090              (goto-char (point-max))
1091              ))
1092       ;; Then search for the beginning. 
1093       (re-search-backward mime-editor/end-tag-regexp nil t)
1094       (beginning-of-line)
1095       (or (looking-at mime-editor/beginning-tag-regexp)
1096           ;; Restore previous point.
1097           (progn
1098             (goto-char current)
1099             nil
1100             ))
1101       )))
1102
1103 (defun mime-editor/content-beginning ()
1104   "Return the point of the beginning of content."
1105   (save-excursion
1106     (let ((beg (save-excursion
1107                  (beginning-of-line) (point))))
1108       (if (mime-editor/goto-tag)
1109           (let ((top (point)))
1110             (goto-char (match-end 0))
1111             (if (and (= beg top)
1112                      (= (following-char) ?\^M))
1113                 (point)
1114               (forward-line 1)
1115               (point)))
1116         ;; Default text/plain tag.
1117         (goto-char (point-min))
1118         (re-search-forward
1119          (concat "\n" (regexp-quote mail-header-separator)
1120                  (if mime-ignore-preceding-spaces
1121                      "[ \t\n]*\n" "\n")) nil 'move)
1122         (point))
1123       )))
1124
1125 (defun mime-editor/content-end ()
1126   "Return the point of the end of content."
1127   (save-excursion
1128     (let ((beg (save-excursion
1129                  (beginning-of-line) (point))))
1130       (if (mime-editor/goto-tag)
1131           (let ((top (point)))
1132             (goto-char (match-end 0))
1133             (if (and (= beg top)        ;Must be on the same line.
1134                      (= (following-char) ?\^M))
1135                 (progn
1136                   (end-of-line)
1137                   (point))
1138               ;; Move to the end of this text.
1139               (if (re-search-forward mime-editor/tag-regexp nil 'move)
1140                   ;; Don't forget a multiline tag.
1141                   (goto-char (match-beginning 0)))
1142               (point)
1143               ))
1144         ;; Assume the message begins with text/plain.
1145         (goto-char (mime-editor/content-beginning))
1146         (if (re-search-forward mime-editor/tag-regexp nil 'move)
1147             ;; Don't forget a multiline tag.
1148             (goto-char (match-beginning 0)))
1149         (point))
1150       )))
1151
1152 (defun mime-editor/define-charset (charset)
1153   "Set charset of current tag to CHARSET."
1154   (save-excursion
1155     (if (mime-editor/goto-tag)
1156         (let ((tag (buffer-substring (match-beginning 0) (match-end 0))))
1157           (delete-region (match-beginning 0) (match-end 0))
1158           (insert
1159            (mime-create-tag (mime-set-parameter
1160                              (mime-editor/get-contype tag) "charset" charset)
1161                             (mime-editor/get-encoding tag))))
1162       )))
1163
1164 (defun mime-editor/define-encoding (encoding)
1165   "Set encoding of current tag to ENCODING."
1166   (save-excursion
1167     (if (mime-editor/goto-tag)
1168         (let ((tag (buffer-substring (match-beginning 0) (match-end 0))))
1169           (delete-region (match-beginning 0) (match-end 0))
1170           (insert (mime-create-tag (mime-editor/get-contype tag) encoding)))
1171       )))
1172
1173 (defun mime-editor/choose-charset ()
1174   "Choose charset of a text following current point."
1175   (save-excursion
1176     (let* ((beg (point))
1177            (end (mime-editor/content-end)))
1178       (car (funcall mime-body-charset-chooser beg end)))))
1179
1180 (defun mime-editor/choose-encoding ()
1181   "Choose encoding of a text following current point."
1182   (save-excursion
1183     (let* ((beg (point))
1184            (end (mime-editor/content-end)))
1185       (cdr (funcall mime-body-charset-chooser beg end)))))
1186
1187 (defun mime-make-text-tag (&optional subtype)
1188   "Make a tag for a text after current point.
1189 Subtype of text type can be specified by an optional argument SUBTYPE.
1190 Otherwise, it is obtained from mime-content-types."
1191   (let* ((pritype "text")
1192          (subtype (or subtype
1193                       (car (car (cdr (assoc pritype mime-content-types)))))))
1194     ;; Charset should be defined later.
1195     (mime-make-tag pritype subtype)))
1196
1197 \f
1198 ;; Tag handling functions
1199
1200 (defun mime-make-tag (pritype subtype &optional parameters encoding)
1201   "Make a tag of MIME message of PRITYPE, SUBTYPE and optional PARAMETERS."
1202   (mime-create-tag (concat (or pritype "") "/" (or subtype "")
1203                            (or parameters ""))
1204                    encoding))
1205
1206 (defun mime-create-tag (contype &optional encoding)
1207   "Make a tag with CONTENT-TYPE and optional ENCODING."
1208   (format (if encoding mime-tag-format-with-encoding mime-tag-format)
1209           contype encoding))
1210
1211 (defun mime-editor/get-contype (tag)
1212   "Return Content-Type (including parameters) of TAG."
1213   (and (stringp tag)
1214        (or (string-match mime-editor/single-part-tag-regexp tag)
1215            (string-match mime-editor/multipart-beginning-regexp tag)
1216            (string-match mime-editor/multipart-end-regexp tag)
1217            )
1218        (substring tag (match-beginning 1) (match-end 1))
1219        ))
1220
1221 (defun mime-editor/get-encoding (tag)
1222   "Return encoding of TAG."
1223   (and (stringp tag)
1224        (string-match mime-editor/single-part-tag-regexp tag)
1225        (match-beginning 3)
1226        (not (= (match-beginning 3) (match-end 3)))
1227        (substring tag (match-beginning 3) (match-end 3))))
1228
1229 (defun mime-get-parameter (contype parameter)
1230   "For given CONTYPE return value for PARAMETER.
1231 Nil if no such parameter."
1232   (if (string-match
1233        (concat
1234         ";[ \t\n]*"
1235         (regexp-quote parameter)
1236         "[ \t\n]*=[ \t\n]*\\([^\" \t\n;]*\\|\"[^\"]*\"\\)\\([ \t\n]*;\\|$\\)")
1237        contype)
1238       (substring contype (match-beginning 1) (match-end 1))
1239     nil                                 ;No such parameter
1240     ))
1241
1242 (defun mime-set-parameter (contype parameter value)
1243   "For given CONTYPE set PARAMETER to VALUE."
1244   (if (string-match
1245        (concat
1246         ";[ \t\n]*\\("
1247         (regexp-quote parameter)
1248         "[ \t\n]*=[ \t\n]*\\([^\" \t\n;]*\\|\"[^\"]*\"\\)\\)[ \t\n]*\\(;\\|$\\)")
1249        contype)
1250       ;; Change value
1251       (concat (substring contype 0 (match-beginning 1))
1252               parameter "=" value
1253               (substring contype (match-end 1)))
1254     (concat contype "; " parameter "=" value)))
1255
1256 (defun mime-strip-parameters (contype)
1257   "Return primary content-type and subtype without parameters for CONTYPE."
1258   (if (string-match "^[ \t]*\\([^; \t\n]*\\)" contype)
1259       (substring contype (match-beginning 1) (match-end 1)) nil))
1260
1261 (defun mime-test-content-type (contype type &optional subtype)
1262   "Test if CONTYPE is a TYPE and an optional SUBTYPE."
1263   (and (stringp contype)
1264        (stringp type)
1265        (string-match
1266         (concat "^[ \t]*" (downcase type) "/" (downcase (or subtype "")))
1267         (downcase contype))))
1268
1269 \f
1270 ;; Basic functions
1271
1272 (defun mime-find-file-type (file)
1273   "Guess Content-Type, subtype, and parameters from FILE."
1274   (let ((guess nil)
1275         (guesses mime-file-types))
1276     (while (and (not guess) guesses)
1277       (if (string-match (car (car guesses)) file)
1278           (setq guess (cdr (car guesses))))
1279       (setq guesses (cdr guesses)))
1280     guess
1281     ))
1282
1283 (defun mime-prompt-for-type ()
1284   "Ask for Content-type."
1285   (let ((type ""))
1286     ;; Repeat until primary content type is specified.
1287     (while (string-equal type "")
1288       (setq type
1289             (completing-read "What content type: "
1290                              mime-content-types
1291                              nil
1292                              'require-match ;Type must be specified.
1293                              nil
1294                              ))
1295       (if (string-equal type "")
1296           (progn
1297             (message "Content type is required.")
1298             (beep)
1299             (sit-for 1)
1300             ))
1301       )
1302     type
1303     ))
1304
1305 (defun mime-prompt-for-subtype (pritype)
1306   "Ask for Content-type subtype of Content-Type PRITYPE."
1307   (let* ((default (car (car (cdr (assoc pritype mime-content-types)))))
1308          (answer
1309           (completing-read
1310            (if default
1311                (concat
1312                 "What content subtype: (default " default ") ")
1313              "What content subtype: ")
1314            (cdr (assoc pritype mime-content-types))
1315            nil
1316            'require-match               ;Subtype must be specified.
1317            nil
1318            )))
1319     (if (string-equal answer "") default answer)))
1320
1321 (defun mime-prompt-for-parameters (pritype subtype &optional delimiter)
1322   "Ask for Content-type parameters of Content-Type PRITYPE and SUBTYPE.
1323 Optional DELIMITER specifies parameter delimiter (';' by default)."
1324   (let* ((delimiter (or delimiter "; "))
1325          (parameters
1326           (mapconcat
1327            (function identity)
1328            (delq nil
1329                  (mime-prompt-for-parameters-1
1330                   (cdr (assoc subtype
1331                               (cdr (assoc pritype mime-content-types))))))
1332            delimiter
1333            )))
1334     (if (and (stringp parameters)
1335              (not (string-equal parameters "")))
1336         (concat delimiter parameters)
1337       ""                                ;"" if no parameters
1338       )))
1339
1340 (defun mime-prompt-for-parameters-1 (optlist)
1341   (apply (function append)
1342          (mapcar (function mime-prompt-for-parameter) optlist)))
1343
1344 (defun mime-prompt-for-parameter (parameter)
1345   "Ask for PARAMETER.
1346 Parameter must be '(PROMPT CHOICE1 (CHOISE2 ...))."
1347   (let* ((prompt (car parameter))
1348          (choices (mapcar (function
1349                            (lambda (e)
1350                              (if (consp e) e (list e))))
1351                           (cdr parameter)))
1352          (default (car (car choices)))
1353          (answer nil))
1354     (if choices
1355         (progn
1356           (setq answer
1357                 (completing-read
1358                  (concat "What " prompt
1359                          ": (default "
1360                          (if (string-equal default "") "\"\"" default)
1361                          ") ")
1362                  choices nil nil ""))
1363           ;; If nothing is selected, use default.
1364           (if (string-equal answer "")
1365               (setq answer default)))
1366       (setq answer
1367             (read-string (concat "What " prompt ": "))))
1368     (cons (if (and answer
1369                    (not (string-equal answer "")))
1370               (concat prompt "="
1371                       ;; Note: control characters ignored!
1372                       (if (string-match mime-tspecials-regexp answer)
1373                           (concat "\"" answer "\"") answer)))
1374           (mime-prompt-for-parameters-1 (cdr (assoc answer (cdr parameter)))))
1375     ))
1376
1377 (defun mime-encode-string (encoding string)
1378   "Using ENCODING encode a STRING.
1379 If the STRING is too long, the encoded string may be broken into
1380 several lines."
1381   (save-excursion
1382     (set-buffer (get-buffer-create " *MIME encoding*"))
1383     (erase-buffer)
1384     (insert string)
1385     (mime-encode-region encoding (point-min) (point-max))
1386     (prog1
1387         (buffer-substring (point-min) (point-max))
1388       (kill-buffer (current-buffer)))))
1389
1390 (defun mime-decode-string (encoding string)
1391   "Using ENCODING decode a STRING."
1392   (save-excursion
1393     (set-buffer (get-buffer-create " *MIME decoding*"))
1394     (erase-buffer)
1395     (insert string)
1396     (mime-decode-region encoding (point-min) (point-max))
1397     (prog1
1398         (buffer-substring (point-min) (point-max))
1399       (kill-buffer (current-buffer)))))
1400
1401 (defun mime-flag-region (from to flag)
1402   "Hides or shows lines from FROM to TO, according to FLAG.
1403 If FLAG is `\\n' (newline character) then text is shown,
1404 while if FLAG is `\\^M' (control-M) the text is hidden."
1405   (let ((buffer-read-only nil)          ;Okay even if write protected.
1406         (modp (buffer-modified-p)))
1407     (unwind-protect
1408         (subst-char-in-region from to
1409                               (if (= flag ?\n) ?\^M ?\n)
1410                               flag t)
1411       (set-buffer-modified-p modp))))
1412
1413 \f
1414 ;;; @ Translate the tagged MIME messages into a MIME compliant message.
1415 ;;;
1416
1417 (defun mime-editor/translate-buffer ()
1418   "Encode the tagged MIME message in current buffer in MIME compliant message."
1419   (interactive)
1420   (save-excursion
1421     (if (catch 'mime-editor/error
1422           (mime/encode-message-header)
1423           (mime-editor/translate-body)
1424           (mime-editor/pgp-processing)
1425           )
1426         (error "Translation error!")
1427       )))
1428
1429 (defun mime-editor/find-inmost ()
1430   (goto-char (point-min))
1431   (if (re-search-forward mime-editor/multipart-beginning-regexp nil t)
1432       (let ((bb (match-beginning 0))
1433             (be (match-end 0))
1434             (type (buffer-substring (match-beginning 1)(match-end 1)))
1435             end-exp eb ee)
1436         (setq end-exp (format "^--}-<<%s>>\n" type))
1437         (widen)
1438         (if (re-search-forward end-exp nil t)
1439             (progn
1440               (setq eb (match-beginning 0))
1441               (setq ee (match-end 0))
1442               )
1443           (setq eb (point-max))
1444           (setq ee (point-max))
1445           )
1446         (narrow-to-region be eb)
1447         (goto-char be)
1448         (if (re-search-forward mime-editor/multipart-beginning-regexp nil t)
1449             (let (ret)
1450               (narrow-to-region (match-beginning 0)(point-max))
1451               (mime-editor/find-inmost)
1452               )
1453           (widen)
1454           ;;(delete-region eb ee)
1455           (list type bb be eb)
1456           ))))
1457
1458 (defun mime-editor/process-multipart-1 (boundary)
1459   (let ((ret (mime-editor/find-inmost)))
1460     (if ret
1461         (let ((type (car ret))
1462               (bb (nth 1 ret))(be (nth 2 ret))
1463               (eb (nth 3 ret))
1464               )
1465           (narrow-to-region bb eb)
1466           (delete-region bb be)
1467           (setq bb (point-min))
1468           (setq eb (point-max))
1469           (widen)
1470           (goto-char eb)
1471           (if (looking-at mime-editor/multipart-end-regexp)
1472               (let ((beg (match-beginning 0))
1473                     (end (match-end 0))
1474                     )
1475                 (delete-region beg end)
1476                 (if (and (not (looking-at mime-editor/single-part-tag-regexp))
1477                          (not (eobp)))
1478                     (insert (concat (mime-make-text-tag) "\n"))
1479                   )))
1480           (cond ((string= type "signed")
1481                  (cond ((eq mime-editor/signing-type 'pgp-elkins)
1482                         (message "Sorry, it is not supported yet")
1483                         )
1484                        ((eq mime-editor/signing-type 'pgp-kazu)
1485                         (mime-editor/process-pgp-kazu 'mc-sign
1486                                                       bb eb boundary)
1487                         )
1488                        ))
1489                 ((string= type "encrypted")
1490                  (cond ((eq mime-editor/signing-type 'pgp-elkins)
1491                         (message "Sorry, it is not supported yet")
1492                         )
1493                        ((eq mime-editor/signing-type 'pgp-kazu)
1494                         (mime-editor/process-pgp-kazu 'mc-encrypt
1495                                                       bb eb boundary)
1496                         )))
1497                 (t
1498                  (setq boundary
1499                        (nth 2 (mime-editor/translate-region bb eb
1500                                                             boundary t)))
1501                  (goto-char bb)
1502                  (insert
1503                   (format "--[[multipart/%s;
1504  boundary=\"%s\"][7bit]]\n"
1505                           type boundary))
1506                  ))
1507           boundary))))
1508
1509 (defun mime-editor/process-pgp-kazu (type beg end boundary)
1510   (save-restriction
1511     (narrow-to-region beg end)
1512     (let* ((ret
1513             (mime-editor/translate-region beg end boundary))
1514            (ctype    (car ret))
1515            (encoding (nth 1 ret))
1516            (parts    (nth 3 ret))
1517            )
1518       (goto-char beg)
1519       (insert (format "Content-Type: %s\n" ctype))
1520       (if encoding
1521           (insert (format "Content-Transfer-Encoding: %s\n" encoding))
1522         )
1523       (insert "\n")
1524       (if (null
1525            (let ((mail-header-separator ""))
1526              (call-interactively type)
1527              ))
1528           (throw 'mime-editor/error 'pgp-error)
1529         )
1530       (goto-char beg)
1531       (insert
1532        "--[[application/pgp; format=mime][7bit]]\n")
1533       ))
1534   )
1535
1536 (defun mime-editor/translate-body ()
1537   "Encode the tagged MIME body in current buffer in MIME compliant message."
1538   (interactive)
1539   (save-excursion
1540     (let ((boundary
1541            (concat mime-multipart-boundary " " (current-time-string)))
1542           (i 1)
1543           (time (current-time-string))
1544           ret)
1545       (while (mime-editor/process-multipart-1
1546               (format "%s %s-%d" mime-multipart-boundary time i))
1547         (setq i (1+ i))
1548         )
1549       (save-restriction
1550         ;; We are interested in message body.
1551         (let* ((beg
1552                 (progn
1553                   (goto-char (point-min))
1554                   (re-search-forward
1555                    (concat "\n" (regexp-quote mail-header-separator)
1556                            (if mime-ignore-preceding-spaces
1557                                "[ \t\n]*\n" "\n")) nil 'move)
1558                   (point)))
1559                (end
1560                 (progn
1561                   (goto-char (point-max))
1562                   (and mime-ignore-trailing-spaces
1563                        (re-search-backward "[^ \t\n]\n" beg t)
1564                        (forward-char 1))
1565                   (point))))
1566           (setq ret (mime-editor/translate-region
1567                      beg end
1568                      (format "%s %s-%d" mime-multipart-boundary time i)))
1569           ))
1570       (let ((contype (car ret))         ;Content-Type
1571             (encoding (nth 1 ret))      ;Content-Transfer-Encoding
1572             )
1573         ;; Make primary MIME headers.
1574         (or (mail-position-on-field "Mime-Version")
1575             (insert mime-editor/mime-version-value))
1576         ;; Remove old Content-Type and other fields.
1577         (save-restriction
1578           (goto-char (point-min))
1579           (search-forward (concat "\n" mail-header-separator "\n") nil t)
1580           (narrow-to-region (point-min) (point))
1581           (goto-char (point-min))
1582           (mime-delete-field "Content-Type")
1583           (mime-delete-field "Content-Transfer-Encoding"))
1584         ;; Then, insert Content-Type and Content-Transfer-Encoding fields.
1585         (mail-position-on-field "Content-Type")
1586         (insert contype)
1587         (if encoding
1588             (progn
1589               (mail-position-on-field "Content-Transfer-Encoding")
1590               (insert encoding)))
1591         ))))
1592
1593 (defun mime-editor/translate-region (beg end &optional boundary multipart)
1594   (if (null boundary)
1595       (setq boundary
1596             (concat mime-multipart-boundary " " (current-time-string)))
1597     )
1598   (save-excursion
1599     (save-restriction
1600       (narrow-to-region beg end)
1601       (let ((tag nil)                   ;MIME tag
1602             (contype nil)               ;Content-Type
1603             (encoding nil)              ;Content-Transfer-Encoding
1604             (nparts 0))                 ;Number of body parts
1605         ;; Normalize the body part by inserting appropriate message
1606         ;; tags for every message contents.
1607         (mime-editor/normalize-body)
1608         ;; Counting the number of Content-Type.
1609         (goto-char (point-min))
1610         (while (re-search-forward mime-editor/single-part-tag-regexp nil t)
1611           (setq nparts (1+ nparts)))
1612         ;; Begin translation.
1613         (cond ((and (<= nparts 1)(not multipart))
1614                ;; It's a singular message.
1615                (goto-char (point-min))
1616                (while (re-search-forward
1617                        mime-editor/single-part-tag-regexp nil t)
1618                  (setq tag
1619                        (buffer-substring (match-beginning 0) (match-end 0)))
1620                  (delete-region (match-beginning 0) (1+ (match-end 0)))
1621                  (setq contype (mime-editor/get-contype tag))
1622                  (setq encoding (mime-editor/get-encoding tag))
1623                  ))
1624               (t
1625                ;; It's a multipart message.
1626                (goto-char (point-min))
1627                (while (re-search-forward
1628                        mime-editor/single-part-tag-regexp nil t)
1629                  (setq tag
1630                        (buffer-substring (match-beginning 0) (match-end 0)))
1631                  (delete-region (match-beginning 0) (match-end 0))
1632                  (setq contype (mime-editor/get-contype tag))
1633                  (setq encoding (mime-editor/get-encoding tag))
1634                  (insert "--" boundary "\n")
1635                  (insert "Content-Type: " contype "\n")
1636                  (if encoding
1637                      (insert "Content-Transfer-Encoding: " encoding "\n"))
1638                  )
1639                ;; Define Content-Type as "multipart/mixed".
1640                (setq contype
1641                      (concat "multipart/mixed;\n boundary=\"" boundary "\""))
1642                ;; Content-Transfer-Encoding must be "7bit".
1643                ;; The following encoding can be `nil', but is
1644                ;; specified as is since there is no way that a user
1645                ;; specifies it.
1646                (setq encoding "7bit")
1647                ;; Insert the trailer.
1648                (goto-char (point-max))
1649                (if multipart
1650                    (insert "--" boundary "--\n")
1651                  (insert "\n--" boundary "--\n")
1652                  )))
1653         (list contype encoding boundary nparts)
1654         ))))
1655
1656 (defun mime-editor/normalize-body ()
1657   "Normalize the body part by inserting appropriate message tags."
1658   ;; Insert the first MIME tags if necessary.
1659   (goto-char (point-min))
1660   (if (not (looking-at mime-editor/single-part-tag-regexp))
1661       (insert (mime-make-text-tag) "\n"))
1662   ;; Check each tag, and add new tag or correct it if necessary.
1663   (goto-char (point-min))
1664   (while (re-search-forward mime-editor/single-part-tag-regexp nil t)
1665     (let* ((tag (buffer-substring (match-beginning 0) (match-end 0)))
1666            (contype (mime-editor/get-contype tag))
1667            (charset (mime-get-parameter contype "charset"))
1668            (encoding (mime-editor/get-encoding tag)))
1669       ;; Remove extra whitespaces after the tag.
1670       (if (looking-at "[ \t]+$")
1671           (delete-region (match-beginning 0) (match-end 0)))
1672       (cond ((= (following-char) ?\^M)
1673              ;; It must be image, audio or video.
1674              (let ((beg (point))
1675                    (end (mime-editor/content-end)))
1676                ;; Insert explicit MIME tags after hidden messages.
1677                (forward-line 1)
1678                (if (and (not (eobp))
1679                         (not (looking-at mime-editor/single-part-tag-regexp)))
1680                    (progn
1681                      (insert (mime-make-text-tag) "\n")
1682                      (forward-line -1)  ;Process it again as text.
1683                      ))
1684                ;; Show a hidden message.  The point is not altered
1685                ;; after the conversion.
1686                (mime-flag-region beg end ?\n)))
1687             ((mime-test-content-type contype "message")
1688              ;; Content-type "message" should be sent as is.
1689              (forward-line 1))
1690             ((mime-test-content-type contype "text")
1691              ;; Define charset for text if necessary.
1692              (setq charset (or charset (mime-editor/choose-charset)))
1693              (mime-editor/define-charset charset)
1694              (if (string-equal contype "text/x-rot13-47")
1695                  (save-excursion
1696                    (forward-line)
1697                    (set-mark (point))
1698                    (goto-char (mime-editor/content-end))
1699                    (tm:caesar-region)
1700                    ))
1701              ;; Point is now on current tag.
1702              ;; Define encoding and encode text if necessary.
1703              (if (null encoding)        ;Encoding is not specified.
1704                  (let* ((encoding (mime-editor/choose-encoding))
1705                         (beg (mime-editor/content-beginning))
1706                         (end (mime-editor/content-end))
1707                         (body (buffer-substring beg end))
1708                         (encoded (funcall mime-string-encoder
1709                                           (cons charset encoding) body)))
1710                    (if (not (string-equal body encoded))
1711                        (progn
1712                          (goto-char beg)
1713                          (delete-region beg end)
1714                          (insert encoded)
1715                          (goto-char beg)))
1716                    (mime-editor/define-encoding encoding)))
1717              (forward-line 1))
1718             ((null encoding)            ;Encoding is not specified.
1719              ;; Application, image, audio, video, and any other
1720              ;; unknown content-type without encoding should be
1721              ;; encoded.
1722              (let* ((encoding "base64") ;Encode in BASE64 by default.
1723                     (beg (mime-editor/content-beginning))
1724                     (end (mime-editor/content-end))
1725                     (body (buffer-substring beg end))
1726                     (encoded (funcall mime-string-encoder
1727                                       (cons nil encoding) body)))
1728                (if (not (string-equal body encoded))
1729                    (progn
1730                      (goto-char beg)
1731                      (delete-region beg end)
1732                      (insert encoded)
1733                      (goto-char beg)))
1734                (mime-editor/define-encoding encoding))
1735              (forward-line 1))
1736             )
1737       )))
1738
1739 (defun mime-delete-field (field)
1740   "Delete header FIELD."
1741   (let ((regexp (format "^%s:[ \t]*" field)))
1742     (goto-char (point-min))
1743     (while (re-search-forward regexp nil t)
1744       (delete-region (match-beginning 0)
1745                      (progn (forward-line 1) (point)))
1746       )))
1747
1748 \f
1749 ;;;
1750 ;;; Platform dependent functions
1751 ;;;
1752
1753 ;; Emacs 18 implementations
1754
1755 (defun mime-body-charset-chooser-for-emacs18 (begin end)
1756   "Return a cons of charset and encoding of a message in a given region.
1757 Encoding name must be a canonical name, such as `base64'."
1758   '("US-ASCII" . nil)                   ;Default charset of MIME.
1759   )
1760
1761 (defun mime-string-encoder-for-emacs18 (method string)
1762   "For given METHOD that is a cons of charset and encoding, encode a STRING."
1763   (let ((charset (car method))
1764         (encoding (cdr method)))
1765     (cond ((stringp encoding)
1766            (mime-encode-string encoding string))
1767           ;; Return string without any encoding.
1768           (t string)
1769           )))
1770
1771 \f
1772 ;; Emacs 19 implementations
1773
1774 (defun mime-body-charset-chooser-for-emacs19 (begin end)
1775   "Return a cons of charset and encoding of a message in a given region.
1776 Encoding name must be a canonical name, such as `base64'.
1777 US-ASCII and ISO-8859-1 are supported on Emacs 19."
1778   (cond ((save-excursion
1779            (goto-char begin)
1780            (re-search-forward "[\200-\377]" end t))
1781          '("ISO-8859-1" . "quoted-printable"))
1782         (t
1783          '("US-ASCII" . nil))           ;Default charset of MIME.
1784         ))
1785
1786 (defun mime-string-encoder-for-emacs19 (method string)
1787   "For given METHOD that is a cons of charset and encoding, encode a STRING."
1788   (let ((charset (car method))
1789         (encoding (cdr method)))
1790     (cond ((stringp encoding)
1791            (mime-encode-string encoding string))
1792           ;; Return string without any encoding.
1793           (t string)
1794           )))
1795
1796 \f
1797 ;; NEmacs implementations
1798
1799 (defun mime-body-charset-chooser-for-nemacs (begin end)
1800   "Return a cons of charset and encoding of a message in a given region.
1801 Encoding name must be a canonical name, such as `base64'.
1802 US-ASCII and ISO-2022-JP are supported on NEmacs."
1803   (cond ((check-region-kanji-code begin end)
1804          ;; The following are safe encoding methods for use in
1805          ;; USENET News systems that strip off all ESCs.
1806          ;; '("ISO-2022-JP" . "quoted-printable")
1807          ;; '("ISO-2022-JP" . "base64")
1808          ;; The following expects transport systems are all MIME
1809          ;; compliants.  For instance, ESCs are never stripped off.
1810          '("ISO-2022-JP" . nil))
1811         (t
1812          '("US-ASCII" . nil))           ;Default charset of MIME.
1813         ))
1814
1815 (defun mime-string-encoder-for-nemacs (method string)
1816   "For given METHOD that is a cons of charset and encoding, encode a STRING.
1817 US-ASCII and ISO-2022-JP are supported on NEmacs."
1818   (let ((charset (car method))
1819         (encoding (cdr method)))
1820     (cond ((stringp encoding)
1821            (mime-encode-string encoding
1822                                ;; Convert internal (EUC) to JIS code.
1823                                (convert-string-kanji-code string 3 2)
1824                                ))
1825           ;; NEmacs can convert into ISO-2022-JP automatically,
1826           ;; but can do it myself as follows:
1827           ;;(t (convert-string-kanji-code string 3 2))
1828
1829           ;; Return string without any encoding.
1830           (t string)
1831           )))
1832
1833 \f
1834 ;; Mule implementations
1835 ;; Thanks to contributions by wkenji@flab.fujitsu.co.jp (Kenji
1836 ;; WAKAMIYA) and handa@etl.go.jp (Kenichi Handa).
1837
1838 (defun mime-body-charset-chooser-for-mule (begin end)
1839   "Return a cons of charset and encoding of a message in a given
1840 region.  Encoding name must be a canonical name, such as `base64'.
1841 US-ASCII, ISO-8859-* (except for ISO-8859-6), ISO-2022-JP,
1842 ISO-2022-JP-2 and ISO-2022-INT-1 are supported on Mule.  Either of
1843 charset ISO-2022-JP-2 or ISO-2022-INT-1 is used for multilingual text
1844 in Mule."
1845   (let ((lclist (find-charset-region begin end)))
1846     (cond ((null lclist)
1847            '("US-ASCII" . nil))         ;Default charset of MIME.
1848           ;; Multilingual capability requred.
1849           ((and (> (length lclist) 1)
1850                 (boundp '*iso-2022-int-1*))
1851            '("ISO-2022-INT-1" . nil))
1852           ((> (length lclist) 1)
1853            '("ISO-2022-JP-2" . nil))
1854           ;; Simple charset.
1855           ((memq lc-ltn1 lclist)
1856            '("ISO-8859-1" . "quoted-printable"))
1857           ((memq lc-ltn2 lclist)
1858            '("ISO-8859-2" . "quoted-printable"))
1859           ((memq lc-ltn3 lclist)
1860            '("ISO-8859-3" . "quoted-printable"))
1861           ((memq lc-ltn4 lclist)
1862            '("ISO-8859-4" . "quoted-printable"))
1863           ((memq lc-crl lclist)
1864            '("ISO-8859-5" . "quoted-printable"))
1865           ;;((memq lc-arb lclist)
1866           ;; '("ISO-8859-6" . "quoted-printable"))
1867           ((memq lc-grk lclist)
1868            '("ISO-8859-7" . "quoted-printable"))
1869           ((memq lc-hbw lclist)
1870            '("ISO-8859-8" . "quoted-printable"))
1871           ((memq lc-ltn5 lclist)
1872            '("ISO-8859-9" . "quoted-printable"))
1873           ((memq lc-jp lclist)
1874            '("ISO-2022-JP" . nil))
1875           ((memq lc-kr lclist)
1876            '("ISO-2022-KR" . nil))
1877           ;; Unknown charset.
1878           ((boundp '*iso-2022-int-1*)
1879            '("ISO-2022-INT-1" . nil))
1880           (t
1881            '("ISO-2022-JP-2" . nil))
1882           )))
1883
1884 (defun mime-string-encoder-for-mule (method string)
1885   "For given METHOD that is a cons of charset and encoding, encode a
1886 STRING.  US-ASCII, ISO-8859-* (except for ISO-8859-6), ISO-2022-JP,
1887 ISO-2022-JP-2 and ISO-2022-INT-1 are supported on Mule.  Either of
1888 charset ISO-2022-JP-2 or ISO-2022-INT-1 is used for multilingual
1889 text."
1890   (let* ((charset (car method))
1891          (encoding (cdr method))
1892          (coding-system
1893           (cdr (assoc (and (stringp charset) (upcase charset))
1894                       '(("ISO-8859-1" . *ctext*)
1895                         ("ISO-8859-2" . *iso-8859-2*)
1896                         ("ISO-8859-3" . *iso-8859-3*)
1897                         ("ISO-8859-4" . *iso-8859-4*)
1898                         ("ISO-8859-5" . *iso-8859-5*)
1899                         ;;("ISO-8859-6" . *iso-8859-6*)
1900                         ("ISO-8859-7" . *iso-8859-7*)
1901                         ("ISO-8859-8" . *iso-8859-8*)
1902                         ("ISO-8859-9" . *iso-8859-9*)
1903                         ("ISO-2022-JP" . *junet*)
1904                         ("ISO-2022-JP-2" . *iso-2022-ss2-7*)
1905                         ("ISO-2022-KR" . *korean-mail*)
1906                         ("ISO-2022-INT-1" . *iso-2022-int-1*)
1907                         )))))
1908     ;; In bilingual environment it may be unnecessary to convert the
1909     ;; coding system of the string unless transfer encoding is
1910     ;; required since such conversion may be performed by mule
1911     ;; automatically.
1912     (if (not (null coding-system))
1913         (setq string (code-convert-string string *internal* coding-system)))
1914     (if (stringp encoding)
1915         (setq string (mime-encode-string encoding string)))
1916     string
1917     ))
1918
1919 \f
1920 ;; Sun implementations
1921
1922 (defun mime-voice-recorder-for-sun ()
1923   "Record voice in a buffer using Sun audio device, and return the buffer.
1924 If the environment variable AUDIOHOST is defined, its value is used as
1925 a recording host instead of local host."
1926   (let ((buffer (get-buffer-create " *MIME audio*"))
1927         (host (getenv "AUDIOHOST")))
1928     (message "Start the recording on %s.  Type C-g to finish the recording..."
1929              (or host (system-name)))
1930     (save-excursion
1931       (set-buffer buffer)
1932       (erase-buffer)
1933       (condition-case errorcode
1934           (let ((selective-display nil) ;Disable ^M to nl translation.
1935                 (mc-flag nil)           ;Mule
1936                 (kanji-flag nil))       ;NEmacs
1937             ;; If AUDIOHOST is defined, use the value as recording host.
1938             (cond ((not (null host))
1939                    ;; Disable automatic conversion of coding system if Mule.
1940                    (if (featurep 'mule)
1941                        (define-program-coding-system nil "rsh" *noconv*))
1942                    (call-process "rsh"
1943                                  nil
1944                                  buffer
1945                                  nil
1946                                  host
1947                                  "cat"
1948                                  "/dev/audio"
1949                                  ))
1950                   (t
1951                    ;; Disable automatic conversion of coding system if Mule.
1952                    (if (featurep 'mule)
1953                        (define-program-coding-system nil "cat" *noconv*))
1954                    (call-process "cat"
1955                                  "/dev/audio"
1956                                  buffer
1957                                  nil
1958                                  ))))
1959         (quit (message "Type C-g to finish recording... done.")
1960               buffer                    ;Return the buffer
1961               )))))
1962
1963 \f
1964 ;;; @ Other useful commands.
1965 ;;;
1966
1967 ;; Message forwarding commands as content-type "message/rfc822".
1968
1969 (defun mime-editor/insert-message (&optional message)
1970   (interactive)
1971   (let ((inserter (assoc-value major-mode mime-editor/message-inserter-alist)))
1972     (if (and inserter (fboundp inserter))
1973         (progn
1974           (mime-editor/insert-tag "message" "rfc822")
1975           (funcall inserter message)
1976           )
1977       (message "Sorry, I don't have message inserter for your MUA.")
1978       )))
1979
1980 (defun mime-editor/insert-mail (&optional message)
1981   (interactive)
1982   (let ((inserter (assoc-value major-mode mime-editor/mail-inserter-alist)))
1983     (if (and inserter (fboundp inserter))
1984         (progn
1985           (mime-editor/insert-tag "message" "rfc822")
1986           (funcall inserter message)
1987           )
1988       (message "Sorry, I don't have mail inserter for your MUA.")
1989       )))
1990
1991 (defun mime-editor/inserted-message-filter ()
1992   (save-excursion
1993     (save-restriction
1994       (let ((header-start (point))
1995             (case-fold-search t)
1996             beg end)
1997         ;; for Emacs 18
1998         ;; (if (re-search-forward "^$" (marker-position (mark-marker)))
1999         (if (re-search-forward "^$" (mark t))
2000             (narrow-to-region header-start (match-beginning 0))
2001           )
2002         (goto-char header-start)
2003         (while (and (re-search-forward
2004                      mime-editor/yank-ignored-field-regexp nil t)
2005                     (setq beg (match-beginning 0))
2006                     (setq end (1+ (rfc822/field-end)))
2007                     )
2008           (delete-region beg end)
2009           )
2010         ))))
2011
2012
2013 ;;; @ multipart enclosure
2014 ;;;
2015
2016 (defun mime-editor/enclose-region (type beg end)
2017   (save-excursion
2018     (goto-char beg)
2019     (let ((current (point))
2020           exist-prev-tag)
2021       (save-excursion
2022         (if (mime-editor/goto-tag)
2023             (or (eq current (match-beginning 0))
2024                 (setq exist-prev-tag t)
2025                 )))
2026       (save-restriction
2027         (narrow-to-region beg end)
2028         (goto-char beg)
2029         (if exist-prev-tag
2030             (insert "\n")
2031           )
2032         (insert (format "--<<%s>>-{\n" type))
2033         (goto-char (point-max))
2034         (insert (format "\n--}-<<%s>>\n" type))
2035         (goto-char (point-max))
2036         )
2037       (if (and (not (looking-at mime-editor/single-part-tag-regexp))
2038                (not (eobp)))
2039           (insert (mime-make-text-tag) "\n")
2040         )
2041       )))
2042
2043 (defun mime-editor/enclose-mixed-region (beg end)
2044   (interactive "*r")
2045   (mime-editor/enclose-region "mixed" beg end)
2046   )
2047
2048 (defun mime-editor/enclose-parallel-region (beg end)
2049   (interactive "*r")
2050   (mime-editor/enclose-region "parallel" beg end)
2051   )
2052
2053 (defun mime-editor/enclose-digest-region (beg end)
2054   (interactive "*r")
2055   (mime-editor/enclose-region "digest" beg end)
2056   )
2057
2058 (defun mime-editor/enclose-alternative-region (beg end)
2059   (interactive "*r")
2060   (mime-editor/enclose-region "alternative" beg end)
2061   )
2062
2063 (defun mime-editor/enclose-signed-region (beg end)
2064   (interactive "*r")
2065   (if mime-editor/signing-type
2066       (mime-editor/enclose-region "signed" beg end)
2067     (message "Please specify signing type.")
2068     ))
2069
2070 (defun mime-editor/enclose-encrypted-region (beg end)
2071   (interactive "*r")
2072   (if mime-editor/signing-type
2073       (mime-editor/enclose-region "encrypted" beg end)
2074     (message "Please specify encrypting type.")
2075     ))
2076
2077
2078 ;;; @ flag setting
2079 ;;;
2080
2081 (defun mime-editor/set-split (arg)
2082   (interactive
2083    (list
2084     (y-or-n-p "Do you want to enable split?")
2085     ))
2086   (setq mime-editor/split-message arg)
2087   (if arg
2088       (message "This message is enabled to split.")
2089     (message "This message is not enabled to split.")
2090     ))
2091
2092
2093 ;;; @ pgp
2094 ;;;
2095
2096 (defun mime-editor/set-sign (arg)
2097   (interactive
2098    (list
2099     (y-or-n-p "Do you want to sign?")
2100     ))
2101   (if arg
2102       (progn
2103         (setq mime-editor/pgp-processing 'sign)
2104         (message "This message will be signed.")
2105         )
2106     (if (eq mime-editor/pgp-processing 'sign)
2107         (setq mime-editor/pgp-processing nil)
2108       )
2109     (message "This message will not be signed.")
2110     ))
2111
2112 (defun mime-editor/set-encrypt (arg)
2113   (interactive
2114    (list
2115     (y-or-n-p "Do you want to encrypt?")
2116     ))
2117   (if arg
2118       (progn
2119         (setq mime-editor/pgp-processing 'encrypt)
2120         (message "This message will be encrypt.")
2121         )
2122     (if (eq mime-editor/pgp-processing 'encrypt)
2123         (setq mime-editor/pgp-processing nil)
2124       )
2125     (message "This message will not be encrypt.")
2126     ))
2127
2128 (defvar mime-editor/pgp-processing nil)
2129 (make-variable-buffer-local 'mime-editor/pgp-processing)
2130
2131 (defun mime-editor/call-mc (command)
2132   (let* ((header (rfc822/get-header-string-except
2133                   "^Content-Type:" mail-header-separator)))
2134     (goto-char (point-min))
2135     (if (search-forward mail-header-separator)
2136         (replace-match "")
2137       )
2138     (goto-char (point-min))
2139     (insert header)
2140     (insert "Content-Type: application/pgp; format=mime\n")
2141     (insert mail-header-separator)
2142     (insert "\n")
2143     (if (null (call-interactively command))
2144         (throw 'mime-editor/error 'pgp-error)
2145       )
2146     ))
2147
2148 (defun mime-editor/pgp-processing ()
2149   (let ((command
2150          (cdr (assq mime-editor/pgp-processing
2151                     '((sign    . mc-sign)
2152                       (encrypt . mc-encrypt)
2153                       )))))
2154     (and command
2155          (mime-editor/call-mc command)
2156          )))
2157
2158
2159 ;;; @ split
2160 ;;;
2161
2162 (defun mime-editor/insert-partial-header
2163   (fields subject id number total separator)
2164   (insert fields)
2165   (insert (format "Subject: %s (%d/%d)\n" subject number total))
2166   (insert (format "Mime-Version: 1.0 (split by tm-edit %s)\n"
2167                   mime-editor/version))
2168   (insert (format "\
2169 Content-Type: message/partial; id=%s; number=%d; total=%d\n%s\n"
2170                   id number total separator))
2171   )
2172
2173 (defun mime-editor/split-and-send
2174   (&optional cmd lines mime-editor/message-max-length)
2175   (interactive)
2176   (or lines
2177       (setq lines
2178             (count-lines (point-min) (point-max)))
2179       )
2180   (or mime-editor/message-max-length
2181       (setq mime-editor/message-max-length
2182             (or (cdr (assq major-mode mime-editor/message-max-length-alist))
2183                 mime-editor/message-default-max-length))
2184       )
2185   (let* ((mime-editor/draft-file-name 
2186           (or (buffer-file-name)
2187               (make-temp-name
2188                (expand-file-name "tm-draft" mime/tmp-dir))))
2189          (separator mail-header-separator)
2190          (config
2191           (eval (cdr (assq major-mode mime-editor/window-config-alist))))
2192          (id (concat "\""
2193                      (replace-space-with-underline (current-time-string))
2194                      "@" (system-name) "\"")))
2195     (run-hooks 'mime-editor/before-split-hook)
2196     (let* ((header (rfc822/get-header-string-except
2197                     mime-editor/split-ignored-field-regexp separator))
2198            (subject (mail-fetch-field "subject"))
2199            (total (+ (/ lines mime-editor/message-max-length)
2200                      (if (> (mod lines mime-editor/message-max-length) 0)
2201                          1)))
2202            (the-buf (current-buffer))
2203            (buf (get-buffer "*tmp-send*"))
2204            (command
2205             (or cmd
2206                 (cdr
2207                  (assq major-mode
2208                        mime-editor/split-message-sender-alist))
2209                 (cdr
2210                  (assq major-mode
2211                        mime-editor/message-default-sender-alist))
2212                 ))
2213            data)
2214       (goto-char (point-min))
2215       (if (re-search-forward (concat "^" (regexp-quote separator) "$")
2216                              nil t)
2217           (replace-match "")
2218         )
2219       (if buf
2220           (progn
2221             (switch-to-buffer buf)
2222             (erase-buffer)
2223             (switch-to-buffer the-buf)
2224             )
2225         (setq buf (get-buffer-create "*tmp-send*"))
2226         )
2227       (switch-to-buffer buf)
2228       (make-local-variable 'mail-header-separator)
2229       (setq mail-header-separator separator)
2230       (switch-to-buffer the-buf)
2231       (goto-char (point-min))
2232       (re-search-forward "^$" nil t)
2233       (let ((mime-editor/partial-number 1))
2234         (setq data (buffer-substring
2235                     (point-min)
2236                     (progn
2237                       (goto-line mime-editor/message-max-length)
2238                       (point))
2239                     ))
2240         (delete-region (point-min)(point))
2241         (switch-to-buffer buf)
2242         (mime-editor/insert-partial-header
2243          header subject id mime-editor/partial-number total separator)
2244         (insert data)
2245         (save-excursion
2246           (save-restriction
2247             (goto-char (point-min))
2248             (search-forward (concat "\n" mail-header-separator "\n"))
2249             (narrow-to-region
2250              (match-end 0)
2251              (if (re-search-forward "^$" nil t)
2252                  (match-beginning 0)
2253                (point-max)
2254                ))
2255             (goto-char (point-min))
2256             (while (re-search-forward
2257                     mime-editor/split-blind-field-regexp nil t)
2258               (delete-region (match-beginning 0)
2259                              (let ((e (rfc822/field-end)))
2260                                (if (< e (point-max))
2261                                    (1+ e)
2262                                  e)))
2263               )
2264             ))
2265         (save-excursion
2266           (message (format "Sending %d/%d..."
2267                            mime-editor/partial-number total))
2268           (call-interactively command)
2269           (message (format "Sending %d/%d... done"
2270                            mime-editor/partial-number total))
2271           )
2272         (erase-buffer)
2273         (switch-to-buffer the-buf)
2274         (setq mime-editor/partial-number 2)
2275         (while (< mime-editor/partial-number total)
2276           (setq data (buffer-substring
2277                       (point-min)
2278                       (progn
2279                         (goto-line mime-editor/message-max-length)
2280                         (point))
2281                       ))
2282           (delete-region (point-min)(point))
2283           (switch-to-buffer buf)
2284           (mime-editor/insert-partial-header
2285            header subject id mime-editor/partial-number total separator)
2286           (insert data)
2287           (save-excursion
2288             (message (format "Sending %d/%d..."
2289                              mime-editor/partial-number total))
2290             (call-interactively command)
2291             (message (format "Sending %d/%d... done"
2292                              mime-editor/partial-number total))
2293             )
2294           (erase-buffer)
2295           (switch-to-buffer the-buf)
2296           (setq mime-editor/partial-number
2297                 (1+ mime-editor/partial-number))
2298           )
2299         (goto-char (point-min))
2300         (mime-editor/insert-partial-header
2301          header subject id mime-editor/partial-number total separator)
2302         (message (format "Sending %d/%d..."
2303                          mime-editor/partial-number total))
2304         ))))
2305
2306 (defun mime-editor/maybe-split-and-send (&optional cmd)
2307   (interactive)
2308   (run-hooks 'mime-editor/before-send-hook)
2309   (let ((mime-editor/message-max-length
2310          (or (cdr (assq major-mode mime-editor/message-max-length-alist))
2311              mime-editor/message-default-max-length))
2312         (lines (count-lines (point-min) (point-max)))
2313         )
2314     (if (and (> lines mime-editor/message-max-length)
2315              mime-editor/split-message)
2316         (mime-editor/split-and-send cmd lines mime-editor/message-max-length)
2317       )))
2318
2319
2320 ;;; @ preview message
2321 ;;;
2322
2323 (defun mime-editor/preview-message ()
2324   "preview editing MIME message. [tm-edit.el]"
2325   (interactive)
2326   (let* ((str (buffer-string))
2327          (separator mail-header-separator)
2328          (the-buf (current-buffer))
2329          (buf-name (buffer-name))
2330          (temp-buf-name (concat "*temp-article:" buf-name "*"))
2331          (buf (get-buffer temp-buf-name))
2332          )
2333     (if buf
2334         (progn
2335           (switch-to-buffer buf)
2336           (erase-buffer)
2337           )
2338       (setq buf (get-buffer-create temp-buf-name))
2339       (switch-to-buffer buf)
2340       )
2341     (insert str)
2342     (setq major-mode 'mime/temporary-message-mode)
2343     (make-local-variable 'mail-header-separator)
2344     (setq mail-header-separator separator)
2345     (make-local-variable 'mime/editing-buffer)
2346     (setq mime/editing-buffer the-buf)
2347     
2348     (run-hooks 'mime-editor/translate-hook)
2349     (mime-editor/translate-buffer)
2350     (goto-char (point-min))
2351     (if (re-search-forward
2352          (concat "^" (regexp-quote separator) "$"))
2353         (replace-match "")
2354       )
2355     (mime/viewer-mode)
2356     ))
2357
2358 (defun mime-editor/quitting-method ()
2359   (let ((temp mime::preview/article-buffer)
2360         buf)
2361     (mime-viewer/kill-buffer)
2362     (set-buffer temp)
2363     (setq buf mime/editing-buffer)
2364     (kill-buffer temp)
2365     (switch-to-buffer buf)
2366     ))
2367
2368 (set-alist 'mime-viewer/quitting-method-alist
2369            'mime/temporary-message-mode
2370            (function mime-editor/quitting-method)
2371            )
2372
2373
2374 ;;; @ draft preview
2375 ;;; 
2376 ;; by "OKABE Yasuo <okabe@kudpc.kyoto-u.ac.jp>
2377 ;;       Mon, 10 Apr 1995 20:03:07 +0900
2378
2379 (defvar mime-editor/draft-header-separator-alist
2380   '((news-reply-mode . mail-header-separator)
2381     (mh-letter-mode . mail-header-separator)
2382     ))
2383
2384 (defvar mime::article/draft-header-separator nil)
2385
2386 (defun mime-editor/draft-preview ()
2387   (interactive)
2388   (let ((sep (cdr (assq major-mode mime-editor/draft-header-separator-alist))))
2389     (or (stringp sep) (setq sep (eval sep)))
2390     (make-variable-buffer-local 'mime::article/draft-header-separator)
2391     (goto-char (point-min))
2392     (re-search-forward
2393      (concat "^\\(" (regexp-quote sep) "\\)?$"))
2394     (setq mime::article/draft-header-separator
2395           (buffer-substring (match-beginning 0) (match-end 0)))
2396     (replace-match "")
2397     (mime/viewer-mode (current-buffer))
2398     (pop-to-buffer (current-buffer))
2399     ))
2400
2401 (defun mime-viewer::quitting-method/draft-preview ()
2402   (let ((mother mime::preview/mother-buffer))
2403     (save-excursion
2404       (switch-to-buffer mother)
2405       (goto-char (point-min))
2406       (if (and
2407            (re-search-forward
2408             (concat "^\\("
2409                     (regexp-quote mime::article/draft-header-separator)
2410                     "\\)?$") nil t)
2411            (bolp))
2412           (progn
2413             (insert mime::article/draft-header-separator)
2414             (set-buffer-modified-p (buffer-modified-p))
2415             )))
2416     (mime-viewer/kill-buffer)
2417     (pop-to-buffer mother)
2418     ))
2419
2420 (set-alist 'mime-viewer/quitting-method-alist
2421            'mh-letter-mode
2422            (function mime-viewer::quitting-method/draft-preview)
2423            )
2424
2425 (set-alist 'mime-viewer/quitting-method-alist
2426            'news-reply-mode
2427            (function mime-viewer::quitting-method/draft-preview)
2428            )
2429
2430
2431 ;;; @ etc
2432 ;;;
2433
2434 (defun replace-space-with-underline (str)
2435   (mapconcat (function
2436               (lambda (arg)
2437                 (char-to-string
2438                  (if (= arg 32)
2439                      ?_
2440                    arg)))) str "")
2441   )
2442
2443
2444 ;;; @ end
2445 ;;;
2446
2447 (provide 'tm-edit)
2448
2449 (run-hooks 'tm-edit-load-hook)
2450
2451 ;;; tm-edit.el ends here