2 ;;; tm-comp.el: attachment for MIME composer
4 ;;; by MORIOKA Tomohiko <morioka@jaist.ac.jp>
5 ;;; and OKABE Yasuo <okabe@kudpc.kyoto-u.ac.jp>
6 ;;; modified by MORITA Masahiro <hiro@isl.ntt.JP>
8 ;;; Kazushi (Jam) MARUKAWA <kazusi-m@is.aist-nara.ac.jp>,
10 ;;; KOBAYASHI Shuhei <shuhei@cmpt01.phys.tohoku.ac.jp>,
11 ;;; and YAMAOKA Katsumi <yamaoka@ga.sony.co.jp>
24 (defconst mime/composer-RCS-ID
25 "$Id: tm-comp.el,v 6.10 1995/08/30 05:37:50 morioka Exp $")
27 (defconst mime/composer-version (get-version-string mime/composer-RCS-ID))
33 (defvar mime/message-default-max-length 1000)
35 (defvar mime/message-max-length-alist
36 '((news-reply-mode . 500)))
38 (defconst mime/message-nuke-headers
39 "\\(^Content-\\|^Subject:\\|^MIME-Version:\\)")
40 (defvar mime/message-blind-headers "\\(^[BDFbdf]cc:\\|^cc:[ \t]*$\\)")
42 (defvar mime/message-default-sender-alist
43 '((mail-mode . mail-send-and-exit)
44 (mh-letter-mode . mh-send-letter)
45 (news-reply-mode . gnus-inews-news)))
47 (defvar mime/message-sender-alist
48 '((mail-mode . (lambda ()
52 (mh-letter-mode . (lambda (&optional arg)
54 (write-region (point-min) (point-max)
57 (format "Sending %d/%d..." (+ i 1) total))
59 (pop-to-buffer "MH mail delivery")
61 (mh-exec-cmd-output mh-send-prog t "-watch" "-nopush"
62 "-nodraftfolder" mh-send-args mime/draft-file-name)
63 (goto-char (point-max)) ; show the interesting part
67 (apply 'mh-exec-cmd-quiet t mh-send-prog
69 (list "-nopush" "-nodraftfolder" "-noverbose" "-nowatch" mh-send-args mime/draft-file-name)))))
71 (format "Sending %d/%d... done" (+ i 1) total))
76 (defvar mime/window-config-alist
78 (mh-letter-mode . mh-previous-window-config)
79 (news-reply-mode . (cond ((boundp 'gnus-winconf-post-news)
81 gnus-winconf-post-news
82 (setq gnus-winconf-post-news nil)
84 ((boundp 'gnus-prev-winconf)
87 (setq gnus-prev-winconf nil)
92 (defvar mime/news-reply-mode-server-running nil)
95 (defvar mime/message-before-send-hook-alist
96 '((mh-letter-mode . mh-before-send-letter-hook)))
98 (defvar mime/message-after-send-hook-alist
99 '((mh-letter-mode . (lambda ()
101 (mh-annotate-msg mh-sent-from-msg
104 "-component" mh-annotate-field
108 (mh-get-field "Cc:"))))))
111 (defvar tm-comp/message-inserter-alist nil)
117 (defun tm-comp::mime-insert-file (file)
118 "Insert a message from a file."
119 (interactive "fInsert file as MIME message: ")
120 (let* ((guess (mime-find-file-type file))
121 (pritype (nth 0 guess))
122 (subtype (nth 1 guess))
123 (parameters (nth 2 guess))
124 (default (nth 3 guess)) ;Guess encoding from its file name.
126 (if (not (interactive-p))
129 (concat "What transfer encoding"
132 (if (string-equal default "") "\"\"" default)
136 mime-transfer-encoders nil t nil))))
137 (if (string-equal encoding "")
138 (setq encoding default))
139 (if (consp parameters)
140 (let ((rest parameters) cell attribute value)
143 (setq cell (car rest))
144 (setq attribute (car cell))
145 (setq value (cdr cell))
147 (setq value (file-name-nondirectory file))
149 (setq parameters (concat parameters "; " attribute "=" value))
150 (setq rest (cdr rest))
152 (mime-insert-tag pritype subtype parameters)
153 (mime-insert-binary-file file encoding)
156 ;; Insert the binary content after MIME tag.
157 ;; modified by MORITA Masahiro <hiro@isl.ntt.JP>
159 (defun tm-comp::mime-insert-binary-file (file &optional encoding)
160 "Insert binary FILE at point.
161 Optional argument ENCODING specifies an encoding method such as base64."
162 (let ((tmpbuf (get-buffer-create " *MIME insert*")))
166 (let ((mc-flag nil) ;Mule
167 (file-coding-system-for-read
168 (if (featurep 'mule) *noconv*))
169 (kanji-flag nil)) ;NEmacs
170 (let (jka-compr-compression-info-list
171 jam-zcat-filename-list)
172 (insert-file-contents file))))
174 (if (and (stringp encoding)
175 (string-equal (downcase encoding) "x-uue"))
176 (let ((mime-transfer-encoders
177 (copy-alist (cons (list "x-uue" "uuencode"
178 (file-name-nondirectory file))
179 mime-transfer-encoders))))
180 (mime-insert-binary-buffer tmpbuf encoding))
181 (mime-insert-binary-buffer tmpbuf encoding))
182 (kill-buffer tmpbuf))))
184 ;; Insert the binary content after MIME tag.
185 ;; modified by MORITA Masahiro <hiro@isl.ntt.JP>
187 (defun tm-comp::mime-insert-binary-buffer (buffer &optional encoding)
188 "Insert binary BUFFER at point.
189 Optional argument ENCODING specifies an encoding method such as base64."
190 (let* ((tagend (1- (point))) ;End of the tag
191 (hide-p (and mime-auto-hide-body
193 (let ((en (downcase encoding)))
194 (or (string-equal en "base64")
195 (string-equal en "x-uue")
199 (narrow-to-region (1- (point)) (point))
200 (let ((start (point)))
201 (insert-buffer-substring buffer)
202 ;; Encode binary message if necessary.
204 (mime-encode-region encoding start (point-max))))
207 (mime-flag-region (point-min) (1- (point-max)) ?\^M)
208 (goto-char (point-max)))
210 ;; Define encoding even if it is 7bit.
211 (if (stringp encoding)
213 (goto-char tagend) ;Make sure which line the tag is on.
214 (mime-define-encoding encoding)))
218 (defun tm-comp/insert-message (&optional message)
220 (let ((inserter (assoc-value major-mode tm-comp/message-inserter-alist)))
221 (if (and inserter (fboundp inserter))
223 (mime-insert-tag "message" "rfc822")
224 (funcall inserter message)
226 (message "Sorry, I don't have message inserter for your MUA.")
233 (defun mime/split-and-send (&optional cmd)
235 (let ((mime/message-max-length
236 (or (cdr (assq major-mode mime/message-max-length-alist))
237 mime/message-default-max-length))
238 (lines (count-lines (point-min) (point-max)))
240 (if (<= lines mime/message-max-length)
242 (or cmd (cdr (assq major-mode mime/message-default-sender-alist))))
243 (let* ((mime/draft-file-name
244 (or (buffer-file-name)
245 (make-temp-name (expand-file-name "tm-draft" mime/tmp-dir))))
246 (separator mail-header-separator)
247 (config (eval (cdr (assq major-mode mime/window-config-alist))))
249 (replace-space-with-underline (current-time-string))
250 "@" (system-name) "\"")))
252 (let ((hook (cdr (assq major-mode
253 mime/message-before-send-hook-alist))))
255 (let* ((header (message/get-header-string-except
256 mime/message-nuke-headers separator))
257 (orig-header (message/get-header-string-except
258 mime/message-blind-headers separator))
259 (subject (mail-fetch-field "subject"))
260 (total (+ (/ lines mime/message-max-length)
261 (if (> (mod lines mime/message-max-length) 0)
264 (l mime/message-max-length)
265 (the-buf (current-buffer))
266 (buf (get-buffer "*tmp-send*"))
269 (cdr (assq major-mode mime/message-sender-alist))
270 (cdr (assq major-mode mime/message-default-sender-alist))))
272 (goto-char (point-min))
273 (if (re-search-forward (concat "^" (regexp-quote separator) "$")
279 (switch-to-buffer buf)
281 (switch-to-buffer the-buf)
283 (setq buf (get-buffer-create "*tmp-send*"))
285 (switch-to-buffer buf)
286 (make-variable-buffer-local 'mail-header-separator)
287 (setq mail-header-separator separator)
288 (switch-to-buffer the-buf)
289 (goto-char (point-min))
290 (re-search-forward "^$" nil t)
292 (setq buf (get-buffer "*tmp-send*"))
293 (setq data (buffer-substring
299 (switch-to-buffer buf)
302 (format "Subject: %s (%d/%d)\n" subject (+ i 1) total))
304 (format "Mime-Version: 1.0 (split by tm-comp %s)\n"
305 mime/composer-version))
308 "Content-Type: message/partial; id=%s; number=%d; total=%d\n%s\n"
309 id (+ i 1) total separator))
311 (insert orig-header))
314 (call-interactively command))
316 (switch-to-buffer the-buf)
317 (setq l (+ l mime/message-max-length))
322 (cdr (assq major-mode mime/message-after-send-hook-alist))))
324 (set-buffer-modified-p nil)
325 (cond ((y-or-n-p "Kill draft buffer? ")
326 (kill-buffer (current-buffer))
328 (set-window-configuration config))))
332 (defun tm-comp::mime-mode-exit-and-run ()
335 (call-interactively 'mime/split-and-send)
342 (add-hook 'mime-mode-hook
345 (if (not (fboundp 'original::mime-insert-file))
347 (fset 'original::mime-insert-file
348 (symbol-function 'mime-insert-file))
349 (fset 'mime-insert-file 'tm-comp::mime-insert-file)
351 (if (not (fboundp 'original::mime-insert-binary-file))
353 (fset 'original::mime-insert-binary-file
354 (symbol-function 'mime-insert-binary-file))
355 (fset 'mime-insert-binary-file
356 'tm-comp::mime-insert-binary-file)
358 (if (not (fboundp 'original::mime-insert-binary-buffer))
360 (fset 'original::mime-insert-binary-buffer
361 (symbol-function 'mime-insert-binary-buffer))
362 (fset 'mime-insert-binary-buffer
363 'tm-comp::mime-insert-binary-buffer)
365 (if (not (fboundp 'original::mime-mode-exit-and-run))
367 (fset 'original::mime-mode-exit-and-run
368 (symbol-function 'mime-mode-exit-and-run))
369 (fset 'mime-mode-exit-and-run
370 'tm-comp::mime-mode-exit-and-run)
372 (define-key (lookup-key (current-local-map) mime-prefix)
373 "m" 'tm-comp/insert-message)
379 ;; by "OKABE Yasuo <okabe@kudpc.kyoto-u.ac.jp>
380 ;; Mon, 10 Apr 1995 20:03:07 +0900
382 (defvar mime/draft-header-separator-alist
383 '((news-reply-mode . mail-header-separator)
384 (mh-letter-mode . mail-header-separator)
387 (defvar mime::article/draft-header-separator nil)
389 (defun mime/draft-preview ()
391 (let ((sep (assoc-value major-mode mime/draft-header-separator-alist)))
392 (or (stringp sep) (setq sep (eval sep)))
393 (make-variable-buffer-local 'mime::article/draft-header-separator)
394 (goto-char (point-min))
396 (concat "^\\(" (regexp-quote sep) "\\)?$"))
397 (setq mime::article/draft-header-separator
398 (buffer-substring (match-beginning 0) (match-end 0)))
400 (mime/viewer-mode (current-buffer))
401 (pop-to-buffer (current-buffer))
404 (defun mime-viewer::quitting-method/draft-preview ()
405 (let ((mother mime/mother-buffer))
407 (switch-to-buffer mother)
408 (goto-char (point-min))
412 (regexp-quote mime::article/draft-header-separator)
416 (insert mime::article/draft-header-separator)
417 (set-buffer-modified-p (buffer-modified-p))
419 (mime-viewer/kill-buffer)
420 (pop-to-buffer mother)
423 (set-alist 'mime-viewer/quitting-method-alist
425 (function mime-viewer::quitting-method/draft-preview)
428 (set-alist 'mime-viewer/quitting-method-alist
430 (function mime-viewer::quitting-method/draft-preview)
437 (defun message/get-header-string-except (pat boundary)
440 (narrow-to-region (goto-char (point-min))
443 (concat "^\\(" (regexp-quote boundary) "\\)?$")
447 (goto-char (point-min))
449 (while (re-search-forward message/field-regexp nil t)
450 (setq field (buffer-substring (match-beginning 0)
453 (if (not (string-match pat field))
454 (setq header (concat header field "\n"))
459 (defun replace-space-with-underline (str)
471 (run-hooks 'tm-comp-load-hook)