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