diff lisp/vm/vm-digest.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-digest.el	Mon Aug 13 08:45:50 2007 +0200
@@ -0,0 +1,425 @@
+;;; Message encapsulation
+;;; Copyright (C) 1989, 1990, 1993, 1994 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-digest)
+
+(defun vm-no-frills-encapsulate-message (m keep-list discard-regexp)
+  "Encapsulate a message M for forwarding, simply.
+No message encapsulation standard is used.  The message is
+inserted at point in the current buffer, surrounded by two dashed
+start/end separator lines.  Point is not moved.
+
+M should be a message struct for a real message, not a virtual message.
+This is the message that will be encapsulated.
+KEEP-LIST should be a list of regexps matching headers to keep.
+DISCARD-REGEXP should be a regexp that matches headers to be discarded.
+KEEP-LIST and DISCARD-REGEXP are used to order and trim the headers
+to be forwarded.  See the docs for vm-reorder-message-headers
+to find out how KEEP-LIST and DISCARD-REGEXP are used."
+  (let ((target-buffer (current-buffer))
+	source-buffer)
+    (save-restriction
+      ;; narrow to a zero length region to avoid interacting
+      ;; with anything that might have already been inserted
+      ;; into the buffer.
+      (narrow-to-region (point) (point))
+      (insert "------- start of forwarded message -------\n")
+      (setq source-buffer (vm-buffer-of m))
+      (save-excursion
+	(set-buffer source-buffer)
+	(save-restriction
+	  (widen)
+	  (save-excursion
+	    (set-buffer target-buffer)
+	    (let ((beg (point)))
+	      (insert-buffer-substring source-buffer (vm-headers-of m)
+				       (vm-text-end-of m))
+	      (goto-char beg)
+	      (vm-reorder-message-headers nil nil "\\(X-VM-\\|Status:\\)")
+	      (vm-reorder-message-headers nil keep-list discard-regexp)))))
+      (goto-char (point-max))
+      (insert "------- end of forwarded message -------\n"))))
+
+(defun vm-rfc934-char-stuff-region (start end)
+  "Quote RFC 934 message separators between START and END.
+START and END are buffer positions in the current buffer.
+Lines beginning with `-' in the region have `- ' prepended to them."
+  (setq end (vm-marker end))
+  (save-excursion
+    (goto-char start)
+    (while (and (< (point) end) (re-search-forward "^-" end t))
+      (replace-match "- -" t t)))
+  (set-marker end nil))
+
+(defun vm-rfc934-char-unstuff-region (start end)
+  "Unquote lines in between START and END as per RFC 934.
+START and END are buffer positions in the current buffer.
+Lines beginning with `- ' in the region have that string stripped
+from them."
+  (setq end (vm-marker end))
+  (save-excursion
+    (goto-char start)
+    (while (and (< (point) end) (re-search-forward "^- "  end t))
+      (replace-match "" t t)
+      (forward-char)))
+  (set-marker end nil))
+
+(defun vm-rfc934-encapsulate-messages (message-list keep-list discard-regexp)
+  "Encapsulate the messages in MESSAGE-LIST as per RFC 934.
+The resulting digest is inserted at point in the current buffer.
+Point is not moved.
+
+MESSAGE-LIST should be a list of message structs (real or virtual).
+These are the messages that will be encapsulated.
+KEEP-LIST should be a list of regexps matching headers to keep.
+DISCARD-REGEXP should be a regexp that matches headers to be discarded.
+KEEP-LIST and DISCARD-REGEXP are used to order and trim the headers
+to be forwarded.  See the docs for vm-reorder-message-headers
+to find out how KEEP-LIST and DISCARD-REGEXP are used."
+  (if message-list
+      (let ((target-buffer (current-buffer))
+	    (mlist message-list)
+	    source-buffer m start n)
+	(save-restriction
+	  ;; narrow to a zero length region to avoid interacting
+	  ;; with anything that might have already been inserted
+	  ;; into the buffer.
+	  (narrow-to-region (point) (point))
+	  (setq start (point))
+	  (while mlist
+	    (insert "---------------\n")
+	    (setq m (vm-real-message-of (car mlist))
+		  source-buffer (vm-buffer-of m))
+	    (save-excursion
+	      (set-buffer source-buffer)
+	      (save-restriction
+		(widen)
+		(save-excursion
+		  (set-buffer target-buffer)
+		  (let ((beg (point)))
+		    (insert-buffer-substring source-buffer (vm-headers-of m)
+					     (vm-text-end-of m))
+		    (goto-char beg)
+		    (vm-reorder-message-headers nil nil
+						"\\(X-VM-\\|Status:\\)")
+		    (vm-reorder-message-headers nil keep-list discard-regexp)
+		    (vm-rfc934-char-stuff-region beg (point-max))))))
+	    (goto-char (point-max))
+	    (insert "---------------")
+	    (setq mlist (cdr mlist)))
+	  (delete-region (point) (progn (beginning-of-line) (point)))
+	  (insert "------- end -------\n")
+	  (goto-char start)
+	  (delete-region (point) (progn (forward-line 1) (point)))
+	  (setq n (length message-list))
+   (insert (format "------- start of %s%s(RFC 934 encapsulation) -------\n"
+			  (if (cdr message-list)
+			      "digest "
+			    "forwarded message ")
+			  (if (cdr message-list)
+			      (format "(%d messages) " n)
+			    "")))
+	  (goto-char start)))))
+
+(defun vm-rfc1153-char-stuff-region (start end)
+  "Quote RFC 1153 message separators between START and END.
+START and END are buffer positions in the current buffer.
+Lines consisting only of 30 hyphens have the first hyphen
+converted to a space."
+  (setq end (vm-marker end))
+  (save-excursion
+    (goto-char start)
+    (while (and (< (point) end)
+		(re-search-forward "^------------------------------$" end t))
+      (replace-match " -----------------------------" t t)))
+  (set-marker end nil))
+
+(defun vm-rfc1153-char-unstuff-region (start end)
+  "Unquote lines in between START and END as per RFC 1153.
+START and END are buffer positions in the current buffer.
+Lines consisting only of a space following by 29 hyphens have the space
+converted to a hyphen."
+  (setq end (vm-marker end))
+  (save-excursion
+    (goto-char start)
+    (while (and (< (point) end)
+		(re-search-forward "^ -----------------------------$" end t))
+      (replace-match "------------------------------" t t)))
+  (set-marker end nil))
+
+(defun vm-rfc1153-encapsulate-messages (message-list keep-list discard-regexp)
+  "Encapsulate the messages in MESSAGE-LIST as per RFC 1153.
+The resulting digest is inserted at point in the current buffer.
+Point is not moved.
+
+MESSAGE-LIST should be a list of message structs (real or virtual).
+These are the messages that will be encapsulated.
+KEEP-LIST should be a list of regexps matching headers to keep.
+DISCARD-REGEXP should be a regexp that matches headers to be discarded.
+KEEP-LIST and DISCARD-REGEXP are used to order and trim the headers
+to be forwarded.  See the docs for vm-reorder-message-headers
+to find out how KEEP-LIST and DISCARD-REGEXP are used."
+  (if message-list
+      (let ((target-buffer (current-buffer))
+	    (mlist message-list)
+	    source-buffer m start)
+	(save-restriction
+	  ;; narrow to a zero length region to avoid interacting
+	  ;; with anything that might have already been inserted
+	  ;; into the buffer.
+	  (narrow-to-region (point) (point))
+	  (setq start (point))
+	  (while mlist
+	    (insert "---------------\n\n")
+	    (setq m (vm-real-message-of (car mlist))
+		  source-buffer (vm-buffer-of m))
+	    (save-excursion
+	      (set-buffer source-buffer)
+	      (save-restriction
+		(widen)
+		(save-excursion
+		  (set-buffer target-buffer)
+		  (let ((beg (point)))
+		    (insert-buffer-substring source-buffer (vm-headers-of m)
+					     (vm-text-end-of m))
+		    (goto-char beg)
+		    (vm-reorder-message-headers nil nil
+						"\\(X-VM-\\|Status:\\)")
+		    (vm-reorder-message-headers nil keep-list discard-regexp)
+		    (vm-rfc1153-char-stuff-region beg (point-max))))))
+	    (goto-char (point-max))
+	    (insert "\n---------------")
+	    (setq mlist (cdr mlist)))
+    (insert "---------------\n\nEnd of this Digest\n******************\n")
+	  (goto-char start)
+	  (delete-region (point) (progn (forward-line 1) (point)))
+	  (insert (format "This is an RFC 1153 digest.\n(%d message%s)\n----------------------------------------------------------------------\n" (length message-list) (if (cdr message-list) "s" "")))
+	  (goto-char start)))))
+
+(defun vm-rfc1153-or-rfc934-burst-message (m rfc1153)
+  "Burst messages from the digest message M.
+M should be a message struct for a real message.
+If RFC1153 is non-nil, assume the digest is of the form specified by
+RFC 1153.  Otherwise assume RFC 934 digests."
+  (let ((work-buffer nil)
+	(match t)
+	(prev-sep nil)
+	(ident-header nil)
+	after-prev-sep prologue-separator-regexp separator-regexp
+	(folder-type vm-folder-type))
+    (if vm-digest-identifier-header-format
+	(setq ident-header (vm-sprintf 'vm-digest-identifier-header-format m)))
+    (if rfc1153
+	(setq prologue-separator-regexp "^----------------------------------------------------------------------\n"
+	      separator-regexp "^------------------------------\n")
+      (setq prologue-separator-regexp "^-[^ ].*\n"
+	    separator-regexp "^-[^ ].*\n"))
+    (save-excursion
+      (vm-save-restriction
+       (widen)
+       (unwind-protect
+	   (catch 'done
+	     (setq work-buffer (generate-new-buffer "*vm-work*"))
+	     (set-buffer work-buffer)
+	     (insert-buffer-substring (vm-buffer-of m)
+				      (vm-text-of m)
+				      (vm-text-end-of m))
+	     (goto-char (point-min))
+	     (if (not (re-search-forward prologue-separator-regexp nil t))
+		 (throw 'done nil))
+	     ;; think of this as a do-while loop.
+	     (while match
+	       (cond ((null prev-sep)
+		      ;; from (point-min) to end of match
+		      ;; is the digest prologue, devour it and
+		      ;; carry on.
+		      (delete-region (point-min) (match-end 0)))
+		     (t
+		      ;; munge previous messages message separators
+		      (let ((md (match-data)))
+			(unwind-protect
+			    (vm-munge-message-separators
+			     folder-type
+			     after-prev-sep
+			     (match-beginning 0))
+			  (store-match-data md)))
+		      ;; eat preceding newlines
+		      (while (= (preceding-char) ?\n)
+			(delete-char -1))
+		      ;; put one back
+		      (insert ?\n)))
+	       ;; insert a trailing message separator
+	       ;; delete the digest separator
+	       ;; insert the leading separator
+	       (if prev-sep
+		   (progn
+		     (delete-region (match-beginning 0) (match-end 0))
+		     (insert (vm-trailing-message-separator folder-type))))
+	       (setq prev-sep (point))
+	       (insert (vm-leading-message-separator folder-type))
+	       (setq after-prev-sep (point))
+	       ;; eat trailing newlines
+	       (while (= (following-char) ?\n)
+		 (delete-char 1))
+	       (insert ident-header)
+	       ;; try to match message separator and repeat.
+	       (setq match (re-search-forward separator-regexp nil t)))
+	     ;; from the last separator to eof is the digest epilogue.
+	     ;; discard it.
+	     (delete-region (or prev-sep (point-min)) (point-max))
+	     ;; Undo the quoting of the embedded message
+	     ;; separators.  This must be done before header
+	     ;; conversions, else the Content-Length offsets might be
+	     ;; rendered invalid by buffer size changes.
+	     (if rfc1153
+		 (vm-rfc1153-char-unstuff-region (point-min) (point-max))
+	       (vm-rfc934-char-unstuff-region (point-min) (point-max)))
+	     ;; do header conversions.
+	     (let ((vm-folder-type folder-type))
+	       (goto-char (point-min))
+	       (while (vm-find-leading-message-separator)
+		 (vm-skip-past-leading-message-separator)
+		 (vm-convert-folder-type-headers folder-type folder-type)
+		 (vm-find-trailing-message-separator)
+		 (vm-skip-past-trailing-message-separator)))
+	     ;; now insert the messages into the folder buffer
+	     (cond ((not (zerop (buffer-size)))
+		    (set-buffer (vm-buffer-of m))
+		    (let ((old-buffer-modified-p (buffer-modified-p))
+			  (buffer-read-only nil)
+			  (inhibit-quit t))
+		      (goto-char (point-max))
+		      (insert-buffer-substring work-buffer)
+		      (set-buffer-modified-p old-buffer-modified-p)
+		      ;; return non-nil so caller knows we found some messages
+		      t ))
+		   ;; return nil so the caller knows we didn't find anything
+		   (t nil)))
+	 (and work-buffer (kill-buffer work-buffer)))))))
+
+(defun vm-rfc934-burst-message (m)
+  "Burst messages from the RFC 934 digest message M.
+M should be a message struct for a real message."
+  (vm-rfc1153-or-rfc934-burst-message m nil))
+
+(defun vm-rfc1153-burst-message (m)
+  "Burst messages from the RFC 1153 digest message M.
+M should be a message struct for a real message."
+  (vm-rfc1153-or-rfc934-burst-message m t))
+
+(defun vm-burst-digest (&optional digest-type)
+  "Burst the current message (a digest) into its individual messages.
+The digest's messages are assimilated into the folder as new mail
+would be.
+
+Optional argument DIGEST-TYPE is a string that tells VM what kind
+of digest the current message is.  If it is not given the value
+defaults to the value of vm-digest-burst-type.  When called
+interactively DIGEST-TYPE will be read from the minibuffer.
+
+If invoked on marked messages (via vm-next-command-uses-marks),
+all marked messages will be burst."
+  (interactive
+   (list
+    (let ((type nil)
+	  (this-command this-command)
+	  (last-command last-command))
+      (setq type (completing-read (format "Digest type: (default %s) "
+					  vm-digest-burst-type)
+				  (append vm-digest-type-alist
+					  (list '("guess")))
+				  'identity nil))
+      (if (string= type "")
+	  vm-digest-burst-type
+	type ))))
+  (or digest-type (setq digest-type vm-digest-burst-type))
+  (vm-follow-summary-cursor)
+  (vm-select-folder-buffer)
+  (vm-check-for-killed-summary)
+  (vm-error-if-folder-empty)
+  (let ((start-buffer (current-buffer)) m totals-blurb
+	(mlist (vm-select-marked-or-prefixed-messages 1)))
+    (while mlist
+      (if (vm-virtual-message-p (car mlist))
+	  (progn
+	    (setq m (vm-real-message-of (car mlist)))
+	    (set-buffer (vm-buffer-of m)))
+	(setq m (car mlist)))
+      (vm-error-if-folder-read-only)
+      (if (equal digest-type "guess")
+	  (progn
+	    (setq digest-type (vm-guess-digest-type m))
+	    (if (null digest-type)
+		(error "Couldn't guess digest type."))))
+      (vm-unsaved-message "Bursting %s digest..." digest-type)
+      (cond
+       ((cond ((equal digest-type "rfc934")
+	       (vm-rfc934-burst-message m))
+	      ((equal digest-type "rfc1153")
+	       (vm-rfc1153-burst-message m))
+	      (t (error "Unknown digest type: %s" digest-type)))
+	(message "Bursting %s digest... done" digest-type)
+	(vm-clear-modification-flag-undos)
+	(vm-set-buffer-modified-p t)
+	(vm-increment vm-modification-counter)
+	(and vm-delete-after-bursting
+	     ;; if start folder was virtual, we're now in the wrong
+	     ;; buffer.  switch back.
+	     (save-excursion
+	       (set-buffer start-buffer)
+	       (vm-delete-message 1)))
+	(vm-assimilate-new-messages t)
+	;; do this now so if we error later in another iteration
+	;; of the loop the summary and mode line will be correct.
+	(vm-update-summary-and-mode-line)))
+      (setq mlist (cdr mlist)))
+    ;; collect this data NOW, before the non-previewers read a
+    ;; message, alter the new message count and confuse
+    ;; themselves.
+    (setq totals-blurb (vm-emit-totals-blurb))
+    (vm-display nil nil '(vm-burst-digest
+			  vm-burst-rfc934-digest
+			  vm-burst-rfc1153-digest)
+		(list this-command))
+    (if (vm-thoughtfully-select-message)
+	(vm-preview-current-message)
+      (vm-update-summary-and-mode-line))
+    (message totals-blurb)))
+
+(defun vm-burst-rfc934-digest ()
+  "Burst an RFC 934 style digest"
+  (interactive)
+  (vm-burst-digest "rfc934"))
+
+(defun vm-burst-rfc1153-digest ()
+  "Burst an RFC 1153 style digest"
+  (interactive)
+  (vm-burst-digest "rfc1153"))
+
+(defun vm-guess-digest-type (m)
+  "Guess the digest type of the message M.
+M should be the message struct of a real message.
+Returns either \"rfc934\" or \"rfc1153\"."
+  (save-excursion
+    (set-buffer (vm-buffer-of m))
+    (save-excursion
+      (save-restriction
+	(widen)
+	(goto-char (vm-text-of m))
+	(if (search-forward "\n----------------------------------------------------------------------\n" nil t)
+	    "rfc1153"
+	  "rfc934")))))