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.3 1995/04/18 16:38:42 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 . (prog1
117 gnus-winconf-post-news
118 (setq gnus-winconf-post-news nil)
122 (defvar mime/news-reply-mode-server-running nil)
124 (defvar mime/message-before-send-hook-alist
125 '((mh-letter-mode . mh-before-send-letter-hook))
126 (news-reply-mode . '(lambda ()
127 (let ((case-fold-search nil))
128 (or (boundp 'mime/news-reply-mode-server-running)
129 (make-variable-buffer-local 'mime/news-reply-mode-server-running))
130 (setq mime/news-reply-mode-server-running (gnus-server-opened))
132 (gnus-start-server-process)
134 (goto-char (point-min))
135 (run-hooks 'news-inews-hook)
140 (goto-char (point-min))
141 (search-forward (concat "\n" mail-header-separator "\n"))
144 (goto-char (point-min))
145 (if (search-forward-regexp "^Newsgroups: +" nil t)
149 (if (re-search-forward "^[^ \t]" nil 'end)
152 (goto-char (point-min))
153 (replace-regexp "\n[ \t]+" " ")
154 (goto-char (point-min))
155 (replace-regexp "[ \t\n]*,[ \t\n]*\\|[ \t]+" ",")
160 (defvar mime/message-after-send-hook-alist
161 '((mh-letter-mode . '(lambda ()
163 (mh-annotate-msg mh-sent-from-msg
166 "-component" mh-annotate-field
170 (mh-get-field "Cc:"))))))
171 (news-reply-mode . '(lambda ()
172 (or mime/news-reply-mode-server-running
174 (and (fboundp 'bury-buffer) (bury-buffer))))
177 (defvar tm-comp/message-inserter-alist nil)
183 ;; Insert the binary content after MIME tag.
184 ;; modified by MORITA Masahiro <hiro@isl.ntt.JP>
186 (defun tm-comp::mime-insert-binary-file (file &optional encoding)
187 "Insert binary FILE at point.
188 Optional argument ENCODING specifies an encoding method such as base64."
189 (let ((tmpbuf (get-buffer-create " *MIME insert*")))
193 (let ((mc-flag nil) ;Mule
194 (file-coding-system-for-read
195 (if (featurep 'mule) *noconv*))
196 (kanji-flag nil)) ;NEmacs
197 (let (jka-compr-compression-info-list
198 jam-zcat-filename-list)
199 (insert-file-contents file))))
201 (if (and (stringp encoding)
202 (string-equal (downcase encoding) "x-uue"))
203 (let ((mime-transfer-encoders
204 (copy-alist (cons (list "x-uue" "uuencode"
205 (file-name-nondirectory file))
206 mime-transfer-encoders))))
207 (mime-insert-binary-buffer tmpbuf encoding))
208 (mime-insert-binary-buffer tmpbuf encoding))
209 (kill-buffer tmpbuf))))
211 ;; Insert the binary content after MIME tag.
212 ;; modified by MORITA Masahiro <hiro@isl.ntt.JP>
214 (defun tm-comp::mime-insert-binary-buffer (buffer &optional encoding)
215 "Insert binary BUFFER at point.
216 Optional argument ENCODING specifies an encoding method such as base64."
217 (let* ((tagend (1- (point))) ;End of the tag
218 (hide-p (and mime-auto-hide-body
220 (let ((en (downcase encoding)))
221 (or (string-equal en "base64")
222 (string-equal en "x-uue")
226 (narrow-to-region (1- (point)) (point))
227 (let ((start (point)))
228 (insert-buffer-substring buffer)
229 ;; Encode binary message if necessary.
231 (mime-encode-region encoding start (point-max))))
234 (mime-flag-region (point-min) (1- (point-max)) ?\^M)
235 (goto-char (point-max)))
237 ;; Define encoding even if it is 7bit.
238 (if (stringp encoding)
240 (goto-char tagend) ;Make sure which line the tag is on.
241 (mime-define-encoding encoding)))
245 (defun tm-comp/insert-message (&optional message)
247 (let ((inserter (assoc-value major-mode tm-comp/message-inserter-alist)))
248 (if (and inserter (fboundp inserter))
250 (mime-insert-tag "message" "rfc822")
251 (funcall inserter message)
253 (message "Sorry, I don't have message inserter for your MUA.")
260 (defun mime/split-and-send (&optional cmd)
262 (let ((mime/message-max-length
263 (or (cdr (assq major-mode mime/message-max-length-alist))
264 mime/message-default-max-length))
265 (lines (count-lines (point-min) (point-max)))
267 (if (<= lines mime/message-max-length)
269 (or cmd (cdr (assq major-mode mime/message-default-sender-alist))))
270 (let* ((mime/draft-file-name
271 (or (buffer-file-name)
272 (make-temp-name (expand-file-name "tm-draft" mime/tmp-dir))))
273 (separator mail-header-separator)
274 (config (eval (cdr (assq major-mode mime/window-config-alist))))
276 (replace-space-with-underline (current-time-string))
277 "@" (system-name) "\"")))
279 (let ((hook (eval (cdr (assq major-mode mime/message-before-send-hook-alist)))))
281 (let* ((header (message/get-header-string-except
282 mime/message-nuke-headers separator))
283 (orig-header (message/get-header-string-except
284 mime/message-blind-headers separator))
285 (subject (mail-fetch-field "subject"))
286 (total (+ (/ lines mime/message-max-length)
287 (if (> (mod lines mime/message-max-length) 0)
290 (l mime/message-max-length)
291 (the-buf (current-buffer))
292 (buf (get-buffer "*tmp-send*"))
295 (cdr (assq major-mode mime/message-sender-alist))
296 (cdr (assq major-mode mime/message-default-sender-alist))))
298 (goto-char (point-min))
299 (if (re-search-forward (concat "^" (regexp-quote separator) "$")
305 (switch-to-buffer buf)
307 (switch-to-buffer the-buf)
309 (setq buf (get-buffer-create "*tmp-send*"))
311 (switch-to-buffer buf)
312 (make-variable-buffer-local 'mail-header-separator)
313 (setq mail-header-separator separator)
314 (switch-to-buffer the-buf)
315 (goto-char (point-min))
316 (re-search-forward "^$" nil t)
318 (setq buf (get-buffer "*tmp-send*"))
319 (setq data (buffer-substring
325 (switch-to-buffer buf)
328 (format "Subject: %s (%d/%d)\n" subject (+ i 1) total))
330 (format "Mime-Version: 1.0\n"))
333 "Content-Type: message/partial; id=%s; number=%d; total=%d\n%s\n"
334 id (+ i 1) total separator))
336 (insert orig-header))
339 (call-interactively command))
341 (switch-to-buffer the-buf)
342 (setq l (+ l mime/message-max-length))
346 (let ((hook (eval (cdr (assq major-mode mime/message-after-send-hook-alist)))))
348 (set-buffer-modified-p nil)
349 (cond ((y-or-n-p "Kill draft buffer? ")
350 (kill-buffer (current-buffer))
352 (set-window-configuration config))))
356 (defun tm-comp::mime-mode-exit-and-run ()
359 (call-interactively 'mime/split-and-send)
366 (add-hook 'mime-mode-hook
369 (if (not (fboundp 'original::mime-insert-binary-file))
371 (fset 'original::mime-insert-binary-file
372 (symbol-function 'mime-insert-binary-file))
373 (fset 'mime-insert-binary-file
374 'tm-comp::mime-insert-binary-file)
376 (if (not (fboundp 'original::mime-insert-binary-buffer))
378 (fset 'original::mime-insert-binary-buffer
379 (symbol-function 'mime-insert-binary-buffer))
380 (fset 'mime-insert-binary-buffer
381 'tm-comp::mime-insert-binary-buffer)
383 (if (not (fboundp 'original::mime-mode-exit-and-run))
385 (fset 'original::mime-mode-exit-and-run
386 (symbol-function 'mime-mode-exit-and-run))
387 (fset 'mime-mode-exit-and-run
388 'tm-comp::mime-mode-exit-and-run)
390 (define-key (lookup-key (current-local-map) mime-prefix)
391 "m" 'tm-comp/insert-message)
397 ;; by "OKABE Yasuo <okabe@kudpc.kyoto-u.ac.jp>
398 ;; Mon, 10 Apr 1995 20:03:07 +0900
400 (defvar mime/draft-header-separator-alist
401 '((news-reply-mode . mail-header-separator)
402 (mh-letter-mode . mail-header-separator)
405 (defvar mime::article/draft-header-separator nil)
407 (defun mime/draft-preview ()
409 (let ((sep (assoc-value major-mode mime/draft-header-separator-alist)))
410 (or (stringp sep) (setq sep (eval sep)))
411 (make-variable-buffer-local 'mime::article/draft-header-separator)
412 (goto-char (point-min))
414 (concat "^\\(" (regexp-quote sep) "\\)?$"))
415 (setq mime::article/draft-header-separator
416 (buffer-substring (match-beginning 0) (match-end 0)))
418 (mime/viewer-mode (current-buffer))
419 (pop-to-buffer (current-buffer))
422 (defun mime-viewer::quitting-method/draft-preview ()
423 (let ((mother mime/mother-buffer))
425 (switch-to-buffer mother)
426 (goto-char (point-min))
430 (regexp-quote mime::article/draft-header-separator)
434 (insert mime::article/draft-header-separator)
435 (set-buffer-modified-p (buffer-modified-p))
437 (mime-viewer/kill-buffer)
438 (pop-to-buffer mother)
441 (set-alist 'mime-viewer/quitting-method-alist
443 (function mime-viewer::quitting-method/draft-preview)
446 (set-alist 'mime-viewer/quitting-method-alist
448 (function mime-viewer::quitting-method/draft-preview)
455 (defun message/get-header-string-except (pat boundary)
458 (narrow-to-region (goto-char (point-min))
461 (concat "^\\(" (regexp-quote boundary) "\\)?$")
465 (goto-char (point-min))
467 (while (re-search-forward message/field-regexp nil t)
468 (setq field (buffer-substring (match-beginning 0)
471 (if (not (string-match pat field))
472 (setq header (concat header field "\n"))
477 (defun replace-space-with-underline (str)
487 (run-hooks 'tm-comp-load-hook)