tm 7.31.
[elisp/tm.git] / tm-edit.el
1 ;;;
2 ;;; tm-edit.el --- Simple MIME Composer for GNU Emacs
3 ;;;
4
5 ;; Copyright (C) 1993 UMEDA Masanobu
6 ;; Copyright (C) 1994,1995 MORIOKA Tomohiko
7
8 ;; Author: UMEDA Masanobu <umerin@mse.kyutech.ac.jp>
9 ;;         MORIOKA Tomohiko <morioka@jaist.ac.jp>
10 ;; Version: $Revision: 7.31 $
11 ;; Keywords: mail, news, MIME, multimedia, multilingual
12
13 ;; This file is not part of GNU Emacs.
14
15 ;; GNU Emacs is free software; you can redistribute it and/or modify
16 ;; it under the terms of the GNU General Public License as published by
17 ;; the Free Software Foundation; either version 2, or (at your option)
18 ;; any later version.
19
20 ;; GNU Emacs is distributed in the hope that it will be useful,
21 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
22 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23 ;; GNU General Public License for more details.
24
25 ;; You should have received a copy of the GNU General Public License
26 ;; along with GNU Emacs; see the file COPYING.  If not, write to
27 ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
28
29 ;;; Commentary:
30
31 ;; This is an Emacs minor mode for editing Internet multimedia
32 ;; messages formatted in MIME (RFC 1521 and RFC 1522). All messages in
33 ;; this mode are composed in the tagged MIME format, that are
34 ;; described in the following examples. The messages composed in the
35 ;; tagged MIME format are automatically translated into a MIME
36 ;; compliant message when exiting the mode.
37
38 ;; Mule (a multilingual extension to Emacs 18 and 19) has a capability
39 ;; of handling multilingual text in limited ISO-2022 manner that is
40 ;; based on early experiences in Japanese Internet community and
41 ;; resulted in RFC 1468 (ISO-2022-JP charset for MIME). In order to
42 ;; enable multilingual capability in single text message in MIME,
43 ;; charset of multilingual text written in Mule is declared as either
44 ;; `ISO-2022-JP-2' [RFC 1554] or `ISO-2022-INT-1'. Mule is required
45 ;; 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/editor-mode automatically:
50 ;;
51 ;; (autoload 'mime/editor-mode "tm-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 'mime/editor-mode)
58 ;; (add-hook 'mail-send-hook 'mime-editor/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 ;;              (mime/editor-mode)
66 ;;              (make-local-variable 'mail-header-separator)
67 ;;              (setq mail-header-separator "--------")
68 ;;              ))))
69 ;; (add-hook 'mh-before-send-letter-hook 'mime-editor/maybe-translate)
70 ;;
71 ;; In case of News mode, you need the following hook definition:
72 ;;
73 ;; (add-hook 'news-reply-mode-hook 'mime/editor-mode)
74 ;; (add-hook 'news-inews-hook 'mime-editor/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/editor-mode-hook
80 ;;           (function
81 ;;            (lambda ()
82 ;;              (font-lock-mode 1)
83 ;;              (setq font-lock-keywords (list mime-editor/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 ;;
98 ;;--[[text/plain; charset=ISO-2022-JP]]
99 ;; \e$B$3$l$O\e(B charset \e$B$r\e(B ISO-2022-JP \e$B$K;XDj$7$?F|K\8l$N\e(B plain \e$B%F%-%9%H$G$9\e(B.
100 ;;
101 ;;--[[text/richtext]]
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 ;; LCD Archive Entry:
109 ;; mime|Masanobu UMEDA|umerin@mse.kyutech.ac.jp|
110 ;; Simple MIME Composer|
111 ;; $Date: 1995/12/07 10:18:56 $|$Revision: 7.31 $|~/misc/mime.el.Z|
112
113 ;;; Code:
114
115 (require 'sendmail)
116 (require 'mail-utils)
117 (require 'mel)
118 (require 'tl-822)
119 (require 'tl-list)
120 (require 'tm-view)
121 (require 'tm-ew-e)
122 (require 'signature)
123
124
125 ;;; @ version
126 ;;;
127
128 (defconst mime-editor/RCS-ID
129   "$Id: tm-edit.el,v 7.31 1995/12/07 10:18:56 morioka Exp $")
130
131 (defconst mime-editor/version (get-version-string mime-editor/RCS-ID))
132
133
134 ;;; @ variables
135 ;;;
136
137 (defvar mime-prefix "\C-c\C-x"
138   "*Keymap prefix for MIME commands.")
139
140 (defvar mime-signature-file "~/.signature.rtf"
141   "*Signature file to be included as a part of a multipart message.")
142
143 (defvar mime-ignore-preceding-spaces nil
144   "*Ignore preceding white spaces if non-nil.")
145
146 (defvar mime-ignore-trailing-spaces nil
147   "*Ignore trailing white spaces if non-nil.")
148
149 (defvar mime-ignore-same-text-tag t
150   "*Ignore preceding text content-type tag that is same with new one.
151 If non-nil, the text tag is not inserted unless something different.")
152
153 (defvar mime-auto-hide-body t
154   "*Hide non-textual body encoded in base64 after insertion if non-nil.")
155
156 (defvar mime-body-charset-chooser
157   (cond ((boundp 'NEMACS)
158          (function mime-body-charset-chooser-for-nemacs))
159         ((featurep 'mule)
160          (function mime-body-charset-chooser-for-mule))
161         ((string-match "^19\\." emacs-version)
162          (function mime-body-charset-chooser-for-emacs19))
163         (t                              ;ASCII only emacs
164          (function mime-body-charset-chooser-for-emacs18)))
165   "*Function to identify charset and encoding of a text in a given region.
166 The value is a form of (CHARSET . ENCODING), where ENCODING must be a
167 full name, such as base64.")
168
169 (defvar mime-string-encoder
170   (cond ((boundp 'NEMACS)
171          (function mime-string-encoder-for-nemacs))
172         ((featurep 'mule)
173          (function mime-string-encoder-for-mule))
174         ((string-match "^19\\." emacs-version)
175          (function mime-string-encoder-for-emacs19))
176         (t                              ;ASCII only emacs
177          (function mime-string-encoder-for-emacs18)))
178   "*Function to encode a string for given encoding method.
179 The method is a form of (CHARSET . ENCODING).")
180
181 (defvar mime-voice-recorder
182   (function mime-voice-recorder-for-sun)
183   "*Function to record a voice message and return a buffer that contains it.")
184
185 (defvar mime/editor-mode-hook nil
186   "*Hook called when enter MIME mode.")
187
188 (defvar mime-editor/translate-hook nil
189   "*Hook called before translating into a MIME compliant message.
190 To insert a signature file specified by mime-signature-file
191 (`.signature.rtf' by default) automatically, call the function
192 `mime-editor/insert-signature' from this hook.")
193
194 (defvar mime-editor/exit-hook nil
195   "*Hook called when exit MIME mode.")
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      ("richtext"
205       ;;("charset" "" "ISO-2022-JP" "US-ASCII" "ISO-8859-1" "ISO-8859-8")
206       )
207      ("enriched"
208       ;;("charset" "" "ISO-2022-JP" "US-ASCII" "ISO-8859-1" "ISO-8859-8")
209       )
210      ("x-latex"
211       ;;("charset" "" "ISO-2022-JP" "US-ASCII" "ISO-8859-1" "ISO-8859-8")
212       )
213      ("html"
214       ;;("charset" "" "ISO-2022-JP" "US-ASCII" "ISO-8859-1" "ISO-8859-8")
215       )
216      ("x-rot13-47")
217      )
218     ("message"
219      ("external-body"
220       ("access-type"
221        ("anon-ftp"
222         ("site" "ftp.jaist.ac.jp" "wnoc-fuk.wide.ad.jp" "nic.karrn.ad.jp")
223         ("directory" "/pub/GNU/elisp/mime")
224         ("name")
225         ("mode" "image" "ascii" "local8"))
226        ("ftp"
227         ("site")
228         ("directory")
229         ("name")
230         ("mode" "image" "ascii" "local8"))
231        ("tftp"        ("site") ("name"))
232        ("afs"         ("site") ("name"))
233        ("local-file"  ("site") ("name"))
234        ("mail-server" ("server" "ftpmail@nic.karrn.ad.jp"))
235        ))
236      ("rfc822")
237      )
238     ("application"
239      ("octet-stream"
240       ("name")
241       ("type" "" "tar" "shar")
242       ("conversions"))
243      ("postscript")
244      ("x-kiss" ("x-cnf")))
245     ("image"
246      ("gif")
247      ("jpeg")
248      ("tiff")
249      ("x-pic")
250      ("x-xwd")
251      ("x-xbm")
252      )
253     ("audio" ("basic"))
254     ("video" ("mpeg"))
255     )
256   "*Alist of content-type, subtype, parameters and its values.")
257
258 (defvar mime-file-types
259   '(("\\.rtf$"
260      "text"     "richtext"      nil     nil)
261     ("\\.html$"
262      "text"     "html"          nil     nil)
263     ("\\.ps$"
264      "application" "postscript" nil     "quoted-printable")
265     ("\\.gif$"
266      "image"    "gif"           nil     "base64"
267      (("Content-Description" . file))
268      )
269     ("\\.jpg$"
270      "image"    "jpeg"          nil     "base64")
271     ("\\.xwd$"
272      "image"    "x-xwd"         nil     "base64")
273     ("\\.xbm$"
274      "image"    "x-xbm"         nil     "base64")
275     ("\\.pic$"
276      "image"    "x-pic"         nil     "base64"
277      (("Content-Description" . file))
278      )
279     ("\\.tiff$"
280      "image"    "tiff"          nil     "base64")
281     ("\\.au$"
282      "audio"    "basic"         nil     "base64")
283     ("\\.mpg$"
284      "video"    "mpeg"          nil     "base64")
285     ("\\.el$"
286      "application" "octet-stream" (("name" . file)
287                                    ("type" . "emacs-lisp")) "7bit")
288     ("\\.lsp$"
289      "application" "octet-stream" (("name" . file)
290                                    ("type" . "common-lisp")) "7bit")
291     ("\\.tar.gz$"
292      "application" "octet-stream" (("name" . file)
293                                    ("type" . "tar")
294                                    ("conversions" . "gzip")) nil)
295     ("\\.diff$"
296      "application" "octet-stream" (("name" . file)
297                                    ("type" . "patch")) nil)
298     ("\\.signature"
299      "text"     "plain"         nil     nil)
300     (".*"       nil             nil     nil     nil)
301     )
302   "*Alist of file name, types, parameters, and default encoding.
303 If encoding is nil, it is determined from its contents.")
304
305 ;;; @@ about message inserting
306 ;;;
307
308 (defvar mime-editor/yank-ignored-field-list
309   '("Received" "Sender" "Approved" "Path" "Status" "X-VM-.*" "X-UIDL")
310   "Delete these fields from original message when it is inserted
311 as message/rfc822 part.
312 Each elements are regexp of field-name. [tm-edit.el]")
313
314 (defvar mime-editor/yank-ignored-field-regexp
315   (concat "^"
316           (apply (function regexp-or) mime-editor/yank-ignored-field-list)
317           ":"))
318
319 (defvar mime-editor/message-inserter-alist nil)
320 (defvar mime-editor/mail-inserter-alist nil)
321
322 ;;; @@ about message splitting
323 ;;;
324
325 (defvar mime-editor/split-message t
326   "*Split large message if it is non-nil. [tm-edit.el]")
327
328 (defvar mime-editor/message-default-max-length 1000
329   "*Default maximum size of a message. [tm-edit.el]")
330
331 (defvar mime-editor/message-max-length-alist
332   '((news-reply-mode . 500)))
333
334 (defconst mime-editor/split-ignored-field-regexp
335   "\\(^Content-\\|^Subject:\\|^Mime-Version:\\)")
336
337 (defvar mime-editor/split-blind-field-regexp
338   "\\(^[BDFbdf]cc:\\|^cc:[ \t]*$\\)")
339
340 (defvar mime-editor/message-default-sender-alist
341   '((mail-mode . mail-send-and-exit)
342     (mh-letter-mode . mh-send-letter)
343     (news-reply-mode . gnus-inews-news)
344     ))
345
346 (defvar mime-editor/split-message-sender-alist
347   '((mail-mode
348      . (lambda ()
349          (interactive)
350          (sendmail-send-it)
351          ))
352     (mh-letter-mode
353      . (lambda (&optional arg)
354          (interactive "P")
355          (write-region (point-min) (point-max)
356                        mime-editor/draft-file-name nil 'no-message)
357          (cond (arg
358                 (pop-to-buffer "MH mail delivery")
359                 (erase-buffer)
360                 (mh-exec-cmd-output mh-send-prog t "-watch" "-nopush"
361                                     "-nodraftfolder"
362                                     mh-send-args mime-editor/draft-file-name)
363                 (goto-char (point-max)) ; show the interesting part
364                 (recenter -1)
365                 (sit-for 1))
366                (t
367                 (apply 'mh-exec-cmd-quiet t mh-send-prog 
368                        (mh-list-to-string
369                         (list "-nopush" "-nodraftfolder"
370                               "-noverbose" "-nowatch"
371                               mh-send-args mime-editor/draft-file-name)))))
372          ))
373     ))
374
375 (defvar mime-editor/window-config-alist
376   '((mail-mode       . nil)
377     (mh-letter-mode  . mh-previous-window-config)
378     (news-reply-mode . (cond ((boundp 'gnus-winconf-post-news)
379                               (prog1
380                                   gnus-winconf-post-news
381                                 (setq gnus-winconf-post-news nil)
382                                 ))
383                              ((boundp 'gnus-prev-winconf)
384                               (prog1
385                                   gnus-prev-winconf
386                                 (setq gnus-prev-winconf nil)
387                                 ))
388                              ))
389     ))
390
391 (defvar mime-editor/news-reply-mode-server-running nil)
392
393 ;;; @@ about tag
394 ;;;
395
396 (defconst mime-editor/single-part-tag-regexp
397   "^--[[][[]\\([^]]*\\)]\\([[]\\([^]]*\\)]\\|\\)]"
398   "*Regexp of MIME tag in the form of [[CONTENT-TYPE][ENCODING]].")
399
400 (defconst mime-editor/multipart-beginning-regexp "^--<<\\([^<>]+\\)>>-{\n")
401
402 (defconst mime-editor/multipart-end-regexp "^--}-<<\\([^<>]+\\)>>\n")
403
404 (defconst mime-editor/beginning-tag-regexp
405   (regexp-or mime-editor/single-part-tag-regexp
406              mime-editor/multipart-beginning-regexp))
407
408 (defconst mime-editor/end-tag-regexp
409   (regexp-or mime-editor/single-part-tag-regexp
410              mime-editor/multipart-end-regexp))
411
412 (defconst mime-editor/tag-regexp
413   (regexp-or mime-editor/single-part-tag-regexp
414              mime-editor/multipart-beginning-regexp
415              mime-editor/multipart-end-regexp))
416
417 (defvar mime-tag-format "--[[%s]]"
418   "*Control-string making a MIME tag.")
419
420 (defvar mime-tag-format-with-encoding "--[[%s][%s]]"
421   "*Control-string making a MIME tag with encoding.")
422
423 ;;; @@ multipart boundary
424 ;;;
425
426 (defvar mime-multipart-boundary "Multipart"
427   "*Boundary of a multipart message.")
428
429
430 ;;; @@ buffer local variables
431 ;;;
432
433 (defvar mime/editor-mode-old-local-map nil)
434 (defvar mime/editor-mode-old-selective-display nil)
435 (defvar mime/editing-buffer nil)
436
437 \f
438 ;;; @ constants
439 ;;;
440
441 (defconst mime-tspecials-regexp "[][()<>@,;:\\\"/?.= \t]"
442   "*Specify MIME tspecials.
443 Tspecials means any character that matches with it in header must be quoted.")
444
445 (defconst mime-editor/mime-version-value
446   (format "1.0 (generated by tm-edit %s)" mime-editor/version)
447   "MIME version number.")
448
449
450 ;;; @ keymap and menu
451 ;;;
452
453 (defvar mime/editor-mode-flag nil)
454 (make-variable-buffer-local 'mime/editor-mode-flag)
455
456 (set-alist 'minor-mode-alist 'mime/editor-mode-flag '(" MIME-Edit"))
457
458 (defun mime-editor/define-keymap (keymap)
459   "Add mime-editor commands to KEYMAP."
460   (if (not (keymapp keymap))
461       nil
462     (define-key keymap "\C-t" 'mime-editor/insert-text)
463     (define-key keymap "\C-i" 'mime-editor/insert-file)
464     (define-key keymap "\C-e" 'mime-editor/insert-external)
465     (define-key keymap "\C-v" 'mime-editor/insert-voice)
466     (define-key keymap "\C-y" 'mime-editor/insert-message)
467     (define-key keymap "\C-m" 'mime-editor/insert-mail)
468     (define-key keymap "\C-w" 'mime-editor/insert-signature)
469     (define-key keymap "\C-s" 'mime-editor/insert-signature)
470     (define-key keymap "t"    'mime-editor/insert-tag)
471     (define-key keymap "a"    'mime-editor/enclose-alternative-region)
472     (define-key keymap "p"    'mime-editor/enclose-parallel-region)
473     (define-key keymap "m"    'mime-editor/enclose-mixed-region)
474     (define-key keymap "d"    'mime-editor/enclose-digest-region)
475     (define-key keymap "\C-p" 'mime-editor/preview-message)
476     (define-key keymap "\C-z" 'mime-editor/exit)
477     (define-key keymap "?"    'mime-editor/help)
478     ))
479
480 (defconst mime-editor/menu-title "MIME-Edit")
481
482 (defconst mime-editor/menu-list
483   '((mime-help  "Describe MIME editor mode" mime-editor/help)
484     (file       "Insert File"           mime-editor/insert-file)
485     (external   "Insert External"       mime-editor/insert-external)
486     (voice      "Insert Voice"          mime-editor/insert-voice)
487     (message    "Insert Message"        mime-editor/insert-message)
488     (mail       "Insert Mail"           mime-editor/insert-mail)
489     (signature  "Insert Signature"      mime-editor/insert-signature)
490     (text       "Insert Text"           mime-editor/insert-text)
491     (tag        "Insert Tag"            mime-editor/insert-tag)
492     (alternative "Enclose as alternative"
493                  mime-editor/enclose-alternative-region)
494     (parallel   "Enclose as parallel"   mime-editor/enclose-parallel-region)
495     (mixed      "Enclose as serial"     mime-editor/enclose-mixed-region)
496     (digest     "Enclose as digest"     mime-editor/enclose-digest-region)
497     (preview    "Preview Message"       mime-editor/preview-message)
498     )
499   "MIME-edit menubar entry.")
500
501 (defun mime-editor/define-menu-for-emacs19 ()
502   "Define menu for Emacs 19."
503   (define-key (current-local-map) [menu-bar mime-edit]
504     (cons mime-editor/menu-title
505           (make-sparse-keymap mime-editor/menu-title)))
506   (mapcar (function
507            (lambda (item)
508              (define-key (current-local-map)
509                (vector 'menu-bar 'mime-edit (car item))
510                (cons (nth 1 item)(nth 2 item))
511                )
512              ))
513           (reverse mime-editor/menu-list)
514           ))
515
516 ;;; modified by Pekka Marjola <pema@niksula.hut.fi>
517 ;;;     1995/9/5 (c.f. [tm-en:69])
518 (defun mime-editor/define-menu-for-xemacs ()
519   "Define menu for Emacs 19."
520   (cond ((featurep 'menubar)
521          (make-local-variable 'current-menubar)
522          (set-buffer-menubar current-menubar)
523          (add-submenu nil
524                       (cons mime-editor/menu-title
525                             (mapcar (function
526                                      (lambda (item)
527                                        (vector (nth 1 item)(nth 2 item)
528                                                mime/editor-mode-flag)
529                                        ))
530                                     mime-editor/menu-list)))
531          )))
532
533 ;;; modified by Steven L. Baur <steve@miranova.com>
534 ;;;     1995/12/6 (c.f. [tm-en:209])
535 (if (and (string-match "XEmacs\\|Lucid" emacs-version)
536          (not (boundp 'mime-editor/popup-menu-for-xemacs)))
537     (setq mime-editor/popup-menu-for-xemacs
538           (append '("MIME Commands" "---")
539                   (mapcar (function (lambda (item)
540                                       (vector (nth 1 item)
541                                               (nth 2 item)
542                                               t)))
543                           mime-editor/menu-list)))
544   )
545 ;;; end
546
547
548 ;;; @ functions
549 ;;;
550
551 ;;;###autoload
552 (defun mime/editor-mode ()
553   "MIME minor mode for editing the tagged MIME message.
554
555 In this mode, basically, the message is composed in the tagged MIME
556 format.  The message tag looks like:
557
558         `--[[text/plain; charset=ISO-2022-JP][7bit]]'.
559
560 The tag specifies the MIME content type, subtype, optional parameters
561 and transfer encoding of the message following the tag.  Messages
562 without any tag are treated as `text/plain' by default.  Charset and
563 transfer encoding are automatically defined unless explicitly
564 specified.  Binary messages such as audio and image are usually hidden
565 using selective-display facility.  The messages in the tagged MIME
566 format are automatically translated into a MIME compliant message when
567 exiting this mode.
568
569 Available charsets depend on Emacs version being used.  The following
570 lists the available charsets of each emacs.
571
572 Emacs18:        US-ASCII is only available.
573 NEmacs:         US-ASCII and ISO-2022-JP are available.
574 Emacs19:        US-ASCII and ISO-8859-1 are available.
575 Mule:           US-ASCII, ISO-8859-* (except for ISO-8859-6),
576                 ISO-2022-JP, ISO-2022-JP-2 and ISO-2022-INT-1 are available.
577
578 ISO-2022-JP-2 and ISO-2022-INT-1 charsets used in Mule is expected to
579 be used to represent multilingual text in intermixed manner.  Any
580 languages that has no registered charset are represented as either
581 ISO-2022-JP-2 or ISO-2022-INT-1 in Mule.
582
583 Following commands are available in addition to major mode commands:
584 \\[mime-editor/insert-text]     insert a text message.
585 \\[mime-editor/insert-file]     insert a (binary) file.
586 \\[mime-editor/insert-external] insert a reference to external body.
587 \\[mime-editor/insert-voice]    insert a voice message.
588 \\[mime-editor/insert-message]  insert a mail or news message.
589 \\[mime-editor/insert-mail]     insert a mail message.
590 \\[mime-editor/insert-signature]        insert a signature file at end.
591 \\[mime-editor/insert-tag]      insert a new MIME tag.
592 \\[mime-editor/enclose-alternative-region]      enclose as multipart/alternative.
593 \\[mime-editor/enclose-parallel-region] enclose as multipart/parallel.
594 \\[mime-editor/enclose-mixed-region]    enclose as multipart/mixed.
595 \\[mime-editor/enclose-digest-region]   enclose as multipart/digest.
596 \\[mime-editor/preview-message] preview editing MIME message.
597 \\[mime-editor/exit]    exit and translate into a MIME compliant message.
598 \\[mime-editor/maybe-translate] exit, translate and run the original command.
599 \\[mime-editor/help]    show this help.
600
601 Additional commands are available in some major modes:
602 C-c C-c         exit, translate and run the original command.
603 C-c C-s         exit, translate and run the original command.
604
605 The following is a message example written in the tagged MIME format.
606 TABs at the beginning of the line are not a part of the message:
607
608         This is a conventional plain text.  It should be translated
609         into text/plain.
610         --[[text/plain]]
611         This is also a plain text.  But, it is explicitly specified as
612         is.
613         --[[text/plain; charset=ISO-2022-JP]]
614         \e$B$3$l$O\e(B charset \e$B$r\e(B ISO-2022-JP \e$B$K;XDj$7$?F|K\8l$N\e(B plain \e$B%F%-%9\e(B
615         \e$B%H$G$9\e(B.
616         --[[text/richtext]]
617         <center>This is a richtext.</center>
618         --[[image/gif][base64]]^M...image encoded in base64 here...
619         --[[audio/basic][base64]]^M...audio encoded in base64 here...
620
621 User customizable variables (not documented all of them):
622  mime-prefix
623     Specifies a key prefix for MIME minor mode commands.
624
625  mime-signature-file
626     Specifies a signature file to be included as part of a multipart
627     message.
628
629  mime-ignore-preceding-spaces
630     Preceding white spaces in a message body are ignored if non-nil.
631
632  mime-ignore-trailing-spaces
633     Trailing white spaces in a message body are ignored if non-nil.
634
635  mime-auto-fill-header
636     Fill header fields that contain encoded-words if non-nil.
637
638  mime-auto-hide-body
639     Hide a non-textual body message encoded in base64 after insertion
640     if non-nil.
641
642  mime-body-charset-chooser
643     Specifies a function to identify charset and encoding of a text in
644     a given region.  The value is a form of (CHARSET . ENCODING),
645     where ENCODING must be a full name, such as base64.
646
647  mime-string-encoder
648     Specifies a function to encode a string for given encoding method.
649     The method is a form of (CHARSET . ENCODING).
650
651  mime-voice-recorder
652     Specifies a function to record a voice message and return a buffer
653     that contains it.  The function mime-voice-recorder-for-sun is for
654     Sun SparcStations.
655
656  mime/editor-mode-hook
657     Turning on MIME mode calls the value of mime/editor-mode-hook, if
658     it is non-nil.
659
660  mime-editor/translate-hook
661     The value of mime-editor/translate-hook is called just before translating
662     the tagged MIME format into a MIME compliant message if it is
663     non-nil.  If the hook call the function mime-editor/insert-signature,
664     the signature file will be inserted automatically.
665
666  mime-editor/exit-hook
667     Turning off MIME mode calls the value of mime-editor/exit-hook, if it is
668     non-nil."
669   (interactive)
670   (if mime/editor-mode-flag
671       (error "You are already editing a MIME message.")
672     (setq mime/editor-mode-flag t)
673     ;; Remember old key bindings.
674     (make-local-variable 'mime/editor-mode-old-local-map)
675     (setq mime/editor-mode-old-local-map (current-local-map))
676     ;; Add MIME commands to current local map.
677     (use-local-map (copy-keymap (current-local-map)))
678     (if (not (lookup-key (current-local-map) mime-prefix))
679         (define-key (current-local-map) mime-prefix (make-sparse-keymap)))
680     (mime-editor/define-keymap (lookup-key (current-local-map) mime-prefix))
681     
682     ;; Define menu.  Menus for other emacs implementations are
683     ;; welcome.
684     ;;  modified by Pekka Marjola <pema@niksula.hut.fi>
685     ;;          1995/9/5 (c.f. [tm-eng:69])
686     (cond ((string-match "XEmacs\\|Lucid" emacs-version)
687            (mime-editor/define-menu-for-xemacs))
688           ((string-match "^19\\." emacs-version)
689            (mime-editor/define-menu-for-emacs19)
690            ))
691     ;; end
692     
693     ;; Remember old selective-display.
694     (make-local-variable 'mime/editor-mode-old-selective-display)
695     (setq mime/editor-mode-old-selective-display selective-display)
696     (setq selective-display t)
697     ;; I don't care about saving these.
698     (setq paragraph-start
699           (concat mime-editor/single-part-tag-regexp "\\|" paragraph-start))
700     (setq paragraph-separate
701           (concat mime-editor/single-part-tag-regexp "\\|" paragraph-separate))
702     (run-hooks 'mime/editor-mode-hook)
703     (message
704      (substitute-command-keys
705       "Type \\[mime-editor/exit] to exit MIME mode, and type \\[mime-editor/help] to get help."))
706     ))
707
708 ;;;###autoload
709 (defalias 'edit-mime 'mime/editor-mode)         ; for convenience
710 (defalias 'mime-mode 'mime/editor-mode)         ; for convenience
711
712 (defun mime-editor/exit (&optional nomime no-error)
713   "Translate the tagged MIME message into a MIME compliant message.
714 With no argument encode a message in the buffer into MIME, otherwise
715 just return to previous mode."
716   (interactive "P")
717   (if (not mime/editor-mode-flag)
718       (if (null no-error)
719           (error "You aren't editing a MIME message.")
720         )
721     (if (not nomime)
722         (progn
723           (run-hooks 'mime-editor/translate-hook)
724           (mime-editor/translate-buffer)))
725     ;; Restore previous state.
726     (setq mime/editor-mode-flag nil)
727     (use-local-map mime/editor-mode-old-local-map)
728     
729     ;; modified by Pekka Marjola <pema@niksula.hut.fi>
730     ;;  1995/9/5 (c.f. [tm-eng:69])
731     (if (string-match "XEmacs\\|Lucid" emacs-version)
732         (progn
733           (delete-menu-item (list mime-editor/menu-title))
734                                         ; should rather be const
735           ;; (while mime-editor/xemacs-old-bindings
736           ;;   (eval (pop mime-editor/xemacs-old-bindings)))
737           (local-unset-key mime-prefix)))
738     ;; end
739     
740     (setq selective-display mime/editor-mode-old-selective-display)
741     (set-buffer-modified-p (buffer-modified-p))
742     (run-hooks 'mime-editor/exit-hook)
743     (message "Exit MIME editor mode.")
744     ))
745
746 (defun mime-editor/maybe-translate ()
747   (interactive)
748   (mime-editor/exit nil t)
749   (call-interactively 'mime-editor/split-and-send)
750   )
751
752 (defun mime-editor/help ()
753   "Show help message about MIME mode."
754   (interactive)
755   (with-output-to-temp-buffer "*Help*"
756     (princ "MIME editor mode:\n")
757     (princ (documentation 'mime/editor-mode))
758     (print-help-return-message)))
759
760 (defun mime-editor/insert-text ()
761   "Insert a text message.
762 Charset is automatically obtained from the mime-body-charset-chooser."
763   (interactive)
764   (if (and (mime-editor/insert-tag "text" nil nil)
765            (looking-at mime-editor/single-part-tag-regexp))
766       (progn
767         ;; Make a space between the following message.
768         (insert "\n")
769         (forward-char -1)
770         )))
771
772 (defun mime-editor/insert-file (file)
773   "Insert a message from a file."
774   (interactive "fInsert file as MIME message: ")
775   (let*  ((guess (mime-find-file-type file))
776           (pritype (nth 0 guess))
777           (subtype (nth 1 guess))
778           (parameters (nth 2 guess))
779           (default (nth 3 guess))       ;Guess encoding from its file name.
780           (fields (nth 4 guess))
781           (encoding
782            (if (not (interactive-p))
783                default
784              (completing-read
785               (concat "What transfer encoding"
786                       (if default
787                           (concat " (default "
788                                   (if (string-equal default "")
789                                       "\"\""
790                                     default)
791                                   ")"
792                                   ))
793                       ": ")
794               mime-encoding-method-alist nil t nil))))
795     (if (string-equal encoding "")
796         (setq encoding default))
797     (if (or (consp parameters) (consp fields))
798         (let ((rest parameters) cell attribute value)
799           (setq parameters "")
800           (while rest
801             (setq cell (car rest))
802             (setq attribute (car cell))
803             (setq value (cdr cell))
804             (if (eq value 'file)
805                 (setq value (file-name-nondirectory file))
806               )
807             (setq parameters (concat parameters "; " attribute "=" value))
808             (setq rest (cdr rest))
809             )
810           (setq rest fields)
811           (while rest
812             (setq cell (car rest))
813             (setq attribute (car cell))
814             (setq value (cdr cell))
815             (if (eq value 'file)
816                 (setq value (file-name-nondirectory file))
817               )
818             (setq parameters (concat parameters "\n" attribute ": " value))
819             (setq rest (cdr rest))
820             )
821           ))
822     (mime-editor/insert-tag pritype subtype parameters)
823     (mime-editor/insert-binary-file file encoding)
824     ))
825
826 (defun mime-editor/insert-external ()
827   "Insert a reference to external body."
828   (interactive)
829   (mime-editor/insert-tag "message" "external-body" nil ";\n\t")
830   ;;(forward-char -1)
831   ;;(insert "Content-Description: " (read-string "Content-Description: ") "\n")
832   ;;(forward-line 1)
833   (let* ((pritype (mime-prompt-for-type))
834          (subtype (mime-prompt-for-subtype pritype))
835          (parameters (mime-prompt-for-parameters pritype subtype ";\n\t")))
836     (and pritype
837          subtype
838          (insert "Content-Type: "
839                  pritype "/" subtype (or parameters "") "\n")))
840   (if (and (not (eobp))
841            (not (looking-at mime-editor/single-part-tag-regexp)))
842       (insert (mime-make-text-tag) "\n")))
843
844 (defun mime-editor/insert-voice ()
845   "Insert a voice message."
846   (interactive)
847   (mime-editor/insert-tag "audio" "basic" nil)
848   (let ((buffer (funcall mime-voice-recorder)))
849     (unwind-protect
850         (mime-editor/insert-binary-buffer buffer "base64")
851       (kill-buffer buffer)
852       )))
853
854 (defun mime-editor/insert-signature (&optional arg)
855   "Insert a signature file specified by mime-signature-file."
856   (interactive "P")
857   (let ((signature
858          (expand-file-name
859           (if arg
860               (read-file-name "Insert your signature: "
861                               (concat signature-file-name "-")
862                               signature-file-name
863                               nil)
864             (signature/get-signature-file-name))))
865         )
866     (if signature-insert-at-eof
867         (goto-char (point-max))
868       )
869     (apply (function mime-editor/insert-tag)
870            (mime-find-file-type signature))
871     (if (file-readable-p signature)
872         (progn
873           (goto-char (point-max))
874           (if (not (bolp))
875               (insert "\n"))
876           (delete-blank-lines)
877           (insert-file-contents signature)
878           (set-buffer-modified-p (buffer-modified-p))
879                                         ; force mode line update
880           ))))
881
882 \f
883 ;; Insert a new tag around a point.
884
885 (defun mime-editor/insert-tag (&optional pritype subtype parameters delimiter)
886   "Insert new MIME tag and return a list of PRITYPE, SUBTYPE, and PARAMETERS.
887 If nothing is inserted, return nil."
888   (interactive)
889   (let ((oldtag nil)
890         (newtag nil)
891         (current (point))
892         exist-prev-tag exist-next-tag)
893     (setq pritype
894           (or pritype
895               (mime-prompt-for-type)))
896     (setq subtype
897           (or subtype
898               (mime-prompt-for-subtype pritype)))
899     (setq parameters
900           (or parameters
901               (mime-prompt-for-parameters pritype subtype delimiter)))
902     ;; Make a new MIME tag.
903     (setq newtag (mime-make-tag pritype subtype parameters))
904     ;; Find an current MIME tag.
905     (setq oldtag
906           (save-excursion
907             (if (mime-editor/goto-tag)
908                 (progn
909                   (if (eq current (match-beginning 0))
910                       (setq exist-next-tag t)
911                     (setq exist-prev-tag t)
912                     )
913                   (buffer-substring (match-beginning 0) (match-end 0))
914                   )
915               ;; Assume content type is 'text/plan'.
916               (mime-make-tag "text" "plain")
917               )))
918     ;; We are only interested in TEXT.
919     (if (and oldtag
920              (not (mime-test-content-type
921                    (mime-editor/get-contype oldtag) "text")))
922         (setq oldtag nil))
923     (cond (exist-prev-tag (insert "\n"))
924           (exist-next-tag (save-excursion
925                             (insert "\n")
926                             )))
927     ;; (beginning-of-line)
928     ;; (cond ((and oldtag                  ;Text
929     ;;             (not (eobp))
930     ;;             (save-excursion
931     ;;               (forward-line -1)
932     ;;               (looking-at mime-editor/beginning-tag-regexp)
933     ;;               )
934     ;;             (or mime-ignore-same-text-tag
935     ;;                 (not (string-equal oldtag newtag))))
936     ;;        ;; If point is at the next of current tag, move to the
937     ;;        ;; beginning of the tag to disable insertion of extra tag.
938     ;;        (forward-line -1))
939     ;;       ((and oldtag                  ;Text
940     ;;             (not (eobp))
941     ;;             (not (looking-at mime-editor/tag-regexp))
942     ;;             (or mime-ignore-same-text-tag
943     ;;                 (not (string-equal oldtag newtag))))
944     ;;        ;; Copy current tag to break a text into two.
945     ;;        (save-excursion
946     ;;          (insert oldtag "\n")))
947     ;;       ((and (null oldtag)           ;Not text
948     ;;             (not (looking-at mime-editor/tag-regexp)))
949     ;;        ;; Adjust insertion point.  In the middle of text, it is
950     ;;        ;; okay to break the text into two.  However, it should not
951     ;;        ;; be broken into two, if otherwise.
952     ;;        (goto-char (mime-editor/content-end))
953     ;;        (if (eolp)
954     ;;            (forward-line 1))
955     ;;        (if (not (bolp))
956     ;;            (insert "\n"))
957     ;;        ))
958     (if (not (bolp))
959         (if exist-prev-tag
960             (forward-line 1)
961           (insert "\n")
962           ))
963     ;; Make a new tag.
964     (if (or (not oldtag)                ;Not text
965             (or mime-ignore-same-text-tag
966                 (not (string-equal oldtag newtag))))
967         (progn
968           ;; Mark the beginning of the tag for convenience.
969           (push-mark (point) 'nomsg)
970           (insert newtag "\n")
971           (list pritype subtype parameters) ;New tag is created.
972           )
973       ;; Restore previous point.
974       (goto-char current)
975       nil                               ;Nothing is created.
976       )
977     ))
978
979 ;; Insert the binary content after MIME tag.
980 ;;      modified by MORITA Masahiro <hiro@isl.ntt.JP>
981 ;;      for x-uue
982 (defun mime-editor/insert-binary-file (file &optional encoding)
983   "Insert binary FILE at point.
984 Optional argument ENCODING specifies an encoding method such as base64."
985   (let ((tmpbuf (get-buffer-create " *MIME insert*")))
986     (save-excursion
987       (set-buffer tmpbuf)
988       (erase-buffer)
989       (let ((mc-flag nil)               ;Mule
990             (file-coding-system-for-read
991              (if (featurep 'mule) *noconv*))
992             (kanji-flag nil)            ;NEmacs
993             (emx-binary-mode t)         ;Stop CRLF to LF conversion in OS/2
994             )
995         (let (jka-compr-compression-info-list
996               jam-zcat-filename-list)
997           (insert-file-contents file))))
998     (prog1
999         (if (and (stringp encoding)
1000                  (string-equal (downcase encoding) "x-uue"))
1001             (progn
1002               (require 'mel-u)
1003               (let ((uuencode-external-encoder
1004                      (cons (car uuencode-external-encoder)
1005                            (list (file-name-nondirectory file))
1006                            )))
1007                 (mime-editor/insert-binary-buffer tmpbuf encoding)
1008                 ))
1009           (mime-editor/insert-binary-buffer tmpbuf encoding))
1010       (kill-buffer tmpbuf))))
1011
1012 ;; Insert the binary content after MIME tag.
1013 ;;      modified by MORITA Masahiro <hiro@isl.ntt.JP>
1014 ;;      for x-uue
1015 (defun mime-editor/insert-binary-buffer (buffer &optional encoding)
1016   "Insert binary BUFFER at point.
1017 Optional argument ENCODING specifies an encoding method such as base64."
1018   (let* ((tagend (1- (point)))          ;End of the tag
1019          (hide-p (and mime-auto-hide-body
1020                       (stringp encoding)
1021                       (let ((en (downcase encoding)))
1022                         (or (string-equal en "base64")
1023                             (string-equal en "x-uue")
1024                             ))))
1025          )
1026     (save-restriction
1027       (narrow-to-region (1- (point)) (point))
1028       (let ((start (point))
1029             (emx-binary-mode t))        ;Stop LF to CRLF conversion in OS/2
1030         (insert-buffer-substring buffer)
1031         ;; Encode binary message if necessary.
1032         (if encoding
1033             (mime-encode-region encoding start (point-max))))
1034       (if hide-p
1035           (progn
1036             (mime-flag-region (point-min) (1- (point-max)) ?\^M)
1037             (goto-char (point-max)))
1038         ))
1039     ;; Define encoding even if it is 7bit.
1040     (if (stringp encoding)
1041         (save-excursion
1042           (goto-char tagend)            ;Make sure which line the tag is on.
1043           (mime-editor/define-encoding encoding)))
1044     ))
1045
1046 \f
1047 ;; Commands work on a current message flagment.
1048
1049 (defun mime-editor/goto-tag ()
1050   "Search for the beginning of the tagged MIME message."
1051   (let ((current (point)) multipart)
1052     (if (looking-at mime-editor/tag-regexp)
1053         t
1054       ;; At first, go to the end.
1055       (cond ((re-search-forward mime-editor/beginning-tag-regexp nil t)
1056              (goto-char (match-beginning 0)) ;For multiline tag
1057              (forward-line -1)
1058              (end-of-line)
1059              )
1060             (t
1061              (goto-char (point-max))
1062              ))
1063       ;; Then search for the beginning. 
1064       (re-search-backward mime-editor/end-tag-regexp nil t)
1065       (beginning-of-line)
1066       (or (looking-at mime-editor/beginning-tag-regexp)
1067           ;; Restore previous point.
1068           (progn
1069             (goto-char current)
1070             nil
1071             ))
1072       )))
1073
1074 (defun mime-editor/content-beginning ()
1075   "Return the point of the beginning of content."
1076   (save-excursion
1077     (let ((beg (save-excursion
1078                  (beginning-of-line) (point))))
1079       (if (mime-editor/goto-tag)
1080           (let ((top (point)))
1081             (goto-char (match-end 0))
1082             (if (and (= beg top)
1083                      (= (following-char) ?\^M))
1084                 (point)
1085               (forward-line 1)
1086               (point)))
1087         ;; Default text/plain tag.
1088         (goto-char (point-min))
1089         (re-search-forward
1090          (concat "\n" (regexp-quote mail-header-separator)
1091                  (if mime-ignore-preceding-spaces
1092                      "[ \t\n]*\n" "\n")) nil 'move)
1093         (point))
1094       )))
1095
1096 (defun mime-editor/content-end ()
1097   "Return the point of the end of content."
1098   (save-excursion
1099     (let ((beg (save-excursion
1100                  (beginning-of-line) (point))))
1101       (if (mime-editor/goto-tag)
1102           (let ((top (point)))
1103             (goto-char (match-end 0))
1104             (if (and (= beg top)        ;Must be on the same line.
1105                      (= (following-char) ?\^M))
1106                 (progn
1107                   (end-of-line)
1108                   (point))
1109               ;; Move to the end of this text.
1110               (if (re-search-forward mime-editor/tag-regexp nil 'move)
1111                   ;; Don't forget a multiline tag.
1112                   (goto-char (match-beginning 0)))
1113               (point)
1114               ))
1115         ;; Assume the message begins with text/plain.
1116         (goto-char (mime-editor/content-beginning))
1117         (if (re-search-forward mime-editor/tag-regexp nil 'move)
1118             ;; Don't forget a multiline tag.
1119             (goto-char (match-beginning 0)))
1120         (point))
1121       )))
1122
1123 (defun mime-editor/define-charset (charset)
1124   "Set charset of current tag to CHARSET."
1125   (save-excursion
1126     (if (mime-editor/goto-tag)
1127         (let ((tag (buffer-substring (match-beginning 0) (match-end 0))))
1128           (delete-region (match-beginning 0) (match-end 0))
1129           (insert
1130            (mime-create-tag (mime-set-parameter
1131                              (mime-editor/get-contype tag) "charset" charset)
1132                             (mime-editor/get-encoding tag))))
1133       )))
1134
1135 (defun mime-editor/define-encoding (encoding)
1136   "Set encoding of current tag to ENCODING."
1137   (save-excursion
1138     (if (mime-editor/goto-tag)
1139         (let ((tag (buffer-substring (match-beginning 0) (match-end 0))))
1140           (delete-region (match-beginning 0) (match-end 0))
1141           (insert (mime-create-tag (mime-editor/get-contype tag) encoding)))
1142       )))
1143
1144 (defun mime-editor/choose-charset ()
1145   "Choose charset of a text following current point."
1146   (save-excursion
1147     (let* ((beg (point))
1148            (end (mime-editor/content-end)))
1149       (car (funcall mime-body-charset-chooser beg end)))))
1150
1151 (defun mime-editor/choose-encoding ()
1152   "Choose encoding of a text following current point."
1153   (save-excursion
1154     (let* ((beg (point))
1155            (end (mime-editor/content-end)))
1156       (cdr (funcall mime-body-charset-chooser beg end)))))
1157
1158 (defun mime-make-text-tag (&optional subtype)
1159   "Make a tag for a text after current point.
1160 Subtype of text type can be specified by an optional argument SUBTYPE.
1161 Otherwise, it is obtained from mime-content-types."
1162   (let* ((pritype "text")
1163          (subtype (or subtype
1164                       (car (car (cdr (assoc pritype mime-content-types)))))))
1165     ;; Charset should be defined later.
1166     (mime-make-tag pritype subtype)))
1167
1168 \f
1169 ;; Tag handling functions
1170
1171 (defun mime-make-tag (pritype subtype &optional parameters encoding)
1172   "Make a tag of MIME message of PRITYPE, SUBTYPE and optional PARAMETERS."
1173   (mime-create-tag (concat (or pritype "") "/" (or subtype "")
1174                            (or parameters ""))
1175                    encoding))
1176
1177 (defun mime-create-tag (contype &optional encoding)
1178   "Make a tag with CONTENT-TYPE and optional ENCODING."
1179   (format (if encoding mime-tag-format-with-encoding mime-tag-format)
1180           contype encoding))
1181
1182 (defun mime-editor/get-contype (tag)
1183   "Return Content-Type (including parameters) of TAG."
1184   (and (stringp tag)
1185        (or (string-match mime-editor/single-part-tag-regexp tag)
1186            (string-match mime-editor/multipart-beginning-regexp tag)
1187            (string-match mime-editor/multipart-end-regexp tag)
1188            )
1189        (substring tag (match-beginning 1) (match-end 1))
1190        ))
1191
1192 (defun mime-editor/get-encoding (tag)
1193   "Return encoding of TAG."
1194   (and (stringp tag)
1195        (string-match mime-editor/single-part-tag-regexp tag)
1196        (match-beginning 3)
1197        (not (= (match-beginning 3) (match-end 3)))
1198        (substring tag (match-beginning 3) (match-end 3))))
1199
1200 (defun mime-get-parameter (contype parameter)
1201   "For given CONTYPE return value for PARAMETER.
1202 Nil if no such parameter."
1203   (if (string-match
1204        (concat
1205         ";[ \t\n]*"
1206         (regexp-quote parameter)
1207         "[ \t\n]*=[ \t\n]*\\([^\" \t\n;]*\\|\"[^\"]*\"\\)\\([ \t\n]*;\\|$\\)")
1208        contype)
1209       (substring contype (match-beginning 1) (match-end 1))
1210     nil                                 ;No such parameter
1211     ))
1212
1213 (defun mime-set-parameter (contype parameter value)
1214   "For given CONTYPE set PARAMETER to VALUE."
1215   (if (string-match
1216        (concat
1217         ";[ \t\n]*\\("
1218         (regexp-quote parameter)
1219         "[ \t\n]*=[ \t\n]*\\([^\" \t\n;]*\\|\"[^\"]*\"\\)\\)[ \t\n]*\\(;\\|$\\)")
1220        contype)
1221       ;; Change value
1222       (concat (substring contype 0 (match-beginning 1))
1223               parameter "=" value
1224               (substring contype (match-end 1)))
1225     (concat contype "; " parameter "=" value)))
1226
1227 (defun mime-strip-parameters (contype)
1228   "Return primary content-type and subtype without parameters for CONTYPE."
1229   (if (string-match "^[ \t]*\\([^; \t\n]*\\)" contype)
1230       (substring contype (match-beginning 1) (match-end 1)) nil))
1231
1232 (defun mime-test-content-type (contype type &optional subtype)
1233   "Test if CONTYPE is a TYPE and an optional SUBTYPE."
1234   (and (stringp contype)
1235        (stringp type)
1236        (string-match
1237         (concat "^[ \t]*" (downcase type) "/" (downcase (or subtype "")))
1238         (downcase contype))))
1239
1240 \f
1241 ;; Basic functions
1242
1243 (defun mime-find-file-type (file)
1244   "Guess Content-Type, subtype, and parameters from FILE."
1245   (let ((guess nil)
1246         (guesses mime-file-types))
1247     (while (and (not guess) guesses)
1248       (if (string-match (car (car guesses)) file)
1249           (setq guess (cdr (car guesses))))
1250       (setq guesses (cdr guesses)))
1251     guess
1252     ))
1253
1254 (defun mime-prompt-for-type ()
1255   "Ask for Content-type."
1256   (let ((type ""))
1257     ;; Repeat until primary content type is specified.
1258     (while (string-equal type "")
1259       (setq type
1260             (completing-read "What content type: "
1261                              mime-content-types
1262                              nil
1263                              'require-match ;Type must be specified.
1264                              nil
1265                              ))
1266       (if (string-equal type "")
1267           (progn
1268             (message "Content type is required.")
1269             (beep)
1270             (sit-for 1)
1271             ))
1272       )
1273     type
1274     ))
1275
1276 (defun mime-prompt-for-subtype (pritype)
1277   "Ask for Content-type subtype of Content-Type PRITYPE."
1278   (let* ((default (car (car (cdr (assoc pritype mime-content-types)))))
1279          (answer
1280           (completing-read
1281            (if default
1282                (concat
1283                 "What content subtype: (default " default ") ")
1284              "What content subtype: ")
1285            (cdr (assoc pritype mime-content-types))
1286            nil
1287            'require-match               ;Subtype must be specified.
1288            nil
1289            )))
1290     (if (string-equal answer "") default answer)))
1291
1292 (defun mime-prompt-for-parameters (pritype subtype &optional delimiter)
1293   "Ask for Content-type parameters of Content-Type PRITYPE and SUBTYPE.
1294 Optional DELIMITER specifies parameter delimiter (';' by default)."
1295   (let* ((delimiter (or delimiter "; "))
1296          (parameters
1297           (mapconcat
1298            (function identity)
1299            (delq nil
1300                  (mime-prompt-for-parameters-1
1301                   (cdr (assoc subtype
1302                               (cdr (assoc pritype mime-content-types))))))
1303            delimiter
1304            )))
1305     (if (and (stringp parameters)
1306              (not (string-equal parameters "")))
1307         (concat delimiter parameters)
1308       ""                                ;"" if no parameters
1309       )))
1310
1311 (defun mime-prompt-for-parameters-1 (optlist)
1312   (apply (function append)
1313          (mapcar (function mime-prompt-for-parameter) optlist)))
1314
1315 (defun mime-prompt-for-parameter (parameter)
1316   "Ask for PARAMETER.
1317 Parameter must be '(PROMPT CHOICE1 (CHOISE2 ...))."
1318   (let* ((prompt (car parameter))
1319          (choices (mapcar (function
1320                            (lambda (e)
1321                              (if (consp e) e (list e))))
1322                           (cdr parameter)))
1323          (default (car (car choices)))
1324          (answer nil))
1325     (if choices
1326         (progn
1327           (setq answer
1328                 (completing-read
1329                  (concat "What " prompt
1330                          ": (default "
1331                          (if (string-equal default "") "\"\"" default)
1332                          ") ")
1333                  choices nil nil ""))
1334           ;; If nothing is selected, use default.
1335           (if (string-equal answer "")
1336               (setq answer default)))
1337       (setq answer
1338             (read-string (concat "What " prompt ": "))))
1339     (cons (if (and answer
1340                    (not (string-equal answer "")))
1341               (concat prompt "="
1342                       ;; Note: control characters ignored!
1343                       (if (string-match mime-tspecials-regexp answer)
1344                           (concat "\"" answer "\"") answer)))
1345           (mime-prompt-for-parameters-1 (cdr (assoc answer (cdr parameter)))))
1346     ))
1347
1348 (defun mime-encode-string (encoding string)
1349   "Using ENCODING encode a STRING.
1350 If the STRING is too long, the encoded string may be broken into
1351 several lines."
1352   (save-excursion
1353     (set-buffer (get-buffer-create " *MIME encoding*"))
1354     (erase-buffer)
1355     (insert string)
1356     (mime-encode-region encoding (point-min) (point-max))
1357     (prog1
1358         (buffer-substring (point-min) (point-max))
1359       (kill-buffer (current-buffer)))))
1360
1361 (defun mime-decode-string (encoding string)
1362   "Using ENCODING decode a STRING."
1363   (save-excursion
1364     (set-buffer (get-buffer-create " *MIME decoding*"))
1365     (erase-buffer)
1366     (insert string)
1367     (mime-decode-region encoding (point-min) (point-max))
1368     (prog1
1369         (buffer-substring (point-min) (point-max))
1370       (kill-buffer (current-buffer)))))
1371
1372 (defun mime-flag-region (from to flag)
1373   "Hides or shows lines from FROM to TO, according to FLAG.
1374 If FLAG is `\\n' (newline character) then text is shown,
1375 while if FLAG is `\\^M' (control-M) the text is hidden."
1376   (let ((buffer-read-only nil)          ;Okay even if write protected.
1377         (modp (buffer-modified-p)))
1378     (unwind-protect
1379         (subst-char-in-region from to
1380                               (if (= flag ?\n) ?\^M ?\n)
1381                               flag t)
1382       (set-buffer-modified-p modp))))
1383
1384 \f
1385 ;; Translate the tagged MIME messages into a MIME compliant message.
1386
1387 (defun mime-editor/translate-buffer ()
1388   "Encode the tagged MIME message in current buffer in MIME compliant message."
1389   (interactive)
1390   (mime/encode-message-header)
1391   (mime-editor/translate-body)
1392   )
1393
1394 (defun mime-editor/translate-body ()
1395   "Encode the tagged MIME body in current buffer in MIME compliant message."
1396   (interactive)
1397   (save-excursion
1398     (let ((boundary
1399            (concat mime-multipart-boundary " " (current-time-string)))
1400           (i 1)
1401           (time (current-time-string))
1402           ret)
1403       (while (mime-editor/process-multipart-1
1404               (format "%s %s-%d" mime-multipart-boundary time i))
1405         (setq i (1+ i))
1406         )
1407       (save-restriction
1408         ;; We are interested in message body.
1409         (let* ((beg
1410                 (progn
1411                   (goto-char (point-min))
1412                   (re-search-forward
1413                    (concat "\n" (regexp-quote mail-header-separator)
1414                            (if mime-ignore-preceding-spaces
1415                                "[ \t\n]*\n" "\n")) nil 'move)
1416                   (point)))
1417                (end
1418                 (progn
1419                   (goto-char (point-max))
1420                   (and mime-ignore-trailing-spaces
1421                        (re-search-backward "[^ \t\n]\n" beg t)
1422                        (forward-char 1))
1423                   (point))))
1424           (setq ret (mime-editor/translate-region
1425                      beg end
1426                      (format "%s %s-%d" mime-multipart-boundary time i)))
1427           ))
1428       (let ((contype (car ret))         ;Content-Type
1429             (encoding (nth 1 ret))      ;Content-Transfer-Encoding
1430             )
1431         ;; Make primary MIME headers.
1432         (or (mail-position-on-field "Mime-Version")
1433             (insert mime-editor/mime-version-value))
1434         ;; Remove old Content-Type and other fields.
1435         (save-restriction
1436           (goto-char (point-min))
1437           (search-forward (concat "\n" mail-header-separator "\n") nil t)
1438           (narrow-to-region (point-min) (point))
1439           (goto-char (point-min))
1440           (mime-delete-field "Content-Type")
1441           (mime-delete-field "Content-Transfer-Encoding"))
1442         ;; Then, insert Content-Type and Content-Transfer-Encoding fields.
1443         (mail-position-on-field "Content-Type")
1444         (insert contype)
1445         (if encoding
1446             (progn
1447               (mail-position-on-field "Content-Transfer-Encoding")
1448               (insert encoding)))
1449         ))))
1450
1451 (defun mime-editor/translate-region (beg end &optional boundary multipart)
1452   (if (null boundary)
1453       (setq boundary
1454             (concat mime-multipart-boundary " " (current-time-string)))
1455     )
1456   (save-excursion
1457     (save-restriction
1458       (narrow-to-region beg end)
1459       (let ((tag nil)                   ;MIME tag
1460             (contype nil)               ;Content-Type
1461             (encoding nil)              ;Content-Transfer-Encoding
1462             (nparts 0))                 ;Number of body parts
1463         ;; Normalize the body part by inserting appropriate message
1464         ;; tags for every message contents.
1465         (mime-editor/normalize-body)
1466         ;; Counting the number of Content-Type.
1467         (goto-char (point-min))
1468         (while (re-search-forward mime-editor/single-part-tag-regexp nil t)
1469           (setq nparts (1+ nparts)))
1470         ;; Begin translation.
1471         (cond ((and (<= nparts 1)(not multipart))
1472                ;; It's a singular message.
1473                (goto-char (point-min))
1474                (while (re-search-forward
1475                        mime-editor/single-part-tag-regexp nil t)
1476                  (setq tag
1477                        (buffer-substring (match-beginning 0) (match-end 0)))
1478                  (delete-region (match-beginning 0) (1+ (match-end 0)))
1479                  (setq contype (mime-editor/get-contype tag))
1480                  (setq encoding (mime-editor/get-encoding tag))
1481                  ))
1482               (t
1483                ;; It's a multipart message.
1484                (goto-char (point-min))
1485                (while (re-search-forward
1486                        mime-editor/single-part-tag-regexp nil t)
1487                  (setq tag
1488                        (buffer-substring (match-beginning 0) (match-end 0)))
1489                  (delete-region (match-beginning 0) (match-end 0))
1490                  (setq contype (mime-editor/get-contype tag))
1491                  (setq encoding (mime-editor/get-encoding tag))
1492                  (insert "--" boundary "\n")
1493                  (insert "Content-Type: " contype "\n")
1494                  (if encoding
1495                      (insert "Content-Transfer-Encoding: " encoding "\n"))
1496                  )
1497                ;; Define Content-Type as "multipart/mixed".
1498                (setq contype
1499                      (concat "multipart/mixed; boundary=\"" boundary "\""))
1500                ;; Content-Transfer-Encoding must be "7bit".
1501                ;; The following encoding can be `nil', but is
1502                ;; specified as is since there is no way that a user
1503                ;; specifies it.
1504                (setq encoding "7bit")
1505                ;; Insert the trailer.
1506                (goto-char (point-max))
1507                (if multipart
1508                    (insert "--" boundary "--\n")
1509                  (insert "\n--" boundary "--\n")
1510                  )))
1511         (list contype encoding boundary nparts)
1512         ))))
1513
1514 (defun mime-editor/normalize-body ()
1515   "Normalize the body part by inserting appropriate message tags."
1516   ;; Insert the first MIME tags if necessary.
1517   (goto-char (point-min))
1518   (if (not (looking-at mime-editor/single-part-tag-regexp))
1519       (insert (mime-make-text-tag) "\n"))
1520   ;; Check each tag, and add new tag or correct it if necessary.
1521   (goto-char (point-min))
1522   (while (re-search-forward mime-editor/single-part-tag-regexp nil t)
1523     (let* ((tag (buffer-substring (match-beginning 0) (match-end 0)))
1524            (contype (mime-editor/get-contype tag))
1525            (charset (mime-get-parameter contype "charset"))
1526            (encoding (mime-editor/get-encoding tag)))
1527       ;; Remove extra whitespaces after the tag.
1528       (if (looking-at "[ \t]+$")
1529           (delete-region (match-beginning 0) (match-end 0)))
1530       (cond ((= (following-char) ?\^M)
1531              ;; It must be image, audio or video.
1532              (let ((beg (point))
1533                    (end (mime-editor/content-end)))
1534                ;; Insert explicit MIME tags after hidden messages.
1535                (forward-line 1)
1536                (if (and (not (eobp))
1537                         (not (looking-at mime-editor/single-part-tag-regexp)))
1538                    (progn
1539                      (insert (mime-make-text-tag) "\n")
1540                      (forward-line -1)  ;Process it again as text.
1541                      ))
1542                ;; Show a hidden message.  The point is not altered
1543                ;; after the conversion.
1544                (mime-flag-region beg end ?\n)))
1545             ((mime-test-content-type contype "message")
1546              ;; Content-type "message" should be sent as is.
1547              (forward-line 1))
1548             ((mime-test-content-type contype "text")
1549              ;; Define charset for text if necessary.
1550              (setq charset (or charset (mime-editor/choose-charset)))
1551              (mime-editor/define-charset charset)
1552              (if (string-equal contype "text/x-rot13-47")
1553                  (save-excursion
1554                    (forward-line)
1555                    (set-mark (point))
1556                    (goto-char (mime-editor/content-end))
1557                    (tm:caesar-region)
1558                    ))
1559              ;; Point is now on current tag.
1560              ;; Define encoding and encode text if necessary.
1561              (if (null encoding)        ;Encoding is not specified.
1562                  (let* ((encoding (mime-editor/choose-encoding))
1563                         (beg (mime-editor/content-beginning))
1564                         (end (mime-editor/content-end))
1565                         (body (buffer-substring beg end))
1566                         (encoded (funcall mime-string-encoder
1567                                           (cons charset encoding) body)))
1568                    (if (not (string-equal body encoded))
1569                        (progn
1570                          (goto-char beg)
1571                          (delete-region beg end)
1572                          (insert encoded)
1573                          (goto-char beg)))
1574                    (mime-editor/define-encoding encoding)))
1575              (forward-line 1))
1576             ((null encoding)            ;Encoding is not specified.
1577              ;; Application, image, audio, video, and any other
1578              ;; unknown content-type without encoding should be
1579              ;; encoded.
1580              (let* ((encoding "base64") ;Encode in BASE64 by default.
1581                     (beg (mime-editor/content-beginning))
1582                     (end (mime-editor/content-end))
1583                     (body (buffer-substring beg end))
1584                     (encoded (funcall mime-string-encoder
1585                                       (cons nil encoding) body)))
1586                (if (not (string-equal body encoded))
1587                    (progn
1588                      (goto-char beg)
1589                      (delete-region beg end)
1590                      (insert encoded)
1591                      (goto-char beg)))
1592                (mime-editor/define-encoding encoding))
1593              (forward-line 1))
1594             )
1595       )))
1596
1597 (defun mime-delete-field (field)
1598   "Delete header FIELD."
1599   (let ((regexp (format "^%s:[ \t]*" field)))
1600     (goto-char (point-min))
1601     (while (re-search-forward regexp nil t)
1602       (delete-region (match-beginning 0)
1603                      (progn (forward-line 1) (point)))
1604       )))
1605
1606 \f
1607 ;;;
1608 ;;; Platform dependent functions
1609 ;;;
1610
1611 ;; Emacs 18 implementations
1612
1613 (defun mime-body-charset-chooser-for-emacs18 (begin end)
1614   "Return a cons of charset and encoding of a message in a given region.
1615 Encoding name must be a canonical name, such as `base64'."
1616   '("US-ASCII" . nil)                   ;Default charset of MIME.
1617   )
1618
1619 (defun mime-string-encoder-for-emacs18 (method string)
1620   "For given METHOD that is a cons of charset and encoding, encode a STRING."
1621   (let ((charset (car method))
1622         (encoding (cdr method)))
1623     (cond ((stringp encoding)
1624            (mime-encode-string encoding string))
1625           ;; Return string without any encoding.
1626           (t string)
1627           )))
1628
1629 \f
1630 ;; Emacs 19 implementations
1631
1632 (defun mime-body-charset-chooser-for-emacs19 (begin end)
1633   "Return a cons of charset and encoding of a message in a given region.
1634 Encoding name must be a canonical name, such as `base64'.
1635 US-ASCII and ISO-8859-1 are supported on Emacs 19."
1636   (cond ((save-excursion
1637            (goto-char begin)
1638            (re-search-forward "[\200-\377]" end t))
1639          '("ISO-8859-1" . "quoted-printable"))
1640         (t
1641          '("US-ASCII" . nil))           ;Default charset of MIME.
1642         ))
1643
1644 (defun mime-string-encoder-for-emacs19 (method string)
1645   "For given METHOD that is a cons of charset and encoding, encode a STRING."
1646   (let ((charset (car method))
1647         (encoding (cdr method)))
1648     (cond ((stringp encoding)
1649            (mime-encode-string encoding string))
1650           ;; Return string without any encoding.
1651           (t string)
1652           )))
1653
1654 \f
1655 ;; NEmacs implementations
1656
1657 (defun mime-body-charset-chooser-for-nemacs (begin end)
1658   "Return a cons of charset and encoding of a message in a given region.
1659 Encoding name must be a canonical name, such as `base64'.
1660 US-ASCII and ISO-2022-JP are supported on NEmacs."
1661   (cond ((check-region-kanji-code begin end)
1662          ;; The following are safe encoding methods for use in
1663          ;; USENET News systems that strip off all ESCs.
1664          ;; '("ISO-2022-JP" . "quoted-printable")
1665          ;; '("ISO-2022-JP" . "base64")
1666          ;; The following expects transport systems are all MIME
1667          ;; compliants.  For instance, ESCs are never stripped off.
1668          '("ISO-2022-JP" . nil))
1669         (t
1670          '("US-ASCII" . nil))           ;Default charset of MIME.
1671         ))
1672
1673 (defun mime-string-encoder-for-nemacs (method string)
1674   "For given METHOD that is a cons of charset and encoding, encode a STRING.
1675 US-ASCII and ISO-2022-JP are supported on NEmacs."
1676   (let ((charset (car method))
1677         (encoding (cdr method)))
1678     (cond ((stringp encoding)
1679            (mime-encode-string encoding
1680                                ;; Convert internal (EUC) to JIS code.
1681                                (convert-string-kanji-code string 3 2)
1682                                ))
1683           ;; NEmacs can convert into ISO-2022-JP automatically,
1684           ;; but can do it myself as follows:
1685           ;;(t (convert-string-kanji-code string 3 2))
1686
1687           ;; Return string without any encoding.
1688           (t string)
1689           )))
1690
1691 \f
1692 ;; Mule implementations
1693 ;; Thanks to contributions by wkenji@flab.fujitsu.co.jp (Kenji
1694 ;; WAKAMIYA) and handa@etl.go.jp (Kenichi Handa).
1695
1696 (defun mime-body-charset-chooser-for-mule (begin end)
1697   "Return a cons of charset and encoding of a message in a given
1698 region.  Encoding name must be a canonical name, such as `base64'.
1699 US-ASCII, ISO-8859-* (except for ISO-8859-6), ISO-2022-JP,
1700 ISO-2022-JP-2 and ISO-2022-INT-1 are supported on Mule.  Either of
1701 charset ISO-2022-JP-2 or ISO-2022-INT-1 is used for multilingual text
1702 in Mule."
1703   (let ((lclist (find-charset-region begin end)))
1704     (cond ((null lclist)
1705            '("US-ASCII" . nil))         ;Default charset of MIME.
1706           ;; Multilingual capability requred.
1707           ((and (> (length lclist) 1)
1708                 (boundp '*iso-2022-int-1*))
1709            '("ISO-2022-INT-1" . nil))
1710           ((> (length lclist) 1)
1711            '("ISO-2022-JP-2" . nil))
1712           ;; Simple charset.
1713           ((memq lc-ltn1 lclist)
1714            '("ISO-8859-1" . "quoted-printable"))
1715           ((memq lc-ltn2 lclist)
1716            '("ISO-8859-2" . "quoted-printable"))
1717           ((memq lc-ltn3 lclist)
1718            '("ISO-8859-3" . "quoted-printable"))
1719           ((memq lc-ltn4 lclist)
1720            '("ISO-8859-4" . "quoted-printable"))
1721           ((memq lc-crl lclist)
1722            '("ISO-8859-5" . "quoted-printable"))
1723           ;;((memq lc-arb lclist)
1724           ;; '("ISO-8859-6" . "quoted-printable"))
1725           ((memq lc-grk lclist)
1726            '("ISO-8859-7" . "quoted-printable"))
1727           ((memq lc-hbw lclist)
1728            '("ISO-8859-8" . "quoted-printable"))
1729           ((memq lc-ltn5 lclist)
1730            '("ISO-8859-9" . "quoted-printable"))
1731           ((memq lc-jp lclist)
1732            '("ISO-2022-JP" . nil))
1733           ((memq lc-kr lclist)
1734            '("ISO-2022-KR" . nil))
1735           ;; Unknown charset.
1736           ((boundp '*iso-2022-int-1*)
1737            '("ISO-2022-INT-1" . nil))
1738           (t
1739            '("ISO-2022-JP-2" . nil))
1740           )))
1741
1742 (defun mime-string-encoder-for-mule (method string)
1743   "For given METHOD that is a cons of charset and encoding, encode a
1744 STRING.  US-ASCII, ISO-8859-* (except for ISO-8859-6), ISO-2022-JP,
1745 ISO-2022-JP-2 and ISO-2022-INT-1 are supported on Mule.  Either of
1746 charset ISO-2022-JP-2 or ISO-2022-INT-1 is used for multilingual
1747 text."
1748   (let* ((charset (car method))
1749          (encoding (cdr method))
1750          (coding-system
1751           (cdr (assoc (and (stringp charset) (upcase charset))
1752                       '(("ISO-8859-1" . *ctext*)
1753                         ("ISO-8859-2" . *iso-8859-2*)
1754                         ("ISO-8859-3" . *iso-8859-3*)
1755                         ("ISO-8859-4" . *iso-8859-4*)
1756                         ("ISO-8859-5" . *iso-8859-5*)
1757                         ;;("ISO-8859-6" . *iso-8859-6*)
1758                         ("ISO-8859-7" . *iso-8859-7*)
1759                         ("ISO-8859-8" . *iso-8859-8*)
1760                         ("ISO-8859-9" . *iso-8859-9*)
1761                         ("ISO-2022-JP" . *junet*)
1762                         ("ISO-2022-JP-2" . *iso-2022-ss2-7*)
1763                         ("ISO-2022-KR" . *korean-mail*)
1764                         ("ISO-2022-INT-1" . *iso-2022-int-1*)
1765                         )))))
1766     ;; In bilingual environment it may be unnecessary to convert the
1767     ;; coding system of the string unless transfer encoding is
1768     ;; required since such conversion may be performed by mule
1769     ;; automatically.
1770     (if (not (null coding-system))
1771         (setq string (code-convert-string string *internal* coding-system)))
1772     (if (stringp encoding)
1773         (setq string (mime-encode-string encoding string)))
1774     string
1775     ))
1776
1777 \f
1778 ;; Sun implementations
1779
1780 (defun mime-voice-recorder-for-sun ()
1781   "Record voice in a buffer using Sun audio device, and return the buffer.
1782 If the environment variable AUDIOHOST is defined, its value is used as
1783 a recording host instead of local host."
1784   (let ((buffer (get-buffer-create " *MIME audio*"))
1785         (host (getenv "AUDIOHOST")))
1786     (message "Start the recording on %s.  Type C-g to finish the recording..."
1787              (or host (system-name)))
1788     (save-excursion
1789       (set-buffer buffer)
1790       (erase-buffer)
1791       (condition-case errorcode
1792           (let ((selective-display nil) ;Disable ^M to nl translation.
1793                 (mc-flag nil)           ;Mule
1794                 (kanji-flag nil))       ;NEmacs
1795             ;; If AUDIOHOST is defined, use the value as recording host.
1796             (cond ((not (null host))
1797                    ;; Disable automatic conversion of coding system if Mule.
1798                    (if (featurep 'mule)
1799                        (define-program-coding-system nil "rsh" *noconv*))
1800                    (call-process "rsh"
1801                                  nil
1802                                  buffer
1803                                  nil
1804                                  host
1805                                  "cat"
1806                                  "/dev/audio"
1807                                  ))
1808                   (t
1809                    ;; Disable automatic conversion of coding system if Mule.
1810                    (if (featurep 'mule)
1811                        (define-program-coding-system nil "cat" *noconv*))
1812                    (call-process "cat"
1813                                  "/dev/audio"
1814                                  buffer
1815                                  nil
1816                                  ))))
1817         (quit (message "Type C-g to finish recording... done.")
1818               buffer                    ;Return the buffer
1819               )))))
1820
1821 \f
1822 ;;; @ Other useful commands.
1823 ;;;
1824
1825 ;; Message forwarding commands as content-type "message/rfc822".
1826
1827 (defun mime-editor/insert-message (&optional message)
1828   (interactive)
1829   (let ((inserter (assoc-value major-mode mime-editor/message-inserter-alist)))
1830     (if (and inserter (fboundp inserter))
1831         (progn
1832           (mime-editor/insert-tag "message" "rfc822")
1833           (funcall inserter message)
1834           )
1835       (message "Sorry, I don't have message inserter for your MUA.")
1836       )))
1837
1838 (defun mime-editor/insert-mail (&optional message)
1839   (interactive)
1840   (let ((inserter (assoc-value major-mode mime-editor/mail-inserter-alist)))
1841     (if (and inserter (fboundp inserter))
1842         (progn
1843           (mime-editor/insert-tag "message" "rfc822")
1844           (funcall inserter message)
1845           )
1846       (message "Sorry, I don't have mail inserter for your MUA.")
1847       )))
1848
1849 (defun mime-editor/inserted-message-filter ()
1850   (save-excursion
1851     (save-restriction
1852       (let ((header-start (point))
1853             (case-fold-search t)
1854             beg end)
1855         ;; for Emacs 18
1856         ;; (if (re-search-forward "^$" (marker-position (mark-marker)))
1857         (if (re-search-forward "^$" (mark t))
1858             (narrow-to-region header-start (match-beginning 0))
1859           )
1860         (goto-char header-start)
1861         (while (and (re-search-forward
1862                      mime-editor/yank-ignored-field-regexp nil t)
1863                     (setq beg (match-beginning 0))
1864                     (setq end (1+ (rfc822/field-end)))
1865                     )
1866           (delete-region beg end)
1867           )
1868         ))))
1869
1870 ;;; mime.el ends here
1871
1872 (defun mime-editor/find-inmost ()
1873   (goto-char (point-min))
1874   (if (re-search-forward mime-editor/multipart-beginning-regexp nil t)
1875       (let ((bb (match-beginning 0))
1876             (be (match-end 0))
1877             (type (buffer-substring (match-beginning 1)(match-end 1)))
1878             end-exp eb ee)
1879         (setq end-exp (format "^--}-<<%s>>\n" type))
1880         (widen)
1881         (if (re-search-forward end-exp nil t)
1882             (progn
1883               (setq eb (match-beginning 0))
1884               (setq ee (match-end 0))
1885               )
1886           (setq eb (point-max))
1887           (setq ee (point-max))
1888           )
1889         (narrow-to-region be eb)
1890         (goto-char be)
1891         (if (re-search-forward mime-editor/multipart-beginning-regexp nil t)
1892             (let (ret)
1893               (narrow-to-region (match-beginning 0)(point-max))
1894               (mime-editor/find-inmost)
1895               )
1896           (widen)
1897           ;;(delete-region eb ee)
1898           (list type bb be eb)
1899           ))))
1900
1901 (defun mime-editor/process-multipart-1 (boundary)
1902   (let ((ret (mime-editor/find-inmost)))
1903     (if ret
1904         (let ((type (car ret))
1905               (bb (nth 1 ret))(be (nth 2 ret))
1906               (eb (nth 3 ret))
1907               )
1908           (narrow-to-region bb eb)
1909           (delete-region bb be)
1910           (setq bb (point-min))
1911           (setq eb (point-max))
1912           (widen)
1913           (goto-char eb)
1914           (if (looking-at mime-editor/multipart-end-regexp)
1915               (let ((beg (match-beginning 0))
1916                     (end (match-end 0))
1917                     )
1918                 (delete-region beg end)
1919                 (if (and (not (looking-at mime-editor/single-part-tag-regexp))
1920                          (not (eobp)))
1921                     (insert (concat (mime-make-text-tag) "\n"))
1922                   )))
1923           (setq boundary
1924                 (nth 2 (mime-editor/translate-region bb eb
1925                                                      boundary t)))
1926           (goto-char bb)
1927           (insert
1928            (format "--[[multipart/%s; boundary=\"%s\"][7bit]]\n"
1929                    type boundary))
1930           boundary))))
1931
1932
1933 ;;; @ multipart enclosure
1934 ;;;
1935
1936 (defun mime-editor/enclose-region (type beg end)
1937   (save-excursion
1938     (goto-char beg)
1939     (let ((current (point))
1940           exist-prev-tag)
1941       (save-excursion
1942         (if (mime-editor/goto-tag)
1943             (or (eq current (match-beginning 0))
1944                 (setq exist-prev-tag t)
1945                 )))
1946       (save-restriction
1947         (narrow-to-region beg end)
1948         (goto-char beg)
1949         (if exist-prev-tag
1950             (insert "\n")
1951           )
1952         (insert (format "--<<%s>>-{\n" type))
1953         (goto-char (point-max))
1954         (insert (format "\n--}-<<%s>>\n" type))
1955         (goto-char (point-max))
1956         )
1957       (if (and (not (looking-at mime-editor/single-part-tag-regexp))
1958                (not (eobp)))
1959           (insert (mime-make-text-tag) "\n")
1960         )
1961       )))
1962
1963 (defun mime-editor/enclose-mixed-region (beg end)
1964   (interactive "*r")
1965   (mime-editor/enclose-region "mixed" beg end)
1966   )
1967
1968 (defun mime-editor/enclose-parallel-region (beg end)
1969   (interactive "*r")
1970   (mime-editor/enclose-region "parallel" beg end)
1971   )
1972
1973 (defun mime-editor/enclose-digest-region (beg end)
1974   (interactive "*r")
1975   (mime-editor/enclose-region "digest" beg end)
1976   )
1977
1978 (defun mime-editor/enclose-alternative-region (beg end)
1979   (interactive "*r")
1980   (mime-editor/enclose-region "alternative" beg end)
1981   )
1982
1983
1984 ;;; @ split
1985 ;;;
1986
1987 (defun mime-editor/insert-partial-header
1988   (fields subject id number total separator)
1989   (insert fields)
1990   (insert (format "Subject: %s (%d/%d)\n" subject number total))
1991   (insert (format "Mime-Version: 1.0 (split by tm-edit %s)\n"
1992                   mime-editor/version))
1993   (insert (format "\
1994 Content-Type: message/partial; id=%s; number=%d; total=%d\n%s\n"
1995                   id number total separator))
1996   )
1997
1998 (defun mime-editor/split-and-send (&optional cmd)
1999   (interactive)
2000   (let ((mime-editor/message-max-length
2001          (or (cdr (assq major-mode mime-editor/message-max-length-alist))
2002              mime-editor/message-default-max-length))
2003         (lines (count-lines (point-min) (point-max)))
2004         )
2005     (if (and (> lines mime-editor/message-max-length)
2006              mime-editor/split-message)
2007         (let* ((mime-editor/draft-file-name 
2008                 (or (buffer-file-name)
2009                     (make-temp-name
2010                      (expand-file-name "tm-draft" mime/tmp-dir))))
2011                (separator mail-header-separator)
2012                (config
2013                 (eval (cdr (assq major-mode mime-editor/window-config-alist))))
2014                (id (concat "\""
2015                            (replace-space-with-underline (current-time-string))
2016                            "@" (system-name) "\"")))
2017           (run-hooks 'mime-editor/before-split-hook)
2018           (let* ((header (rfc822/get-header-string-except
2019                           mime-editor/split-ignored-field-regexp separator))
2020                  (subject (mail-fetch-field "subject"))
2021                  (total (+ (/ lines mime-editor/message-max-length)
2022                            (if (> (mod lines mime-editor/message-max-length) 0)
2023                                1)))
2024                  (the-buf (current-buffer))
2025                  (buf (get-buffer "*tmp-send*"))
2026                  (command
2027                   (or cmd
2028                       (cdr
2029                        (assq major-mode
2030                              mime-editor/split-message-sender-alist))
2031                       (cdr
2032                        (assq major-mode
2033                              mime-editor/message-default-sender-alist))
2034                       ))
2035                  data)
2036             (goto-char (point-min))
2037             (if (re-search-forward (concat "^" (regexp-quote separator) "$")
2038                                    nil t)
2039                 (replace-match "")
2040               )
2041             (if buf
2042                 (progn
2043                   (switch-to-buffer buf)
2044                   (erase-buffer)
2045                   (switch-to-buffer the-buf)
2046                   )
2047               (setq buf (get-buffer-create "*tmp-send*"))
2048               )
2049             (switch-to-buffer buf)
2050             (make-local-variable 'mail-header-separator)
2051             (setq mail-header-separator separator)
2052             (switch-to-buffer the-buf)
2053             (goto-char (point-min))
2054             (re-search-forward "^$" nil t)
2055             (let ((mime-editor/partial-number 1))
2056               (setq data (buffer-substring
2057                           (point-min)
2058                           (progn
2059                             (goto-line mime-editor/message-max-length)
2060                             (point))
2061                           ))
2062               (delete-region (point-min)(point))
2063               (switch-to-buffer buf)
2064               (mime-editor/insert-partial-header
2065                header subject id mime-editor/partial-number total separator)
2066               (insert data)
2067               (save-excursion
2068                 (save-restriction
2069                   (goto-char (point-min))
2070                   (search-forward (concat "\n" mail-header-separator "\n"))
2071                   (narrow-to-region
2072                    (match-end 0)
2073                    (if (re-search-forward "^$" nil t)
2074                        (match-beginning 0)
2075                      (point-max)
2076                      ))
2077                   (goto-char (point-min))
2078                   (while (re-search-forward
2079                           mime-editor/split-blind-field-regexp nil t)
2080                     (delete-region (match-beginning 0)
2081                                    (let ((e (rfc822/field-end)))
2082                                      (if (< e (point-max))
2083                                          (1+ e)
2084                                        e)))
2085                     )
2086                   ))
2087               (save-excursion
2088                 (message (format "Sending %d/%d..."
2089                                  mime-editor/partial-number total))
2090                 (call-interactively command)
2091                 (message (format "Sending %d/%d... done"
2092                                  mime-editor/partial-number total))
2093                 )
2094               (erase-buffer)
2095               (switch-to-buffer the-buf)
2096               (setq mime-editor/partial-number 2)
2097               (while (< mime-editor/partial-number total)
2098                 (setq data (buffer-substring
2099                             (point-min)
2100                             (progn
2101                               (goto-line mime-editor/message-max-length)
2102                               (point))
2103                             ))
2104                 (delete-region (point-min)(point))
2105                 (switch-to-buffer buf)
2106                 (mime-editor/insert-partial-header
2107                  header subject id mime-editor/partial-number total separator)
2108                 (insert data)
2109                 (save-excursion
2110                   (message (format "Sending %d/%d..."
2111                                    mime-editor/partial-number total))
2112                   (call-interactively command)
2113                   (message (format "Sending %d/%d... done"
2114                                    mime-editor/partial-number total))
2115                   )
2116                 (erase-buffer)
2117                 (switch-to-buffer the-buf)
2118                 (setq mime-editor/partial-number
2119                       (1+ mime-editor/partial-number))
2120                 )
2121               (goto-char (point-min))
2122               (mime-editor/insert-partial-header
2123                header subject id mime-editor/partial-number total separator)
2124               (message (format "Sending %d/%d..."
2125                                mime-editor/partial-number total))
2126               ))))))
2127
2128
2129 ;;; @ preview message
2130 ;;;
2131
2132 (defun mime-editor/preview-message ()
2133   "preview editing MIME message. [tm-edit.el]"
2134   (interactive)
2135   (let* ((str (buffer-string))
2136          (separator mail-header-separator)
2137          (the-buf (current-buffer))
2138          (buf-name (buffer-name))
2139          (temp-buf-name (concat "*temp-article:" buf-name "*"))
2140          (buf (get-buffer temp-buf-name))
2141          )
2142     (if buf
2143         (progn
2144           (switch-to-buffer buf)
2145           (erase-buffer)
2146           )
2147       (setq buf (get-buffer-create temp-buf-name))
2148       (switch-to-buffer buf)
2149       )
2150     (insert str)
2151     (setq major-mode 'mime/temporary-message-mode)
2152     (make-local-variable 'mail-header-separator)
2153     (setq mail-header-separator separator)
2154     (make-local-variable 'mime/editing-buffer)
2155     (setq mime/editing-buffer the-buf)
2156     
2157     (run-hooks 'mime-editor/translate-hook)
2158     (mime-editor/translate-buffer)
2159     (goto-char (point-min))
2160     (if (re-search-forward
2161          (concat "^" (regexp-quote separator) "$"))
2162         (replace-match "")
2163       )
2164     (mime/viewer-mode)
2165     ))
2166
2167 (defun mime-editor/quitting-method ()
2168   (let ((temp mime::preview/article-buffer)
2169         buf)
2170     (mime-viewer/kill-buffer)
2171     (set-buffer temp)
2172     (setq buf mime/editing-buffer)
2173     (kill-buffer temp)
2174     (switch-to-buffer buf)
2175     ))
2176
2177 (set-alist 'mime-viewer/quitting-method-alist
2178            'mime/temporary-message-mode
2179            (function mime-editor/quitting-method)
2180            )
2181
2182
2183 ;;; @ draft preview
2184 ;;; 
2185 ;; by "OKABE Yasuo <okabe@kudpc.kyoto-u.ac.jp>
2186 ;;       Mon, 10 Apr 1995 20:03:07 +0900
2187
2188 (defvar mime-editor/draft-header-separator-alist
2189   '((news-reply-mode . mail-header-separator)
2190     (mh-letter-mode . mail-header-separator)
2191     ))
2192
2193 (defvar mime::article/draft-header-separator nil)
2194
2195 (defun mime-editor/draft-preview ()
2196   (interactive)
2197   (let ((sep (assoc-value major-mode mime-editor/draft-header-separator-alist)))
2198     (or (stringp sep) (setq sep (eval sep)))
2199     (make-variable-buffer-local 'mime::article/draft-header-separator)
2200     (goto-char (point-min))
2201     (re-search-forward
2202      (concat "^\\(" (regexp-quote sep) "\\)?$"))
2203     (setq mime::article/draft-header-separator
2204           (buffer-substring (match-beginning 0) (match-end 0)))
2205     (replace-match "")
2206     (mime/viewer-mode (current-buffer))
2207     (pop-to-buffer (current-buffer))
2208     ))
2209
2210 (defun mime-viewer::quitting-method/draft-preview ()
2211   (let ((mother mime::preview/mother-buffer))
2212     (save-excursion
2213       (switch-to-buffer mother)
2214       (goto-char (point-min))
2215       (if (and
2216            (re-search-forward
2217             (concat "^\\("
2218                     (regexp-quote mime::article/draft-header-separator)
2219                     "\\)?$") nil t)
2220            (bolp))
2221           (progn
2222             (insert mime::article/draft-header-separator)
2223             (set-buffer-modified-p (buffer-modified-p))
2224             )))
2225     (mime-viewer/kill-buffer)
2226     (pop-to-buffer mother)
2227     ))
2228
2229 (set-alist 'mime-viewer/quitting-method-alist
2230            'mh-letter-mode
2231            (function mime-viewer::quitting-method/draft-preview)
2232            )
2233
2234 (set-alist 'mime-viewer/quitting-method-alist
2235            'news-reply-mode
2236            (function mime-viewer::quitting-method/draft-preview)
2237            )
2238
2239
2240 ;;; @ etc
2241 ;;;
2242
2243 (defun replace-space-with-underline (str)
2244   (mapconcat (function
2245               (lambda (arg)
2246                 (char-to-string
2247                  (if (= arg 32)
2248                      ?_
2249                    arg)))) str "")
2250   )
2251
2252
2253 ;;; @ end
2254 ;;;
2255
2256 (provide 'tm-edit)
2257
2258 (run-hooks 'tm-edit-load-hook)