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