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