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