Importing Gnus v5.8.5.
[elisp/gnus.git-] / lisp / message.el
index 7056b1b..4414e43 100644 (file)
@@ -1,5 +1,6 @@
 ;;; message.el --- composing mail and news messages
-;; Copyright (C) 1996,97,98,99 Free Software Foundation, Inc.
+;; Copyright (C) 1996, 1997, 1998, 1999, 2000
+;;        Free Software Foundation, Inc.
 
 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
 ;; Keywords: mail, news
@@ -298,6 +299,11 @@ The provided functions are:
   :group 'message-forwarding
   :type 'boolean)
 
+(defcustom message-forward-before-signature t
+  "*If non-nil, put forwarded message before signature, else after."
+  :group 'message-forwarding
+  :type 'boolean)
+
 (defcustom message-wash-forwarded-subjects nil
   "*If non-nil, try to remove as much old cruft as possible from the subject of messages before generating the new subject of a forward."
   :group 'message-forwarding
@@ -308,7 +314,7 @@ The provided functions are:
   :group 'message-interface
   :type 'regexp)
 
-(defcustom message-forward-ignored-headers nil
+(defcustom message-forward-ignored-headers "Content-Transfer-Encoding"
   "*All headers that match this regexp will be deleted when forwarding a message."
   :group 'message-forwarding
   :type '(choice (const :tag "None" nil)
@@ -319,7 +325,7 @@ The provided functions are:
   :group 'message-insertion
   :type 'regexp)
 
-(defcustom message-cancel-message "I am canceling my own article."
+(defcustom message-cancel-message "I am canceling my own article.\n"
   "Message to be inserted in the cancel message."
   :group 'message-interface
   :type 'string)
@@ -1057,6 +1063,7 @@ The cdr of ech entry is a function for applying the face to a region.")
 (defun message-fetch-field (header &optional not-all)
   "The same as `mail-fetch-field', only remove all newlines."
   (let* ((inhibit-point-motion-hooks t)
+        (case-fold-search t)
         (value (mail-fetch-field header nil (not not-all))))
     (when value
       (while (string-match "\n[\t ]+" value)
@@ -1462,6 +1469,8 @@ M-RET    message-newline-and-reformat (break the line and reformat)."
   (setq adaptive-fill-first-line-regexp
        (concat "[ \t]*[-a-z0-9A-Z]*\\(>[ \t]*\\)+[ \t]*\\|"
                adaptive-fill-first-line-regexp))
+  (make-local-variable 'auto-fill-inhibit-regexp)
+  (setq auto-fill-inhibit-regexp "^[A-Z][^: \n\t]+:")
   (mm-enable-multibyte)
   (make-local-variable 'indent-tabs-mode) ;Turn off tabs for indentation.
   (setq indent-tabs-mode nil)
@@ -1577,6 +1586,24 @@ With the prefix argument FORCE, insert the header anyway."
   (insert (or (message-fetch-reply-field "reply-to")
              (message-fetch-reply-field "from") "")))
 
+(defun message-widen-reply ()
+  "Widen the reply to include maximum recipients."
+  (interactive)
+  (let ((follow-to
+        (and message-reply-buffer
+             (buffer-name message-reply-buffer)
+             (save-excursion
+               (set-buffer message-reply-buffer)
+               (message-get-reply-headers t)))))
+    (save-excursion
+      (save-restriction
+       (message-narrow-to-headers)
+       (dolist (elem follow-to)
+         (message-remove-header (symbol-name (car elem)))
+         (goto-char (point-min))
+         (insert (symbol-name (car elem)) ": "
+                 (cdr elem) "\n"))))))
+
 (defun message-insert-newsgroups ()
   "Insert the Newsgroups header from the article being replied to."
   (interactive)
@@ -1704,14 +1731,7 @@ text was killed."
              (/= (aref message-caesar-translation-table ?a) (+ ?a n)))
       (setq message-caesar-translation-table
            (message-make-caesar-translation-table n)))
-    ;; Then we translate the region.  Do it this way to retain
-    ;; text properties.
-    (while (< b e)
-      (when (< (char-after b) 255)
-       (subst-char-in-region
-        b (1+ b) (char-after b)
-        (aref message-caesar-translation-table (char-after b))))
-      (incf b))))
+    (translate-region b e message-caesar-translation-table)))
 
 (defun message-make-caesar-translation-table (n)
   "Create a rot table with offset N."
@@ -1748,11 +1768,8 @@ Mail and USENET news headers are not rotated."
     (save-restriction
       (when (message-goto-body)
         (narrow-to-region (point) (point-max)))
-      (let ((body (buffer-substring (point-min) (point-max))))
-        (unless (equal 0 (call-process-region
-                         (point-min) (point-max) program t t))
-         (insert body)
-         (message "%s failed" program))))))
+      (shell-command-on-region
+       (point-min) (point-max) program nil t))))
 
 (defun message-rename-buffer (&optional enter-string)
   "Rename the *message* buffer to \"*message* RECIPIENT\".
@@ -1885,6 +1902,8 @@ prefix, and don't delete any headers."
               message-indent-citation-function
             (list message-indent-citation-function)))))
     (mml-quote-region start end)
+    ;; Allow undoing.
+    (undo-boundary)
     (goto-char end)
     (when (re-search-backward message-signature-separator start t)
       ;; Also peel off any blank lines before the signature.
@@ -3082,7 +3101,7 @@ Headers already prepared in the buffer are not modified."
                  ;; The element is a symbol.  We insert the value
                  ;; of this symbol, if any.
                  (symbol-value header))
-                (t
+                ((not (message-check-element header))
                  ;; We couldn't generate a value for this header,
                  ;; so we just ask the user.
                  (read-from-minibuffer
@@ -3238,7 +3257,10 @@ than 988 characters long, and if they are not, trim them until they are."
 
     ;; If folding is disallowed, make sure the total length (including
     ;; the spaces between) will be less than MAXSIZE characters.
-    (when message-cater-to-broken-inn
+    ;;
+    ;; Only disallow folding for News messages. At this point the headers
+    ;; have not been generated, thus we use message-this-is-news directly.
+    (when (and message-this-is-news message-cater-to-broken-inn)
       (let ((maxsize 988)
            (totalsize (+ (apply #'+ (mapcar #'length refs))
                          (1- count)))
@@ -3256,7 +3278,7 @@ than 988 characters long, and if they are not, trim them until they are."
     ;; Finally, collect the references back into a string and insert
     ;; it into the buffer.
     (let ((refstring (mapconcat #'identity refs " ")))
-      (if message-cater-to-broken-inn
+      (if (and message-this-is-news message-cater-to-broken-inn)
          (insert (capitalize (symbol-name header)) ": "
                  refstring "\n")
        (message-fill-header header refstring)))))
@@ -3476,6 +3498,68 @@ OTHER-HEADERS is an alist of header/value pairs."
     (message-setup `((Newsgroups . ,(or newsgroups ""))
                     (Subject . ,(or subject ""))))))
 
+(defun message-get-reply-headers (wide &optional to-address)
+  (let (follow-to mct never-mct from to cc reply-to ccalist)
+    ;; Find all relevant headers we need.
+    (setq from (message-fetch-field "from")
+         to (message-fetch-field "to")
+         cc (message-fetch-field "cc")
+         mct (message-fetch-field "mail-copies-to")
+         reply-to (message-fetch-field "reply-to"))
+
+    ;; Handle special values of Mail-Copies-To.
+    (when mct
+      (cond ((or (equal (downcase mct) "never")
+                (equal (downcase mct) "nobody"))
+            (setq never-mct t)
+            (setq mct nil))
+           ((or (equal (downcase mct) "always")
+                (equal (downcase mct) "poster"))
+            (setq mct (or reply-to from)))))
+
+    (if (or (not wide)
+           to-address)
+       (progn
+         (setq follow-to (list (cons 'To (or to-address reply-to from))))
+         (when (and wide mct)
+           (push (cons 'Cc mct) follow-to)))
+      (let (ccalist)
+       (save-excursion
+         (message-set-work-buffer)
+         (unless never-mct
+           (insert (or reply-to from "")))
+         (insert (if to (concat (if (bolp) "" ", ") to "") ""))
+         (insert (if mct (concat (if (bolp) "" ", ") mct) ""))
+         (insert (if cc (concat (if (bolp) "" ", ") cc) ""))
+         (goto-char (point-min))
+         (while (re-search-forward "[ \t]+" nil t)
+           (replace-match " " t t))
+         ;; Remove addresses that match `rmail-dont-reply-to-names'.
+         (let ((rmail-dont-reply-to-names message-dont-reply-to-names))
+           (insert (prog1 (rmail-dont-reply-to (buffer-string))
+                     (erase-buffer))))
+         (goto-char (point-min))
+         ;; Perhaps "Mail-Copies-To: never" removed the only address?
+         (when (eobp)
+           (insert (or reply-to from "")))
+         (setq ccalist
+               (mapcar
+                (lambda (addr)
+                  (cons (mail-strip-quoted-names addr) addr))
+                (message-tokenize-header (buffer-string))))
+         (let ((s ccalist))
+           (while s
+             (setq ccalist (delq (assoc (car (pop s)) s) ccalist)))))
+       (setq follow-to (list (cons 'To (cdr (pop ccalist)))))
+       (when ccalist
+         (let ((ccs (cons 'Cc (mapconcat
+                               (lambda (addr) (cdr addr)) ccalist ", "))))
+           (when (string-match "^ +" (cdr ccs))
+             (setcdr ccs (substring (cdr ccs) (match-end 0))))
+           (push ccs follow-to)))))
+    follow-to))
+
+
 ;;;###autoload
 (defun message-reply (&optional to-address wide)
   "Start editing a reply to the article in the current buffer."
@@ -3485,7 +3569,7 @@ OTHER-HEADERS is an alist of header/value pairs."
        references message-id follow-to
        (inhibit-point-motion-hooks t)
        (message-this-is-mail t)
-       mct never-mct gnus-warning)
+       gnus-warning)
     (save-restriction
       (message-narrow-to-head)
       ;; Allow customizations to have their say.
@@ -3498,82 +3582,28 @@ OTHER-HEADERS is an alist of header/value pairs."
            (save-excursion
              (setq follow-to
                    (funcall message-wide-reply-to-function)))))
-      ;; Find all relevant headers we need.
-      (setq from (message-fetch-field "from")
-           date (message-fetch-field "date")
-           subject (or (message-fetch-field "subject") "none")
-           to (message-fetch-field "to")
-           cc (message-fetch-field "cc")
-           mct (message-fetch-field "mail-copies-to")
-           reply-to (message-fetch-field "reply-to")
+      (setq message-id (message-fetch-field "message-id" t)
            references (message-fetch-field "references")
-           message-id (message-fetch-field "message-id" t))
-      ;; Remove any (buggy) Re:'s that are present and make a
-      ;; proper one.
-      (when (string-match message-subject-re-regexp subject)
-       (setq subject (substring subject (match-end 0))))
-      (setq subject (concat "Re: " subject))
-
-      (when (and (setq gnus-warning (message-fetch-field "gnus-warning"))
-                (string-match "<[^>]+>" gnus-warning))
-       (setq message-id (match-string 0 gnus-warning)))
-
-      ;; Handle special values of Mail-Copies-To.
-      (when mct
-       (cond ((or (equal (downcase mct) "never")
-                  (equal (downcase mct) "nobody"))
-              (setq never-mct t)
-              (setq mct nil))
-             ((or (equal (downcase mct) "always")
-                  (equal (downcase mct) "poster"))
-              (setq mct (or reply-to from)))))
-
-      (unless follow-to
-       (if (or (not wide)
-               to-address)
-           (progn
-             (setq follow-to (list (cons 'To (or to-address reply-to from))))
-             (when (and wide mct)
-               (push (cons 'Cc mct) follow-to)))
-         (let (ccalist)
-           (save-excursion
-             (message-set-work-buffer)
-             (unless never-mct
-               (insert (or reply-to from "")))
-             (insert (if to (concat (if (bolp) "" ", ") to "") ""))
-             (insert (if mct (concat (if (bolp) "" ", ") mct) ""))
-             (insert (if cc (concat (if (bolp) "" ", ") cc) ""))
-             (goto-char (point-min))
-             (while (re-search-forward "[ \t]+" nil t)
-               (replace-match " " t t))
-             ;; Remove addresses that match `rmail-dont-reply-to-names'.
-             (let ((rmail-dont-reply-to-names message-dont-reply-to-names))
-               (insert (prog1 (rmail-dont-reply-to (buffer-string))
-                         (erase-buffer))))
-             (goto-char (point-min))
-             ;; Perhaps Mail-Copies-To: never removed the only address?
-             (when (eobp)
-               (insert (or reply-to from "")))
-             (setq ccalist
-                   (mapcar
-                    (lambda (addr)
-                      (cons (mail-strip-quoted-names addr) addr))
-                    (message-tokenize-header (buffer-string))))
-             (let ((s ccalist))
-               (while s
-                 (setq ccalist (delq (assoc (car (pop s)) s) ccalist)))))
-           (setq follow-to (list (cons 'To (cdr (pop ccalist)))))
-           (when ccalist
-             (let ((ccs (cons 'Cc (mapconcat
-                                   (lambda (addr) (cdr addr)) ccalist ", "))))
-               (when (string-match "^ +" (cdr ccs))
-                 (setcdr ccs (substring (cdr ccs) (match-end 0))))
-               (push ccs follow-to))))))
-      (widen))
-
-    (message-pop-to-buffer (message-buffer-name
-                           (if wide "wide reply" "reply") from
-                           (if wide to-address nil)))
+           date (message-fetch-field "date")
+           from (message-fetch-field "from")
+           subject (or (message-fetch-field "subject") "none"))
+    ;; Remove any (buggy) Re:'s that are present and make a
+    ;; proper one.
+    (when (string-match message-subject-re-regexp subject)
+      (setq subject (substring subject (match-end 0))))
+    (setq subject (concat "Re: " subject))
+
+    (when (and (setq gnus-warning (message-fetch-field "gnus-warning"))
+              (string-match "<[^>]+>" gnus-warning))
+      (setq message-id (match-string 0 gnus-warning)))
+
+    (unless follow-to
+      (setq follow-to (message-get-reply-headers wide to-address))))
+
+    (message-pop-to-buffer
+     (message-buffer-name
+      (if wide "wide reply" "reply") from
+      (if wide to-address nil)))
 
     (setq message-reply-headers
          (vector 0 subject from date message-id references 0 0 ""))
@@ -3707,9 +3737,10 @@ responses here are directed to other newsgroups."))
 
 
 ;;;###autoload
-(defun message-cancel-news ()
-  "Cancel an article you posted."
-  (interactive)
+(defun message-cancel-news (&optional arg)
+  "Cancel an article you posted.
+If ARG, allow editing of the cancellation message."
+  (interactive "P")
   (unless (message-news-p)
     (error "This is not a news article; canceling is impossible"))
   (when (yes-or-no-p "Do you really want to cancel this article? ")
@@ -3734,7 +3765,9 @@ responses here are directed to other newsgroups."))
                                      (message-make-from))))))
          (error "This article is not yours"))
        ;; Make control message.
-       (setq buf (set-buffer (get-buffer-create " *message cancel*")))
+       (if arg
+           (message-news)
+         (setq buf (set-buffer (get-buffer-create " *message cancel*"))))
        (erase-buffer)
        (insert "Newsgroups: " newsgroups "\n"
                "From: " (message-make-from) "\n"
@@ -3746,12 +3779,13 @@ responses here are directed to other newsgroups."))
                mail-header-separator "\n"
                message-cancel-message)
        (run-hooks 'message-cancel-hook)
-       (message "Canceling your article...")
-       (if (let ((message-syntax-checks
-                  'dont-check-for-anything-just-trust-me))
-             (funcall message-send-news-function))
-           (message "Canceling your article...done"))
-       (kill-buffer buf)))))
+       (unless arg
+         (message "Canceling your article...")
+         (if (let ((message-syntax-checks
+                    'dont-check-for-anything-just-trust-me))
+               (funcall message-send-news-function))
+             (message "Canceling your article...done"))
+         (kill-buffer buf))))))
 
 ;;;###autoload
 (defun message-supersede ()
@@ -3895,16 +3929,19 @@ Optional NEWS will use news to forward instead of mail."
       (message-mail nil subject))
     ;; Put point where we want it before inserting the forwarded
     ;; message.
-    (message-goto-body)
+    (if message-forward-before-signature
+        (message-goto-body)
+      (goto-char (point-max)))
     (if message-forward-as-mime
        (insert "\n\n<#part type=message/rfc822 disposition=inline>\n")
-      (insert "\n\n"))
+      (insert "\n-------------------- Start of forwarded message --------------------\n"))
     (let ((b (point))
          e)
       (mml-insert-buffer cur)
       (setq e (point))
-      (and message-forward-as-mime
-          (insert "<#/part>\n"))
+      (if message-forward-as-mime
+         (insert "<#/part>\n")
+       (insert "\n-------------------- End of forwarded message --------------------\n"))
       (when (and (not current-prefix-arg)
                 message-forward-ignored-headers)
        (save-restriction