diff lisp/vm/vm-reply.el @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children ac2d302a0011
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/vm/vm-reply.el	Mon Aug 13 08:45:50 2007 +0200
@@ -0,0 +1,987 @@
+;;; Mailing, forwarding, and replying commands for VM
+;;; Copyright (C) 1989, 1990, 1991, 1993, 1994, 1995 Kyle E. Jones
+;;;
+;;; This program is free software; you can redistribute it and/or modify
+;;; it under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 1, or (at your option)
+;;; any later version.
+;;;
+;;; This program is distributed in the hope that it will be useful,
+;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with this program; if not, write to the Free Software
+;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+(provide 'vm-reply)
+
+(defun vm-do-reply (to-all include-text count)
+    (let ((mlist (vm-select-marked-or-prefixed-messages count))
+	  (dir default-directory)
+	  (message-pointer vm-message-pointer)
+	  (case-fold-search t)
+	  to cc subject mp in-reply-to references tmp tmp2 newsgroups)
+      (setq mp mlist)
+      (while mp 
+	(cond
+	 ((eq mlist mp)
+	  (cond ((setq to
+		       (let ((reply-to
+			      (vm-get-header-contents (car mp) "Reply-To:")))
+			 (if (vm-ignored-reply-to reply-to)
+			     nil
+			   reply-to ))))
+		((setq to (vm-get-header-contents (car mp) "From:")))
+		;; bad, but better than nothing for some
+		((setq to (vm-grok-From_-author (car mp))))
+		(t (error "No From: or Reply-To: header in message")))
+	  (setq subject (vm-get-header-contents (car mp) "Subject:")
+		in-reply-to
+		(and vm-in-reply-to-format
+		     (let ((vm-summary-uninteresting-senders nil))
+		       (vm-sprintf 'vm-in-reply-to-format (car mp))))
+		in-reply-to (and (not (equal "" in-reply-to)) in-reply-to))
+	  (and subject vm-reply-subject-prefix
+	       (let ((case-fold-search t))
+		 (not
+		  (equal
+		   (string-match (regexp-quote vm-reply-subject-prefix)
+				 subject)
+		   0)))
+	       (setq subject (concat vm-reply-subject-prefix subject))))
+	 (t (cond ((setq tmp (vm-get-header-contents (car mp) "Reply-To:"))
+		   (setq to (concat to "," tmp)))
+		  ((setq tmp (vm-get-header-contents (car mp) "From:"))
+		   (setq to (concat to "," tmp)))
+		  ;; bad, but better than nothing for some
+		  ((setq tmp (vm-grok-From_-author (car mp)))
+		   (setq to (concat to "," tmp)))
+		  (t (error "No From: or Reply-To: header in message")))))
+	(if to-all
+	    (progn
+	      (setq tmp (vm-get-header-contents (car mp) "To:"))
+	      (setq tmp2 (vm-get-header-contents (car mp) "Cc:"))
+	      (if tmp
+		  (if cc
+		      (setq cc (concat cc "," tmp))
+		    (setq cc tmp)))
+	      (if tmp2
+		  (if cc
+		      (setq cc (concat cc "," tmp2))
+		    (setq cc tmp2)))))
+	(setq references
+	      (cons (vm-get-header-contents (car mp) "References:")
+		    (cons (vm-get-header-contents (car mp) "In-reply-to:")
+			  (cons (vm-get-header-contents (car mp) "Message-ID:")
+				references))))
+	(setq newsgroups
+	      (cons (or (and to-all (vm-get-header-contents (car mp) "Followup-To:"))
+			(vm-get-header-contents (car mp) "Newsgroups:"))
+		    newsgroups))
+	(setq mp (cdr mp)))
+      (if vm-strip-reply-headers
+	  (let ((mail-use-rfc822 t))
+	    (and to (setq to (mail-strip-quoted-names to)))
+	    (and cc (setq cc (mail-strip-quoted-names cc)))))
+      (setq to (vm-parse-addresses to)
+	    cc (vm-parse-addresses cc))
+      (if vm-reply-ignored-addresses
+	  (setq to (vm-strip-ignored-addresses to)
+		cc (vm-strip-ignored-addresses cc)))
+      (setq to (vm-delete-duplicates to nil t))
+      (setq cc (vm-delete-duplicates
+		(append (vm-delete-duplicates cc nil t)
+			to (copy-sequence to))
+		t t))
+      (and to (setq to (mapconcat 'identity to ",\n    ")))
+      (and cc (setq cc (mapconcat 'identity cc ",\n    ")))
+      (and (null to) (setq to cc cc nil))
+      (setq references (delq nil references)
+	    references (mapconcat 'identity references " ")
+	    references (vm-parse references "[^<]*\\(<[^>]+>\\)")
+	    references (vm-delete-duplicates references)
+	    references (if references (mapconcat 'identity references "\n\t")))
+      (setq newsgroups (delq nil newsgroups)
+	    newsgroups (mapconcat 'identity newsgroups ",")
+	    newsgroups (vm-parse newsgroups "[ \t\f\r\n,]*\\([^ \t\f\r\n,]+\\)")
+	    newsgroups (vm-delete-duplicates newsgroups)
+	    newsgroups (if newsgroups (mapconcat 'identity newsgroups ",")))
+      (vm-mail-internal
+       (format "reply to %s%s" (vm-su-full-name (car mlist))
+	       (if (cdr mlist) ", ..." ""))
+       to subject in-reply-to cc references newsgroups)
+      (make-local-variable 'vm-reply-list)
+      (setq vm-system-state 'replying
+	    vm-reply-list mlist
+	    default-directory dir)
+      (if include-text
+	  (save-excursion
+	    (goto-char (point-min))
+	    (let ((case-fold-search nil))
+	      (re-search-forward
+	       (concat "^" (regexp-quote mail-header-separator) "$") nil 0))
+	    (forward-char 1)
+	    (while mlist
+	      (vm-yank-message (car mlist))
+	      (goto-char (point-max))
+	      (setq mlist (cdr mlist)))))
+      (run-hooks 'vm-reply-hook)
+      (run-hooks 'vm-mail-mode-hook)))
+
+(defun vm-strip-ignored-addresses (addresses)
+  (setq addresses (copy-sequence addresses))
+  (let (re-list list addr-list)
+    (setq re-list vm-reply-ignored-addresses)
+    (while re-list
+      (setq addr-list addresses)
+      (while addr-list
+	(if (string-match (car re-list) (car addr-list))
+	    (setq addresses (delq (car addr-list) addresses)))
+	(setq addr-list (cdr addr-list)))
+      (setq re-list (cdr re-list))))
+  addresses )
+
+(defun vm-ignored-reply-to (reply-to)
+  (if reply-to
+      (let (re-list result)
+	(setq re-list vm-reply-ignored-reply-tos)
+	(while re-list
+	  (if (string-match (car re-list) reply-to)
+	      (setq result t re-list nil)
+	    (setq re-list (cdr re-list))))
+	result )))
+
+(defun vm-mail-yank-default (message)
+  (save-excursion
+    (vm-reorder-message-headers nil vm-included-text-headers
+				vm-included-text-discard-header-regexp)
+    ;; if all the headers are gone, delete the trailing blank line, too.
+    (if (eq (following-char) ?\n)
+	(delete-char 1))
+    (if vm-included-text-attribution-format
+	(let ((vm-summary-uninteresting-senders nil))
+	  (insert (vm-sprintf 'vm-included-text-attribution-format message))))
+    ; turn off zmacs-regions for Lucid Emacs 19
+    ; and get around transient-mark-mode in FSF Emacs 19
+    ; all this so that (mark) does what it did in v18, sheesh.
+    (let* ((zmacs-regions nil)
+	   (mark-even-if-inactive t)
+	   (end (mark-marker)))
+      (while (< (point) end)
+	(insert vm-included-text-prefix)
+	(forward-line 1)))))
+
+(defun vm-yank-message-other-folder (folder)
+  "Like vm-yank-message except the message is yanked from a folder other
+than the one that spawned the current Mail mode buffer.  The name of the
+folder is read from the minibuffer.
+
+Don't call this function from a program."
+  (interactive
+   (list
+    (let ((dir (if vm-folder-directory
+		    (expand-file-name vm-folder-directory)
+		  default-directory))
+	  (last-command last-command)
+	  (this-command this-command))
+      (read-file-name "Yank from folder: " dir nil t))))
+  (let ((b (current-buffer)) newbuf sumbuf default result prompt mp)
+    (set-buffer (or (vm-get-file-buffer folder) (find-file-noselect folder)))
+    (setq newbuf (current-buffer))
+    (if (not (eq major-mode 'vm-mode))
+	(vm-mode))
+    (if (null vm-message-pointer)
+	(error "No messages in folder %s" folder))
+    (setq default (vm-number-of (car vm-message-pointer)))
+    (save-excursion
+      (save-window-excursion
+	(save-window-excursion
+	  (vm-summarize))
+	(vm-display vm-summary-buffer t '(vm-yank-message-other-folder)
+		    '(vm-yank-message-other-folder composing-message))
+	(setq sumbuf (current-buffer))
+	(setq prompt (format "Yank message number: (default %s) " default)
+	      result 0)
+	(while (zerop result)
+	  (setq result (read-string prompt))
+	  (and (string= result "") default (setq result default))
+	  (setq result (string-to-int result)))
+	(if (null (setq mp (nthcdr (1- result) vm-message-list)))
+	    (error "No such message."))))
+    (set-buffer b)
+    (unwind-protect
+	(let ((vm-mail-buffer newbuf))
+	  (vm-yank-message (car mp)))
+      (vm-bury-buffer newbuf)
+      (vm-bury-buffer sumbuf))))
+
+(defun vm-yank-message (message)
+  "Yank message number N into the current buffer at point.
+When called interactively N is always read from the minibuffer.  When
+called non-interactively the first argument is expected to be a
+message struct.
+
+This command is meant to be used in VM created Mail mode buffers; the
+yanked message comes from the mail buffer containing the message you
+are replying to, forwarding, or invoked VM's mail command from.
+
+All message headers are yanked along with the text.  Point is
+left before the inserted text, the mark after.  Any hook
+functions bound to mail-citation-hook are run, after inserting
+the text and setting point and mark.  For backward compatibility,
+if mail-citation-hook is set to nil, `mail-yank-hooks' is run
+instead.
+
+If mail-citation-hook and mail-yank-hooks are both nil, this
+default action is taken: the yanked headers are trimmed as
+specified by vm-included-text-headers and
+vm-included-text-discard-header-regexp, and the value of
+vm-included-text-prefix is prepended to every yanked line."
+  (interactive
+   (list
+   ;; What we really want for the first argument is a message struct,
+   ;; but if called interactively, we let the user type in a message
+   ;; number instead.
+    (let (mp default
+	  (result 0)
+	  prompt
+	  (last-command last-command)
+	  (this-command this-command))
+      (save-excursion
+	(vm-select-folder-buffer)
+	(setq default (and vm-message-pointer
+			   (vm-number-of (car vm-message-pointer)))
+	      prompt (if default
+			 (format "Yank message number: (default %s) "
+				 default)
+		       "Yank message number: "))
+	(while (zerop result)
+	  (setq result (read-string prompt))
+	  (and (string= result "") default (setq result default))
+	  (setq result (string-to-int result)))
+	(if (null (setq mp (nthcdr (1- result) vm-message-list)))
+	    (error "No such message.")))
+      (car mp))))
+  (if (not (bufferp vm-mail-buffer))
+      (error "This is not a VM Mail mode buffer."))
+  (if (null (buffer-name vm-mail-buffer))
+      (error "The folder buffer containing message %d has been killed."
+	     (vm-number-of message)))
+  (vm-display nil nil '(vm-yank-message) '(vm-yank-message composing-message))
+  (setq message (vm-real-message-of message))
+  (let ((b (current-buffer)) (start (point)) end)
+    (save-restriction
+      (widen)
+      (save-excursion
+	(set-buffer (vm-buffer-of message))
+	(save-restriction
+	  (widen)
+	  (append-to-buffer b (vm-headers-of message) (vm-text-end-of message))
+	  (setq end (vm-marker (+ start (- (vm-text-end-of message)
+					   (vm-headers-of message))) b))))
+      (push-mark end)
+      (cond (mail-citation-hook (run-hooks 'mail-citation-hook))
+	    (mail-yank-hooks (run-hooks 'mail-yank-hooks))
+	    (t (vm-mail-yank-default message))))))
+
+(defun vm-mail-send-and-exit (arg)
+  "Just like mail-send-and-exit except that VM flags the appropriate message(s)
+as having been replied to, if appropriate."
+  (interactive "P")
+  (let ((b (current-buffer)))
+    (vm-mail-send)
+    (cond ((null (buffer-name b)) ;; dead buffer
+	   (vm-display nil nil '(vm-mail-send-and-exit)
+		       '(vm-mail-send-and-exit reading-message startup)))
+	  (t
+	   (vm-display b nil '(vm-mail-send-and-exit)
+		       '(vm-mail-send-and-exit reading-message startup))
+	   (vm-bury-buffer b)))))
+
+(defun vm-keep-mail-buffer (buffer)
+  ;; keep this buffer if the user demands it
+  (if (memq buffer vm-kept-mail-buffers)
+      (setq vm-kept-mail-buffers
+	    (delq buffer vm-kept-mail-buffers)))
+  (setq vm-kept-mail-buffers (cons buffer vm-kept-mail-buffers)
+	vm-kept-mail-buffers (vm-delete 'buffer-name
+					vm-kept-mail-buffers t))
+  (if (not (eq vm-keep-sent-messages t))
+      (let ((extras (nthcdr (or vm-keep-sent-messages 0)
+				vm-kept-mail-buffers)))
+	(mapcar (function
+		 (lambda (b)
+		   (and (buffer-name b) (kill-buffer b))))
+		extras)
+	(and vm-kept-mail-buffers extras
+	     (setcdr (memq (car extras) vm-kept-mail-buffers) nil)))))
+
+(defun vm-help-tale ()
+  (save-excursion
+    (goto-char (point-min))
+    (while (vm-match-header)
+      (if (not (vm-match-header "To:\\|Resent-To:\\|Cc:\\|Resent-Cc:"))
+	  (goto-char (vm-matched-header-end))
+	(goto-char (vm-matched-header-contents-start))
+	(if (re-search-forward "[^, \t][ \t]*\n[ \t\n]+[^ \t\n]"
+			       (vm-matched-header-contents-end)
+			       t)
+	    (error "tale is an idiot, and so are you. :-)"))
+	(goto-char (vm-matched-header-end))))))
+
+(defun vm-mail-send ()
+  "Just like mail-send except that VM flags the appropriate message(s)
+as replied to, forwarded, etc, if appropriate."
+  (interactive)
+  (if vm-tale-is-an-idiot
+      (vm-help-tale))
+  (if (and vm-confirm-mail-send
+	   (not (y-or-n-p "Send the message? ")))
+      (error "Message not sent."))
+  ;; this to prevent Emacs 19 from asking whether a message that
+  ;; has already been sent should be sent again.  VM renames mail
+  ;; buffers after the message has been sent, so the user should
+  ;; already know that the message has been sent.
+  (set-buffer-modified-p t)
+  ;; don't want a buffer change to occur here
+  ;; save-excursion to be sure.
+  (save-excursion
+    (mail-send))
+  (vm-rename-current-mail-buffer)
+  (cond ((eq vm-system-state 'replying)
+	 (vm-mail-mark-replied))
+	((eq vm-system-state 'forwarding)
+	 (vm-mail-mark-forwarded))
+	((eq vm-system-state 'redistributing)
+	 (vm-mail-mark-redistributed)))
+  (vm-keep-mail-buffer (current-buffer))
+  (vm-display nil nil '(vm-mail-send) '(vm-mail-send)))
+
+(defun vm-rename-current-mail-buffer ()
+  (if vm-rename-current-buffer-function
+      (funcall vm-rename-current-buffer-function)
+    (let ((case-fold-search nil))
+      (if (not (string-match "^sent " (buffer-name)))
+	  (let (prefix name n)
+	    (if (not (= ?* (aref (buffer-name) 0)))
+		(setq prefix (format "sent %s" (buffer-name)))
+	      (let (recipients)
+		(cond ((not (zerop (length (setq recipients
+						 (mail-fetch-field "To"))))))
+		      ((not (zerop (length (setq recipients
+						 (mail-fetch-field "Cc"))))))
+		      ((not (zerop (length (setq recipients
+						 (mail-fetch-field "Bcc"))))))
+					; can't happen?!?
+		      (t (setq recipients "the horse with no name")))
+		(setq prefix (format "sent mail to %s" recipients))))
+	    (if (> (length prefix) 44)
+		(setq prefix (concat (substring prefix 0 40) " ...")))
+	    (setq name prefix n 2)
+	    (while (get-buffer name)
+	      (setq name (format "%s<%d>" prefix n))
+	      (vm-increment n))
+	    (rename-buffer name))))))
+
+(defun vm-mail-mark-replied ()
+  (save-excursion
+    (let ((mp vm-reply-list))
+      (while mp
+	(if (null (buffer-name (vm-buffer-of (car mp))))
+	    ()
+	  (set-buffer (vm-buffer-of (car mp)))
+	  (cond ((and (memq (car mp) vm-message-list)
+		      (null (vm-replied-flag (car mp))))
+		 (vm-set-replied-flag (car mp) t))))
+	(setq mp (cdr mp)))
+      (vm-update-summary-and-mode-line))))
+
+(defun vm-mail-mark-forwarded ()
+  (save-excursion
+    (let ((mp vm-forward-list))
+      (while mp
+	(if (null (buffer-name (vm-buffer-of (car mp))))
+	    ()
+	  (set-buffer (vm-buffer-of (car mp)))
+	  (cond ((and (memq (car mp) vm-message-list)
+		      (null (vm-forwarded-flag (car mp))))
+		 (vm-set-forwarded-flag (car mp) t))))
+	(setq mp (cdr mp)))
+      (vm-update-summary-and-mode-line))))
+
+(defun vm-mail-mark-redistributed ()
+  (save-excursion
+    (let ((mp vm-redistribute-list))
+      (while mp
+	(if (null (buffer-name (vm-buffer-of (car mp))))
+	    ()
+	  (set-buffer (vm-buffer-of (car mp)))
+	  (cond ((and (memq (car mp) vm-message-list)
+		      (null (vm-redistributed-flag (car mp))))
+		 (vm-set-redistributed-flag (car mp) t))))
+	(setq mp (cdr mp)))
+      (vm-update-summary-and-mode-line))))
+
+(defun vm-reply (count)
+  "Reply to the sender of the current message.
+Numeric prefix argument N means to reply to the current message plus the
+next N-1 messages.  A negative N means reply to the current message and
+the previous N-1 messages. 
+
+If invoked on marked messages (via vm-next-command-uses-marks),
+all marked messages will be replied to.
+
+You will be placed into a standard Emacs Mail mode buffer to compose and
+send your message.  See the documentation for the function `mail' for
+more info.
+
+Note that the normal binding of C-c C-y in the reply buffer is
+automatically changed to vm-yank-message during a reply.  This
+allows you to yank any message from the current folder into a
+reply.
+
+Normal VM commands may be accessed in the reply buffer by prefixing them
+with C-c C-v."
+  (interactive "p")
+  (vm-follow-summary-cursor)
+  (vm-select-folder-buffer)
+  (vm-check-for-killed-summary)
+  (vm-error-if-folder-empty)
+  (vm-do-reply nil nil count))
+
+(defun vm-reply-include-text (count)
+  "Reply to the sender (only) of the current message and include text
+from the message.  See the documentation for function vm-reply for details."
+  (interactive "p")
+  (vm-follow-summary-cursor)
+  (vm-select-folder-buffer)
+  (vm-check-for-killed-summary)
+  (vm-error-if-folder-empty)
+  (vm-do-reply nil t count))
+
+(defun vm-followup (count)
+  "Reply to all recipients of the current message.
+See the documentation for the function vm-reply for details."
+  (interactive "p")
+  (vm-follow-summary-cursor)
+  (vm-select-folder-buffer)
+  (vm-check-for-killed-summary)
+  (vm-error-if-folder-empty)
+  (vm-do-reply t nil count))
+
+(defun vm-followup-include-text (count)
+  "Reply to all recipients of the current message and include text from
+the message.  See the documentation for the function vm-reply for details."
+  (interactive "p")
+  (vm-follow-summary-cursor)
+  (vm-select-folder-buffer)
+  (vm-check-for-killed-summary)
+  (vm-error-if-folder-empty)
+  (vm-do-reply t t count))
+
+(defun vm-forward-message-all-headers ()
+  "Like vm-forward-message but always forwards all the headers."
+  (interactive)
+  (let ((vm-forwarded-headers nil)
+	(vm-unforwarded-header-regexp "only-drop-this-header"))
+    (vm-forward-message)))
+
+(defun vm-forward-message ()
+  "Forward the current message to one or more recipients.
+You will be placed in a Mail mode buffer as you would with a
+reply, but you must fill in the To: header and perhaps the
+Subject: header manually."
+  (interactive)
+  (vm-follow-summary-cursor)
+  (vm-select-folder-buffer)
+  (vm-check-for-killed-summary)
+  (vm-error-if-folder-empty)
+  (if (eq last-command 'vm-next-command-uses-marks)
+      (let ((vm-digest-send-type vm-forwarding-digest-type))
+	(setq this-command 'vm-next-command-uses-marks)
+	(command-execute 'vm-send-digest))
+    (let ((dir default-directory)
+	  (mp vm-message-pointer))
+      (save-restriction
+	(widen)
+	(vm-mail-internal
+	 (format "forward of %s's note re: %s"
+		 (vm-su-full-name (car vm-message-pointer))
+		 (vm-su-subject (car vm-message-pointer)))
+	 nil
+	 (and vm-forwarding-subject-format
+	      (let ((vm-summary-uninteresting-senders nil))
+		(vm-sprintf 'vm-forwarding-subject-format (car mp)))))
+	(make-local-variable 'vm-forward-list)
+	(setq vm-system-state 'forwarding
+	      vm-forward-list (list (car mp))
+	      default-directory dir)
+	(goto-char (point-min))
+	(re-search-forward
+	 (concat "^" (regexp-quote mail-header-separator) "\n") nil 0)
+	(cond ((equal vm-forwarding-digest-type "rfc934")
+	       (vm-rfc934-encapsulate-messages
+		vm-forward-list vm-forwarded-headers
+		vm-unforwarded-header-regexp))
+	      ((equal vm-forwarding-digest-type "rfc1153")
+	       (vm-rfc1153-encapsulate-messages
+		vm-forward-list vm-forwarded-headers
+		vm-unforwarded-header-regexp))
+	      ((equal vm-forwarding-digest-type nil)
+	       (vm-no-frills-encapsulate-message
+		(car vm-forward-list) vm-forwarded-headers
+		vm-unforwarded-header-regexp)))
+	(mail-position-on-field "To"))
+      (run-hooks 'vm-forward-message-hook)
+      (run-hooks 'vm-mail-mode-hook))))
+
+(defun vm-resend-bounced-message ()
+  "Extract the original text from a bounced message and resend it.
+You will be placed in a Mail mode buffer with the extracted message and
+you can change the recipient address before resending the message."
+  (interactive)
+  (vm-follow-summary-cursor)
+  (vm-select-folder-buffer)
+  (vm-check-for-killed-summary)
+  (vm-error-if-folder-empty)
+  (let ((b (current-buffer)) start
+	(dir default-directory)
+	(lim (vm-text-end-of (car vm-message-pointer))))
+      (save-restriction
+	(widen)
+	(save-excursion
+	  (goto-char (vm-text-of (car vm-message-pointer)))
+	  (let ((case-fold-search t))
+	    ;; What a wonderful world it would be if mailers used a single
+	    ;; message encapsulation standard instead all the weird variants
+	    ;; It is useless to try to cover them all.
+	    ;; This simple rule should cover the sanest of the formats
+	    (if (not (re-search-forward "^Received:" lim t))
+		(error "This doesn't look like a bounced message."))
+	    (beginning-of-line)
+	    (setq start (point))))
+	;; briefly nullify vm-mail-header-from to keep vm-mail-internal
+	;; from inserting another From header.
+	(let ((vm-mail-header-from nil))
+	  (vm-mail-internal
+	   (format "retry of bounce from %s"
+		   (vm-su-from (car vm-message-pointer)))))
+	(goto-char (point-min))
+	(insert-buffer-substring b start lim)
+	(delete-region (point) (point-max))
+	(goto-char (point-min))
+	;; delete all but pertinent headers
+	(vm-reorder-message-headers nil nil "\\(X-VM-\\|Status:\\|Sender:\\)")
+	(vm-reorder-message-headers nil vm-resend-bounced-headers
+				    vm-resend-bounced-discard-header-regexp)
+	(if (search-forward "\n\n" nil t)
+	    (replace-match "")
+	  (goto-char (point-max)))
+	(insert ?\n mail-header-separator ?\n)
+	(mail-position-on-field "To")
+	(setq default-directory dir)))
+  (run-hooks 'vm-resend-bounced-message-hook)
+  (run-hooks 'vm-mail-mode-hook))
+
+(defun vm-resend-message ()
+  "Resend the current message to someone else.
+The current message will be copied to a Mail mode buffer and you
+can edit the message and send it as usual.
+
+NOTE: since you are doing a resend, a Resent-To header is
+provided for you to fill in.  If you don't fill it in, when you
+send the message it will go to the original recipients listed in
+the To and Cc headers.  You may also create a Resent-Cc header."
+  (interactive)
+  (vm-follow-summary-cursor)
+  (vm-select-folder-buffer)
+  (vm-check-for-killed-summary)
+  (vm-error-if-folder-empty)
+  (save-restriction
+    (widen)
+    (let ((b (current-buffer))
+	  (dir default-directory)
+	  (vmp vm-message-pointer)
+	  (start (vm-headers-of (car vm-message-pointer)))
+	  (lim (vm-text-end-of (car vm-message-pointer))))
+      ;; briefly nullify vm-mail-header-from to keep vm-mail-internal
+      ;; from inserting another From header.
+      (let ((vm-mail-header-from nil))
+	(vm-mail-internal
+	 (format "resend of %s's note re: %s"
+		 (vm-su-full-name (car vm-message-pointer))
+		 (vm-su-subject (car vm-message-pointer)))))
+      (goto-char (point-min))
+      (insert-buffer-substring b start lim)
+      (delete-region (point) (point-max))
+      (goto-char (point-min))
+      (if vm-mail-header-from
+	  (insert "Resent-From: " vm-mail-header-from ?\n))
+      (insert "Resent-To: \n")
+      (if mail-self-blind
+	  (insert "Bcc: " (user-login-name) ?\n))
+      (if mail-archive-file-name
+	  (insert "FCC: " mail-archive-file-name ?\n))
+      ;; delete all but pertinent headers
+      (vm-reorder-message-headers nil nil "\\(X-VM-\\|Status:\\|Sender:\\)")
+      (vm-reorder-message-headers nil vm-resend-headers
+				  vm-resend-discard-header-regexp)
+      (if (search-forward "\n\n" nil t)
+	  (replace-match ""))
+      (insert ?\n mail-header-separator ?\n)
+      (goto-char (point-min))
+      (mail-position-on-field "Resent-To")
+      (make-local-variable 'vm-redistribute-list)
+      (setq vm-system-state 'redistributing
+	    vm-redistribute-list (list (car vmp))
+	    default-directory dir)
+      (run-hooks 'vm-resend-message-hook)
+      (run-hooks 'vm-mail-mode-hook))))
+
+(defun vm-send-digest (&optional prefix)
+  "Send a digest of all messages in the current folder to recipients.
+The type of the digest is specified by the variable vm-digest-send-type.
+You will be placed in a Mail mode buffer as is usual with replies, but you
+must fill in the To: and Subject: headers manually.
+
+Prefix arg means to insert a list of preamble lines at the beginning of
+the digest.  One line is generated for each message being digestified.
+The variable vm-digest-preamble-format determines the format of the
+preamble lines.
+
+If invoked on marked messages (via vm-next-command-uses-marks),
+only marked messages will be put into the digest."
+  (interactive "P")
+  (vm-select-folder-buffer)
+  (vm-check-for-killed-summary)
+  (vm-error-if-folder-empty)
+  (let ((dir default-directory)
+	(mp vm-message-pointer)
+	;; prefix arg doesn't have "normal" meaning here, so only call
+	;; vm-select-marked-or-prefixed-messages if we're using marks.
+	(mlist (if (eq last-command 'vm-next-command-uses-marks)
+		   (vm-select-marked-or-prefixed-messages 0)
+		 vm-message-list))
+	start)
+    (save-restriction
+      (widen)
+      (vm-mail-internal (format "digest from %s" (buffer-name)))
+      (make-local-variable 'vm-forward-list)
+      (setq vm-system-state 'forwarding
+	    vm-forward-list mlist
+	    default-directory dir)
+      (goto-char (point-min))
+      (re-search-forward (concat "^" (regexp-quote mail-header-separator)
+				 "\n"))
+      (goto-char (match-end 0))
+      (setq start (point)
+	    mp mlist)
+      (vm-unsaved-message "Building %s digest..." vm-digest-send-type)
+      (cond ((equal vm-digest-send-type "rfc934")
+	     (vm-rfc934-encapsulate-messages
+	      mlist vm-rfc934-digest-headers
+	      vm-rfc934-digest-discard-header-regexp))
+	    ((equal vm-digest-send-type "rfc1153")
+	     (vm-rfc1153-encapsulate-messages
+	      mlist vm-rfc1153-digest-headers
+	      vm-rfc1153-digest-discard-header-regexp)))
+      (goto-char start)
+      (setq mp mlist)
+      (if prefix
+	  (progn
+	    (vm-unsaved-message "Building digest preamble...")
+	    (while mp
+	      (let ((vm-summary-uninteresting-senders nil))
+		(insert (vm-sprintf 'vm-digest-preamble-format (car mp)) "\n"))
+	      (if vm-digest-center-preamble
+		  (progn
+		    (forward-char -1)
+		    (center-line)
+		    (forward-char 1)))
+	      (setq mp (cdr mp)))))
+      (mail-position-on-field "To")
+      (message "Building %s digest... done" vm-digest-send-type)))
+  (run-hooks 'vm-send-digest-hook)
+  (run-hooks 'vm-mail-mode-hook))
+
+(defun vm-send-rfc934-digest (&optional preamble)
+  "Like vm-send-digest but always sends an RFC 934 digest."
+  (interactive "P")
+  (let ((vm-digest-send-type "rfc934"))
+    (vm-send-digest preamble)))
+
+(defun vm-send-rfc1153-digest (&optional preamble)
+  "Like vm-send-digest but always sends an RFC 1153 digest."
+  (interactive "P")
+  (let ((vm-digest-send-type "rfc1153"))
+    (vm-send-digest preamble)))
+
+(defun vm-continue-composing-message (&optional not-picky)
+  "Find and select the most recently used mail composition buffer.
+If the selected buffer is already a Mail mode buffer then it is
+buried before beginning the search.  Non Mail mode buffers and
+unmodified Mail buffers are skipped.  Prefix arg means unmodified
+Mail mode buffers are not skipped.  If no suitable buffer is
+found, the current buffer remains selected."
+  (interactive "P")
+  (if (eq major-mode 'mail-mode)
+      (vm-bury-buffer (current-buffer)))
+  (let ((b (vm-find-composition-buffer not-picky)))
+    (if (not (or (null b) (eq b (current-buffer))))
+	(progn
+	  ;; avoid having the window configuration code choose a
+	  ;; different composition buffer.
+	  (vm-unbury-buffer b)
+	  (set-buffer b)
+	  (if (and vm-frame-per-composition (vm-multiple-frames-possible-p)
+		   ;; only pop up a frame if there's an undisplay
+		   ;; hook in place to make the frame go away.
+		   vm-undisplay-buffer-hook)
+	      (let ((w (vm-get-buffer-window b)))
+		(if (null w)
+		    (vm-goto-new-frame 'composition)
+		  (select-window w)
+		  (and vm-warp-mouse-to-new-frame
+		       (vm-warp-mouse-to-frame-maybe (vm-window-frame w))))
+		;; need to do this here too, since XEmacs has per
+		;; frame buffer lists.
+		(vm-unbury-buffer b)
+		(vm-set-hooks-for-frame-deletion)))
+	  (vm-display b t '(vm-continue-composing-message)
+		      '(vm-continue-composing-message composing-message)))
+      (message "No composition buffers found"))))
+
+;; to quiet the v19 byte compiler
+(defvar mail-mode-map)
+(defvar mail-aliases)
+(defvar mail-default-reply-to)
+(defvar mail-signature-file)
+
+(defun vm-mail-internal
+    (&optional buffer-name to subject in-reply-to cc references newsgroups)
+  (let ((folder-buffer nil))
+    (if (memq major-mode '(vm-mode vm-virtual-mode))
+	(setq folder-buffer (current-buffer)))
+    (set-buffer (generate-new-buffer (or buffer-name "*VM-mail*")))
+    ;; avoid trying to write auto-save files in potentially
+    ;; unwritable directories.
+    (setq default-directory (or vm-folder-directory (expand-file-name "~/")))
+    (auto-save-mode (if auto-save-default 1 -1))
+    (mail-mode)
+    (use-local-map vm-mail-mode-map)
+    ;; make mail-mode-map the parent of this vm-mail-mode-map, if we can.
+    ;; do it only once.
+    (if (not vm-mail-mode-map-parented)
+	(cond ((fboundp 'set-keymap-parents)
+	       (set-keymap-parents vm-mail-mode-map (list mail-mode-map)))
+	      ((consp mail-mode-map)
+	       (nconc vm-mail-mode-map mail-mode-map)
+	       (setq vm-mail-mode-map-parented t))))
+    (setq vm-mail-buffer folder-buffer
+	  mode-popup-menu (and vm-use-menus
+			       (vm-menu-support-possible-p)
+			       (vm-menu-mode-menu)))
+    ;; sets up popup menu for FSF Emacs
+    (and vm-use-menus (vm-menu-support-possible-p)
+	 (vm-menu-install-mail-mode-menu))
+    (if (fboundp 'mail-aliases-setup) ; use mail-abbrevs.el if present
+	(mail-aliases-setup)
+      (if (eq mail-aliases t)
+	  (progn
+	    (setq mail-aliases nil)
+	    (if (file-exists-p "~/.mailrc")
+		(build-mail-aliases)))))
+    (if (stringp vm-mail-header-from)
+	(insert "From: " vm-mail-header-from "\n"))
+    (insert "To: " (or to "") "\n")
+    (and cc (insert "Cc: " cc "\n"))
+    (insert "Subject: " (or subject "") "\n")
+    (and newsgroups (insert "Newsgroups: " newsgroups "\n"))
+    (and in-reply-to (insert "In-Reply-To: " in-reply-to "\n"))
+    (and references (insert "References: " references "\n"))
+    ;; REPLYTO support for FSF Emacs v19.29
+    (and (eq mail-default-reply-to t)
+	 (setq mail-default-reply-to (getenv "REPLYTO")))
+    (if mail-default-reply-to
+	(insert "Reply-To: " mail-default-reply-to "\n"))
+    (if mail-self-blind
+	(insert "Bcc: " (user-login-name) "\n"))
+    (if mail-archive-file-name
+	(insert "FCC: " mail-archive-file-name "\n"))
+    (if mail-default-headers
+	(insert mail-default-headers))
+    (if (not (= (preceding-char) ?\n))
+	(insert ?\n))
+    (insert mail-header-separator "\n")
+    (cond ((stringp mail-signature)
+	   (save-excursion
+	     (insert mail-signature)))
+	  ((eq mail-signature t)
+	   (save-excursion
+	     (insert "-- \n")
+	     (insert-file-contents (or (and (boundp 'mail-signature-file)
+					    (stringp mail-signature-file)
+					    mail-signature-file)
+				       "~/.signature")))))
+    ;; move this buffer to the head of the buffer list so window
+    ;; config stuff will select it as the composition buffer.
+    (vm-unbury-buffer (current-buffer))
+    ;; make a new frame if the user wants it.
+    (if (and vm-frame-per-composition (vm-multiple-frames-possible-p))
+	(progn
+	  (vm-goto-new-frame 'composition)
+	  (vm-set-hooks-for-frame-deletion)))
+    ;; now do window configuration
+    (vm-display (current-buffer) t
+		'(vm-mail
+		  vm-mail-other-frame
+		  vm-mail-other-window
+		  vm-reply
+		  vm-reply-other-frame
+		  vm-reply-include-text
+		  vm-reply-include-text-other-frame
+		  vm-followup
+		  vm-followup-other-frame
+		  vm-followup-include-text
+		  vm-followup-include-text-other-frame
+		  vm-send-digest
+		  vm-send-digest-other-frame
+		  vm-send-rfc934-digest
+		  vm-send-rfc934-digest-other-frame
+		  vm-send-rfc1153-digest
+		  vm-send-rfc1153-digest-other-frame
+		  vm-forward-message
+		  vm-forward-message-other-frame
+		  vm-forward-message-all-headers
+		  vm-forward-message-all-headers-other-frame
+		  vm-resend-message
+		  vm-resend-message-other-frame
+		  vm-resend-bounced-message
+		  vm-resend-bounced-message-other-frame)
+		(list this-command 'composing-message))
+    (if (null to)
+	(mail-position-on-field "To"))
+    (run-hooks 'mail-setup-hook)))
+
+(defun vm-reply-other-frame (count)
+  "Like vm-reply, but run in a newly created frame."
+  (interactive "p")
+  (if (vm-multiple-frames-possible-p)
+      (vm-goto-new-frame 'composition))
+  (let ((vm-frame-per-composition nil)
+	(vm-search-other-frames nil))
+    (vm-reply count))
+  (if (vm-multiple-frames-possible-p)
+      (vm-set-hooks-for-frame-deletion)))
+
+(defun vm-reply-include-text-other-frame (count)
+  "Like vm-reply-include-text, but run in a newly created frame."
+  (interactive "p")
+  (if (vm-multiple-frames-possible-p)
+      (vm-goto-new-frame 'composition))
+  (let ((vm-frame-per-composition nil)
+	(vm-search-other-frames nil))
+    (vm-reply-include-text count))
+  (if (vm-multiple-frames-possible-p)
+      (vm-set-hooks-for-frame-deletion)))
+
+(defun vm-followup-other-frame (count)
+  "Like vm-followup, but run in a newly created frame."
+  (interactive "p")
+  (if (vm-multiple-frames-possible-p)
+      (vm-goto-new-frame 'composition))
+  (let ((vm-frame-per-composition nil)
+	(vm-search-other-frames nil))
+    (vm-followup count))
+  (if (vm-multiple-frames-possible-p)
+      (vm-set-hooks-for-frame-deletion)))
+
+(defun vm-followup-include-text-other-frame (count)
+  "Like vm-followup-include-text, but run in a newly created frame."
+  (interactive "p")
+  (if (vm-multiple-frames-possible-p)
+      (vm-goto-new-frame 'composition))
+  (let ((vm-frame-per-composition nil)
+	(vm-search-other-frames nil))
+    (vm-followup-include-text count))
+  (if (vm-multiple-frames-possible-p)
+      (vm-set-hooks-for-frame-deletion)))
+
+(defun vm-forward-message-all-headers-other-frame ()
+  "Like vm-forward-message-all-headers, but run in a newly created frame."
+  (interactive)
+  (if (vm-multiple-frames-possible-p)
+      (vm-goto-new-frame 'composition))
+  (let ((vm-frame-per-composition nil)
+	(vm-search-other-frames nil))
+    (vm-forward-message-all-headers))
+  (if (vm-multiple-frames-possible-p)
+      (vm-set-hooks-for-frame-deletion)))
+
+(defun vm-forward-message-other-frame ()
+  "Like vm-forward-message, but run in a newly created frame."
+  (interactive)
+  (if (vm-multiple-frames-possible-p)
+      (vm-goto-new-frame 'composition))
+  (let ((vm-frame-per-composition nil)
+	(vm-search-other-frames nil))
+    (vm-forward-message))
+  (if (vm-multiple-frames-possible-p)
+      (vm-set-hooks-for-frame-deletion)))
+
+(defun vm-resend-message-other-frame ()
+  "Like vm-resend-message, but run in a newly created frame."
+  (interactive)
+  (if (vm-multiple-frames-possible-p)
+      (vm-goto-new-frame 'composition))
+  (let ((vm-frame-per-composition nil)
+	(vm-search-other-frames nil))
+    (vm-resend-message))
+  (if (vm-multiple-frames-possible-p)
+      (vm-set-hooks-for-frame-deletion)))
+
+(defun vm-resend-bounced-message-other-frame ()
+  "Like vm-resend-bounced-message, but run in a newly created frame."
+  (interactive)
+  (if (vm-multiple-frames-possible-p)
+      (vm-goto-new-frame 'composition))
+  (let ((vm-frame-per-composition nil)
+	(vm-search-other-frames nil))
+    (vm-resend-bounced-message))
+  (if (vm-multiple-frames-possible-p)
+      (vm-set-hooks-for-frame-deletion)))
+
+(defun vm-send-digest-other-frame (&optional prefix)
+  "Like vm-send-digest, but run in a newly created frame."
+  (interactive "P")
+  (if (vm-multiple-frames-possible-p)
+      (vm-goto-new-frame 'composition))
+  (let ((vm-frame-per-composition nil)
+	(vm-search-other-frames nil))
+    (vm-send-digest prefix))
+  (if (vm-multiple-frames-possible-p)
+      (vm-set-hooks-for-frame-deletion)))
+
+(defun vm-send-rfc934-digest-other-frame (&optional prefix)
+  "Like vm-send-rfc934-digest, but run in a newly created frame."
+  (interactive "P")
+  (if (vm-multiple-frames-possible-p)
+      (vm-goto-new-frame 'composition))
+  (let ((vm-frame-per-composition nil)
+	(vm-search-other-frames nil))
+    (vm-send-rfc934-digest prefix))
+  (if (vm-multiple-frames-possible-p)
+      (vm-set-hooks-for-frame-deletion)))
+
+(defun vm-send-rfc1153-digest-other-frame (&optional prefix)
+  "Like vm-send-rfc1153-digest, but run in a newly created frame."
+  (interactive "P")
+  (if (vm-multiple-frames-possible-p)
+      (vm-goto-new-frame 'composition))
+  (let ((vm-frame-per-composition nil)
+	(vm-search-other-frames nil))
+    (vm-send-rfc1153-digest prefix))
+  (if (vm-multiple-frames-possible-p)
+      (vm-set-hooks-for-frame-deletion)))