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.7 1995/06/12 05:33:22 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 "\\(^[Cc]ontent-\\|^[Ss]ubject:\\|^[Mm][Ii][Mm][Ee]-[Vv]ersion:\\)")
40 (defvar mime/message-blind-headers
41 "\\(^[BDFbdf][Cc][Cc]:\\|^[Cc][Cc]:[ \t]*$\\)")
43 (defvar mime/message-default-sender-alist
44 '((mail-mode . mail-send-and-exit)
45 (mh-letter-mode . mh-send-letter)
46 (news-reply-mode . gnus-inews-news)))
48 (defvar mime/message-sender-alist
49 '((mail-mode . (lambda ()
53 (mh-letter-mode . (lambda (&optional arg)
55 (write-region (point-min) (point-max)
58 (format "Sending %d/%d..." (+ i 1) total))
60 (pop-to-buffer "MH mail delivery")
62 (mh-exec-cmd-output mh-send-prog t "-watch" "-nopush"
63 "-nodraftfolder" mh-send-args mime/draft-file-name)
64 (goto-char (point-max)) ; show the interesting part
68 (apply 'mh-exec-cmd-quiet t mh-send-prog
70 (list "-nopush" "-nodraftfolder" "-noverbose" "-nowatch" mh-send-args mime/draft-file-name)))))
72 (format "Sending %d/%d... done" (+ i 1) total))
74 (news-reply-mode . (lambda ()
77 (goto-char (point-min))
82 (goto-char (point-min))
83 (search-forward (concat "\n" mail-header-separator "\n"))
85 ;; Mail the message too if To: or Cc: exists.
86 (if (or (mail-fetch-field "to" nil t)
87 (mail-fetch-field "cc" nil t))
88 (if gnus-mail-send-method
91 (format "Sending (%d/%d) via mail..." (+ i 1) total))
93 (funcall gnus-mail-send-method)
95 (format "Sending (%d/%d) via mail... done" (+ i 1) total))
97 (message "No mailer defined. To: and/or Cc: fields ignored.")
100 (format "Posting %d/%d to USENET..." (+ i 1) total))
101 (if (gnus-inews-article)
103 (format "Posting %d/%d to USENET... done" (+ i 1) total))
104 ;; We cannot signal an error.
107 (format "Article %d/%d rejected: %s" (+ i 1) total (gnus-status-message)))
113 (defvar mime/window-config-alist
115 (mh-letter-mode . mh-previous-window-config)
116 (news-reply-mode . (cond ((boundp 'gnus-winconf-post-news)
118 gnus-winconf-post-news
119 (setq gnus-winconf-post-news nil)
121 ((boundp 'gnus-prev-winconf)
124 (setq gnus-prev-winconf nil)
129 (defvar mime/news-reply-mode-server-running nil)
131 (defun tm-gnus4/message-before-send ()
132 (let ((case-fold-search nil))
133 (or (boundp 'mime/news-reply-mode-server-running)
134 (make-variable-buffer-local 'mime/news-reply-mode-server-running))
135 (setq mime/news-reply-mode-server-running (gnus-server-opened))
137 (gnus-start-news-server)
139 (goto-char (point-min))
140 (run-hooks 'news-inews-hook)
145 (goto-char (point-min))
146 (search-forward (concat "\n" mail-header-separator "\n"))
149 (goto-char (point-min))
150 (if (search-forward-regexp "^Newsgroups: +" nil t)
154 (if (re-search-forward "^[^ \t]" nil 'end)
157 (goto-char (point-min))
158 (replace-regexp "\n[ \t]+" " ")
159 (goto-char (point-min))
160 (replace-regexp "[ \t\n]*,[ \t\n]*\\|[ \t]+" ",")
164 (defvar mime/message-before-send-hook-alist
165 '((mh-letter-mode . mh-before-send-letter-hook)
166 (news-reply-mode . tm-gnus4/message-before-send)
169 (defvar mime/message-after-send-hook-alist
170 '((mh-letter-mode . '(lambda ()
172 (mh-annotate-msg mh-sent-from-msg
175 "-component" mh-annotate-field
179 (mh-get-field "Cc:"))))))
180 (news-reply-mode . '(lambda ()
181 (or mime/news-reply-mode-server-running
183 (and (fboundp 'bury-buffer) (bury-buffer))))
186 (defvar tm-comp/message-inserter-alist nil)
192 ;; Insert the binary content after MIME tag.
193 ;; modified by MORITA Masahiro <hiro@isl.ntt.JP>
195 (defun tm-comp::mime-insert-binary-file (file &optional encoding)
196 "Insert binary FILE at point.
197 Optional argument ENCODING specifies an encoding method such as base64."
198 (let ((tmpbuf (get-buffer-create " *MIME insert*")))
202 (let ((mc-flag nil) ;Mule
203 (file-coding-system-for-read
204 (if (featurep 'mule) *noconv*))
205 (kanji-flag nil)) ;NEmacs
206 (let (jka-compr-compression-info-list
207 jam-zcat-filename-list)
208 (insert-file-contents file))))
210 (if (and (stringp encoding)
211 (string-equal (downcase encoding) "x-uue"))
212 (let ((mime-transfer-encoders
213 (copy-alist (cons (list "x-uue" "uuencode"
214 (file-name-nondirectory file))
215 mime-transfer-encoders))))
216 (mime-insert-binary-buffer tmpbuf encoding))
217 (mime-insert-binary-buffer tmpbuf encoding))
218 (kill-buffer tmpbuf))))
220 ;; Insert the binary content after MIME tag.
221 ;; modified by MORITA Masahiro <hiro@isl.ntt.JP>
223 (defun tm-comp::mime-insert-binary-buffer (buffer &optional encoding)
224 "Insert binary BUFFER at point.
225 Optional argument ENCODING specifies an encoding method such as base64."
226 (let* ((tagend (1- (point))) ;End of the tag
227 (hide-p (and mime-auto-hide-body
229 (let ((en (downcase encoding)))
230 (or (string-equal en "base64")
231 (string-equal en "x-uue")
235 (narrow-to-region (1- (point)) (point))
236 (let ((start (point)))
237 (insert-buffer-substring buffer)
238 ;; Encode binary message if necessary.
240 (mime-encode-region encoding start (point-max))))
243 (mime-flag-region (point-min) (1- (point-max)) ?\^M)
244 (goto-char (point-max)))
246 ;; Define encoding even if it is 7bit.
247 (if (stringp encoding)
249 (goto-char tagend) ;Make sure which line the tag is on.
250 (mime-define-encoding encoding)))
254 (defun tm-comp/insert-message (&optional message)
256 (let ((inserter (assoc-value major-mode tm-comp/message-inserter-alist)))
257 (if (and inserter (fboundp inserter))
259 (mime-insert-tag "message" "rfc822")
260 (funcall inserter message)
262 (message "Sorry, I don't have message inserter for your MUA.")
269 (defun mime/split-and-send (&optional cmd)
271 (let ((mime/message-max-length
272 (or (cdr (assq major-mode mime/message-max-length-alist))
273 mime/message-default-max-length))
274 (lines (count-lines (point-min) (point-max)))
276 (if (<= lines mime/message-max-length)
278 (or cmd (cdr (assq major-mode mime/message-default-sender-alist))))
279 (let* ((mime/draft-file-name
280 (or (buffer-file-name)
281 (make-temp-name (expand-file-name "tm-draft" mime/tmp-dir))))
282 (separator mail-header-separator)
283 (config (eval (cdr (assq major-mode mime/window-config-alist))))
285 (replace-space-with-underline (current-time-string))
286 "@" (system-name) "\"")))
288 (let ((hook (cdr (assq major-mode
289 mime/message-before-send-hook-alist))))
291 (let* ((header (message/get-header-string-except
292 mime/message-nuke-headers separator))
293 (orig-header (message/get-header-string-except
294 mime/message-blind-headers separator))
295 (subject (mail-fetch-field "subject"))
296 (total (+ (/ lines mime/message-max-length)
297 (if (> (mod lines mime/message-max-length) 0)
300 (l mime/message-max-length)
301 (the-buf (current-buffer))
302 (buf (get-buffer "*tmp-send*"))
305 (cdr (assq major-mode mime/message-sender-alist))
306 (cdr (assq major-mode mime/message-default-sender-alist))))
308 (goto-char (point-min))
309 (if (re-search-forward (concat "^" (regexp-quote separator) "$")
315 (switch-to-buffer buf)
317 (switch-to-buffer the-buf)
319 (setq buf (get-buffer-create "*tmp-send*"))
321 (switch-to-buffer buf)
322 (make-variable-buffer-local 'mail-header-separator)
323 (setq mail-header-separator separator)
324 (switch-to-buffer the-buf)
325 (goto-char (point-min))
326 (re-search-forward "^$" nil t)
328 (setq buf (get-buffer "*tmp-send*"))
329 (setq data (buffer-substring
335 (switch-to-buffer buf)
338 (format "Subject: %s (%d/%d)\n" subject (+ i 1) total))
340 (format "Mime-Version: 1.0\n"))
343 "Content-Type: message/partial; id=%s; number=%d; total=%d\n%s\n"
344 id (+ i 1) total separator))
346 (insert orig-header))
349 (call-interactively command))
351 (switch-to-buffer the-buf)
352 (setq l (+ l mime/message-max-length))
356 (let ((hook (eval (cdr (assq major-mode mime/message-after-send-hook-alist)))))
358 (set-buffer-modified-p nil)
359 (cond ((y-or-n-p "Kill draft buffer? ")
360 (kill-buffer (current-buffer))
362 (set-window-configuration config))))
366 (defun tm-comp::mime-mode-exit-and-run ()
369 (call-interactively 'mime/split-and-send)
376 (add-hook 'mime-mode-hook
379 (if (not (fboundp 'original::mime-insert-binary-file))
381 (fset 'original::mime-insert-binary-file
382 (symbol-function 'mime-insert-binary-file))
383 (fset 'mime-insert-binary-file
384 'tm-comp::mime-insert-binary-file)
386 (if (not (fboundp 'original::mime-insert-binary-buffer))
388 (fset 'original::mime-insert-binary-buffer
389 (symbol-function 'mime-insert-binary-buffer))
390 (fset 'mime-insert-binary-buffer
391 'tm-comp::mime-insert-binary-buffer)
393 (if (not (fboundp 'original::mime-mode-exit-and-run))
395 (fset 'original::mime-mode-exit-and-run
396 (symbol-function 'mime-mode-exit-and-run))
397 (fset 'mime-mode-exit-and-run
398 'tm-comp::mime-mode-exit-and-run)
400 (define-key (lookup-key (current-local-map) mime-prefix)
401 "m" 'tm-comp/insert-message)
407 ;; by "OKABE Yasuo <okabe@kudpc.kyoto-u.ac.jp>
408 ;; Mon, 10 Apr 1995 20:03:07 +0900
410 (defvar mime/draft-header-separator-alist
411 '((news-reply-mode . mail-header-separator)
412 (mh-letter-mode . mail-header-separator)
415 (defvar mime::article/draft-header-separator nil)
417 (defun mime/draft-preview ()
419 (let ((sep (assoc-value major-mode mime/draft-header-separator-alist)))
420 (or (stringp sep) (setq sep (eval sep)))
421 (make-variable-buffer-local 'mime::article/draft-header-separator)
422 (goto-char (point-min))
424 (concat "^\\(" (regexp-quote sep) "\\)?$"))
425 (setq mime::article/draft-header-separator
426 (buffer-substring (match-beginning 0) (match-end 0)))
428 (mime/viewer-mode (current-buffer))
429 (pop-to-buffer (current-buffer))
432 (defun mime-viewer::quitting-method/draft-preview ()
433 (let ((mother mime/mother-buffer))
435 (switch-to-buffer mother)
436 (goto-char (point-min))
440 (regexp-quote mime::article/draft-header-separator)
444 (insert mime::article/draft-header-separator)
445 (set-buffer-modified-p (buffer-modified-p))
447 (mime-viewer/kill-buffer)
448 (pop-to-buffer mother)
451 (set-alist 'mime-viewer/quitting-method-alist
453 (function mime-viewer::quitting-method/draft-preview)
456 (set-alist 'mime-viewer/quitting-method-alist
458 (function mime-viewer::quitting-method/draft-preview)
465 (defun message/get-header-string-except (pat boundary)
468 (narrow-to-region (goto-char (point-min))
471 (concat "^\\(" (regexp-quote boundary) "\\)?$")
475 (goto-char (point-min))
477 (while (re-search-forward message/field-regexp nil t)
478 (setq field (buffer-substring (match-beginning 0)
481 (if (not (string-match pat field))
482 (setq header (concat header field "\n"))
487 (defun replace-space-with-underline (str)
497 (run-hooks 'tm-comp-load-hook)