Revision: miles@gnu.org--gnu-2005/gnus--devo--0--patch-242
[gnus] / lisp / message.el
index 2a52946..4f93901 100644 (file)
@@ -1,6 +1,7 @@
 ;;; message.el --- composing mail and news messages
-;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
-;;        Free Software Foundation, Inc.
+
+;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+;;   2005 Free Software Foundation, Inc.
 
 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
 ;; Keywords: mail, news
@@ -877,15 +878,23 @@ configuration.  See the variable `gnus-cite-attribution-suffix'."
 (defcustom message-yank-prefix "> "
   "*Prefix inserted on the lines of yanked messages.
 Fix `message-cite-prefix-regexp' if it is set to an abnormal value.
-See also `message-yank-cited-prefix'."
+See also `message-yank-cited-prefix' and `message-yank-empty-prefix'."
   :type 'string
   :link '(custom-manual "(message)Insertion Variables")
   :group 'message-insertion)
 
 (defcustom message-yank-cited-prefix ">"
-  "*Prefix inserted on cited or empty lines of yanked messages.
+  "*Prefix inserted on cited lines of yanked messages.
 Fix `message-cite-prefix-regexp' if it is set to an abnormal value.
-See also `message-yank-prefix'."
+See also `message-yank-prefix' and `message-yank-empty-prefix'."
+  :version "22.1"
+  :type 'string
+  :link '(custom-manual "(message)Insertion Variables")
+  :group 'message-insertion)
+
+(defcustom message-yank-empty-prefix ">"
+  "*Prefix inserted on empty lines of yanked messages.
+See also `message-yank-prefix' and `message-yank-cited-prefix'."
   :version "22.1"
   :type 'string
   :link '(custom-manual "(message)Insertion Variables")
@@ -903,7 +912,7 @@ Used by `message-yank-original' via `message-yank-cite'."
   "*Function for citing an original message.
 Predefined functions include `message-cite-original' and
 `message-cite-original-without-signature'.
-Note that `message-cite-original' uses `mail-citation-hook' if that is non-nil."
+Note that these functions use `mail-citation-hook' if that is non-nil."
   :type '(radio (function-item message-cite-original)
                (function-item message-cite-original-without-signature)
                (function-item sc-cite-original)
@@ -1358,10 +1367,10 @@ starting with `not' and followed by regexps."
 (put 'message-mode 'font-lock-defaults '(message-font-lock-keywords t))
 
 (defvar message-face-alist
-  '((bold . bold-region)
+  '((bold . message-bold-region)
     (underline . underline-region)
     (default . (lambda (b e)
-                (unbold-region b e)
+                (message-unbold-region b e)
                 (ununderline-region b e))))
   "Alist of mail and news faces for facemenu.
 The cdr of each entry is a function for applying the face to a region.")
@@ -1416,8 +1425,13 @@ should be sent in several parts.  If it is nil, the size is unlimited."
                 (integer 1000000)))
 
 (defcustom message-alternative-emails nil
-  "A regexp to match the alternative email addresses.
-The first matched address (not primary one) is used in the From field."
+  "*Regexp matching alternative email addresses.
+The first address in the To, Cc or From headers of the original
+article matching this variable is used as the From field of
+outgoing messages.
+
+This variable has precedence over posting styles and anything that runs
+off `message-setup-hook'."
   :group 'message-headers
   :link '(custom-manual "(message)Message Headers")
   :type '(choice (const :tag "Always use primary" nil)
@@ -1482,8 +1496,13 @@ no, only reply back to the author."
                                   (file-error))
                                 (mm-coding-system-p 'utf-8)
                                 (executable-find idna-program)
-                                'ask)
-  "Whether to encode non-ASCII in domain names into ASCII according to IDNA."
+                                (string= (idna-to-ascii "räksmörgås")
+                                         "xn--rksmrgs-5wao1o")
+                                t)
+  "Whether to encode non-ASCII in domain names into ASCII according to IDNA.
+GNU Libidn, and in particular the elisp package \"idna.el\" and
+the external program \"idn\", must be installed for this
+functionality to work."
   :version "22.1"
   :group 'message-headers
   :link '(custom-manual "(message)IDNA")
@@ -1851,7 +1870,6 @@ Leading \"Re: \" is not stripped by this function.  Use the function
 
 ;;; Suggested by Jonas Steverud  @  www.dtek.chalmers.se/~d4jonas/
 
-;;;###autoload
 (defun message-change-subject (new-subject)
   "Ask for NEW-SUBJECT header, append (was: <Old Subject>)."
   ;; <URL:http://www.landfield.com/usefor/drafts/draft-ietf-usefor-useage--1.02.unpaged>
@@ -1883,32 +1901,31 @@ Leading \"Re: \" is not stripped by this function.  Use the function
                                    " (was: "
                                    old-subject ")\n")))))))))
 
-;;;###autoload
-(defun message-mark-inserted-region (beg end)
+(defun message-mark-inserted-region (beg end &optional verbatim)
   "Mark some region in the current article with enclosing tags.
-See `message-mark-insert-begin' and `message-mark-insert-end'."
-  (interactive "r")
+See `message-mark-insert-begin' and `message-mark-insert-end'.
+If VERBATIM, use slrn style verbatim marks (\"#v+\" and \"#v-\")."
+  (interactive "r\nP")
   (save-excursion
     ;; add to the end of the region first, otherwise end would be invalid
     (goto-char end)
-    (insert message-mark-insert-end)
+    (insert (if verbatim "#v-\n" message-mark-insert-end))
     (goto-char beg)
-    (insert message-mark-insert-begin)))
+    (insert (if verbatim "#v+\n" message-mark-insert-begin))))
 
-;;;###autoload
-(defun message-mark-insert-file (file)
+(defun message-mark-insert-file (file &optional verbatim)
   "Insert FILE at point, marking it with enclosing tags.
-See `message-mark-insert-begin' and `message-mark-insert-end'."
-  (interactive "fFile to insert: ")
+See `message-mark-insert-begin' and `message-mark-insert-end'.
+If VERBATIM, use slrn style verbatim marks (\"#v+\" and \"#v-\")."
+  (interactive "fFile to insert: \nP")
     ;; reverse insertion to get correct result.
   (let ((p (point)))
-    (insert message-mark-insert-end)
+    (insert (if verbatim "#v-\n" message-mark-insert-end))
     (goto-char p)
     (insert-file-contents file)
     (goto-char p)
-    (insert message-mark-insert-begin)))
+    (insert (if verbatim "#v+\n" message-mark-insert-begin))))
 
-;;;###autoload
 (defun message-add-archive-header ()
   "Insert \"X-No-Archive: Yes\" in the header and a note in the body.
 The note can be customized using `message-archive-note'.  When called with a
@@ -1928,7 +1945,6 @@ body, set  `message-archive-note' to nil."
       (message-add-header message-archive-header)
       (message-sort-headers)))
 
-;;;###autoload
 (defun message-cross-post-followup-to-header (target-group)
   "Mangles FollowUp-To and Newsgroups header to point to TARGET-GROUP.
 With prefix-argument just set Follow-Up, don't cross-post."
@@ -1972,7 +1988,6 @@ With prefix-argument just set Follow-Up, don't cross-post."
       (insert (concat "\nFollowup-To: " target-group)))
   (setq message-cross-post-old-target target-group))
 
-;;;###autoload
 (defun message-cross-post-insert-note (target-group cross-post in-old
                                                    old-groups)
   "Insert a in message body note about a set Followup or Crosspost.
@@ -2005,7 +2020,6 @@ been made to before the user asked for a Crosspost."
        (insert (concat message-followup-to-note target-group "\n"))
       (insert (concat message-cross-post-note target-group "\n")))))
 
-;;;###autoload
 (defun message-cross-post-followup-to (target-group)
   "Crossposts message and set Followup-To to TARGET-GROUP.
 With prefix-argument just set Follow-Up, don't cross-post."
@@ -2047,7 +2061,6 @@ With prefix-argument just set Follow-Up, don't cross-post."
 
 ;;; Reduce To: to Cc: or Bcc: header
 
-;;;###autoload
 (defun message-reduce-to-to-cc ()
  "Replace contents of To: header with contents of Cc: or Bcc: header."
  (interactive)
@@ -2290,6 +2303,7 @@ Point is left at the beginning of the narrowed-to region."
   (define-key message-mode-map "\C-c\C-fw" 'message-insert-wide-reply)
   (define-key message-mode-map "\C-c\C-n" 'message-insert-newsgroups)
   (define-key message-mode-map "\C-c\C-l" 'message-to-list-only)
+  (define-key message-mode-map "\C-c\C-f\C-e" 'message-insert-expires)
 
   (define-key message-mode-map "\C-c\C-u" 'message-insert-or-toggle-importance)
   (define-key message-mode-map "\C-c\M-n"
@@ -2398,7 +2412,8 @@ Point is left at the beginning of the narrowed-to region."
     ;; ["Followup-To (with note in body)" message-cross-post-followup-to t]
     ["Crosspost / Followup-To..." message-cross-post-followup-to t]
     ["Distribution" message-goto-distribution t]
-    ["X-No-Archive:" message-add-archive-header t ]
+    ["Expires" message-insert-expires t ]
+    ["X-No-Archive" message-add-archive-header t ]
     "----"
     ;; (typical) mailing-lists stuff
     ["Fetch To" message-insert-to
@@ -2501,6 +2516,7 @@ C-c C-f  move to a header field (and create it if there isn't):
          C-c C-f C-o  move to From (\"Originator\")
         C-c C-f C-f  move to Followup-To
         C-c C-f C-m  move to Mail-Followup-To
+        C-c C-f C-e  move to Expires
         C-c C-f C-i  cycle through Importance values
         C-c C-f s    change subject and append \"(was: <Old Subject>)\"
         C-c C-f x    crossposting with FollowUp-To header and note in body
@@ -2715,6 +2731,11 @@ M-RET    `message-newline-and-reformat' (break the line and reformat)."
   (message-goto-body)
   (forward-line -1))
 
+(defun message-in-body-p ()
+  "Return t if point is in the message body."
+  (let ((body (save-excursion (message-goto-body) (point))))
+    (>= (point) body)))
+
 (defun message-goto-signature ()
   "Move point to the beginning of the message signature.
 If there is no signature in the article, go to the end and
@@ -2888,7 +2909,7 @@ of lines before the signature intact."
            (end-of-line -1)))
        (unless (= point (point))
          (kill-region point (point))
-         (unless (bol)
+         (unless (bolp)
            (insert "\n")))))))
 
 (defun message-newline-and-reformat (&optional arg not-break)
@@ -2980,7 +3001,9 @@ Prefix arg means justify as well."
       (if point (goto-char point)))))
 
 (defun message-fill-paragraph (&optional arg)
-  "Like `fill-paragraph'."
+  "Message specific function to fill a paragraph.
+This function is used as the value of `fill-paragraph-function' in
+Message buffers and is not meant to be called directly."
   (interactive (list (if current-prefix-arg 'full)))
   (if (if (boundp 'filladapt-mode) filladapt-mode)
       nil
@@ -3242,9 +3265,12 @@ However, if `message-yank-prefix' is non-nil, insert that prefix on each line."
       (save-excursion
        (goto-char start)
        (while (< (point) (mark t))
-         (if (or (looking-at ">") (looking-at "^$"))
-             (insert message-yank-cited-prefix)
-           (insert message-yank-prefix))
+         (cond ((looking-at ">")
+                (insert message-yank-cited-prefix))
+               ((looking-at "^$")
+                (insert message-yank-empty-prefix))
+               (t
+                (insert message-yank-prefix)))
          (forward-line 1))))
     (goto-char start)))
 
@@ -3292,53 +3318,14 @@ prefix, and don't delete any headers."
          (push (buffer-name buffer) buffers))))
     (nreverse buffers)))
 
-(defun message-cite-original-without-signature ()
-  "Cite function in the standard Message manner."
-  (let* ((start (point))
-        (end (mark t))
-        (functions
-         (when message-indent-citation-function
-           (if (listp message-indent-citation-function)
-               message-indent-citation-function
-             (list message-indent-citation-function))))
-        ;; This function may be called by `gnus-summary-yank-message' and
-        ;; may insert a different article from the original.  So, we will
-        ;; modify the value of `message-reply-headers' with that article.
-        (message-reply-headers
-         (save-restriction
-           (narrow-to-region start end)
-           (message-narrow-to-head-1)
-           (vector 0
-                   (or (message-fetch-field "subject") "none")
-                   (message-fetch-field "from")
-                   (message-fetch-field "date")
-                   (message-fetch-field "message-id" t)
-                   (message-fetch-field "references")
-                   0 0 ""))))
-    (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.
-      (forward-line -1)
-      (while (looking-at "^[ \t]*$")
-       (forward-line -1))
-      (forward-line 1)
-      (delete-region (point) end)
-      (unless (search-backward "\n\n" start t)
-       ;; Insert a blank line if it is peeled off.
-       (insert "\n")))
-    (goto-char start)
-    (mapc 'funcall functions)
-    (when message-citation-line-function
-      (unless (bolp)
-       (insert "\n"))
-      (funcall message-citation-line-function))))
+(eval-when-compile (defvar mail-citation-hook))        ; Compiler directive
 
-(eval-when-compile (defvar mail-citation-hook))        ;Compiler directive
-(defun message-cite-original ()
-  "Cite function in the standard Message manner."
+(defun message-cite-original-1 (strip-signature)
+  "Cite an original message.
+If STRIP-SIGNATURE is non-nil, strips off the signature from the
+original message.
+
+This function uses `mail-citation-hook' if that is non-nil."
   (if (and (boundp 'mail-citation-hook)
           mail-citation-hook)
       (run-hooks 'mail-citation-hook)
@@ -3366,6 +3353,20 @@ prefix, and don't delete any headers."
                      (message-fetch-field "references")
                      0 0 ""))))
       (mml-quote-region start end)
+      (when strip-signature
+       ;; 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.
+         (forward-line -1)
+         (while (looking-at "^[ \t]*$")
+           (forward-line -1))
+         (forward-line 1)
+         (delete-region (point) end)
+         (unless (search-backward "\n\n" start t)
+           ;; Insert a blank line if it is peeled off.
+           (insert "\n"))))
       (goto-char start)
       (mapc 'funcall functions)
       (when message-citation-line-function
@@ -3380,6 +3381,15 @@ prefix, and don't delete any headers."
        (insert "> [Quoted text removed due to X-No-Archive]\n")
        (forward-line -1)))))
 
+(defun message-cite-original ()
+  "Cite function in the standard Message manner."
+  (message-cite-original-1 nil))
+
+(defun message-cite-original-without-signature ()
+  "Cite function in the standard Message manner.
+This function strips off the signature from the original message."
+  (message-cite-original-1 t))
+
 (defun message-insert-citation-line ()
   "Insert a simple citation line."
   (when message-reply-headers
@@ -4282,7 +4292,7 @@ Otherwise, generate and save a value for `canlock-password' first."
                   (zerop
                    (length
                     (setq to (completing-read
-                              "Followups to (default: no Followup-To header) "
+                              "Followups to (default no Followup-To header): "
                               (mapcar #'list
                                       (cons "poster"
                                             (message-tokenize-header
@@ -4685,6 +4695,22 @@ If NOW, use that time instead."
   (let ((system-time-locale "C"))
     (format-time-string "%a, %d %b %Y %T %z" now)))
 
+(defun message-insert-expires (days)
+  "Insert the Expires header.  Expiry in DAYS days."
+  (interactive "NExpire article in how many days? ")
+  (save-excursion
+    (message-position-on-field "Expires" "X-Draft-From")
+    (insert (message-make-expires-date days))))
+
+(defun message-make-expires-date (days)
+  "Make date string for the Expires header.  Expiry in DAYS days.
+
+In posting styles use `(\"Expires\" (make-expires-date 30))'."
+  (let* ((cur (decode-time (current-time)))
+        (nday (+ days (nth 3 cur))))
+    (setf (nth 3 cur) nday)
+    (message-make-date (apply 'encode-time cur))))
+
 (defun message-make-message-id ()
   "Make a unique Message-ID."
   (concat "<" (message-unique-id)
@@ -5024,13 +5050,17 @@ subscribed address (and not the additional To and Cc header contents)."
   (let ((field (message-fetch-field header))
        rhs ace  address)
     (when field
-      (dolist (address (mail-header-parse-addresses field))
-       (setq address (car address)
-             rhs (downcase (or (cadr (split-string address "@")) ""))
-             ace (downcase (idna-to-ascii rhs)))
+      (dolist (rhs
+              (mm-delete-duplicates
+               (mapcar (lambda (rhs) (or (cadr (split-string rhs "@")) ""))
+                       (mapcar 'downcase
+                               (mapcar
+                                'car (mail-header-parse-addresses field))))))
+       (setq ace (downcase (idna-to-ascii rhs)))
        (when (and (not (equal rhs ace))
                   (or (not (eq message-use-idna 'ask))
-                      (y-or-n-p (format "Replace %s with %s? " rhs ace))))
+                      (y-or-n-p (format "Replace %s with %s in %s:? "
+                                        rhs ace header))))
          (goto-char (point-min))
          (while (re-search-forward (concat "^" header ":") nil t)
            (message-narrow-to-field)
@@ -5050,6 +5080,8 @@ See `message-idna-encode'."
        (message-idna-to-ascii-rhs-1 "From")
        (message-idna-to-ascii-rhs-1 "To")
        (message-idna-to-ascii-rhs-1 "Reply-To")
+       (message-idna-to-ascii-rhs-1 "Mail-Reply-To")
+       (message-idna-to-ascii-rhs-1 "Mail-Followup-To")
        (message-idna-to-ascii-rhs-1 "Cc")))))
 
 (defun message-generate-headers (headers)
@@ -5609,10 +5641,6 @@ are not included."
     (when message-default-mail-headers
       (insert message-default-mail-headers)
       (or (bolp) (insert ?\n)))
-    (save-restriction
-      (message-narrow-to-headers)
-      (if message-alternative-emails
-         (message-use-alternative-email-as-from)))
     (when message-generate-headers-first
       (message-generate-headers
        (message-headers-to-generate
@@ -5631,6 +5659,12 @@ are not included."
     ;; Generate hashcash headers for recipients already known
     (mail-add-payment-async))
   (run-hooks 'message-setup-hook)
+  ;; Do this last to give it precedence over posting styles, etc.
+  (when (message-mail-p)
+    (save-restriction
+      (message-narrow-to-headers)
+      (if message-alternative-emails
+         (message-use-alternative-email-as-from))))
   (message-position-point)
   (undo-boundary))
 
@@ -6599,7 +6633,7 @@ you."
 ;; This code should be moved to underline.el (from which it is stolen).
 
 ;;;###autoload
-(defun bold-region (start end)
+(defun message-bold-region (start end)
   "Bold all nonblank characters in the region.
 Works by overstriking characters.
 Called from program, takes two arguments START and END
@@ -6615,7 +6649,7 @@ which specify the range to operate on."
        (forward-char 1)))))
 
 ;;;###autoload
-(defun unbold-region (start end)
+(defun message-unbold-region (start end)
   "Remove all boldness (overstruck characters) in the region.
 Called from program, takes two arguments START and END
 which specify the range to operate on."
@@ -6650,9 +6684,8 @@ which specify the range to operate on."
 
 (defun message-tool-bar-local-item-from-menu (command icon in-map &optional from-map &rest props)
   ;; We need to make tool bar entries in local keymaps with
-  ;; `tool-bar-local-item-from-menu' in Emacs > 21.3
+  ;; `tool-bar-local-item-from-menu' in Emacs >= 22
   (if (fboundp 'tool-bar-local-item-from-menu)
-      ;; This is for Emacs 21.3
       (tool-bar-local-item-from-menu command icon in-map from-map props)
     (tool-bar-add-item-from-menu command icon from-map props)))
 
@@ -6727,7 +6760,8 @@ If nil, the function bound in `text-mode-map' or `global-map' is executed."
   :version "22.1"
   :group 'message
   :link '(custom-manual "(message)Various Commands")
-  :type 'function)
+  :type '(choice (const nil)
+                function))
 
 (defun message-tab ()
   "Complete names according to `message-completion-alist'.
@@ -6917,6 +6951,9 @@ regexp VARSTR."
       (read-string prompt initial-contents))))
 
 (defun message-use-alternative-email-as-from ()
+  "Set From field of the outgoing message to the first matching
+address in `message-alternative-emails', looking at To, Cc and
+From headers in the original article."
   (require 'mail-utils)
   (let* ((fields '("To" "Cc" "From"))
         (emails
@@ -6931,6 +6968,7 @@ regexp VARSTR."
                emails nil))
       (pop emails))
     (unless (or (not email) (equal email user-mail-address))
+      (message-remove-header "From")
       (goto-char (point-max))
       (insert "From: " (let ((user-mail-address email)) (message-make-from))
              "\n"))))