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>
23 (defconst mime/composer-RCS-ID
24 "$Id: tm-comp.el,v 6.11 1995/09/04 00:47:37 morioka Exp $")
26 (defconst mime/composer-version (get-version-string mime/composer-RCS-ID))
32 (defvar mime/message-default-max-length 1000)
34 (defvar mime/message-max-length-alist
35 '((news-reply-mode . 500)))
37 (defconst mime/message-nuke-headers
38 "\\(^Content-\\|^Subject:\\|^MIME-Version:\\)")
39 (defvar mime/message-blind-headers "\\(^[BDFbdf]cc:\\|^cc:[ \t]*$\\)")
41 (defvar mime/message-default-sender-alist
42 '((mail-mode . mail-send-and-exit)
43 (mh-letter-mode . mh-send-letter)
44 (news-reply-mode . gnus-inews-news)))
46 (defvar mime/message-sender-alist
47 '((mail-mode . (lambda ()
51 (mh-letter-mode . (lambda (&optional arg)
53 (write-region (point-min) (point-max)
56 (format "Sending %d/%d..." (+ i 1) total))
58 (pop-to-buffer "MH mail delivery")
60 (mh-exec-cmd-output mh-send-prog t "-watch" "-nopush"
61 "-nodraftfolder" mh-send-args mime/draft-file-name)
62 (goto-char (point-max)) ; show the interesting part
66 (apply 'mh-exec-cmd-quiet t mh-send-prog
68 (list "-nopush" "-nodraftfolder" "-noverbose" "-nowatch" mh-send-args mime/draft-file-name)))))
70 (format "Sending %d/%d... done" (+ i 1) total))
75 (defvar mime/window-config-alist
77 (mh-letter-mode . mh-previous-window-config)
78 (news-reply-mode . (cond ((boundp 'gnus-winconf-post-news)
80 gnus-winconf-post-news
81 (setq gnus-winconf-post-news nil)
83 ((boundp 'gnus-prev-winconf)
86 (setq gnus-prev-winconf nil)
91 (defvar mime/news-reply-mode-server-running nil)
94 (defvar mime/message-before-send-hook-alist
95 '((mh-letter-mode . mh-before-send-letter-hook)))
97 (defvar mime/message-after-send-hook-alist
98 '((mh-letter-mode . (lambda ()
100 (mh-annotate-msg mh-sent-from-msg
103 "-component" mh-annotate-field
107 (mh-get-field "Cc:"))))))
110 (defvar tm-comp/message-inserter-alist nil)
116 (defun tm-comp::mime-insert-file (file)
117 "Insert a message from a file."
118 (interactive "fInsert file as MIME message: ")
119 (let* ((guess (mime-find-file-type file))
120 (pritype (nth 0 guess))
121 (subtype (nth 1 guess))
122 (parameters (nth 2 guess))
123 (default (nth 3 guess)) ;Guess encoding from its file name.
125 (if (not (interactive-p))
128 (concat "What transfer encoding"
131 (if (string-equal default "") "\"\"" default)
135 mime-transfer-encoders nil t nil))))
136 (if (string-equal encoding "")
137 (setq encoding default))
138 (if (consp parameters)
139 (let ((rest parameters) cell attribute value)
142 (setq cell (car rest))
143 (setq attribute (car cell))
144 (setq value (cdr cell))
146 (setq value (file-name-nondirectory file))
148 (setq parameters (concat parameters "; " attribute "=" value))
149 (setq rest (cdr rest))
151 (mime-insert-tag pritype subtype parameters)
152 (mime-insert-binary-file file encoding)
155 ;; Insert the binary content after MIME tag.
156 ;; modified by MORITA Masahiro <hiro@isl.ntt.JP>
158 (defun tm-comp::mime-insert-binary-file (file &optional encoding)
159 "Insert binary FILE at point.
160 Optional argument ENCODING specifies an encoding method such as base64."
161 (let ((tmpbuf (get-buffer-create " *MIME insert*")))
165 (let ((mc-flag nil) ;Mule
166 (file-coding-system-for-read
167 (if (featurep 'mule) *noconv*))
168 (kanji-flag nil)) ;NEmacs
169 (let (jka-compr-compression-info-list
170 jam-zcat-filename-list)
171 (insert-file-contents file))))
173 (if (and (stringp encoding)
174 (string-equal (downcase encoding) "x-uue"))
175 (let ((mime-transfer-encoders
176 (copy-alist (cons (list "x-uue" "uuencode"
177 (file-name-nondirectory file))
178 mime-transfer-encoders))))
179 (mime-insert-binary-buffer tmpbuf encoding))
180 (mime-insert-binary-buffer tmpbuf encoding))
181 (kill-buffer tmpbuf))))
183 ;; Insert the binary content after MIME tag.
184 ;; modified by MORITA Masahiro <hiro@isl.ntt.JP>
186 (defun tm-comp::mime-insert-binary-buffer (buffer &optional encoding)
187 "Insert binary BUFFER at point.
188 Optional argument ENCODING specifies an encoding method such as base64."
189 (let* ((tagend (1- (point))) ;End of the tag
190 (hide-p (and mime-auto-hide-body
192 (let ((en (downcase encoding)))
193 (or (string-equal en "base64")
194 (string-equal en "x-uue")
198 (narrow-to-region (1- (point)) (point))
199 (let ((start (point)))
200 (insert-buffer-substring buffer)
201 ;; Encode binary message if necessary.
203 (mime-encode-region encoding start (point-max))))
206 (mime-flag-region (point-min) (1- (point-max)) ?\^M)
207 (goto-char (point-max)))
209 ;; Define encoding even if it is 7bit.
210 (if (stringp encoding)
212 (goto-char tagend) ;Make sure which line the tag is on.
213 (mime-define-encoding encoding)))
217 (defun tm-comp/insert-message (&optional message)
219 (let ((inserter (assoc-value major-mode tm-comp/message-inserter-alist)))
220 (if (and inserter (fboundp inserter))
222 (mime-insert-tag "message" "rfc822")
223 (funcall inserter message)
225 (message "Sorry, I don't have message inserter for your MUA.")
232 (defun mime/split-and-send (&optional cmd)
234 (let ((mime/message-max-length
235 (or (cdr (assq major-mode mime/message-max-length-alist))
236 mime/message-default-max-length))
237 (lines (count-lines (point-min) (point-max)))
239 (if (<= lines mime/message-max-length)
241 (or cmd (cdr (assq major-mode mime/message-default-sender-alist))))
242 (let* ((mime/draft-file-name
243 (or (buffer-file-name)
244 (make-temp-name (expand-file-name "tm-draft" mime/tmp-dir))))
245 (separator mail-header-separator)
246 (config (eval (cdr (assq major-mode mime/window-config-alist))))
248 (replace-space-with-underline (current-time-string))
249 "@" (system-name) "\"")))
251 (let ((hook (cdr (assq major-mode
252 mime/message-before-send-hook-alist))))
254 (let* ((header (message/get-header-string-except
255 mime/message-nuke-headers separator))
256 (orig-header (message/get-header-string-except
257 mime/message-blind-headers separator))
258 (subject (mail-fetch-field "subject"))
259 (total (+ (/ lines mime/message-max-length)
260 (if (> (mod lines mime/message-max-length) 0)
263 (l mime/message-max-length)
264 (the-buf (current-buffer))
265 (buf (get-buffer "*tmp-send*"))
268 (cdr (assq major-mode mime/message-sender-alist))
269 (cdr (assq major-mode mime/message-default-sender-alist))))
271 (goto-char (point-min))
272 (if (re-search-forward (concat "^" (regexp-quote separator) "$")
278 (switch-to-buffer buf)
280 (switch-to-buffer the-buf)
282 (setq buf (get-buffer-create "*tmp-send*"))
284 (switch-to-buffer buf)
285 (make-variable-buffer-local 'mail-header-separator)
286 (setq mail-header-separator separator)
287 (switch-to-buffer the-buf)
288 (goto-char (point-min))
289 (re-search-forward "^$" nil t)
291 (setq buf (get-buffer "*tmp-send*"))
292 (setq data (buffer-substring
298 (switch-to-buffer buf)
301 (format "Subject: %s (%d/%d)\n" subject (+ i 1) total))
303 (format "Mime-Version: 1.0 (split by tm-comp %s)\n"
304 mime/composer-version))
307 "Content-Type: message/partial; id=%s; number=%d; total=%d\n%s\n"
308 id (+ i 1) total separator))
310 (insert orig-header))
313 (call-interactively command))
315 (switch-to-buffer the-buf)
316 (setq l (+ l mime/message-max-length))
321 (cdr (assq major-mode mime/message-after-send-hook-alist))))
323 (set-buffer-modified-p nil)
324 (cond ((y-or-n-p "Kill draft buffer? ")
325 (kill-buffer (current-buffer))
327 (set-window-configuration config))))
331 (defun tm-comp::mime-mode-exit-and-run ()
334 (call-interactively 'mime/split-and-send)
341 (add-hook 'mime-mode-hook
344 (if (not (fboundp 'original::mime-insert-file))
346 (fset 'original::mime-insert-file
347 (symbol-function 'mime-insert-file))
348 (fset 'mime-insert-file 'tm-comp::mime-insert-file)
350 (if (not (fboundp 'original::mime-insert-binary-file))
352 (fset 'original::mime-insert-binary-file
353 (symbol-function 'mime-insert-binary-file))
354 (fset 'mime-insert-binary-file
355 'tm-comp::mime-insert-binary-file)
357 (if (not (fboundp 'original::mime-insert-binary-buffer))
359 (fset 'original::mime-insert-binary-buffer
360 (symbol-function 'mime-insert-binary-buffer))
361 (fset 'mime-insert-binary-buffer
362 'tm-comp::mime-insert-binary-buffer)
364 (if (not (fboundp 'original::mime-mode-exit-and-run))
366 (fset 'original::mime-mode-exit-and-run
367 (symbol-function 'mime-mode-exit-and-run))
368 (fset 'mime-mode-exit-and-run
369 'tm-comp::mime-mode-exit-and-run)
371 (define-key (lookup-key (current-local-map) mime-prefix)
372 "m" 'tm-comp/insert-message)
378 ;; by "OKABE Yasuo <okabe@kudpc.kyoto-u.ac.jp>
379 ;; Mon, 10 Apr 1995 20:03:07 +0900
381 (defvar mime/draft-header-separator-alist
382 '((news-reply-mode . mail-header-separator)
383 (mh-letter-mode . mail-header-separator)
386 (defvar mime::article/draft-header-separator nil)
388 (defun mime/draft-preview ()
390 (let ((sep (assoc-value major-mode mime/draft-header-separator-alist)))
391 (or (stringp sep) (setq sep (eval sep)))
392 (make-variable-buffer-local 'mime::article/draft-header-separator)
393 (goto-char (point-min))
395 (concat "^\\(" (regexp-quote sep) "\\)?$"))
396 (setq mime::article/draft-header-separator
397 (buffer-substring (match-beginning 0) (match-end 0)))
399 (mime/viewer-mode (current-buffer))
400 (pop-to-buffer (current-buffer))
403 (defun mime-viewer::quitting-method/draft-preview ()
404 (let ((mother mime/mother-buffer))
406 (switch-to-buffer mother)
407 (goto-char (point-min))
411 (regexp-quote mime::article/draft-header-separator)
415 (insert mime::article/draft-header-separator)
416 (set-buffer-modified-p (buffer-modified-p))
418 (mime-viewer/kill-buffer)
419 (pop-to-buffer mother)
422 (set-alist 'mime-viewer/quitting-method-alist
424 (function mime-viewer::quitting-method/draft-preview)
427 (set-alist 'mime-viewer/quitting-method-alist
429 (function mime-viewer::quitting-method/draft-preview)
436 (defun message/get-header-string-except (pat boundary)
439 (narrow-to-region (goto-char (point-min))
442 (concat "^\\(" (regexp-quote boundary) "\\)?$")
446 (goto-char (point-min))
448 (while (re-search-forward message/field-regexp nil t)
449 (setq field (buffer-substring (match-beginning 0)
452 (if (not (string-match pat field))
453 (setq header (concat header field "\n"))
458 (defun replace-space-with-underline (str)
470 (run-hooks 'tm-comp-load-hook)