-;;; message.el --- composing mail and news messages
+;;; message.el --- composing mail and news messages -*- coding: iso-latin-1 -*-
;; Copyright (C) 1996, 1997, 1998, 1999, 2000
;; Free Software Foundation, Inc.
(eval-when-compile
(require 'cl)
(defvar gnus-list-identifiers)) ; gnus-sum is required where necessary
-
(require 'mailheader)
(require 'nnheader)
;; This is apparently necessary even though things are autoloaded:
:group 'message-buffers
:type 'boolean)
-(defvar gnus-local-organization)
+(eval-when-compile
+ (defvar gnus-local-organization))
(defcustom message-user-organization
(or (and (boundp 'gnus-local-organization)
(stringp gnus-local-organization)
(defcustom message-forward-as-mime t
"*If non-nil, forward messages as an inline/rfc822 MIME section. Otherwise, directly inline the old message in the forwarded message."
+ :version "21.1"
:group 'message-forwarding
:type 'boolean)
(defcustom message-forward-show-mml t
"*If non-nil, forward messages are shown as mml. Otherwise, forward messages are unchanged."
+ :version "21.1"
:group 'message-forwarding
:type 'boolean)
(defcustom message-forward-ignored-headers "^Content-Transfer-Encoding:\\|^X-Gnus"
"*All headers that match this regexp will be deleted when forwarding a message."
+ :version "21.1"
:group 'message-forwarding
:type '(choice (const :tag "None" nil)
regexp))
Folding `References' makes ancient versions of INN create incorrect
NOV lines.")
-(defvar gnus-post-method)
-(defvar gnus-select-method)
+(eval-when-compile
+ (defvar gnus-post-method)
+ (defvar gnus-select-method))
(defcustom message-post-method
(cond ((and (boundp 'gnus-post-method)
(listp gnus-post-method)
(define-widget 'message-header-lines 'text
"All header lines must be LFD terminated."
- :format "%t:%n%v"
+ :format "%{%t%}:%n%v"
:valid-regexp "^\\'"
:error "All header lines must be newline terminated")
PREDICATE returns non-nil. FUNCTION is called with one parameter --
the prefix.")
-(defvar message-mail-alias-type 'abbrev
+(defcustom 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.")
+mail aliases off."
+ :group 'message
+ :link '(custom-manual "(message)Mail Aliases")
+ :type '(choice (const :tag "Use Mailabbrev" abbrev)
+ (const :tag "No expansion" nil)))
(defcustom message-auto-save-directory
- (nnheader-concat message-directory "drafts/")
+ (file-name-as-directory (nnheader-concat message-directory "drafts"))
"*Directory where Message auto-saves buffers if Gnus isn't running.
If nil, Message won't auto-save."
:group 'message-buffers
(defcustom message-buffer-naming-style 'unique
"*The way new message buffers are named.
Valid valued are `unique' and `unsent'."
+ :version "21.1"
:group 'message-buffers
:type '(choice (const :tag "unique" unique)
(const :tag "unsent" unsent)))
(and (not (mm-multibyte-p)) 'iso-8859-1)
"Default charset used in non-MULE Emacsen.
If nil, you might be asked to input the charset."
+ :version "21.1"
:group 'message
:type 'symbol)
(and (boundp 'rmail-dont-reply-to-names) rmail-dont-reply-to-names)
"*A regexp specifying names to prune when doing wide replies.
A value of nil means exclude your own name only."
+ :version "21.1"
:group 'message
:type '(choice (const :tag "Yourself" nil)
regexp))
+(defvar message-shoot-gnksa-feet nil
+ "*A list of GNKSA feet you are allowed to shoot.
+Gnus gives you all the opportunity you could possibly want for
+shooting yourself in the foot. Also, Gnus allows you to shoot the
+feet of Good Net-Keeping Seal of Approval. The following are foot
+candidates:
+`empty-article' Allow you to post an empty article;
+`quoted-text-only' Allow you to post quoted text only;
+`multiple-copies' Allow you to post multiple copies.")
+
+(defsubst message-gnksa-enable-p (feature)
+ (or (not (listp message-shoot-gnksa-feet))
+ (memq feature message-shoot-gnksa-feet)))
+
;;; Internal variables.
;;; Well, not really internal.
(defvar message-mode-abbrev-table text-mode-abbrev-table
"Abbrev table used in Message mode buffers.
Defaults to `text-mode-abbrev-table'.")
-(defgroup message-headers nil
- "Message headers."
- :link '(custom-manual "(message)Variables")
- :group 'message)
(defface message-header-to-face
'((((class color)
"The limitation of messages sent as message/partial.
The lower bound of message size in characters, beyond which the message
should be sent in several parts. If it is nil, the size is unlimited."
+ :version "21.1"
:group 'message-buffers
:type '(choice (const :tag "unlimited" nil)
(integer 1000000)))
:type '(choice (const :tag "Always use primary" nil)
regexp))
+(defcustom message-mail-user-agent nil
+ "Like `mail-user-agent'.
+Except if it is `nil', use Gnus native MUA; if it is t, use
+`mail-user-agent'."
+ :type '(radio (const :tag "Gnus native"
+ :format "%t\n"
+ nil)
+ (const :tag "`mail-user-agent'"
+ :format "%t\n"
+ t)
+ (function-item :tag "Default Emacs mail"
+ :format "%t\n"
+ sendmail-user-agent)
+ (function-item :tag "Emacs interface to MH"
+ :format "%t\n"
+ mh-e-user-agent)
+ (function :tag "Other"))
+ :version "21.1"
+ :group 'message)
+
;;; Internal variables.
(defvar message-sending-message "Sending...")
(defvar message-posting-charset nil)
;; Byte-compiler warning
-(defvar gnus-active-hashtb)
-(defvar gnus-read-active-file)
+(eval-when-compile
+ (defvar gnus-active-hashtb)
+ (defvar gnus-read-active-file))
;;; Regexp matching the delimiter of messages in UNIX mail format
;;; (UNIX From lines), minus the initial ^. It should be a copy
(point-max)))
(goto-char (point-min)))
-(defun message-narrow-to-head ()
- "Narrow the buffer to the head of the message.
-Point is left at the beginning of the narrowed-to region."
- (widen)
+(defun message-narrow-to-head-1 ()
+ "Like `message-narrow-to-head'. Don't widen."
(narrow-to-region
(goto-char (point-min))
(if (search-forward "\n\n" nil 1)
(point-max)))
(goto-char (point-min)))
+(defun message-narrow-to-head ()
+ "Narrow the buffer to the head of the message.
+Point is left at the beginning of the narrowed-to region."
+ (widen)
+ (message-narrow-to-head-1))
+
(defun message-narrow-to-headers-or-head ()
"Narrow the buffer to the head of the message."
(widen)
(easy-menu-define
message-mode-menu message-mode-map "Message Menu."
- '("Message"
+ `("Message"
["Sort Headers" message-sort-headers t]
["Yank Original" message-yank-original t]
["Fill Yanked Message" message-fill-yanked-message t]
["Kill To Signature" message-kill-to-signature t]
["Newline and Reformat" message-newline-and-reformat t]
["Rename buffer" message-rename-buffer t]
- ["Spellcheck" ispell-message t]
- ["Attach file as MIME" mml-attach-file t]
+ ["Spellcheck" ispell-message
+ ,@(if (featurep 'xemacs) nil
+ '(:help "Spellcheck this message"))]
+ ["Attach file as MIME" mml-attach-file
+ ,@(if (featurep 'xemacs) nil
+ '(:help "Attach a file at point"))]
"----"
- ["Send Message" message-send-and-exit t]
- ["Abort Message" message-dont-send t]
- ["Kill Message" message-kill-buffer t]))
+ ["Send Message" message-send-and-exit
+ ,@(if (featurep 'xemacs) nil
+ '(:help "Send this message"))]
+ ["Abort Message" message-dont-send
+ ,@(if (featurep 'xemacs) nil
+ '(:help "File this draft message and exit"))]
+ ["Kill Message" message-kill-buffer
+ ,@(if (featurep 'xemacs) nil
+ '(:help "Delete this message without sending"))]))
(easy-menu-define
message-mode-field-menu message-mode-map ""
["Body" message-goto-body t]
["Signature" message-goto-signature t]))
-(defvar facemenu-add-face-function)
-(defvar facemenu-remove-face-function)
+(defvar message-tool-bar-map nil)
+
+(eval-when-compile
+ (defvar facemenu-add-face-function)
+ (defvar facemenu-remove-face-function))
;;;###autoload
(defun message-mode ()
"Major mode for editing mail and news to be sent.
Like Text Mode but with these additional commands:
C-c C-s message-send (send the message) C-c C-c message-send-and-exit
-C-c C-d Pospone sending the message C-c C-k Kill the message
+C-c C-d Postpone sending the message C-c C-k Kill the message
C-c C-f move to a header field (and create it if there isn't):
C-c C-f C-t move to To C-c C-f C-s move to Subject
C-c C-f C-c move to Cc C-c C-f C-b move to Bcc
(set (make-local-variable 'message-checksum) nil)
(set (make-local-variable 'message-mime-part) 0)
(message-setup-fill-variables)
+ ;; Allow using comment commands to add/remove quoting.
+ (set (make-local-variable 'comment-start) message-yank-prefix)
;;(when (fboundp 'mail-hist-define-keys)
;; (mail-hist-define-keys))
(if (featurep 'xemacs)
(message-setup-toolbar)
(set (make-local-variable 'font-lock-defaults)
- '(message-font-lock-keywords t)))
+ '(message-font-lock-keywords t))
+ (if (boundp 'tool-bar-map)
+ (set (make-local-variable 'tool-bar-map) (message-tool-bar-map))))
(easy-menu-add message-mode-menu message-mode-map)
(easy-menu-add message-mode-field-menu message-mode-map)
;; Allow mail alias things.
(insert "\n"))
(funcall message-citation-line-function))))
-(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."
(if (and (boundp 'mail-citation-hook)
(when (funcall (cadr elem))
(when (and (or (not (memq (car elem)
message-sent-message-via))
- (y-or-n-p
- (format
- "Already sent message via %s; resend? "
- (car elem))))
+ (if (or (message-gnksa-enable-p 'multiple-copies)
+ (not (eq (car elem) 'news)))
+ (y-or-n-p
+ (format
+ "Already sent message via %s; resend? "
+ (car elem)))
+ (error "Denied posting -- multiple copies.")))
(setq success (funcall (caddr elem) arg)))
(setq sent t))))
(unless (or sent (not success))
(re-search-backward message-signature-separator nil t)
(beginning-of-line)
(or (re-search-backward "[^ \n\t]" b t)
- (y-or-n-p "Empty article. Really post? "))))
+ (if (message-gnksa-enable-p 'empty-article)
+ (y-or-n-p "Empty article. Really post? ")
+ (message "Denied posting -- Empty article.")
+ nil))))
;; Check for control characters.
(message-check 'control-chars
(if (re-search-forward "[\000-\007\013\015-\032\034-\037\200-\237]" nil t)
(or
(not message-checksum)
(not (eq (message-checksum) message-checksum))
- (y-or-n-p
- "It looks like no new text has been added. Really post? ")))
+ (if (message-gnksa-enable-p 'quoted-text-only)
+ (y-or-n-p
+ "It looks like no new text has been added. Really post? ")
+ (message "Denied posting -- no new text has been added.")
+ nil)))
;; Check the length of the signature.
(message-check 'signature
(goto-char (point-max))
(message-check 'quoting-style
(goto-char (point-max))
(let ((no-problem t))
- (when (search-backward-regexp "^>[^\n]*\n>" nil t)
- (setq no-problem nil)
- (while (not (eobp))
- (when (and (not (eolp)) (looking-at "[^> \t]"))
- (setq no-problem t))
- (forward-line)))
+ (when (search-backward-regexp "^>[^\n]*\n" nil t)
+ (setq no-problem (search-forward-regexp "^[ \t]*[^>\n]" nil t)))
(if no-problem
t
- (y-or-n-p "Your text should follow quoted text. Really post? "))))))
+ (if (message-gnksa-enable-p 'quoted-text-only)
+ (y-or-n-p "Your text should follow quoted text. Really post? ")
+ ;; Ensure that
+ (goto-char (point-min))
+ (re-search-forward
+ (concat "^" (regexp-quote mail-header-separator) "$"))
+ (if (search-forward-regexp "^[ \t]*[^>\n]" nil t)
+ (y-or-n-p "Your text should follow quoted text. Really post? ")
+ (message "Denied posting -- only quoted text.")
+ nil)))))))
(defun message-checksum ()
"Return a \"checksum\" for the current buffer."
(mail-header-references message-reply-headers)
(mail-header-subject message-reply-headers)
psubject
- (mail-header-subject message-reply-headers)
(not (string=
(message-strip-subject-re
(mail-header-subject message-reply-headers))
(setq message-buffer-list
(nconc message-buffer-list (list (current-buffer))))))
-(defvar mc-modes-alist)
-(defun message-setup (headers &optional replybuffer actions)
- (when (and (boundp 'mc-modes-alist)
- (not (assq 'message-mode mc-modes-alist)))
- (push '(message-mode (encrypt . mc-encrypt-message)
- (sign . mc-sign-message))
- mc-modes-alist))
+(defun message-mail-user-agent ()
+ (let ((mua (cond
+ ((not message-mail-user-agent) nil)
+ ((eq message-mail-user-agent t) mail-user-agent)
+ (t message-mail-user-agent))))
+ (if (memq mua '(message-user-agent gnus-user-agent))
+ nil
+ mua)))
+
+(defun message-setup (headers &optional replybuffer actions switch-function)
+ (let ((mua (message-mail-user-agent))
+ subject to field yank-action)
+ (if (not (and message-this-is-mail mua))
+ (message-setup-1 headers replybuffer actions)
+ (if replybuffer
+ (setq yank-action (list 'insert-buffer replybuffer)))
+ (setq headers (copy-sequence headers))
+ (setq field (assq 'Subject headers))
+ (when field
+ (setq subject (cdr field))
+ (setq headers (delq field headers)))
+ (setq field (assq 'To headers))
+ (when field
+ (setq to (cdr field))
+ (setq headers (delq field headers)))
+ (let ((mail-user-agent mua))
+ (compose-mail to subject
+ (mapcar (lambda (item)
+ (cons
+ (format "%s" (car item))
+ (cdr item)))
+ headers)
+ nil switch-function yank-action actions)))))
+
+;;;(defvar mc-modes-alist)
+(defun message-setup-1 (headers &optional replybuffer actions)
+;;; (when (and (boundp 'mc-modes-alist)
+;;; (not (assq 'message-mode mc-modes-alist)))
+;;; (push '(message-mode (encrypt . mc-encrypt-message)
+;;; (sign . mc-sign-message))
+;;; mc-modes-alist))
(when actions
(setq message-send-actions actions))
(setq message-reply-buffer replybuffer)
(defun message-set-auto-save-file-name ()
"Associate the message buffer with a file in the drafts directory."
(when message-auto-save-directory
+ (unless (file-directory-p
+ (directory-file-name message-auto-save-directory))
+ (gnus-make-directory message-auto-save-directory))
(if (gnus-alive-p)
(setq message-draft-article
(nndraft-request-associate-buffer "drafts"))
OTHER-HEADERS is an alist of header/value pairs."
(interactive)
(let ((message-this-is-mail t))
- (message-pop-to-buffer (message-buffer-name "mail" to))
+ (unless (message-mail-user-agent)
+ (message-pop-to-buffer (message-buffer-name "mail" to)))
(message-setup
(nconc
`((To . ,(or to "")) (Subject . ,(or subject "")))
(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."
(message-this-is-mail t)
gnus-warning)
(save-restriction
- (message-narrow-to-head)
+ (message-narrow-to-head-1)
;; Allow customizations to have their say.
(if (not wide)
;; This is a regular reply.
(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)))
+ (unless (message-mail-user-agent)
+ (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 ""))
(save-excursion
;; Get header info from original article.
(save-restriction
- (message-narrow-to-head)
+ (message-narrow-to-head-1)
(setq from (message-fetch-field "from")
sender (message-fetch-field "sender")
newsgroups (message-fetch-field "newsgroups")
(message-pop-to-buffer (message-buffer-name "supersede"))
(insert-buffer-substring cur)
(mime-to-mml)
- (message-narrow-to-head)
+ (message-narrow-to-head-1)
;; Remove unwanted headers.
(when message-ignored-supersedes-headers
(message-remove-header message-ignored-supersedes-headers t))
"Return a Subject header suitable for the message in the current buffer."
(save-excursion
(save-restriction
- (current-buffer)
- (message-narrow-to-head)
+ (message-narrow-to-head-1)
(let ((funcs message-make-forward-subject-function)
- (subject (if message-wash-forwarded-subjects
- (message-wash-subject
- (or (message-fetch-field "Subject") ""))
- (or (message-fetch-field "Subject") ""))))
+ (subject (message-fetch-field "Subject")))
+ (setq subject
+ (if subject
+ (mail-decode-encoded-word-string subject)
+ ""))
+ (if message-wash-forwarded-subjects
+ (setq subject (message-wash-subject subject)))
;; Make sure funcs is a list.
(and funcs
(not (listp funcs))
Optional DIGEST will use digest to forward."
(interactive "P")
(let* ((cur (current-buffer))
- (subject (if message-forward-show-mml
- (message-make-forward-subject)
- (mail-decode-encoded-word-string
- (message-make-forward-subject))))
+ (subject (message-make-forward-subject))
art-beg)
(if news
(message-news nil subject)
(insert-buffer-substring cur)
(mml-insert-buffer cur))
(if message-forward-show-mml
- (insert-buffer-substring cur)
- (mml-insert-buffer cur)))
+ (let ((target (current-buffer)) tmp)
+ (with-temp-buffer
+ (mm-disable-multibyte) ;; Must copy buffer in unibyte mode
+ (setq tmp (current-buffer))
+ (set-buffer cur)
+ (mm-with-unibyte-current-buffer
+ (set-buffer tmp)
+ (insert-buffer-substring cur)
+ (set-buffer cur))
+ (set-buffer tmp)
+ (mm-enable-multibyte)
+ (mime-to-mml)
+ (goto-char (point-min))
+ (when (looking-at "From ")
+ (replace-match "X-From-Line: "))
+ (set-buffer target)
+ (insert-buffer-substring tmp)
+ (set-buffer tmp)))
+ (save-restriction
+ (narrow-to-region (point) (point))
+ (mml-insert-buffer cur)
+ (goto-char (point-min))
+ (when (looking-at "From ")
+ (replace-match "X-From-Line: "))
+ (goto-char (point-max)))))
(setq e (point))
(if message-forward-as-mime
(if digest
(let ((cur (current-buffer))
beg)
;; We first set up a normal mail buffer.
- (set-buffer (get-buffer-create " *message resend*"))
- (erase-buffer)
- (message-setup `((To . ,address)))
+ (unless (message-mail-user-agent)
+ (set-buffer (get-buffer-create " *message resend*"))
+ (erase-buffer))
+ (let ((message-this-is-mail t))
+ (message-setup `((To . ,address))))
;; Insert our usual headers.
(message-generate-headers '(From Date To))
(message-narrow-to-headers)
(mm-enable-multibyte)
(mime-to-mml)
(save-restriction
- (message-narrow-to-head)
+ (message-narrow-to-head-1)
(message-remove-header message-ignored-bounced-headers t)
(goto-char (point-max))
(insert mail-header-separator))
(defun message-mail-other-window (&optional to subject)
"Like `message-mail' command, but display mail buffer in another window."
(interactive)
- (let ((pop-up-windows t)
- (special-display-buffer-names nil)
- (special-display-regexps nil)
- (same-window-buffer-names nil)
- (same-window-regexps nil))
- (message-pop-to-buffer (message-buffer-name "mail" to)))
+ (unless (message-mail-user-agent)
+ (let ((pop-up-windows t)
+ (special-display-buffer-names nil)
+ (special-display-regexps nil)
+ (same-window-buffer-names nil)
+ (same-window-regexps nil))
+ (message-pop-to-buffer (message-buffer-name "mail" to))))
(let ((message-this-is-mail t))
- (message-setup `((To . ,(or to "")) (Subject . ,(or subject ""))))))
+ (message-setup `((To . ,(or to "")) (Subject . ,(or subject "")))
+ nil nil 'switch-to-buffer-other-window)))
;;;###autoload
(defun message-mail-other-frame (&optional to subject)
"Like `message-mail' command, but display mail buffer in another frame."
(interactive)
- (let ((pop-up-frames t)
- (special-display-buffer-names nil)
- (special-display-regexps nil)
- (same-window-buffer-names nil)
- (same-window-regexps nil))
- (message-pop-to-buffer (message-buffer-name "mail" to)))
+ (unless (message-mail-user-agent)
+ (let ((pop-up-frames t)
+ (special-display-buffer-names nil)
+ (special-display-regexps nil)
+ (same-window-buffer-names nil)
+ (same-window-regexps nil))
+ (message-pop-to-buffer (message-buffer-name "mail" to))))
(let ((message-this-is-mail t))
- (message-setup `((To . ,(or to "")) (Subject . ,(or subject ""))))))
+ (message-setup `((To . ,(or to "")) (Subject . ,(or subject "")))
+ nil nil 'switch-to-buffer-other-frame)))
;;;###autoload
(defun message-news-other-window (&optional newsgroups subject)
(defalias 'message-exchange-point-and-mark 'exchange-point-and-mark)
;; Support for toolbar
-(when (featurep 'xemacs)
- (require 'messagexmas))
+(if (featurep 'xemacs)
+ (require 'messagexmas))
+
+(eval-when-compile
+ (defvar tool-bar-map)
+ (defvar tool-bar-mode))
+
+(defun message-tool-bar-map ()
+ (or message-tool-bar-map
+ (setq message-tool-bar-map
+ (and (fboundp 'tool-bar-add-item-from-menu)
+ tool-bar-mode
+ (let ((tool-bar-map (copy-keymap tool-bar-map))
+ (load-path (mm-image-load-path)))
+ ;; Zap some items which aren't so relevant and take
+ ;; up space.
+ (dolist (key '(print-buffer kill-buffer save-buffer
+ write-file dired open-file))
+ (define-key tool-bar-map (vector key) nil))
+ (tool-bar-add-item-from-menu
+ 'message-send-and-exit "mail_send" message-mode-map)
+ (tool-bar-add-item-from-menu
+ 'message-kill-buffer "close" message-mode-map)
+ (tool-bar-add-item-from-menu
+ 'message-dont-send "cancel" message-mode-map)
+ (tool-bar-add-item-from-menu
+ 'mml-attach-file "attach" message-mode-map)
+ (tool-bar-add-item-from-menu
+ 'ispell-message "spell" message-mode-map)
+ tool-bar-map)))))
;;; Group name completion.
(message-expand-group)
(tab-to-tab-stop)))
-(defvar gnus-active-hashtb)
(defun message-expand-group ()
"Expand the group name under point."
(let* ((b (save-excursion