*** empty log message ***
[gnus] / lisp / message.el
index 4347677..2f1bb0e 100644 (file)
   :group 'message-buffers
   :type 'function)
 
-;;;###autoload
 (defcustom message-fcc-handler-function 'message-output
   "*A function called to save outgoing articles.
 This function will be called with the name of the file to store the
@@ -592,6 +591,25 @@ actually occur."
 (defvar message-mh-deletable-headers '(Message-ID Date Lines Sender)
   "If non-nil, delete the deletable headers before feeding to mh.")
 
+(defvar message-send-method-alist
+  '((news message-news-p message-send-via-news)
+    (mail message-mail-p message-send-via-mail))
+  "Alist of ways to send outgoing messages.
+Each element has the form
+
+  \(TYPE PREDICATE FUNCTION)
+
+where TYPE is a symbol that names the method; PREDICATE is a function
+called without any parameters to determine whether the message is
+a message of type TYPE; and FUNCTION is a function to be called if
+PREDICATE returns non-nil.  FUNCTION is called with one parameter --
+the prefix.")
+
+(defvar message-mail-alias-type 'abbrev
+  "*What alias expansion type to use in Message buffers.
+The default is `abbrev', which uses mailabbrev.  nil switches
+mail aliases off.")
+
 ;;; Internal variables.
 ;;; Well, not really internal.
 
@@ -721,16 +739,16 @@ Defaults to `text-mode-abbrev-table'.")
   (let* ((cite-prefix "A-Za-z")
         (cite-suffix (concat cite-prefix "0-9_.@-"))
         (content "[ \t]*\\(.+\\(\n[ \t].*\\)*\\)"))
-    `((,(concat "^\\(To:\\)" content)
+    `((,(concat "^\\([Tt]o:\\)" content)
        (1 'message-header-name-face)
        (2 'message-header-to-face nil t))
-      (,(concat "^\\(^[GBF]?[Cc][Cc]:\\|^Reply-To:\\)" content)
+      (,(concat "^\\(^[GBF]?[Cc][Cc]:\\|^[Rr]eply-[Tt]o:\\)" content)
        (1 'message-header-name-face)
        (2 'message-header-cc-face nil t))
-      (,(concat "^\\(Subject:\\)" content)
+      (,(concat "^\\([Ss]ubject:\\)" content)
        (1 'message-header-name-face)
        (2 'message-header-subject-face nil t))
-      (,(concat "^\\(Newsgroups:\\|Followup-to:\\)" content)
+      (,(concat "^\\([Nn]ewsgroups:\\|Followup-[Tt]o:\\)" content)
        (1 'message-header-name-face)
        (2 'message-header-newsgroups-face nil t))
       (,(concat "^\\([^: \n\t]+:\\)" content)
@@ -929,6 +947,19 @@ The cdr of ech entry is a function for applying the face to a region.")
     (when value
       (nnheader-replace-chars-in-string value ?\n ? ))))
 
+(defun message-add-header (&rest headers)
+  "Add the HEADERS to the message header, skipping those already present."
+  (while headers
+    (let (hclean)
+      (unless (string-match "^\\([^:]+\\):[ \t]*[^ \t]" (car headers))
+       (error "Invalid header `%s'" (car headers)))
+      (setq hclean (match-string 1 (car headers)))
+    (save-restriction
+      (message-narrow-to-headers)
+      (unless (re-search-forward (concat "^" (regexp-quote hclean) ":") nil t)
+       (insert (car headers) ?\n))))
+    (setq headers (cdr headers))))
+
 (defun message-fetch-reply-field (header)
   "Fetch FIELD from the message we're replying to."
   (when (and message-reply-buffer
@@ -949,7 +980,8 @@ The cdr of ech entry is a function for applying the face to a region.")
 (defun message-functionp (form)
   "Return non-nil if FORM is funcallable."
   (or (and (symbolp form) (fboundp form))
-      (and (listp form) (eq (car form) 'lambda))))
+      (and (listp form) (eq (car form) 'lambda))
+      (compiled-function-p form)))
 
 (defun message-strip-subject-re (subject)
   "Remove \"Re:\" from subject lines."
@@ -1181,10 +1213,10 @@ C-c C-r  message-caesar-buffer-body (rot13 the message body)."
   (kill-all-local-variables)
   (make-local-variable 'message-reply-buffer)
   (setq message-reply-buffer nil)
-  (set (make-local-variable 'message-send-actions) nil)
-  (set (make-local-variable 'message-exit-actions) nil)
-  (set (make-local-variable 'message-kill-actions) nil)
-  (set (make-local-variable 'message-postpone-actions) nil)
+  (make-local-variable 'message-send-actions) 
+  (make-local-variable 'message-exit-actions) 
+  (make-local-variable 'message-kill-actions)
+  (make-local-variable 'message-postpone-actions)
   (set-syntax-table message-mode-syntax-table)
   (use-local-map message-mode-map)
   (setq local-abbrev-table message-mode-abbrev-table)
@@ -1229,9 +1261,10 @@ C-c C-r  message-caesar-buffer-body (rot13 the message body)."
   (easy-menu-add message-mode-menu message-mode-map)
   (easy-menu-add message-mode-field-menu message-mode-map)
   ;; Allow mail alias things.
-  (if (fboundp 'mail-abbrevs-setup)
-      (mail-abbrevs-setup)
-    (funcall (intern "mail-aliases-setup")))
+  (when (eq message-mail-alias-type 'abbrev)
+    (if (fboundp 'mail-abbrevs-setup)
+       (mail-abbrevs-setup)
+      (funcall (intern "mail-aliases-setup"))))
   (run-hooks 'text-mode-hook 'message-mode-hook))
 
 \f
@@ -1314,11 +1347,15 @@ C-c C-r  message-caesar-buffer-body (rot13 the message body)."
 
 \f
 
-(defun message-insert-to ()
-  "Insert a To header that points to the author of the article being replied to."
-  (interactive)
-  (let ((co (message-fetch-field "courtesy-copies-to")))
-    (when (and co
+(defun message-insert-to (&optional force)
+  "Insert a To header that points to the author of the article being replied to.
+If the original author requested not to be sent mail, the function signals
+an error.
+With the prefix argument FORCE, insert the header anyway."
+  (interactive "P")
+  (let ((co (message-fetch-reply-field "mail-copies-to")))
+    (when (and (null force)
+              co
               (equal (downcase co) "never"))
       (error "The user has requested not to have copies sent via mail")))
   (when (and (message-position-on-field "To")
@@ -1502,14 +1539,20 @@ However, if `message-yank-prefix' is non-nil, insert that prefix on each line."
   (let ((start (point)))
     ;; Remove unwanted headers.
     (when message-ignored-cited-headers
-      (save-restriction
-       (narrow-to-region
-        (goto-char start)
-        (if (search-forward "\n\n" nil t)
-            (1- (point))
-          (point)))
-       (message-remove-header message-ignored-cited-headers t)
-       (goto-char (point-max))))
+      (let (all-removed)
+       (save-restriction
+         (narrow-to-region
+          (goto-char start)
+          (if (search-forward "\n\n" nil t)
+              (1- (point))
+            (point)))
+         (message-remove-header message-ignored-cited-headers t)
+         (when (= (point-min) (point-max))
+           (setq all-removed t))
+         (goto-char (point-max)))
+       (if all-removed
+           (goto-char start)
+         (forward-line 1))))
     ;; Delete blank lines at the start of the buffer.
     (while (and (point-min)
                (eolp)
@@ -1656,7 +1699,8 @@ The text will also be indented the normal way."
 (defun message-kill-buffer ()
   "Kill the current buffer."
   (interactive)
-  (when (yes-or-no-p "Kill the buffer? ")
+  (when (or (not (buffer-modified-p))
+           (yes-or-no-p "Message modified; kill anyway? "))
     (let ((actions message-kill-actions))
       (kill-buffer (current-buffer))
       (message-do-actions actions))))
@@ -1692,30 +1736,41 @@ the user from the mailer."
     (message-fix-before-sending)
     (run-hooks 'message-send-hook)
     (message "Sending...")
-    (when (and (or (not (message-news-p))
-                  (and (or (not (memq 'news message-sent-message-via))
-                           (y-or-n-p
-                            "Already sent message via news; resend? "))
-                       (funcall message-send-news-function arg)))
-              (or (not (message-mail-p))
-                  (and (or (not (memq 'mail message-sent-message-via))
-                           (y-or-n-p
-                            "Already sent message via mail; resend? "))
-                       (message-send-mail arg))))
-      (message-do-fcc)
-      ;;(when (fboundp 'mail-hist-put-headers-into-history)
-      ;; (mail-hist-put-headers-into-history))
-      (run-hooks 'message-sent-hook)
-      (message "Sending...done")
-      ;; If buffer has no file, mark it as unmodified and delete autosave.
-      (unless buffer-file-name
-       (set-buffer-modified-p nil)
-       (delete-auto-save-file-if-necessary t))
-      ;; Delete other mail buffers and stuff.
-      (message-do-send-housekeeping)
-      (message-do-actions message-send-actions)
-      ;; Return success.
-      t)))
+    (let ((alist message-send-method-alist)
+         elem sent)
+      (while (setq elem (pop alist))
+       (when (and (or (not (funcall (cadr elem)))
+                      (and (or (not (memq (car elem)
+                                          message-sent-message-via))
+                               (y-or-n-p
+                                (format
+                                 "Already sent message via %s; resend? "
+                                 (car elem))))
+                           (funcall (caddr elem) arg))))
+         (setq sent t)))
+      (when sent
+       (message-do-fcc)
+       ;;(when (fboundp 'mail-hist-put-headers-into-history)
+       ;; (mail-hist-put-headers-into-history))
+       (run-hooks 'message-sent-hook)
+       (message "Sending...done")
+       ;; If buffer has no file, mark it as unmodified and delete autosave.
+       (unless buffer-file-name
+         (set-buffer-modified-p nil)
+         (delete-auto-save-file-if-necessary t))
+       ;; Delete other mail buffers and stuff.
+       (message-do-send-housekeeping)
+       (message-do-actions message-send-actions)
+       ;; Return success.
+       t))))
+
+(defun message-send-via-mail (arg)
+  "Send the current message via mail."  
+  (message-send-mail arg))
+
+(defun message-send-via-news (arg)
+  "Send the current message via news."
+  (funcall message-send-news-function arg))
 
 (defun message-fix-before-sending ()
   "Do various things to make the message nice before sending it."
@@ -2012,6 +2067,16 @@ to find out how to use this."
 
 (defun message-check-news-header-syntax ()
   (and
+   ;; Check the Subject header.
+   (message-check 'subject
+     (let* ((case-fold-search t)
+           (subject (message-fetch-field "subject")))
+       (or
+       (and subject
+            (not (string-match "\\`[ \t]*\\'" subject)))
+       (ignore
+        (message
+         "The subject field is empty or missing.  Posting is denied.")))))
    ;; Check for commands in Subject.
    (message-check 'subject-cmsg
      (if (string-match "^cmsg " (message-fetch-field "subject"))
@@ -2085,16 +2150,6 @@ to find out how to use this."
           (y-or-n-p
            (format "The Message-ID looks strange: \"%s\".  Really post? "
                    message-id)))))
-   ;; Check the Subject header.
-   (message-check 'subject
-     (let* ((case-fold-search t)
-           (subject (message-fetch-field "subject")))
-       (or
-       (and subject
-            (not (string-match "\\`[ \t]*\\'" subject)))
-       (ignore
-        (message
-         "The subject field is empty or missing.  Posting is denied.")))))
    ;; Check the Newsgroups & Followup-To headers.
    (message-check 'existing-newsgroups
      (let* ((case-fold-search t)
@@ -2461,7 +2516,8 @@ to find out how to use this."
 
 (defun message-make-from ()
   "Make a From header."
-  (let* ((login (message-make-address))
+  (let* ((style message-from-style)
+        (login (message-make-address))
         (fullname
          (or (and (boundp 'user-full-name)
                   user-full-name)
@@ -2471,11 +2527,11 @@ to find out how to use this."
     (save-excursion
       (message-set-work-buffer)
       (cond
-       ((or (null message-from-style)
+       ((or (null style)
            (equal fullname ""))
        (insert login))
-       ((or (eq message-from-style 'angles)
-           (and (not (eq message-from-style 'parens))
+       ((or (eq style 'angles)
+           (and (not (eq style 'parens))
                 ;; Use angles if no quoting is needed, or if parens would
                 ;; need quoting too.
                 (or (not (string-match "[^- !#-'*+/-9=?A-Z^-~]" fullname))
@@ -2687,7 +2743,9 @@ Headers already prepared in the buffer are not modified."
            (beginning-of-line)
            (insert "Original-")
            (beginning-of-line))
-         (insert "Sender: " secure-sender "\n"))))))
+         (when (or (message-news-p)
+                   (string-match "^[^@]@.+\\..+" secure-sender))
+           (insert "Sender: " secure-sender "\n")))))))
 
 (defun message-insert-courtesy-copy ()
   "Insert a courtesy message in mail copies of combined messages."
@@ -2895,6 +2953,7 @@ Headers already prepared in the buffer are not modified."
     (message-narrow-to-headers)
     (run-hooks 'message-header-setup-hook))
   (set-buffer-modified-p nil)
+  (setq buffer-undo-list nil)
   (run-hooks 'message-setup-hook)
   (message-position-point)
   (undo-boundary))
@@ -3004,9 +3063,12 @@ Headers already prepared in the buffer are not modified."
              (message-set-work-buffer)
              (unless never-mct
                (insert (or reply-to from "")))
-             (insert (if (bolp) "" ", ") (or to ""))
+             (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'.
              (insert (prog1 (rmail-dont-reply-to (buffer-string))
                        (erase-buffer)))
@@ -3048,10 +3110,10 @@ Headers already prepared in the buffer are not modified."
      cur)))
 
 ;;;###autoload
-(defun message-wide-reply (&optional to-address)
+(defun message-wide-reply (&optional to-address ignore-reply-to)
   "Make a \"wide\" reply to the message in the current buffer."
   (interactive)
-  (message-reply to-address t))
+  (message-reply to-address t ignore-reply-to))
 
 ;;;###autoload
 (defun message-followup (&optional to-newsgroups)
@@ -3199,9 +3261,10 @@ responses here are directed to other newsgroups."))
                mail-header-separator "\n"
                message-cancel-message)
        (message "Canceling your article...")
-       (let ((message-syntax-checks 'dont-check-for-anything-just-trust-me))
-         (funcall message-send-news-function))
-       (message "Canceling your article...done")
+       (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
@@ -3374,8 +3437,7 @@ you."
             (forward-line 2))
        (and (re-search-forward message-unsent-separator nil t)
             (forward-line 1))
-       (and (search-forward "\n\n" nil t)
-            (re-search-forward "^Return-Path:.*\n" nil t)))
+       (re-search-forward "^Return-Path:.*\n" nil t))
     ;; We remove everything before the bounced mail.
     (delete-region
      (point-min)