2 ;;; tm-vm.el --- tm-MUA for VM
4 ;;; Copyright (C) 1995 Free Software Foundation, Inc.
6 ;;; Author: MASUTANI Yasuhiro <masutani@me.es.osaka-u.ac.jp>
7 ;;; Kenji Wakamiya <wkenji@flab.fujitsu.co.jp>
8 ;;; MORIOKA Tomohiko <morioka@jaist.ac.jp>
9 ;;; Shuhei KOBAYASHI <shuhei@cmpt01.phys.tohoku.ac.jp>
10 ;;; Oscar Figueiredo <figueire@lspsun2.epfl.ch>
11 ;;; modified by SHIONO Jun'ichi <jun@case.nm.fujitsu.co.jp>
12 ;;; Maintainer: MORIOKA Tomohiko <morioka@jaist.ac.jp>
13 ;;; Created: 1994/10/29
14 ;;; Version: $Revision: 7.29 $
15 ;;; Keywords: news, MIME, multimedia, multilingual, encoded-word
17 ;;; This file is part of tm (Tools for MIME).
19 ;;; Plese insert (require 'tm-vm) in your ~/.vm or ~/.emacs file.
21 ;;; This program is free software; you can redistribute it and/or
22 ;;; modify it under the terms of the GNU General Public License as
23 ;;; published by the Free Software Foundation; either version 2, or
24 ;;; (at your option) any later version.
26 ;;; This program is distributed in the hope that it will be useful,
27 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
28 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
29 ;;; General Public License for more details.
31 ;;; You should have received a copy of the GNU General Public License
32 ;;; along with This program. If not, write to the Free Software
33 ;;; Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
38 (defconst tm-vm/RCS-ID
39 "$Id: tm-vm.el,v 7.29 1995/12/06 08:49:15 morioka Exp $")
40 (defconst tm-vm/version (get-version-string tm-vm/RCS-ID))
42 (define-key vm-mode-map "Z" 'tm-vm/view-message)
43 (define-key vm-mode-map "T" 'tm-vm/decode-message-header)
44 (define-key vm-mode-map "\et" 'tm-vm/toggle-preview-mode)
47 ;;; @ for MIME encoded-words
50 (defvar tm-vm/use-tm-patch nil
51 "Does not decode encoded-words in summary buffer if it is t.
52 If you use tiny-mime patch for VM (by RIKITAKE Kenji
53 <kenji@reseau.toyonaka.osaka.jp>), please set it t [tm-vm.el]")
55 (or tm-vm/use-tm-patch
58 (defvar tm-vm/chop-full-name-function 'tm-vm/default-chop-full-name)
59 (setq vm-chop-full-name-function tm-vm/chop-full-name-function)
61 (defun tm-vm/default-chop-full-name (address)
62 (let* ((ret (vm-default-chop-full-name address))
65 (if (stringp full-name)
66 (cons (mime-eword/decode-string full-name)
71 (or (fboundp 'tm:vm-su-subject)
72 (fset 'tm:vm-su-subject (symbol-function 'vm-su-subject))
74 (defun vm-su-subject (m)
75 (mime-eword/decode-string (tm:vm-su-subject m))
80 (defun tm-vm/decode-message-header (&optional count)
81 "Decode MIME header of current message through tiny-mime.
82 Numeric prefix argument COUNT means to decode the current message plus
83 the next COUNT-1 messages. A negative COUNT means decode the current
84 message and the previous COUNT-1 messages.
85 When invoked on marked messages (via vm-next-command-uses-marks),
86 all marked messages are affected, other messages are ignored."
88 (or count (setq count 1))
89 (vm-follow-summary-cursor)
90 (vm-select-folder-buffer)
91 (vm-check-for-killed-summary)
92 (vm-error-if-folder-empty)
93 (vm-error-if-folder-read-only)
94 (let ((mlist (vm-select-marked-or-prefixed-messages count))
100 (setq realm (vm-real-message-of (car mlist)))
101 ;; Go to real folder of this message.
102 ;; But maybe this message is already real message...
103 (set-buffer (vm-buffer-of realm))
104 (let ((buffer-read-only nil))
106 (narrow-to-region (vm-headers-of realm) (vm-text-of realm))
107 (mime/decode-message-header))
108 (let ((vm-message-pointer (list realm))
110 (vm-discard-cached-data))
111 ;; Mark each virtual and real message for later summary
113 (setq vlist (cons realm (vm-virtual-messages-of realm)))
115 (vm-mark-for-summary-update (car vlist))
116 ;; Remember virtual and real folders related this message,
117 ;; for later display update.
118 (or (memq (vm-buffer-of (car vlist)) vbufs)
119 (setq vbufs (cons (vm-buffer-of (car vlist)) vbufs)))
120 (setq vlist (cdr vlist)))
121 (if (eq vm-flush-interval t)
122 (vm-stuff-virtual-attributes realm)
123 (vm-set-modflag-of realm t)))
124 (setq mlist (cdr mlist)))
125 ;; Update mail-buffers and summaries.
127 (set-buffer (car vbufs))
128 (vm-preview-current-message)
129 (setq vbufs (cdr vbufs))))))
132 ;;; @ automatic MIME preview
135 (defvar tm-vm/automatic-mime-preview t
136 "*If non-nil, show MIME processed article.")
138 (defun tm-vm/preview-current-message ()
139 ;;; suggested by Simon Rowe <smr@robots.oxford.ac.uk>
140 ;;; (cf. [tm-eng:163])
141 ;; Selecting a new mail message, but we're already displaying a mime
142 ;; on in the window, make sure that the mail buffer is displayed.
143 (if (get-buffer-window mime/output-buffer-name)
144 (delete-window (get-buffer-window (get-buffer mime/output-buffer-name)))
146 ;; fixed by Shuhei KOBAYASHI <shuhei@cmpt01.phys.tohoku.ac.jp>
147 ;; 1995/12/4 (cf. [tm-ja:1190])
148 (if (and vm-message-pointer tm-vm/automatic-mime-preview
149 ;; fixed by SHIONO Jun'ichi <jun@case.nm.fujitsu.co.jp>
150 ;; 1995/11/17 (cf. [tm-ja:1120])
151 (display-buffer (current-buffer))
152 (let* ((mp (car vm-message-pointer))
153 (ct (vm-get-header-contents mp "Content-Type:"))
154 (cte (vm-get-header-contents
155 mp "Content-Transfer-Encoding:"))
157 ;; Check if this message actually is a mime, or just a text
158 ;; one sent by someone using PINE or similar.
160 (not (and (string= (car (mime/parse-Content-Type ct))
162 (member cte '("7bit" "8bit" "binary"))
165 (let ((win (selected-window)) buf)
166 (setq buf (window-buffer win))
167 (let ((pwin (and mime::article/preview-buffer
168 (get-buffer mime::article/preview-buffer)
169 (get-buffer-window mime::article/preview-buffer))))
179 vm-scroll-forward vm-scroll-backward)
180 (list this-command 'reading-message))
181 (setq win (get-buffer-window buf))
185 (save-window-excursion
186 (vm-select-folder-buffer)
187 (setq win (get-buffer-window (current-buffer)))
188 ;; (vm-display (current-buffer) t
189 ;; '(vm-scroll-forward vm-scroll-backward)
190 ;; (list this-command 'reading-message))
191 ;; (select-window (get-buffer-window (current-buffer)))
193 (setq buf (current-buffer))
194 (run-hooks 'tm-vm/select-message-hook)
196 (set-window-buffer win buf)
197 ;;(select-window win)
199 ;; fixed by Oscar Figueiredo <figueire@lspsun2.epfl.ch>
201 (if (and mime::article/preview-buffer
202 (get-buffer mime::article/preview-buffer))
203 (kill-buffer mime::article/preview-buffer))
204 (if tm-vm/automatic-mime-preview
205 (let (buffer-read-only)
206 (mime/decode-message-header)
207 (run-hooks 'tm-vm/select-message-hook)
211 (add-hook 'vm-select-message-hook 'tm-vm/preview-current-message)
213 (defun tm-vm/visit-folder-function ()
214 (tm-vm/preview-current-message)
215 (and vm-mail-buffer (set-buffer vm-mail-buffer))
218 (add-hook 'vm-visit-folder-hook 'tm-vm/visit-folder-function)
220 ;; fixed by Oscar Figueiredo <figueire@lspsun2.epfl.ch>
221 ;; 1995/11/14 (cf.[tm-eng:162])
222 (defun tm-vm/scroll-forward (&optional arg)
224 (if (not tm-vm/automatic-mime-preview)
225 ;; fixed by SHIONO Jun'ichi <jun@case.nm.fujitsu.co.jp>
226 ;; 1995/11/17 (cf.[tm-ja:1119])
228 (setq this-command 'vm-scroll-forward)
229 (vm-scroll-forward arg))
230 (let* ((summary-buffer (or vm-summary-buffer
231 (and (eq major-mode 'vm-summary-mode)
233 (summary-win (get-buffer-window summary-buffer))
234 (mail-buffer (save-excursion
235 (set-buffer summary-buffer)
237 (mail-win (get-buffer-window mail-buffer))
238 (preview-buf (save-excursion
239 (set-buffer mail-buffer)
240 mime::article/preview-buffer))
241 (preview-win (and preview-buf (get-buffer-window preview-buf)))
245 (select-window preview-win)
246 (if (pos-visible-in-window-p (point-max) preview-win)
248 (switch-to-buffer mail-buffer)
249 (goto-char (point-max))
250 (select-window summary-win))
252 (switch-to-buffer mail-buffer)
253 (select-window summary-win))))
254 ;; fixed by SHIONO Jun'ichi <jun@case.nm.fujitsu.co.jp>
255 ;; 1995/11/17 (cf.[tm-ja:1119])
256 (setq this-command 'vm-scroll-forward)
257 (vm-scroll-forward arg)
259 (set-buffer summary-buffer)
260 (setq mail-win (get-buffer-window vm-mail-buffer)))
261 ;; fixed by Oscar Figueiredo <figueire@lspsun2.epfl.ch>
264 mime::article/preview-buffer
265 (get-buffer mime::article/preview-buffer))
267 (select-window mail-win)
268 (switch-to-buffer mime::article/preview-buffer)
269 (select-window summary-win)))
272 (defun tm-vm/scroll-backward (&optional arg)
274 (if (not tm-vm/automatic-mime-preview)
275 ;; fixed by SHIONO Jun'ichi <jun@case.nm.fujitsu.co.jp>
276 ;; 1995/11/17 (cf.[tm-ja:1119])
278 (setq this-command 'vm-scroll-backward)
279 (vm-scroll-backward arg))
280 (let* ((summary-buffer (or vm-summary-buffer
281 (and (eq major-mode 'vm-summary-mode)
283 (summary-win (get-buffer-window summary-buffer))
284 (mail-buffer (save-excursion
285 (set-buffer summary-buffer)
287 (mail-win (get-buffer-window mail-buffer))
288 (preview-buf (save-excursion
289 (set-buffer mail-buffer)
290 mime::article/preview-buffer))
291 (preview-win (and preview-buf (get-buffer-window preview-buf)))
295 (select-window preview-win)
296 (if (pos-visible-in-window-p (point-min) preview-win)
298 (switch-to-buffer mail-buffer)
299 (goto-char (point-min))
300 (select-window summary-win))
302 (switch-to-buffer mail-buffer)
303 (select-window summary-win))))
304 ;; fixed by SHIONO Jun'ichi <jun@case.nm.fujitsu.co.jp>
305 ;; 1995/11/17 (cf.[tm-ja:1119])
306 (setq this-command 'vm-scroll-backward)
307 (vm-scroll-backward arg)
309 (set-buffer summary-buffer)
310 (setq mail-win (get-buffer-window vm-mail-buffer)))
312 mime::article/preview-buffer
313 (get-buffer mime::article/preview-buffer))
315 (select-window mail-win)
316 (goto-char (point-max))
317 (switch-to-buffer mime::article/preview-buffer)
318 (select-window summary-win)))
321 (defun tm-vm/over-to-previous-method ()
322 (set-buffer mime::preview/article-buffer)
323 (setq this-command 'vm-previous-message)
325 (save-window-excursion
326 (vm-previous-message 1 nil t)
328 (if (and mime::article/preview-buffer
329 (get-buffer mime::article/preview-buffer))
330 mime::article/preview-buffer
334 (set-window-buffer (selected-window) buf)
337 (defun tm-vm/over-to-next-method ()
338 (set-buffer mime::preview/article-buffer)
339 (setq this-command 'vm-next-message)
341 (save-window-excursion
342 (vm-next-message 1 nil t)
344 (if (and mime::article/preview-buffer
345 (get-buffer mime::article/preview-buffer)
347 mime::article/preview-buffer
351 (set-window-buffer (selected-window) buf)
354 (set-alist 'mime-viewer/over-to-previous-method-alist
355 'vm-mode 'tm-vm/over-to-previous-method)
356 (set-alist 'mime-viewer/over-to-next-method-alist
357 'vm-mode 'tm-vm/over-to-next-method)
358 (set-alist 'mime-viewer/over-to-previous-method-alist
359 'vm-virtual-mode 'tm-vm/over-to-previous-method)
360 (set-alist 'mime-viewer/over-to-next-method-alist
361 'vm-virtual-mode 'tm-vm/over-to-next-method)
363 ;; 1995/11/16 by Oscar Figueiredo <figueire@lspsun2.epfl.ch>
364 (defun tm-vm/expunge-folder ()
366 (let* ((summary-buf (or (and (eq major-mode 'vm-summary-mode)
369 (preview-buf (save-excursion
370 (set-buffer (save-excursion
371 (set-buffer summary-buf)
373 mime::article/preview-buffer))
374 (preview-win (and preview-buf
375 (get-buffer-window preview-buf)))
376 (win (selected-window)))
381 (set-buffer summary-buf)
382 (set-buffer vm-mail-buffer)
383 (if (eq (point-min) (point-max))
384 (kill-buffer preview-buf))))
387 ;; fixed by Oscar Figueiredo <figueire@lspsun2.epfl.ch>
388 ;; 1995/11/14 (cf. [tm-eng:162])
392 (vm-select-folder-buffer)
393 (if (and mime::article/preview-buffer
394 (get-buffer mime::article/preview-buffer))
395 (kill-buffer mime::article/preview-buffer)))
399 (substitute-key-definition 'vm-scroll-forward
400 'tm-vm/scroll-forward vm-mode-map)
401 (substitute-key-definition 'vm-scroll-backward
402 'tm-vm/scroll-backward vm-mode-map)
403 (substitute-key-definition 'vm-expunge-folder
404 'tm-vm/expunge-folder vm-mode-map)
405 (substitute-key-definition 'vm-quit
406 'tm-vm/quit vm-mode-map)
410 (defun tm-vm/toggle-preview-mode ()
412 (if tm-vm/automatic-mime-preview
414 (setq tm-vm/automatic-mime-preview nil)
415 (vm-select-folder-buffer)
416 (vm-display (current-buffer) t
417 '(tm-vm/toggle-preview-mode)
418 '(tm-vm/toggle-preview-mode reading-message))
420 (setq tm-vm/automatic-mime-preview t)
421 (let ((win (selected-window)))
422 (vm-select-folder-buffer)
423 (save-window-excursion
424 (let* ((mp (car vm-message-pointer))
425 (ct (vm-get-header-contents mp "Content-Type:"))
426 (cte (vm-get-header-contents mp "Content-Transfer-Encoding:"))
428 (mime/viewer-mode nil (mime/parse-Content-Type (or ct "")) cte)
430 (vm-display mime::article/preview-buffer t
431 '(tm-vm/toggle-preview-mode)
432 '(tm-vm/toggle-preview-mode reading-message))
441 (defun tm-vm/quit-view-message ()
442 "Quit MIME-viewer and go back to VM.
443 This function is called by `mime-viewer/quit' command via
444 `mime-viewer/quitting-method-alist'."
445 (mime-viewer/kill-buffer)
446 (if (get-buffer mime/output-buffer-name)
447 (bury-buffer mime/output-buffer-name))
448 (vm-select-folder-buffer)
449 (vm-display (current-buffer) t '(mime-viewer/quit mime-viewer/up-content)
450 '(mime-viewer/quit reading-message)))
452 (defun tm-vm/view-message ()
453 "Decode and view MIME encoded message, under VM."
455 (vm-follow-summary-cursor)
456 (vm-select-folder-buffer)
457 (vm-check-for-killed-summary)
458 (vm-error-if-folder-empty)
459 (vm-display (current-buffer) t '(tm-vm/view-message)
460 '(tm-vm/view-mesage reading-message))
461 (let* ((mp (car vm-message-pointer))
462 (ct (vm-get-header-contents mp "Content-Type:"))
463 (cte (vm-get-header-contents mp "Content-Transfer-Encoding:"))
464 (exposed (= (point-min) (vm-start-of mp))))
467 ;; vm-widen-page hides exposed header if pages are delimited.
468 ;; So, here we expose it again.
470 (narrow-to-region (vm-start-of mp) (point-max)))
471 (select-window (vm-get-buffer-window (current-buffer)))
472 (mime/viewer-mode nil
473 (mime/parse-Content-Type (or ct ""))
477 (set-alist 'mime-viewer/quitting-method-alist
479 'tm-vm/quit-view-message)
481 (set-alist 'mime-viewer/quitting-method-alist
483 'tm-vm/quit-view-message)
493 (set-atype 'mime/content-decoding-condition
494 '((type . "message/partial")
495 (method . mime-article/grab-message/partials)
496 (major-mode . vm-mode)
497 (summary-buffer-exp . vm-summary-buffer)
499 (set-alist 'tm-partial/preview-article-method-alist
511 ;;; @@ for multipart/digest
514 (defun tm-vm/enclose-messages (mlist)
515 "Enclose the messages in MLIST as multipart/digest.
516 The resulting digest is inserted at point in the current buffer.
518 MLIST should be a list of message structs (real or virtual).
519 These are the messages that will be enclosed."
521 (let ((digest (consp (cdr mlist)))
524 (narrow-to-region (point) (point))
526 (setq m (vm-real-message-of (car mlist)))
527 (mime-editor/insert-tag "message" "rfc822")
528 (tm-mail/insert-message m)
529 (goto-char (point-max))
530 (setq mlist (cdr mlist)))
532 (mime-editor/enclose-digest-region (point-min) (point-max)))
535 (defun tm-vm/forward-message ()
536 "Forward the current message to one or more recipients.
537 You will be placed in a Mail mode buffer as you would with a
538 reply, but you must fill in the To: header and perhaps the
539 Subject: header manually."
541 (if (not (equal vm-forwarding-digest-type "rfc1521"))
543 (vm-follow-summary-cursor)
544 (vm-select-folder-buffer)
545 (vm-check-for-killed-summary)
546 (vm-error-if-folder-empty)
547 (if (eq last-command 'vm-next-command-uses-marks)
548 (let ((vm-digest-send-type vm-forwarding-digest-type))
549 (setq this-command 'vm-next-command-uses-marks)
550 (command-execute 'tm-vm/send-digest))
551 (let ((dir default-directory)
552 (mp vm-message-pointer))
556 (format "forward of %s's note re: %s"
557 (vm-su-full-name (car vm-message-pointer))
558 (vm-su-subject (car vm-message-pointer)))
560 (and vm-forwarding-subject-format
561 (let ((vm-summary-uninteresting-senders nil))
562 (vm-sprintf 'vm-forwarding-subject-format (car mp)))))
563 (make-local-variable 'vm-forward-list)
564 (setq vm-system-state 'forwarding
565 vm-forward-list (list (car mp))
566 default-directory dir)
567 (goto-char (point-min))
569 (concat "^" (regexp-quote mail-header-separator) "\n") nil 0)
570 (tm-vm/enclose-messages vm-forward-list)
571 (mail-position-on-field "To"))
572 (run-hooks 'tm-vm/forward-message-hook)
573 (run-hooks 'vm-mail-mode-hook)))))
575 (defun tm-vm/send-digest (&optional prefix)
576 "Send a digest of all messages in the current folder to recipients.
577 The type of the digest is specified by the variable vm-digest-send-type.
578 You will be placed in a Mail mode buffer as is usual with replies, but you
579 must fill in the To: and Subject: headers manually.
581 If invoked on marked messages (via vm-next-command-uses-marks),
582 only marked messages will be put into the digest."
584 (if (not (equal vm-digest-send-type "rfc1521"))
585 (vm-send-digest prefix)
586 (vm-select-folder-buffer)
587 (vm-check-for-killed-summary)
588 (vm-error-if-folder-empty)
589 (let ((dir default-directory)
590 (mp vm-message-pointer)
591 (mlist (if (eq last-command 'vm-next-command-uses-marks)
592 (vm-select-marked-or-prefixed-messages 0)
597 (vm-mail-internal (format "digest from %s" (buffer-name)))
598 (setq vm-system-state 'forwarding
599 vm-forward-list mlist
600 default-directory dir)
601 (goto-char (point-min))
602 (re-search-forward (concat "^" (regexp-quote mail-header-separator)
604 (goto-char (match-end 0))
607 (vm-unsaved-message "Building %s digest..." vm-digest-send-type)
608 (tm-vm/enclose-messages mlist)
613 (mime-editor/insert-tag "text" "plain")
614 (vm-unsaved-message "Building digest preamble...")
616 (let ((vm-summary-uninteresting-senders nil))
617 (insert (vm-sprintf 'vm-digest-preamble-format (car mp)) "\n"))
618 (if vm-digest-center-preamble
623 (setq mp (cdr mp)))))
624 (mail-position-on-field "To")
625 (message "Building %s digest... done" vm-digest-send-type)))
626 (run-hooks 'tm-vm/send-digest-hook)
627 (run-hooks 'vm-mail-mode-hook)))
633 (substitute-key-definition 'vm-forward-message
634 'tm-vm/forward-message vm-mode-map)
635 (substitute-key-definition 'vm-send-digest
636 'tm-vm/send-digest vm-mode-map)
642 (autoload 'tm-mail/insert-message "tm-mail")
643 (set-alist 'mime-editor/message-inserter-alist
644 'mail-mode (function tm-mail/insert-message))
651 ;;(remove-hook 'mail-mode-hook 'mime/editor-mode)
652 ;;(add-hook 'vm-mail-mode-hook 'mime/editor-mode)
653 (setq vm-forwarding-digest-type "rfc1521")
654 (setq vm-digest-send-type "rfc1521")
665 (or (fboundp 'tm:bbdb/vm-update-record)
666 (fset 'tm:bbdb/vm-update-record
667 (symbol-function 'bbdb/vm-update-record))
669 (defun bbdb/vm-update-record (&optional offer-to-create)
670 (vm-select-folder-buffer)
671 (let ((vm-mail-buffer
672 (if (and mime::article/preview-buffer
673 (get-buffer mime::article/preview-buffer))
674 mime::article/preview-buffer
677 (bbdb/vm-update-record-recursive
678 (boundp 'bbdb/vm-update-record-recursive))
679 bbdb/vm-update-record-recursive ret)
680 (let ((bbdb/vm-update-record-answer
681 (if (boundp 'bbdb/vm-update-record-answer)
682 (setq bbdb/vm-update-record-answer
683 (or bbdb/vm-update-record-answer
684 (tm:bbdb/vm-update-record)
686 (setq ret (tm:bbdb/vm-update-record))
688 (or bbdb/vm-update-record-answer ret)
690 (defun tm-vm/bbdb-update-record (&optional offer-to-create)
691 (let ((vm-mail-buffer (current-buffer)))
692 (tm:bbdb/vm-update-record offer-to-create)
694 (remove-hook 'vm-select-message-hook 'bbdb/vm-update-record)
695 (remove-hook 'vm-show-message-hook 'bbdb/vm-update-record)
696 (add-hook 'tm-vm/select-message-hook 'tm-vm/bbdb-update-record)