;; MORIOKA Tomohiko <morioka@jaist.ac.jp>
;; Maintainer: MORIOKA Tomohiko <morioka@jaist.ac.jp>
;; Created: 1994/08/21 renamed from mime.el
-;; Version: $Revision: 7.82 $
+;; Version: $Revision: 7.96 $
;; Keywords: mail, news, MIME, multimedia, multilingual
;; This file is part of tm (Tools for MIME).
;; General Public License for more details.
;; You should have received a copy of the GNU General Public License
-;; along with this program; see the file COPYING. If not, write to
-;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; along with GNU Emacs; see the file COPYING. If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;; This is an Emacs minor mode for editing Internet multimedia
-;; messages formatted in MIME (RFC 1521 and RFC 1522). All messages in
-;; this mode are composed in the tagged MIME format, that are
-;; described in the following examples. The messages composed in the
-;; tagged MIME format are automatically translated into a MIME
-;; compliant message when exiting the mode.
+;; messages formatted in MIME (RFC 2045, 2046, 2047, 2048 and 2049).
+;; All messages in this mode are composed in the tagged MIME format,
+;; that are described in the following examples. The messages
+;; composed in the tagged MIME format are automatically translated
+;; into a MIME compliant message when exiting the mode.
;; Mule (a multilingual extension to Emacs 18 and 19) has a capability
;; of handling multilingual text in limited ISO-2022 manner that is
;; based on early experiences in Japanese Internet community and
-;; resulted in RFC 1468 (ISO-2022-JP charset for MIME). In order to
+;; resulted in RFC 1468 (ISO-2022-JP charset for MIME). In order to
;; enable multilingual capability in single text message in MIME,
;; charset of multilingual text written in Mule is declared as either
-;; `ISO-2022-JP-2' [RFC 1554] or `ISO-2022-INT-1'. Mule is required
+;; `ISO-2022-JP-2' [RFC 1554] or `ISO-2022-INT-1'. Mule is required
;; for reading the such messages.
;; This MIME composer can work with Mail mode, mh-e letter Mode, and
;;;
(defconst mime-editor/RCS-ID
- "$Id: tm-edit.el,v 7.82 1996/09/05 12:08:56 morioka Exp $")
+ "$Id: tm-edit.el,v 7.96 1996/12/10 06:28:34 morioka Exp $")
(defconst mime-editor/version (get-version-string mime-editor/RCS-ID))
(defvar mime-auto-hide-body t
"*Hide non-textual body encoded in base64 after insertion if non-nil.")
-(defvar mime-voice-recorder
- (function mime-voice-recorder-for-sun)
- "*Function to record a voice message and return a buffer that contains it.")
+(defvar mime-editor/voice-recorder
+ (function mime-editor/voice-recorder-for-sun)
+ "*Function to record a voice message and encode it. [tm-edit.el]")
(defvar mime/editor-mode-hook nil
"*Hook called when enter MIME mode.")
;;;
(defvar mime-editor/yank-ignored-field-list
- '("Received" "Approved" "Path" "Replied" "Status" "X-VM-.*" "X-UIDL")
+ '("Received" "Approved" "Path" "Replied" "Status"
+ "Xref" "X-UIDL" "X-Filter" "X-Gnus-.*" "X-VM-.*")
"Delete these fields from original message when it is inserted
as message/rfc822 part.
Each elements are regexp of field-name. [tm-edit.el]")
(defvar mime-editor/split-message t
"*Split large message if it is non-nil. [tm-edit.el]")
-(defvar mime-editor/message-default-max-length 1000
- "*Default maximum size of a message. [tm-edit.el]")
+(defvar mime-editor/message-default-max-lines 1000
+ "*Default maximum lines of a message. [tm-edit.el]")
-(defvar mime-editor/message-max-length-alist
- '((news-reply-mode . 500)))
+(defvar mime-editor/message-max-lines-alist
+ '((news-reply-mode . 500))
+ "Alist of major-mode vs maximum lines of a message.
+If it is not specified for a major-mode,
+`mime-editor/message-default-max-lines' is used. [tm-edit.el]")
(defconst mime-editor/split-ignored-field-regexp
"\\(^Content-\\|^Subject:\\|^Mime-Version:\\)")
;;; @@ about PGP
;;;
-(defvar mime-editor/signing-type nil
+(defvar mime-editor/signing-type 'pgp-elkins
"*PGP signing type (pgp-elkins, pgp-kazu or nil). [tm-edit.el]")
-(defvar mime-editor/encrypting-type nil
+(defvar mime-editor/encrypting-type 'pgp-elkins
"*PGP encrypting type (pgp-elkins, pgp-kazu or nil). [tm-edit.el]")
-(if (or mime-editor/signing-type mime-editor/encrypting-type)
- (require 'mailcrypt)
- )
+(defvar mime-editor/pgp-sign-function 'tm:mc-pgp-sign-region)
+(defvar mime-editor/pgp-encrypt-function 'tm:mc-pgp-encrypt-region)
+(defvar mime-editor/traditional-pgp-sign-function 'mc-pgp-sign-region)
+(defvar mime-editor/pgp-insert-public-key-function 'mc-insert-public-key)
+
+(autoload mime-editor/pgp-sign-function "tm-edit-mc")
+(autoload mime-editor/pgp-encrypt-function "tm-edit-mc")
+(autoload mime-editor/traditional-pgp-sign-function "mc-pgp")
+(autoload mime-editor/pgp-insert-public-key-function "mc-toplev")
;;; @@ about tag
Hide a non-textual body message encoded in base64 after insertion
if non-nil.
- mime-voice-recorder
- Specifies a function to record a voice message and return a buffer
- that contains it. The function mime-voice-recorder-for-sun is for
- Sun SparcStations.
+ mime-editor/voice-recorder
+ Specifies a function to record a voice message and encode it.
+ The function `mime-editor/voice-recorder-for-sun' is for Sun
+ SparcStations.
mime/editor-mode-hook
Turning on MIME mode calls the value of mime/editor-mode-hook, if
"Insert a text message.
Charset is automatically obtained from the `mime/lc-charset-alist'."
(interactive)
- (if (and (mime-editor/insert-tag "text" nil nil)
- (looking-at mime-editor/single-part-tag-regexp))
+ (let ((ret (mime-editor/insert-tag "text" nil nil)))
+ (if ret
(progn
- ;; Make a space between the following message.
- (insert "\n")
- (forward-char -1)
- )))
+ (if (looking-at mime-editor/single-part-tag-regexp)
+ (progn
+ ;; Make a space between the following message.
+ (insert "\n")
+ (forward-char -1)
+ ))
+ (if (and (member (second ret) '("enriched" "richtext"))
+ (fboundp 'enriched-mode)
+ )
+ (enriched-mode t)
+ (if (boundp 'enriched-mode)
+ (enriched-mode nil)
+ ))))))
(defun mime-editor/insert-file (file)
"Insert a message from a file."
")"
))
": ")
- mime-encoding-method-alist nil t nil))))
+ mime-file-encoding-method-alist nil t nil))))
(if (string-equal encoding "")
(setq encoding default))
(if (or (consp parameters) (stringp disposition-type))
(setq attribute (car cell))
(setq value (cdr cell))
(if (eq value 'file)
- (setq value (rfc822/wrap-as-quoted-string
+ (setq value (std11-wrap-as-quoted-string
(file-name-nondirectory file)))
)
(setq parameters (concat parameters "; " attribute "=" value))
(setq attribute (car cell))
(setq value (cdr cell))
(if (eq value 'file)
- (setq value (rfc822/wrap-as-quoted-string
+ (setq value (std11-wrap-as-quoted-string
(file-name-nondirectory file)))
)
(setq parameters
(defun mime-editor/insert-voice ()
"Insert a voice message."
(interactive)
- (mime-editor/insert-tag "audio" "basic" nil)
- (let ((buffer (funcall mime-voice-recorder)))
- (unwind-protect
- (mime-editor/insert-binary-buffer buffer "base64")
- (kill-buffer buffer)
- )))
+ (let ((encoding
+ (completing-read
+ "What transfer encoding: "
+ mime-file-encoding-method-alist nil t nil)))
+ (mime-editor/insert-tag "audio" "basic" nil)
+ (mime-editor/define-encoding encoding)
+ (save-restriction
+ (narrow-to-region (1- (point))(point))
+ (unwind-protect
+ (funcall mime-editor/voice-recorder encoding)
+ (progn
+ (insert "\n")
+ (invisible-region (point-min)(point-max))
+ (goto-char (point-max))
+ )))))
(defun mime-editor/insert-signature (&optional arg)
"Insert a signature file."
"Insert new MIME tag and return a list of PRITYPE, SUBTYPE, and PARAMETERS.
If nothing is inserted, return nil."
(interactive)
+ (let ((p (point)))
+ (mime-editor/goto-tag)
+ (if (and (re-search-forward mime-editor/tag-regexp nil t)
+ (< (match-beginning 0) p)
+ (< p (match-end 0))
+ )
+ (goto-char (match-beginning 0))
+ (goto-char p)
+ ))
(let ((oldtag nil)
(newtag nil)
(current (point))
)
))
-;; Insert the binary content after MIME tag.
-;; modified by MORITA Masahiro <hiro@isl.ntt.JP>
-;; for x-uue
(defun mime-editor/insert-binary-file (file &optional encoding)
"Insert binary FILE at point.
Optional argument ENCODING specifies an encoding method such as base64."
- (let ((tmpbuf (get-buffer-create " *MIME insert*")))
- (save-excursion
- (set-buffer tmpbuf)
- (erase-buffer)
- (let ((mc-flag nil) ;Mule
- (file-coding-system-for-read
- (if (featurep 'mule) *noconv*))
- (kanji-flag nil) ;NEmacs
- (emx-binary-mode t) ;Stop CRLF to LF conversion in OS/2
- )
- (let (jka-compr-compression-info-list
- jam-zcat-filename-list)
- (insert-file-contents file))))
- (prog1
- (if (and (stringp encoding)
- (string-equal (downcase encoding) "x-uue"))
- (progn
- (require 'mel-u)
- (let ((uuencode-external-encoder
- (cons (car uuencode-external-encoder)
- (list (file-name-nondirectory file))
- )))
- (mime-editor/insert-binary-buffer tmpbuf encoding)
- ))
- (mime-editor/insert-binary-buffer tmpbuf encoding))
- (kill-buffer tmpbuf))))
-
-;; Insert the binary content after MIME tag.
-;; modified by MORITA Masahiro <hiro@isl.ntt.JP>
-;; for x-uue
-(defun mime-editor/insert-binary-buffer (buffer &optional encoding)
- "Insert binary BUFFER at point.
-Optional argument ENCODING specifies an encoding method such as base64."
(let* ((tagend (1- (point))) ;End of the tag
(hide-p (and mime-auto-hide-body
(stringp encoding)
- (let ((en (downcase encoding)))
- (or (string-equal en "base64")
- (string-equal en "x-uue")
- ))))
+ (not
+ (let ((en (downcase encoding)))
+ (or (string-equal en "7bit")
+ (string-equal en "8bit")
+ (string-equal en "binary")
+ )))))
)
(save-restriction
- (narrow-to-region (1- (point)) (point))
- (let ((start (point))
- (emx-binary-mode t)) ;Stop LF to CRLF conversion in OS/2
- (insert-buffer-substring buffer)
- ;; Encode binary message if necessary.
- (if encoding
- (mime-encode-region start (point-max) encoding)
- ))
+ (narrow-to-region tagend (point))
+ (mime-insert-encoded-file file encoding)
(if hide-p
(progn
(invisible-region (point-min) (point-max))
(goto-char (point-max))
)
+ (goto-char (point-max))
))
+ (or hide-p
+ (looking-at mime-editor/tag-regexp)
+ (= (point)(point-max))
+ (mime-editor/insert-tag "text" "plain")
+ )
;; Define encoding even if it is 7bit.
(if (stringp encoding)
(save-excursion
- (goto-char tagend) ;Make sure which line the tag is on.
- (mime-editor/define-encoding encoding)))
+ (goto-char tagend) ; Make sure which line the tag is on.
+ (mime-editor/define-encoding encoding)
+ ))
))
\f
(replace-match (concat "-" (substring tag 2)))
)))))
-(autoload 'mc-pgp-lookup-key "mc-pgp")
-(autoload 'mc-pgp-sign-region "mc-pgp")
-(autoload 'mc-pgp-encrypt-region "mc-pgp")
-
-(defun tm:mc-pgp-generic-parser (result)
- (let ((ret (mc-pgp-generic-parser result)))
- (if (consp ret)
- (vector (car ret)(cdr ret))
- )))
-
-(defun tm:mc-process-region
- (beg end passwd program args parser &optional buffer boundary)
- (let ((obuf (current-buffer))
- (process-connection-type nil)
- mybuf result rgn proc)
- (unwind-protect
- (progn
- (setq mybuf (or buffer (generate-new-buffer " *mailcrypt temp")))
- (set-buffer mybuf)
- (erase-buffer)
- (set-buffer obuf)
- (buffer-disable-undo mybuf)
- (setq proc
- (apply 'start-process "*PGP*" mybuf program args))
- (if passwd
- (progn
- (process-send-string proc (concat passwd "\n"))
- (or mc-passwd-timeout (mc-deactivate-passwd t))))
- (process-send-region proc beg end)
- (process-send-eof proc)
- (while (eq 'run (process-status proc))
- (accept-process-output proc 5))
- (setq result (process-exit-status proc))
- ;; Hack to force a status_notify() in Emacs 19.29
- (delete-process proc)
- (set-buffer mybuf)
- (goto-char (point-max))
- (if (re-search-backward "\nProcess \\*PGP.*\n\\'" nil t)
- (delete-region (match-beginning 0) (match-end 0)))
- (goto-char (point-min))
- ;; CRNL -> NL
- (while (search-forward "\r\n" nil t)
- (replace-match "\n"))
- ;; Hurm. FIXME; must get better result codes.
- (if (stringp result)
- (error "%s exited abnormally: '%s'" program result)
- (setq rgn (funcall parser result))
- ;; If the parser found something, migrate it
- (if (consp rgn)
- (progn
- (set-buffer obuf)
- (if boundary
- (save-restriction
- (narrow-to-region beg end)
- (goto-char beg)
- (insert (format "--%s\n" boundary))
- (goto-char (point-max))
- (insert (format "\n--%s
-Content-Type: application/pgp-signature
-Content-Transfer-Encoding: 7bit
-
-" boundary))
- (insert-buffer-substring mybuf (car rgn) (cdr rgn))
- (goto-char (point-max))
- (insert (format "\n--%s--\n" boundary))
- )
- (delete-region beg end)
- (goto-char beg)
- (insert-buffer-substring mybuf (car rgn) (cdr rgn))
- )
- (set-buffer mybuf)
- (delete-region (car rgn) (cdr rgn)))))
- ;; Return nil on failure and exit code on success
- (if rgn result))
- ;; Cleanup even on nonlocal exit
- (if (and proc (eq 'run (process-status proc)))
- (interrupt-process proc))
- (set-buffer obuf)
- (or buffer (null mybuf) (kill-buffer mybuf)))))
-
-(defun tm:mc-pgp-sign-region (start end &optional id unclear boundary)
- (if (not (boundp 'mc-pgp-user-id))
- (load "mc-pgp")
- )
- (let ((process-environment process-environment)
- (buffer (get-buffer-create mc-buffer-name))
- passwd args key
- (parser (function mc-pgp-generic-parser))
- (pgp-path mc-pgp-path)
- )
- (setq key (mc-pgp-lookup-key (or id mc-pgp-user-id)))
- (setq passwd
- (mc-activate-passwd
- (cdr key)
- (format "PGP passphrase for %s (%s): " (car key) (cdr key))))
- (setenv "PGPPASSFD" "0")
- (setq args
- (cons
- (if boundary
- "-fbast"
- "-fast")
- (list "+verbose=1" "+language=en"
- (format "+clearsig=%s" (if unclear "off" "on"))
- "+batchmode" "-u" (cdr key))))
- (if mc-pgp-comment
- (setq args (cons (format "+comment=%s" mc-pgp-comment) args))
- )
- (message "Signing as %s ..." (car key))
- (if (tm:mc-process-region
- start end passwd pgp-path args parser buffer boundary)
- (progn
- (if boundary
- (progn
- (goto-char (point-min))
- (insert
- (format "\
---[[multipart/signed; protocol=\"application/pgp-signature\";
- boundary=\"%s\"; micalg=pgp-md5][7bit]]\n" boundary))
- ))
- (message "Signing as %s ... Done." (car key))
- t)
- nil)))
-
(defun mime-editor/sign-pgp-elkins (beg end boundary)
(save-excursion
(save-restriction
(insert (format "Content-Transfer-Encoding: %s\n" encoding))
)
(insert "\n")
- (or (tm:mc-pgp-sign-region (point-min)(point-max)
- nil nil pgp-boundary)
+ (or (funcall mime-editor/pgp-sign-function
+ (point-min)(point-max) nil nil pgp-boundary)
(throw 'mime-editor/error 'pgp-error)
)
))))
(insert (format "Content-Transfer-Encoding: %s\n" encoding))
)
(insert "\n")
- (if (null
- (let ((mc-pgp-always-sign 'never))
- (mc-pgp-encrypt-region
- (mc-split "\\([ \t\n]*,[ \t\n]*\\)+" recipients)
- (point-min) (point-max) from nil)
- ))
+ (or (funcall mime-editor/pgp-encrypt-function
+ recipients (point-min) (point-max) from)
(throw 'mime-editor/error 'pgp-error)
- )
+ )
(goto-char beg)
(insert (format "--[[multipart/encrypted;
boundary=\"%s\";
(insert (format "Content-Transfer-Encoding: %s\n" encoding))
)
(insert "\n")
- (or (as-binary-process (mc-pgp-sign-region beg (point-max)))
+ (or (as-binary-process
+ (funcall mime-editor/traditional-pgp-sign-function
+ beg (point-max)))
(throw 'mime-editor/error 'pgp-error)
)
(goto-char beg)
)
(insert "\n")
(or (as-binary-process
- (mc-pgp-encrypt-region
- (mc-split "\\([ \t\n]*,[ \t\n]*\\)+" recipients)
- beg (point-max))
+ (funcall mime-editor/pgp-encrypt-function
+ recipients beg (point-max) nil 'maybe)
)
(throw 'mime-editor/error 'pgp-error)
)
(point)))
(end (mime-editor/content-end))
)
+ ;; Patch for hard newlines
+ ;; (save-excursion
+ ;; (goto-char beg)
+ ;; (while (search-forward "\n" end t)
+ ;; (put-text-property (match-beginning 0)
+ ;; (point)
+ ;; 'hard t)))
+ ;; End patch for hard newlines
(enriched-encode beg end)
(goto-char beg)
(if (search-forward "\n\n")
;; Sun implementations
-(defun mime-voice-recorder-for-sun ()
- "Record voice in a buffer using Sun audio device, and return the buffer.
-If the environment variable AUDIOHOST is defined, its value is used as
-a recording host instead of local host."
- (let ((buffer (get-buffer-create " *MIME audio*"))
- (host (getenv "AUDIOHOST")))
- (message "Start the recording on %s. Type C-g to finish the recording..."
- (or host (system-name)))
- (save-excursion
- (set-buffer buffer)
- (erase-buffer)
- (condition-case errorcode
- (let ((selective-display nil) ;Disable ^M to nl translation.
- (mc-flag nil) ;Mule
- (kanji-flag nil)) ;NEmacs
- ;; If AUDIOHOST is defined, use the value as recording host.
- (cond ((not (null host))
- ;; Disable automatic conversion of coding system if Mule.
- (if (featurep 'mule)
- (define-program-coding-system nil "rsh" *noconv*))
- (call-process "rsh"
- nil
- buffer
- nil
- host
- "cat"
- "/dev/audio"
- ))
- (t
- ;; Disable automatic conversion of coding system if Mule.
- (if (featurep 'mule)
- (define-program-coding-system nil "cat" *noconv*))
- (call-process "cat"
- "/dev/audio"
- buffer
- nil
- ))))
- (quit (message "Type C-g to finish recording... done.")
- buffer ;Return the buffer
- )))))
+(defun mime-editor/voice-recorder-for-sun (encoding)
+ "Record voice in a buffer using Sun audio device,
+and insert data encoded as ENCODING. [tm-edit.el]"
+ (message "Start the recording on %s. Type C-g to finish the recording..."
+ (system-name))
+ (mime-insert-encoded-file "/dev/audio" encoding)
+ )
\f
;;; @ Other useful commands.
(interactive "P")
(mime-editor/insert-tag "application" "pgp-keys")
(mime-editor/define-encoding "7bit")
- (mc-insert-public-key)
+ (funcall mime-editor/pgp-insert-public-key-function)
)
)
(or mime-editor/message-max-length
(setq mime-editor/message-max-length
- (or (cdr (assq major-mode mime-editor/message-max-length-alist))
- mime-editor/message-default-max-length))
+ (or (cdr (assq major-mode mime-editor/message-max-lines-alist))
+ mime-editor/message-default-max-lines))
)
(let* ((mime-editor/draft-file-name
(or (buffer-file-name)
(run-hooks 'mime-editor/before-split-hook)
(let ((the-buf (current-buffer))
(copy-buf (get-buffer-create " *Original Message*"))
- (header (rfc822/get-header-string-except
+ (header (std11-header-string-except
mime-editor/split-ignored-field-regexp separator))
(subject (mail-fetch-field "subject"))
(total (+ (/ lines mime-editor/message-max-length)
(cdr
(assq major-mode
mime-editor/split-message-sender-alist))
+ (function
+ (lambda ()
+ (interactive)
+ (error "Split sender is not specified for `%s'." major-mode)
+ ))
))
(mime-editor/partial-number 1)
data)
(goto-char (point-min))
(while (re-search-forward mime-editor/split-blind-field-regexp nil t)
(delete-region (match-beginning 0)
- (1+ (rfc822/field-end)))
+ (1+ (std11-field-end)))
)))
(while (< mime-editor/partial-number total)
(erase-buffer)
(interactive)
(run-hooks 'mime-editor/before-send-hook)
(let ((mime-editor/message-max-length
- (or (cdr (assq major-mode mime-editor/message-max-length-alist))
- mime-editor/message-default-max-length))
+ (or (cdr (assq major-mode mime-editor/message-max-lines-alist))
+ mime-editor/message-default-max-lines))
(lines (count-lines (point-min) (point-max)))
)
(if (and (> lines mime-editor/message-max-length)
(t
(let* (charset
(pstr
- (mapconcat (function
- (lambda (attr)
- (if (string-equal (car attr)
- "charset")
- (progn
- (setq charset (cdr attr))
- "")
- (concat ";" (car attr)
- "=" (cdr attr))
- )
- ))
- params ""))
+ (let ((bytes (+ 14 (length ctype))))
+ (mapconcat (function
+ (lambda (attr)
+ (if (string-equal (car attr) "charset")
+ (progn
+ (setq charset (cdr attr))
+ "")
+ (let* ((str
+ (concat (car attr)
+ "=" (cdr attr))
+ )
+ (bs (length str))
+ )
+ (setq bytes (+ bytes bs 2))
+ (if (< bytes 76)
+ (concat "; " str)
+ (setq bytes (+ bs 1))
+ (concat ";\n " str)
+ )
+ ))))
+ params "")))
encoding
encoded)
(save-excursion
"Content-Transfer-Encoding:" nil t)
(let ((beg (match-beginning 0))
(hbeg (match-end 0))
- (end (rfc822/field-end)))
+ (end (std11-field-end)))
(setq encoding
(eliminate-top-spaces
- (rfc822/unfolding-string
+ (std11-unfold-string
(buffer-substring hbeg end))))
(if (or charset (string-equal type "text"))
(progn
(goto-char (point-min))
(while (re-search-forward
"^\\(Content-.*\\|Mime-Version\\):" nil t)
- (delete-region (match-beginning 0) (1+ (rfc822/field-end)))
+ (delete-region (match-beginning 0) (1+ (std11-field-end)))
))
(or no-separator
(and (re-search-forward "^$")