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