(message-get-reply-buffer): Abolished.
[elisp/gnus.git-] / lisp / message.el
index 3973cbf..bd94eae 100644 (file)
@@ -138,6 +138,11 @@ mailbox format."
   :group 'message-sending
   :type 'function)
 
+(defcustom message-8bit-encoding-list '(8bit binary)
+  "*8bit encoding type in Content-Transfer-Encoding field."
+  :group 'message-sending
+  :type '(repeat (symbol :tag "Type")))
+
 (defcustom message-courtesy-message
   "The following message is a courtesy copy of an article\nthat has been posted to %s as well.\n\n"
   "*This is inserted at the start of a mailed copy of a posted message.
@@ -152,6 +157,11 @@ If this variable is nil, no such courtesy message will be added."
   :group 'message-interface
   :type 'regexp)
 
+(defcustom message-bounce-setup-function 'message-bounce-setup-for-mime-edit
+  "Function to setup a re-sending bounced message."
+  :group 'message-sending
+  :type 'function)
+
 ;;;###autoload
 (defcustom message-from-style 'default
   "*Specifies how \"From\" headers look.
@@ -464,18 +474,12 @@ variable isn't used."
   :group 'message-headers
   :type 'boolean)
 
-(defcustom message-setup-hook nil
+(defcustom message-setup-hook '(message-mime-setup)
   "Normal hook, run each time a new outgoing message is initialized.
 The function `message-setup' runs this hook."
   :group 'message-various
   :type 'hook)
 
-(defcustom message-mime-setup-function
-  'turn-on-mime-edit
-  "*A function called to set up MIME edit mode."
-  :group 'message-various
-  :type 'function)
-
 (defcustom message-signature-setup-hook nil
   "Normal hook, run each time a new outgoing message is initialized.
 It is run after the headers have been inserted and before
@@ -483,6 +487,12 @@ the signature is inserted."
   :group 'message-various
   :type 'hook)
 
+(defcustom message-bounce-setup-hook nil
+  "Normal hook, run each time a a re-sending bounced message is initialized.
+The function `message-bounce' runs this hook."
+  :group 'message-various
+  :type 'hook)
+
 (defcustom message-mode-hook nil
   "Hook run in message mode buffers."
   :group 'message-various
@@ -494,13 +504,13 @@ to the headers."
   :group 'message-various
   :type 'hook)
 
-(defcustom message-encode-header-function
+(defcustom message-header-encode-function
   'eword-encode-header
-  "A function called to after header encode."
+  "A function called to encode header."
   :group 'message-various
   :type 'function)
 
-(defcustom message-after-header-encode-hook nil
+(defcustom message-header-encoded-hook nil
   "Hook run in a message mode after header encoded. Buffer narrowed 
 to the headers."
   :group 'message-various
@@ -1025,6 +1035,22 @@ The cdr of ech entry is a function for applying the face to a region.")
 ;;;
 ;;; Utility functions.
 ;;;
+(defun message-eval-parameter (parameter)
+  (condition-case ()
+      (if (symbolp parameter)
+         (if (functionp parameter)
+             (funcall parameter)
+           (eval parameter))
+       parameter)
+    (error nil)))
+
+(defsubst message-get-parameter (key &optional alist)
+  (unless alist
+    (setq alist message-parameter-alist))
+  (cdr (assq key alist)))
+
+(defmacro message-get-parameter-with-eval (key &optional alist)
+  `(message-eval-parameter (message-get-parameter ,alist ,key)))
 
 (defmacro message-y-or-n-p (question show &rest text)
   "Ask QUESTION, displaying the rest of the arguments in a temp. buffer if SHOW"
@@ -1101,7 +1127,7 @@ 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."
-  (let ((buffer (message-get-reply-buffer)))
+  (let ((buffer (message-eval-parameter message-reply-buffer)))
     (when (and buffer
               (buffer-name buffer))
       (save-excursion
@@ -1246,22 +1272,6 @@ Return the number of headers removed."
               (- max rank)
             (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
 
 ;;;
@@ -1826,7 +1836,7 @@ 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))
-       (buffer (message-get-reply-buffer)))
+       (buffer (message-eval-parameter message-reply-buffer)))
     (when (and buffer
               message-cite-function)
       (delete-windows-on buffer t)
@@ -1972,9 +1982,12 @@ The text will also be indented the normal way."
   (interactive)
   (set-buffer-modified-p t)
   (save-buffer)
-  (let ((actions message-postpone-actions))
+  (let ((actions message-postpone-actions)
+       (frame (selected-frame))
+       (org-frame message-original-frame))
     (message-bury (current-buffer))
-    (message-do-actions actions)))
+    (message-do-actions actions)
+    (message-delete-frame frame org-frame)))
 
 (defun message-kill-buffer ()
   "Kill the current buffer."
@@ -2133,37 +2146,41 @@ the user from the mailer."
        (message-generate-headers message-required-mail-headers))
       ;; Let the user do all of the above.
       (run-hooks 'message-header-hook)
-      (when (functionp message-encode-header-function)
-       (funcall message-encode-header-function))
-      (run-hooks 'message-after-header-encode-hook))
-    (unwind-protect
-       (save-excursion
-         (set-buffer tembuf)
-         (erase-buffer)
-         (insert-buffer message-encoding-buffer)
-         ;; Remove some headers.
-         (save-restriction
-           (message-narrow-to-headers)
+      (when (functionp message-header-encode-function)
+       (funcall message-header-encode-function))
+      (run-hooks 'message-header-encoded-hook))
+    (if (not (message-check-mail-syntax))
+       (progn
+         (message "")
+         ;;(message "Posting not performed")
+         nil)
+      (unwind-protect
+         (save-excursion
+           (set-buffer tembuf)
+           (erase-buffer)
+           (insert-buffer message-encoding-buffer)
            ;; Remove some headers.
-           (message-remove-header message-ignored-mail-headers t))
-         (goto-char (point-max))
-         ;; require one newline at the end.
-         (or (= (preceding-char) ?\n)
-             (insert ?\n))
-         (when (and news
-                    (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)
-             )))
-         (funcall message-send-mail-function))
-      (kill-buffer tembuf))
-    (set-buffer message-edit-buffer)
-    (push 'mail message-sent-message-via)))
+           (save-restriction
+             (message-narrow-to-headers)
+             (message-remove-header message-ignored-mail-headers t))
+           (goto-char (point-max))
+           ;; require one newline at the end.
+           (or (= (preceding-char) ?\n)
+               (insert ?\n))
+           (when (and news
+                      (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)
+               )))
+           (funcall message-send-mail-function))
+       (kill-buffer tembuf))
+      (set-buffer message-edit-buffer)
+      (push 'mail message-sent-message-via))))
 
 (defun message-send-mail-with-sendmail ()
   "Send off the prepared buffer with sendmail."
@@ -2446,12 +2463,13 @@ to find out how to use this."
       (message-generate-headers message-required-news-headers)
       ;; Let the user do all of the above.
       (run-hooks 'message-header-hook)
-      (when (functionp message-encode-header-function)
-       (funcall message-encode-header-function))
-      (run-hooks 'message-after-header-encode-hook))
+      (when (functionp message-header-encode-function)
+       (funcall message-header-encode-function))
+      (run-hooks 'message-header-encoded-hook))
     (message-cleanup-headers)
     (if (not (message-check-news-syntax))
        (progn
+         (message "")
          ;;(message "Posting not performed")
          nil)
       (unwind-protect
@@ -2762,6 +2780,9 @@ to find out how to use this."
         (y-or-n-p
          "The article contains control characters.  Really post? ")
        t))
+   ;; Check 8bit characters.
+   (message-check '8bit
+     (message-check-8bit))
    ;; Check excessive size.
    (message-check 'size
      (if (> (buffer-size) 60000)
@@ -2789,6 +2810,54 @@ to find out how to use this."
             (1- (count-lines (point) (point-max)))))
         t)))))
 
+(defun message-check-mail-syntax ()
+  "Check the syntax of the message."
+  (save-excursion
+    (save-restriction
+      (widen)
+      (and
+       ;; We narrow to the headers and check them first.
+       (save-excursion
+        (save-restriction
+          (message-narrow-to-headers)
+          (message-check-mail-header-syntax)))
+       ;; Check the body.
+       (save-excursion
+        (set-buffer message-edit-buffer)
+        (message-check-mail-body-syntax))))))
+
+(defun message-check-mail-header-syntax ()
+  t)
+
+(defun message-check-mail-body-syntax ()
+  (and
+   ;; Check 8bit characters.
+   (message-check '8bit
+     (message-check-8bit)
+     )))
+
+(defun message-check-8bit ()
+  "Check the article contains 8bit characters."
+  (save-excursion
+    (set-buffer message-encoding-buffer)
+    (message-narrow-to-headers)
+    (let* ((case-fold-search t)
+          (field-value (message-fetch-field "content-transfer-encoding")))
+      (if (and field-value
+              (member (downcase field-value) message-8bit-encoding-list))
+         t
+       (widen)
+       (set-buffer (get-buffer-create " message syntax"))
+       (erase-buffer)
+       (goto-char (point-min))
+       (set-buffer-multibyte nil)
+       (insert-buffer message-encoding-buffer)
+       (goto-char (point-min))
+       (if (re-search-forward "[^\x00-\x7f]" nil t)
+           (y-or-n-p
+            "The article contains 8bit characters.  Really post? ")
+         t)))))
+
 (defun message-checksum ()
   "Return a \"checksum\" for the current buffer."
   (let ((sum 0))
@@ -2817,12 +2886,7 @@ to find out how to use this."
        (message-narrow-to-headers)
        (while (setq file (message-fetch-field "fcc"))
          (push file list)
-         (message-remove-header "fcc" nil t))
-       (run-hooks 'message-header-hook)
-       (when (functionp message-encode-header-function)
-         (funcall message-encode-header-function))
-       (run-hooks 'message-after-header-encode-hook))
-      (run-hooks 'message-before-do-fcc-hook)
+         (message-remove-header "fcc" nil t)))
       (goto-char (point-min))
       (re-search-forward (concat "^" (regexp-quote mail-header-separator) "$"))
       (replace-match "" t t)
@@ -2990,17 +3054,21 @@ to find out how to use this."
   "Return the In-Reply-To header for this message."
   (when message-reply-headers
     (let ((from (mail-header-from message-reply-headers))
-         (date (mail-header-date message-reply-headers)))
-      (when from
-       (let ((stop-pos
-              (string-match "  *at \\|  *@ \\| *(\\| *<" from)))
-         (concat (if (and stop-pos
-                          (not (zerop stop-pos)))
-                     (substring from 0 stop-pos) from)
-                 "'s message of \""
-                 (if (or (not date) (string= date ""))
-                     "(unknown date)" date)
-                 "\""))))))
+         (date (mail-header-date message-reply-headers))
+         (msg-id (mail-header-message-id message-reply-headers)))
+      (when msg-id
+       (concat msg-id
+               (when from
+                 (let ((stop-pos
+                        (string-match "  *at \\|  *@ \\| *(\\| *<" from)))
+                   (concat " ("
+                           (if (and stop-pos
+                                    (not (zerop stop-pos)))
+                               (substring from 0 stop-pos) from)
+                           "'s message of \""
+                           (if (or (not date) (string= date ""))
+                               "(unknown date)" date)
+                           "\")"))))))))
 
 (defun message-make-distribution ()
   "Make a Distribution header."
@@ -3496,7 +3564,7 @@ Headers already prepared in the buffer are not modified."
   (when actions
     (setq message-send-actions actions))
   (setq message-reply-buffer
-       (or (cdr (assq 'reply-buffer message-parameter-alist))
+       (or (message-get-parameter 'reply-buffer)
            replybuffer))
   (goto-char (point-min))
   ;; Insert all the headers.
@@ -3545,8 +3613,6 @@ Headers already prepared in the buffer are not modified."
     (run-hooks 'message-header-setup-hook))
   (set-buffer-modified-p nil)
   (setq buffer-undo-list nil)
-  (when (functionp message-mime-setup-function)
-    (funcall message-mime-setup-function))
   (run-hooks 'message-setup-hook)
   (message-position-point)
   (undo-boundary))
@@ -3985,10 +4051,12 @@ the message."
       (let ((funcs message-make-forward-subject-function)
            (subject (if message-wash-forwarded-subjects
                         (message-wash-subject
-                         (or (eword-decode-unstructured-field-body
-                              (message-fetch-field "Subject")) ""))
-                      (or (eword-decode-unstructured-field-body
-                           (message-fetch-field "Subject")) ""))))
+                         (or (nnheader-decode-subject
+                              (message-fetch-field "Subject"))
+                             ""))
+                      (or (nnheader-decode-subject
+                           (message-fetch-field "Subject"))
+                          ""))))
        ;; Make sure funcs is a list.
        (and funcs
             (not (listp funcs))
@@ -4089,6 +4157,13 @@ Optional NEWS will use news to forward instead of mail."
       (kill-buffer (current-buffer)))
     (message "Resending message to %s...done" address)))
 
+(defun message-bounce-setup-for-mime-edit ()
+  (goto-char (point-min))
+  (when (search-forward (concat "\n" mail-header-separator "\n") nil t)
+    (replace-match "\n\n"))
+  (set (make-local-variable 'message-setup-hook) nil)
+  (mime-edit-again))
+
 ;;;###autoload
 (defun message-bounce ()
   "Re-mail the current message.
@@ -4128,6 +4203,9 @@ you."
       (message-remove-header message-ignored-bounced-headers t)
       (goto-char (point-max))
       (insert mail-header-separator))
+    (when message-bounce-setup-function
+      (funcall message-bounce-setup-function))
+    (run-hooks 'message-bounce-setup-hook)
     (message-position-point)))
 
 ;;;
@@ -4340,7 +4418,6 @@ regexp varstr."
                (cdr local)))))
      locals)))
 
-
 ;;; @ for MIME Edit mode
 ;;;
 
@@ -4360,7 +4437,8 @@ regexp varstr."
 (defun message-mime-insert-article (&optional message)
   (interactive)
   (let ((message-cite-function 'mime-edit-inserted-message-filter)
-       (message-reply-buffer (message-get-original-reply-buffer))
+       (message-reply-buffer
+        (message-get-parameter-with-eval 'original-buffer))
        (start (point)))
     (message-yank-original nil)
     (save-excursion
@@ -4375,6 +4453,31 @@ regexp varstr."
 (set-alist 'mime-edit-message-inserter-alist
           'message-mode (function message-mime-insert-article))
 
+(defun message-mime-encode (start end &optional orig-buf)
+  (save-restriction
+    (narrow-to-region start end)
+    (when (with-current-buffer orig-buf
+           mime-edit-mode-flag)
+      (run-hooks 'mime-edit-translate-hook)
+      (mime-edit-translate-buffer)
+      )
+    (goto-char start)
+    (and (search-forward (concat "\n" mail-header-separator "\n") nil t)
+        (replace-match "\n\n"))
+    ))
+
+(set-alist 'format-alist
+          'mime-message
+          '("MIME message."
+            "1\\(^\\)"
+            nil
+            message-mime-encode
+            t nil))
+
+(defun message-mime-setup ()
+  (turn-on-mime-edit)
+  (add-to-list 'buffer-file-format 'mime-message))
+
 ;;; Miscellaneous functions
 
 ;; stolen (and renamed) from nnheader.el