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