Sync up with pgnus-0.34
[elisp/gnus.git-] / lisp / message.el
index 48d0ce2..ad81728 100644 (file)
@@ -1,4 +1,3 @@
-
 ;;; message.el --- composing mail and news messages
 ;; Copyright (C) 1996,97,98 Free Software Foundation, Inc.
 
@@ -312,6 +311,15 @@ should return the new buffer name."
   :group 'message-buffers
   :type 'boolean)
 
+(defcustom message-kill-buffer-query-function 'yes-or-no-p
+  "*A function called to query the user whether to kill buffer anyway or not.
+If it is t, the buffer will be killed peremptorily."
+  :type '(radio (function-item yes-or-no-p)
+               (function-item y-or-n-p)
+               (function-item nnheader-Y-or-n-p)
+               (function :tag "Other" t))
+  :group 'message-buffers)
+
 (defvar gnus-local-organization)
 (defcustom message-user-organization
   (or (and (boundp 'gnus-local-organization)
@@ -533,8 +541,7 @@ variable isn't used."
   :group 'message-headers
   :type 'boolean)
 
-(defcustom message-setup-hook
-  '(message-maybe-setup-default-charset turn-on-mime-edit)
+(defcustom message-setup-hook '(turn-on-mime-edit)
   "Normal hook, run each time a new outgoing message is initialized.
 The function `message-setup' runs this hook."
   :group 'message-various
@@ -654,6 +661,8 @@ If stringp, use this; if non-nil, use no host name (user name only)."
 (defvar message-postpone-actions nil
   "A list of actions to be performed after postponing a message.")
 (defvar message-original-frame nil)
+(defvar message-parameter-alist nil)
+(defvar message-startup-parameter-alist nil)
 
 (define-widget 'message-header-lines 'text
   "All header lines must be LFD terminated."
@@ -1136,7 +1145,9 @@ The cdr of ech entry is a function for applying the face to a region.")
   (let* ((inhibit-point-motion-hooks t)
         (value (mail-fetch-field header nil (not not-all))))
     (when value
-      (nnheader-replace-chars-in-string value ?\n ? ))))
+      (while (string-match "\n[\t ]+" value)
+       (setq value (replace-match " " t t value)))
+      value)))
 
 (defun message-narrow-to-field ()
   "Narrow the buffer to the header on the current line."
@@ -1167,11 +1178,12 @@ The cdr of ech entry is a function for applying the face to a region.")
 
 (defun message-fetch-reply-field (header)
   "Fetch FIELD from the message we're replying to."
-  (when (and message-reply-buffer
-            (buffer-name message-reply-buffer))
-    (save-excursion
-      (set-buffer message-reply-buffer)
-      (message-fetch-field header))))
+  (let ((buffer (message-get-reply-buffer)))
+    (when (and buffer
+              (buffer-name buffer))
+      (save-excursion
+       (set-buffer buffer)
+       (message-fetch-field header)))))
 
 (defun message-set-work-buffer ()
   (if (get-buffer " *message work*")
@@ -1249,6 +1261,21 @@ Point is left at the beginning of the narrowed-to region."
      (point-max)))
   (goto-char (point-min)))
 
+(defun message-narrow-to-headers-or-head ()
+  "Narrow the buffer to the head of the message."
+  (widen)
+  (narrow-to-region
+   (goto-char (point-min))
+   (cond
+    ((re-search-forward
+      (concat "^" (regexp-quote mail-header-separator) "\n") nil t)
+     (match-beginning 0))
+    ((search-forward "\n\n" nil t)
+     (1- (point)))
+    (t
+     (point-max))))
+  (goto-char (point-min)))
+
 (defun message-news-p ()
   "Say whether the current buffer contains a news message."
   (and (not message-this-is-mail)
@@ -1312,6 +1339,22 @@ Point is left at the beginning of the narrowed-to region."
             (1+ max)))))
       (message-sort-headers-1))))
 
+(defun message-eval-parameter (parameter)
+  (condition-case ()
+      (if (symbolp parameter)
+         (if (functionp parameter)
+             (funcall parameter)
+           (eval parameter))
+       parameter)
+    (error nil)))
+
+(defun message-get-reply-buffer ()
+  (message-eval-parameter message-reply-buffer))
+
+(defun message-get-original-reply-buffer ()
+  (message-eval-parameter
+   (cdr (assq 'original-buffer message-parameter-alist))))
+
 \f
 
 ;;;
@@ -1362,7 +1405,9 @@ Point is left at the beginning of the narrowed-to region."
   (define-key message-mode-map "\C-c\C-z" 'message-kill-to-signature)
   (define-key message-mode-map "\M-\r" 'message-newline-and-reformat)
 
-  (define-key message-mode-map "\t" 'message-tab))
+  (define-key message-mode-map "\t" 'message-tab)
+
+  (define-key message-mode-map "\C-xk" 'message-kill-buffer))
 
 (easy-menu-define
  message-mode-menu message-mode-map "Message Menu."
@@ -1479,6 +1524,9 @@ C-c C-r  message-caesar-buffer-body (rot13 the message body)."
   (setq message-sent-message-via nil)
   (make-local-variable 'message-checksum)
   (setq message-checksum nil)
+  (make-local-variable 'message-parameter-alist)
+  (setq message-parameter-alist
+       (copy-sequence message-startup-parameter-alist))
   ;;(when (fboundp 'mail-hist-define-keys)
   ;;  (mail-hist-define-keys))
   (when (string-match "XEmacs\\|Lucid" emacs-version)
@@ -1892,13 +1940,12 @@ This function uses `message-cite-function' to do the actual citing.
 Just \\[universal-argument] as argument means don't indent, insert no
 prefix, and don't delete any headers."
   (interactive "P")
-  (let ((modified (buffer-modified-p)))
-    (when (and message-reply-buffer
+  (let ((modified (buffer-modified-p))
+       (buffer (message-get-reply-buffer)))
+    (when (and buffer
               message-cite-function)
-      (gnus-copy-article-buffer)
-      (setq message-reply-buffer gnus-article-copy)
-      (delete-windows-on message-reply-buffer t)
-      (insert-buffer message-reply-buffer)
+      (delete-windows-on buffer t)
+      (insert-buffer buffer)
       (funcall message-cite-function)
       (message-exchange-point-and-mark)
       (unless (bolp)
@@ -2048,14 +2095,17 @@ The text will also be indented the normal way."
   "Kill the current buffer."
   (interactive)
   (when (or (not (buffer-modified-p))
-           (yes-or-no-p "Message modified; kill anyway? "))
+           (eq t message-kill-buffer-query-function)
+           (funcall message-kill-buffer-query-function
+                    "The buffer modified; kill anyway? "))
     (let ((actions message-kill-actions)
          (frame (selected-frame))
          (org-frame message-original-frame))
       (setq buffer-file-name nil)
       (kill-buffer (current-buffer))
       (message-do-actions actions)
-      (message-delete-frame frame org-frame))))
+      (message-delete-frame frame org-frame)))
+  (message ""))
 
 (defun message-delete-frame (frame org-frame)
   "Delete frame for editing message."
@@ -2101,7 +2151,6 @@ the user from the mailer."
     (undo-boundary)
     (let ((inhibit-read-only t))
       (put-text-property (point-min) (point-max) 'read-only nil))
-    (message-fix-before-sending)
     (run-hooks 'message-send-hook)
     (message "Sending...")
     (let ((message-encoding-buffer
@@ -2116,6 +2165,7 @@ the user from the mailer."
        (erase-buffer)
        (insert-buffer message-edit-buffer)
        (funcall message-encode-function)
+       (message-fix-before-sending)
        (while (and success
                    (setq elem (pop alist)))
          (when (and (or (not (funcall (cadr elem)))
@@ -2158,10 +2208,11 @@ the user from the mailer."
   (unless (bolp)
     (insert "\n"))
   ;; Delete all invisible text.
-  (when (text-property-any (point-min) (point-max) 'invisible t)
-    (put-text-property (point-min) (point-max) 'invisible nil)
-    (unless (yes-or-no-p "Invisible text found and made visible; continue posting?")
-      (error "Invisible text found and made visible"))))
+  (message-check 'invisible-text
+    (when (text-property-any (point-min) (point-max) 'invisible t)
+      (put-text-property (point-min) (point-max) 'invisible nil)
+      (unless (yes-or-no-p "Invisible text found and made visible; continue posting? ")
+       (error "Invisible text found and made visible")))))
 
 (defun message-add-action (action &rest types)
   "Add ACTION to be performed when doing an exit of type TYPES."
@@ -2215,12 +2266,24 @@ the user from the mailer."
                     (or (message-fetch-field "cc")
                         (message-fetch-field "to")))
            (message-insert-courtesy-copy))
+;;       (mime-edit-maybe-split-and-send
+;;        (function
+;;         (lambda ()
+;;           (interactive)
+;;           (funcall message-send-mail-function)
+;;           )))
          (mime-edit-maybe-split-and-send
           (function
            (lambda ()
              (interactive)
-             (funcall message-send-mail-function)
-             )))
+             (save-restriction
+               (std11-narrow-to-header mail-header-separator)
+               (goto-char (point-min))
+               (when (re-search-forward "^Message-Id:" nil t)
+                 (delete-region (match-end 0)(std11-field-end))
+                 (insert (concat " " (message-make-message-id)))
+                 ))
+             (funcall message-send-mail-function))))
          (funcall message-send-mail-function))
       (kill-buffer tembuf))
     (set-buffer message-edit-buffer)
@@ -2400,13 +2463,11 @@ to find out how to use this."
       (run-hooks 'message-header-hook))
     (message-cleanup-headers)
     (if (not (message-check-news-syntax))
-       (progn
-         ;;(message "Posting not performed")
-         nil)
+       nil
       (unwind-protect
          (save-excursion
            (set-buffer tembuf)
-           (buffer-disable-undo (current-buffer))
+           (buffer-disable-undo)
            (erase-buffer)
            (insert-buffer message-encoding-buffer)
            ;; Remove some headers.
@@ -2759,7 +2820,6 @@ to find out how to use this."
        list file)
     (save-excursion
       (set-buffer (get-buffer-create " *message temp*"))
-      (buffer-disable-undo (current-buffer))
       (erase-buffer)
       (insert-buffer-substring message-encoding-buffer)
       (save-restriction
@@ -2838,8 +2898,11 @@ If NOW, use that time instead."
   (let* ((now (or now (current-time)))
         (zone (nth 8 (decode-time now)))
         (sign "+"))
+    (when (< zone 0)
+      (setq sign ""))
     ;; We do all of this because XEmacs doesn't have the %z spec.
-    (concat (format-time-string "%d %b %Y %H:%M:%S " (or now (current-time)))
+    (concat (format-time-string
+            "%d %b %Y %H:%M:%S " (or now (current-time)))
            (format "%s%02d%02d"
                    sign (/ zone 3600)
                    (% zone 3600)))))
@@ -3104,70 +3167,26 @@ give as trustworthy answer as possible."
 (defvar mule-version)
 (defvar emacs-beta-version)
 (defvar xemacs-codename)
+(defvar gnus-inviolable-extended-version)
 
 (defun message-make-user-agent ()
-  "Return user-agent info."
-  (let ((user-agent
-        (concat
-         ;; SEMI: '("SEMI" "CODENAME" V1 V2 V3)
-         (format "%s/%s (%s)"
-                 (nth 0 mime-user-interface-version)
-                 (mapconcat #'number-to-string
-                            (cdr (cdr mime-user-interface-version))
-                            ".")
-                 (nth 1 mime-user-interface-version))
-         ;; FLIM: "FLIM VERSION - \"CODENAME\"[...]"
-         (if (string-match
-              "\\`\\([^ ]+\\) \\([^ ]+\\) - \"\\([^\"]+\\)\"\\(.*\\)\\'"
-              mime-library-version-string)
-             (format " %s/%s (%s%s)"
-                     (match-string 1 mime-library-version-string)
-                     (match-string 2 mime-library-version-string)
-                     (match-string 3 mime-library-version-string)
-                     (match-string 4 mime-library-version-string))
-           " FLIM")
-         "\n "
-         ;; EMACS/VERSION
-         (if (featurep 'xemacs)
-             ;; XEmacs
-             (concat
-              (format "XEmacs/%d.%d" emacs-major-version emacs-minor-version)
-              (if (and (boundp 'emacs-beta-version) emacs-beta-version)
-                  (format "beta%d" emacs-beta-version)
-                "")
-              (if (and (boundp 'xemacs-codename) xemacs-codename)
-                  (concat " (" xemacs-codename ")")
-                "")
-              )
-           ;; not XEmacs
-           (concat
-            (format "Emacs/%d.%d" emacs-major-version emacs-minor-version)
-            (if (>= emacs-major-version 20)
-                (if (and (boundp 'enable-multibyte-characters)
-                         enable-multibyte-characters)
-                    ""                 ; Should return " (multibyte)"?
-                  " (unibyte)"))
-            ))
-         ;; MULE[/VERSION]
-         (if (featurep 'mule)
-             (if (and (boundp 'mule-version) mule-version)
-                 (concat " MULE/" mule-version)
-               " MULE")                ; no mule-version
-           "")                         ; not Mule
-         ;; Meadow/VERSION
-         (if (featurep 'meadow)
-             (let ((version (Meadow-version)))
-               (if (string-match "\\`Meadow.\\([^ ]*\\)\\( (.*)\\)\\'" version)
-                   (concat " Meadow/"
-                           (match-string 1 version)
-                           (match-string 2 version)
-                           )
-                 "Meadow"))            ; unknown format
-           "")                         ; not Meadow
-         )))
-    (if message-user-agent
-       (concat message-user-agent "\n " user-agent)
-      user-agent)))
+  "Return user-agent info if the value `message-user-agent' is non-nil. If the
+\"User-Agent\" field has already exist, it's value will be added in the return
+string."
+  (when message-user-agent
+    (save-excursion
+      (goto-char (point-min))
+      (let ((case-fold-search t)
+           user-agent start p end)
+       (if (re-search-forward "^User-Agent:[\t ]*" nil t)
+           (progn
+             (setq start (match-beginning 0)
+                   p (match-end 0)
+                   end (std11-field-end)
+                   user-agent (buffer-substring-no-properties p end))
+             (delete-region start (1+ end))
+             (concat message-user-agent " " user-agent))
+         message-user-agent)))))
 
 (defun message-generate-headers (headers)
   "Prepare article HEADERS.
@@ -3496,6 +3515,7 @@ Headers already prepared in the buffer are not modified."
          (nconc message-buffer-list (list (current-buffer))))))
 
 (defvar mc-modes-alist)
+(defvar message-get-reply-buffer-function nil)
 (defun message-setup (headers &optional replybuffer actions)
   (when (and (boundp 'mc-modes-alist)
             (not (assq 'message-mode mc-modes-alist)))
@@ -3504,7 +3524,9 @@ Headers already prepared in the buffer are not modified."
          mc-modes-alist))
   (when actions
     (setq message-send-actions actions))
-  (setq message-reply-buffer replybuffer)
+  (setq message-reply-buffer
+       (or (cdr (assq 'reply-buffer message-parameter-alist))
+           replybuffer))
   (goto-char (point-min))
   ;; Insert all the headers.
   (mail-header-format
@@ -3953,7 +3975,6 @@ that further discussion should take place only in "
          (error "This article is not yours"))
        ;; Make control message.
        (setq buf (set-buffer (get-buffer-create " *message cancel*")))
-       (buffer-disable-undo (current-buffer))
        (erase-buffer)
        (insert "Newsgroups: " newsgroups "\n"
                "From: " (message-make-from) "\n"
@@ -4148,7 +4169,6 @@ Optional NEWS will use news to forward instead of mail."
          beg)
       ;; We first set up a normal mail buffer.
       (set-buffer (get-buffer-create " *message resend*"))
-      (buffer-disable-undo (current-buffer))
       (erase-buffer)
       ;; avoid to turn-on-mime-edit
       (let (message-setup-hook)
@@ -4379,7 +4399,7 @@ Do a `tab-to-tab-stop' if not in those headers."
          (message "No matching groups")
        (save-selected-window
          (pop-to-buffer "*Completions*")
-         (buffer-disable-undo (current-buffer))
+         (buffer-disable-undo)
          (let ((buffer-read-only nil))
            (erase-buffer)
            (let ((standard-output (current-buffer)))
@@ -4471,14 +4491,22 @@ regexp varstr."
     (run-hooks 'mime-edit-exit-hook)
     ))
 
-;;; XXX: currently broken; message-yank-original resets message-reply-buffer.
-(defun message-mime-insert-article (&optional message)
-  (interactive)
+(defun message-mime-insert-article (&optional full-headers)
+  (interactive "P")
   (let ((message-cite-function 'mime-edit-inserted-message-filter)
-        (message-reply-buffer gnus-original-article-buffer)
-       )
+       (message-reply-buffer (message-get-original-reply-buffer))
+       (start (point)))
     (message-yank-original nil)
-    ))
+    (save-excursion
+      (narrow-to-region (goto-char start)
+                       (if (search-forward "\n\n" nil t)
+                           (1- (point))
+                         (point-max)))
+      (goto-char (point-min))
+      (let ((message-included-forward-headers
+            (if full-headers "" message-included-forward-headers)))
+       (message-remove-header message-included-forward-headers t nil t))
+      (widen))))
 
 (set-alist 'mime-edit-message-inserter-alist
           'message-mode (function message-mime-insert-article))
@@ -4498,6 +4526,39 @@ regexp varstr."
       (setq idx (1+ idx)))
     string))
 
+;;;
+;;; MIME functions
+;;;
+
+(defun message-encode-message-body ()
+  "Examine the message body, encode it, and add the requisite headers."
+  (when (featurep 'mule)
+    (let (old-headers)
+      (save-excursion
+       (save-restriction
+         (message-narrow-to-headers-or-head)
+         (unless (setq old-headers (message-fetch-field "mime-version"))
+           (message-remove-header
+            "^Content-Transfer-Encoding:\\|^Content-Type:\\|^Mime-Version:" t))
+         (goto-char (point-max))
+         (widen)
+         (narrow-to-region (point) (point-max))
+         (let* ((charset (mm-encode-body))
+                (encoding (mm-body-encoding)))
+           (when (consp charset)
+             (error "Can't encode messages with multiple charsets (yet)"))
+           (widen)
+           (message-narrow-to-headers-or-head)
+           (goto-char (point-max))
+           (setq charset (or charset
+                             (mm-mule-charset-to-mime-charset 'ascii)))
+           ;; We don't insert MIME headers if they only say the default.
+           (when (and (not old-headers)
+                      (not (and (eq charset 'us-ascii)
+                                (eq encoding '7bit))))
+             (mm-insert-rfc822-headers charset encoding))
+           (mm-encode-body)))))))
+
 (run-hooks 'message-load-hook)
 
 (provide 'message)