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