# HG changeset patch # User Chong Yidong # Date 1229177996 0 # Node ID e5f10d15806c80a0eefcfbe2b3b27512eafd5b94 # Parent 8271c30cd3838a773c308c674442f97fcc4d7a83 (pmail-output-to-babyl-file): Rewrite, assuming mbox internal format. (pmail-convert-to-babyl-format, pmail-nuke-pinhead-header): New functions, moved from pmail.el. diff -r 8271c30cd383 -r e5f10d15806c lisp/mail/pmailout.el --- a/lisp/mail/pmailout.el Sat Dec 13 14:19:24 2008 +0000 +++ b/lisp/mail/pmailout.el Sat Dec 13 14:19:56 2008 +0000 @@ -171,79 +171,234 @@ (if (pmail-message-deleted-p pmail-current-message) (progn (setq redelete t) (pmail-set-attribute pmail-deleted-attr-index nil))) - (save-restriction - (widen) - ;; Decide whether to append to a file or to an Emacs buffer. - (save-excursion - (let ((buf (find-buffer-visiting file-name)) - (cur (current-buffer)) - (beg (1+ (pmail-msgbeg pmail-current-message))) - (end (1+ (pmail-msgend pmail-current-message))) - (coding-system-for-write - (or pmail-file-coding-system - 'emacs-mule-unix))) - (if (not buf) - ;; Output to a file. - (if pmail-fields-not-to-output - ;; Delete some fields while we output. - (let ((obuf (current-buffer))) - (set-buffer (get-buffer-create " pmail-out-temp")) - (insert-buffer-substring obuf beg end) - (pmail-delete-unwanted-fields) - (append-to-file (point-min) (point-max) file-name) - (set-buffer obuf) - (kill-buffer (get-buffer " pmail-out-temp"))) - (append-to-file beg end file-name)) - (if (eq buf (current-buffer)) - (error "Can't output message to same file it's already in")) - ;; File has been visited, in buffer BUF. - (set-buffer buf) - (let ((buffer-read-only nil) - (msg (and (boundp 'pmail-current-message) - pmail-current-message))) - ;; If MSG is non-nil, buffer is in PMAIL mode. - (if msg - (progn - ;; Turn on auto save mode, if it's off in this - ;; buffer but enabled by default. - (and (not buffer-auto-save-file-name) - auto-save-default - (auto-save-mode t)) - (pmail-maybe-set-message-counters) - (widen) - (narrow-to-region (point-max) (point-max)) - (insert-buffer-substring cur beg end) - (goto-char (point-min)) - (widen) - (search-backward "\n\^_") - (narrow-to-region (point) (point-max)) - (pmail-delete-unwanted-fields) - (pmail-count-new-messages t) - (if (pmail-summary-exists) - (pmail-select-summary - (pmail-update-summary))) - (pmail-show-message msg)) - ;; Output file not in pmail mode => just insert at the end. - (narrow-to-region (point-min) (1+ (buffer-size))) - (goto-char (point-max)) - (insert-buffer-substring cur beg end) - (pmail-delete-unwanted-fields))))))) + (let ((coding-system-for-write + (or pmail-file-coding-system + 'emacs-mule-unix)) + cur beg end) + (pmail-swap-buffers-maybe) + (setq cur (current-buffer)) + (save-restriction + (save-excursion + (widen) + (setq beg (pmail-msgbeg pmail-current-message) + end (pmail-msgend pmail-current-message)) + ;; Output to a file. + (set-buffer (get-buffer-create " pmail-out-temp")) + (insert-buffer-substring cur beg end) + (if pmail-fields-not-to-output + (pmail-delete-unwanted-fields)) + ;; Convert to Babyl format. + (pmail-convert-to-babyl-format) + (append-to-file (point-min) (point-max) file-name) + (set-buffer cur) + (kill-buffer (get-buffer " pmail-out-temp"))))) (pmail-set-attribute pmail-filed-attr-index t)) (if redelete (pmail-set-attribute pmail-deleted-attr-index t)))) (setq count (1- count)) (if pmail-delete-after-output - (unless - (if (and (= count 0) stay) - (pmail-delete-message) - (pmail-delete-forward)) + (unless (if (and (= count 0) stay) + (pmail-delete-message) + (pmail-delete-forward)) (setq count 0)) (if (> count 0) - (unless - (if (not stay) (pmail-next-undeleted-message 1)) - (setq count 0))))))) + (unless (if (not stay) + (pmail-next-undeleted-message 1)) + (setq count 0)))))) + (pmail-show-message)) (defalias 'pmail-output-to-pmail-file 'pmail-output-to-babyl-file) +(defun pmail-convert-to-babyl-format () + (let ((count 0) start + (case-fold-search nil) + (buffer-undo-list t)) + (goto-char (point-min)) + (save-restriction + (while (not (eobp)) + (setq start (point)) + (unless (looking-at "^From ") + (error "Invalid mbox message")) + (insert "\^L\n0, unseen,,\n*** EOOH ***\n") + (pmail-nuke-pinhead-header) + ;; If this message has a Content-Length field, + ;; skip to the end of the contents. + (let* ((header-end (save-excursion + (and (re-search-forward "\n\n" nil t) + (1- (point))))) + (case-fold-search t) + (quoted-printable-header-field-end + (save-excursion + (re-search-forward + "^content-transfer-encoding:\\(\n?[\t ]\\)*quoted-printable\\(\n?[\t ]\\)*" + header-end t))) + (base64-header-field-end + (and + ;; Don't decode non-text data. + (save-excursion + (re-search-forward + "^content-type:\\(\n?[\t ]\\)\\(text\\|message\\)/" + header-end t)) + (save-excursion + (re-search-forward + "^content-transfer-encoding:\\(\n?[\t ]\\)*base64\\(\n?[\t ]\\)*" + header-end t)))) + (size + ;; Get the numeric value from the Content-Length field. + (save-excursion + ;; Back up to end of prev line, + ;; in case the Content-Length field comes first. + (forward-char -1) + (and (search-forward "\ncontent-length: " + header-end t) + (let ((beg (point)) + (eol (progn (end-of-line) (point)))) + (string-to-number (buffer-substring beg eol))))))) + (and size + (if (and (natnump size) + (<= (+ header-end size) (point-max)) + ;; Make sure this would put us at a position + ;; that we could continue from. + (save-excursion + (goto-char (+ header-end size)) + (skip-chars-forward "\n") + (or (eobp) + (and (looking-at "BABYL OPTIONS:") + (search-forward "\n\^_" nil t)) + (and (looking-at "\^L") + (search-forward "\n\^_" nil t)) + (let ((case-fold-search t)) + (looking-at pmail-mmdf-delim1)) + (looking-at "From ")))) + (goto-char (+ header-end size)) + (message "Ignoring invalid Content-Length field") + (sit-for 1 0 t))) + (if (let ((case-fold-search nil)) + (re-search-forward + (concat "^[\^_]?\\(" + pmail-unix-mail-delimiter + "\\|" + pmail-mmdf-delim1 "\\|" + "^BABYL OPTIONS:\\|" + "\^L\n[01],\\)") nil t)) + (goto-char (match-beginning 1)) + (goto-char (point-max))) + (setq count (1+ count)) + (if quoted-printable-header-field-end + (save-excursion + (unless (mail-unquote-printable-region + header-end (point) nil t t) + (message "Malformed MIME quoted-printable message")) + ;; Change "quoted-printable" to "8bit", + ;; to reflect the decoding we just did. + (goto-char quoted-printable-header-field-end) + (delete-region (point) (search-backward ":")) + (insert ": 8bit"))) + (if base64-header-field-end + (save-excursion + (when (condition-case nil + (progn + (base64-decode-region + (1+ header-end) + (save-excursion + ;; Prevent base64-decode-region + ;; from removing newline characters. + (skip-chars-backward "\n\t ") + (point))) + t) + (error nil)) + ;; Change "base64" to "8bit", to reflect the + ;; decoding we just did. + (goto-char base64-header-field-end) + (delete-region (point) (search-backward ":")) + (insert ": 8bit"))))) + (save-excursion + (save-restriction + (narrow-to-region start (point)) + (goto-char (point-min)) + (while (search-forward "\n\^_" nil t) ; single char + (replace-match "\n^_")))) ; 2 chars: "^" and "_" + ;; This is for malformed messages that don't end in newline. + ;; There shouldn't be any, but some users say occasionally + ;; there are some. + (or (bolp) (newline)) + (insert ?\^_) + (setq last-coding-system-used nil) + (or pmail-enable-mime + (not pmail-enable-multibyte) + (let ((mime-charset + (if (and pmail-decode-mime-charset + (save-excursion + (goto-char start) + (search-forward "\n\n" nil t) + (let ((case-fold-search t)) + (re-search-backward + pmail-mime-charset-pattern + start t)))) + (intern (downcase (match-string 1)))))) + (pmail-decode-region start (point) mime-charset))) + (save-excursion + (goto-char start) + (forward-line 3) + (insert "X-Coding-System: " + (symbol-name last-coding-system-used) + "\n")) + (narrow-to-region (point) (point-max)) + (and (= 0 (% count 10)) + (message "Converting to Babyl format...%d" count)))))) + +;; Delete the "From ..." line, creating various other headers with +;; information from it if they don't already exist. Now puts the +;; original line into a mail-from: header line for debugging and for +;; use by the pmail-output function. +(defun pmail-nuke-pinhead-header () + (save-excursion + (save-restriction + (let ((start (point)) + (end (progn + (condition-case () + (search-forward "\n\n") + (error + (goto-char (point-max)) + (insert "\n\n"))) + (point))) + has-from has-date) + (narrow-to-region start end) + (let ((case-fold-search t)) + (goto-char start) + (setq has-from (search-forward "\nFrom:" nil t)) + (goto-char start) + (setq has-date (and (search-forward "\nDate:" nil t) (point))) + (goto-char start)) + (let ((case-fold-search nil)) + (if (re-search-forward (concat "^" pmail-unix-mail-delimiter) nil t) + (replace-match + (concat + "Mail-from: \\&" + ;; Keep and reformat the date if we don't + ;; have a Date: field. + (if has-date + "" + (concat + "Date: \\2, \\4 \\3 \\9 \\5 " + + ;; The timezone could be matched by group 7 or group 10. + ;; If neither of them matched, assume EST, since only + ;; Easterners would be so sloppy. + ;; It's a shame the substitution can't use "\\10". + (cond + ((/= (match-beginning 7) (match-end 7)) "\\7") + ((/= (match-beginning 10) (match-end 10)) + (buffer-substring (match-beginning 10) + (match-end 10))) + (t "EST")) + "\n")) + ;; Keep and reformat the sender if we don't + ;; have a From: field. + (if has-from + "" + "From: \\1\n")) + t))))))) + ;;;###autoload (defcustom pmail-fields-not-to-output nil "*Regexp describing fields to exclude when outputting a message to a file."