;;; message.el --- composing mail and news messages
-;; Copyright (C) 1996 Free Software Foundation, Inc.
+;; Copyright (C) 1996,97 Free Software Foundation, Inc.
;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
;; Keywords: mail, news
;;; Code:
-(eval-when-compile
- (require 'cl))
+(require 'cl)
(require 'mailheader)
(require 'rmail)
(require 'nnheader)
(require 'mail-abbrevs)
(require 'mailabbrev))
-(defgroup message nil
+(defgroup message '((user-mail-address custom-variable)
+ (user-full-name custom-variable))
"Mail and news message composing."
- :group 'emacs)
+ :link '(custom-manual "(message)Top")
+ :group 'mail
+ :group 'news)
+
+(put 'user-mail-address 'custom-type 'string)
+(put 'user-full-name 'custom-type 'string)
+
+(defgroup message-various nil
+ "Various Message Variables"
+ :link '(custom-manual "(message)Various Message Variables")
+ :group 'message)
+
+(defgroup message-buffers nil
+ "Message Buffers"
+ :link '(custom-manual "(message)Message Buffers")
+ :group 'message)
+
+(defgroup message-sending nil
+ "Message Sending"
+ :link '(custom-manual "(message)Sending Variables")
+ :group 'message)
+
+(defgroup message-interface nil
+ "Message Interface"
+ :link '(custom-manual "(message)Interface")
+ :group 'message)
+
+(defgroup message-forwarding nil
+ "Message Forwarding"
+ :link '(custom-manual "(message)Forwarding")
+ :group 'message-interface)
+
+(defgroup message-insertion nil
+ "Message Insertion"
+ :link '(custom-manual "(message)Insertion")
+ :group 'message)
+
+(defgroup message-headers nil
+ "Message Headers"
+ :link '(custom-manual "(message)Message Headers")
+ :group 'message)
+
+(defgroup message-news nil
+ "Composing News Messages"
+ :group 'message)
+
+(defgroup message-mail nil
+ "Composing Mail Messages"
+ :group 'message)
+
+(defgroup message-faces nil
+ "Faces used for message composing."
+ :group 'message
+ :group 'faces)
(defcustom message-directory "~/Mail/"
"*Directory from which all other mail file variables are derived."
- :group 'message
+ :group 'message-various
:type 'directory)
(defcustom message-max-buffers 10
"*How many buffers to keep before starting to kill them off."
- :group 'message
+ :group 'message-buffers
:type 'integer)
(defcustom message-send-rename-function nil
"Function called to rename the buffer after sending it."
- :group 'message
+ :group 'message-buffers
:type 'function)
-;;;###autoload
-(defvar message-fcc-handler-function 'rmail-output
+(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
-article in. The default function is `rmail-output' which saves in Unix
-mailbox format.")
-(put 'message-fcc-handler-function
- 'custom-type '(radio (function-item rmail-output)
- (function :tag "Other")))
-(put 'message-fcc-handler-function
- 'factory-value '('rmail-output))
-(custom-add-to-group 'message 'message-fcc-handler-function 'custom-variable)
+article in. The default function is `message-output' which saves in Unix
+mailbox format."
+ :type '(radio (function-item message-output)
+ (function :tag "Other"))
+ :group 'message-sending)
(defcustom message-courtesy-message
"The following message is a courtesy copy of an article\nthat has been posted to %s as well.\n\n"
If the string contains the format spec \"%s\", the Newsgroups
the article has been posted to will be inserted there.
If this variable is nil, no such courtesy message will be added."
- :group 'message
+ :group 'message-sending
:type 'string)
(defcustom message-ignored-bounced-headers "^\\(Received\\|Return-Path\\):"
"*Regexp that matches headers to be removed in resent bounced mail."
- :group 'message
+ :group 'message-interface
:type 'regexp)
;;;###autoload
-(defvar message-from-style 'default
+(defcustom message-from-style 'default
"*Specifies how \"From\" headers look.
If `nil', they contain just the return address like:
Elvis Parsley <king@grassland.com>
Otherwise, most addresses look like `angles', but they look like
-`parens' if `angles' would need quoting and `parens' would not.")
-(put 'message-from-style
- 'custom-type '(choice (const :tag "simple" nil)
- (const parens)
- (const angles)
- (const default)))
-(put 'message-from-style
- 'factory-value '('default))
-(custom-add-to-group 'message 'message-from-style 'custom-variable)
-
-(defvar message-syntax-checks nil
+`parens' if `angles' would need quoting and `parens' would not."
+ :type '(choice (const :tag "simple" nil)
+ (const parens)
+ (const angles)
+ (const default))
+ :group 'message-headers)
+
+(defcustom message-syntax-checks nil
;; Guess this one shouldn't be easy to customize...
"Controls what syntax checks should not be performed on outgoing posts.
To disable checking of long signatures, for instance, add
Checks include subject-cmsg multiple-headers sendsys message-id from
long-lines control-chars size new-text redirected-followup signature
approved sender empty empty-headers message-id from subject
-shorten-followup-to existing-newsgroups.")
+shorten-followup-to existing-newsgroups."
+ :group 'message-news)
(defcustom message-required-news-headers
- '(From Newsgroups Subject Date Message-ID
- (optional . Organization) Lines
+ '(From Newsgroups Subject Date Message-ID
+ (optional . Organization) Lines
(optional . X-Newsreader))
"Headers to be generated or prompted for when posting an article.
RFC977 and RFC1036 require From, Date, Newsgroups, Subject,
Message-ID. Organization, Lines, In-Reply-To, Expires, and
X-Newsreader are optional. If don't you want message to insert some
header, remove it from this list."
- :group 'message
- :type 'sexp)
+ :group 'message-news
+ :group 'message-headers
+ :type '(repeat sexp))
-(defcustom message-required-mail-headers
+(defcustom message-required-mail-headers
'(From Subject Date (optional . In-Reply-To) Message-ID Lines
(optional . X-Mailer))
"Headers to be generated or prompted for when mailing a message.
RFC822 required that From, Date, To, Subject and Message-ID be
included. Organization, Lines and X-Mailer are optional."
- :group 'message
- :type 'sexp)
+ :group 'message-mail
+ :group 'message-headers
+ :type '(repeat sexp))
(defcustom message-deletable-headers '(Message-ID Date Lines)
"Headers to be deleted if they already exist and were generated by message previously."
- :group 'message
+ :group 'message-headers
:type 'sexp)
-(defcustom message-ignored-news-headers
+(defcustom message-ignored-news-headers
"^NNTP-Posting-Host:\\|^Xref:\\|^Bcc:\\|^Gcc:\\|^Fcc:\\|^Resent-Fcc:"
"*Regexp of headers to be removed unconditionally before posting."
- :group 'message
+ :group 'message-news
+ :group 'message-headers
:type 'regexp)
(defcustom message-ignored-mail-headers "^Gcc:\\|^Fcc:\\|^Resent-Fcc:"
"*Regexp of headers to be removed unconditionally before mailing."
- :group 'message
+ :group 'message-mail
+ :group 'message-headers
:type 'regexp)
(defcustom message-ignored-supersedes-headers "^Path:\\|^Date\\|^NNTP-Posting-Host:\\|^Xref:\\|^Lines:\\|^Received:\\|^X-From-Line:\\|Return-Path:\\|^Supersedes:"
"*Header lines matching this regexp will be deleted before posting.
It's best to delete old Path and Date headers before posting to avoid
any confusion."
- :group 'message
+ :group 'message-interface
:type 'regexp)
;;;###autoload
-(defvar message-signature-separator "^-- *$"
- "Regexp matching the signature separator.")
-(put 'message-signature-separator
- 'custom-type 'regexp)
-(put 'message-signature-separator
- 'factory-value '("^-- *$"))
-(custom-add-to-group 'message 'message-signature-separator 'custom-variable)
-
-(defcustom message-interactive nil
+(defcustom message-signature-separator "^-- *$"
+ "Regexp matching the signature separator."
+ :type 'regexp
+ :group 'message-various)
+
+(defcustom message-elide-elipsis "\n[...]\n\n"
+ "*The string which is inserted for elided text.")
+
+(defcustom message-interactive nil
"Non-nil means when sending a message wait for and display errors.
nil means let mailer mail back a message to report errors."
- :group 'message
+ :group 'message-sending
+ :group 'message-mail
:type 'boolean)
(defcustom message-generate-new-buffers t
If this is a function, call that function with three parameters: The type,
the to address and the group name. (Any of these may be nil.) The function
should return the new buffer name."
- :group 'message
+ :group 'message-buffers
:type '(choice (const :tag "off" nil)
(const :tag "on" t)
(function fun)))
(defcustom message-kill-buffer-on-exit nil
"*Non-nil means that the message buffer will be killed after sending a message."
- :group 'message
+ :group 'message-buffers
:type 'boolean)
(defvar gnus-local-organization)
-(defcustom message-user-organization
+(defcustom message-user-organization
(or (and (boundp 'gnus-local-organization)
+ (stringp gnus-local-organization)
gnus-local-organization)
(getenv "ORGANIZATION")
t)
"*String to be used as an Organization header.
If t, use `message-user-organization-file'."
- :group 'message
+ :group 'message-headers
:type '(choice string
(const :tag "consult file" t)))
;;;###autoload
-(defvar message-user-organization-file "/usr/lib/news/organization"
- "*Local news organization file.")
-(put 'message-user-organization-file
- 'custom-type 'file)
-(put 'message-user-organization-file
- 'factory-value '("/usr/lib/news/organization"))
-(custom-add-to-group 'message 'message-user-organization-file 'custom-variable)
+(defcustom message-user-organization-file "/usr/lib/news/organization"
+ "*Local news organization file."
+ :type 'file
+ :group 'message-headers)
(defcustom message-autosave-directory "~/"
; (concat (file-name-as-directory message-directory) "drafts/")
"*Directory where message autosaves buffers.
If nil, message won't autosave."
- :group 'message
+ :group 'message-buffers
:type 'directory)
-(defcustom message-forward-start-separator
+(defcustom message-forward-start-separator
"------- Start of forwarded message -------\n"
"*Delimiter inserted before forwarded messages."
- :group 'message
+ :group 'message-forwarding
:type 'string)
(defcustom message-forward-end-separator
"------- End of forwarded message -------\n"
"*Delimiter inserted after forwarded messages."
- :group 'message
+ :group 'message-forwarding
:type 'string)
(defcustom message-signature-before-forwarded-message t
"*If non-nil, put the signature before any included forwarded message."
- :group 'message
+ :group 'message-forwarding
:type 'boolean)
-(defcustom message-included-forward-headers
+(defcustom message-included-forward-headers
"^From:\\|^Newsgroups:\\|^Subject:\\|^Date:\\|^Followup-To:\\|^Reply-To:\\|^Organization:\\|^Summary:\\|^Keywords:\\|^To:\\|^Cc:\\|^Posted-To:\\|^Mail-Copies-To:\\|^Apparently-To:\\|^Gnus-Warning:\\|^Resent-\\|^Message-ID:\\|^References:"
"*Regexp matching headers to be included in forwarded messages."
- :group 'message
+ :group 'message-forwarding
:type 'regexp)
(defcustom message-ignored-resent-headers "^Return-receipt"
"*All headers that match this regexp will be deleted when resending a message."
- :group 'message
+ :group 'message-interface
:type 'regexp)
(defcustom message-ignored-cited-headers "."
"*Delete these headers from the messages you yank."
- :group 'message
+ :group 'message-insertion
:type 'regexp)
(defcustom message-cancel-message "I am canceling my own article."
"Message to be inserted in the cancel message."
- :group 'message
+ :group 'message-interface
:type 'string)
;; Useful to set in site-init.el
;;;###autoload
-(defvar message-send-mail-function 'message-send-mail-with-sendmail
+(defcustom message-send-mail-function 'message-send-mail-with-sendmail
"Function to call to send the current buffer as mail.
The headers should be delimited by a line whose contents match the
variable `mail-header-separator'.
Legal values include `message-send-mail-with-sendmail' (the default),
-`message-send-mail-with-mh' and `message-send-mail-with-qmail'.")
-(put 'message-send-mail-function
- 'custom-type '(radio (function-item message-send-mail-with-sendmail)
- (function-item message-send-mail-with-mh)
- (function-item message-send-mail-with-qmail)
- (function :tag "Other")))
-(put 'message-send-mail-function
- 'factory-value '('rmail-output))
-(custom-add-to-group 'message 'message-send-mail-function 'custom-variable)
-
+`message-send-mail-with-mh' and `message-send-mail-with-qmail'."
+ :type '(radio (function-item message-send-mail-with-sendmail)
+ (function-item message-send-mail-with-mh)
+ (function-item message-send-mail-with-qmail)
+ (function :tag "Other"))
+ :group 'message-sending
+ :group 'message-mail)
(defcustom message-send-news-function 'message-send-news
"Function to call to send the current buffer as news.
The headers should be delimited by a line whose contents match the
variable `mail-header-separator'."
- :group 'message
+ :group 'message-sending
+ :group 'message-news
:type 'function)
(defcustom message-reply-to-function nil
"Function that should return a list of headers.
This function should pick out addresses from the To, Cc, and From headers
and respond with new To and Cc headers."
- :group 'message
+ :group 'message-interface
:type 'function)
(defcustom message-wide-reply-to-function nil
"Function that should return a list of headers.
This function should pick out addresses from the To, Cc, and From headers
and respond with new To and Cc headers."
- :group 'message
+ :group 'message-interface
:type 'function)
(defcustom message-followup-to-function nil
"Function that should return a list of headers.
This function should pick out addresses from the To, Cc, and From headers
and respond with new To and Cc headers."
- :group 'message
+ :group 'message-interface
:type 'function)
(defcustom message-use-followup-to 'ask
"*Specifies what to do with Followup-To header.
-If nil, ignore the header. If it is t, use its value, but query before
-using the \"poster\" value. If it is the symbol `ask', query the user
-whether to ignore the \"poster\" value. If it is the symbol `use',
-always use the value."
- :group 'message
+If nil, always ignore the header. If it is t, use its value, but
+query before using the \"poster\" value. If it is the symbol `ask',
+always query the user whether to use the value. If it is the symbol
+`use', always use the value."
+ :group 'message-interface
:type '(choice (const :tag "ignore" nil)
(const use)
(const ask)))
+;; stuff relating to broken sendmail in MMDF
+(defcustom message-sendmail-f-is-evil nil
+ "*Non-nil means that \"-f username\" should not be added to the sendmail
+command line, because it is even more evil than leaving it out."
+ :group 'message-sending
+ :type 'boolean)
+
;; qmail-related stuff
(defcustom message-qmail-inject-program "/var/qmail/bin/qmail-inject"
"Location of the qmail-inject program."
- :group 'message
+ :group 'message-sending
:type 'file)
(defcustom message-qmail-inject-args nil
For e.g., if you wish to set the envelope sender address so that bounces
go to the right place or to deal with listserv's usage of that address, you
might set this variable to '(\"-f\" \"you@some.where\")."
- :group 'message
+ :group 'message-sending
:type '(repeat string))
(defvar gnus-post-method)
(defvar gnus-select-method)
-(defcustom message-post-method
+(defcustom message-post-method
(cond ((and (boundp 'gnus-post-method)
gnus-post-method)
gnus-post-method)
gnus-select-method)
(t '(nnspool "")))
"Method used to post news."
- :group 'message
+ :group 'message-news
+ :group 'message-sending
;; This should be the `gnus-select-method' widget, but that might
;; create a dependence to `gnus.el'.
:type 'sexp)
(defcustom message-generate-headers-first nil
"*If non-nil, generate all possible headers before composing."
- :group 'message
+ :group 'message-headers
:type 'boolean)
(defcustom message-setup-hook nil
"Normal hook, run each time a new outgoing message is initialized.
The function `message-setup' runs this hook."
- :group 'message
+ :group 'message-various
:type 'hook)
(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
+It is run after the headers have been inserted and before
the signature is inserted."
- :group 'message
+ :group 'message-various
:type 'hook)
(defcustom message-mode-hook nil
"Hook run in message mode buffers."
- :group 'message
+ :group 'message-various
:type 'hook)
(defcustom message-header-hook nil
"Hook run in a message mode buffer narrowed to the headers."
- :group 'message
+ :group 'message-various
:type 'hook)
(defcustom message-header-setup-hook nil
"Hook called narrowed to the headers when setting up a message
buffer."
- :group 'message
+ :group 'message-various
:type 'hook)
;;;###autoload
-(defvar message-citation-line-function 'message-insert-citation-line
- "*Function called to insert the \"Whomever writes:\" line.")
-(put 'message-citation-line-function
- 'custom-type 'function)
-(put 'message-citation-line-function
- 'factory-value '('message-insert-citation-line))
-(custom-add-to-group 'message 'message-citation-line-function 'custom-variable)
-
+(defcustom message-citation-line-function 'message-insert-citation-line
+ "*Function called to insert the \"Whomever writes:\" line."
+ :type 'function
+ :group 'message-insertion)
;;;###autoload
-(defvar message-yank-prefix "> "
+(defcustom message-yank-prefix "> "
"*Prefix inserted on the lines of yanked messages.
-nil means use indentation.")
-(put 'message-yank-prefix
- 'custom-type 'string)
-(put 'message-yank-prefix
- 'factory-value '("> "))
-(custom-add-to-group 'message 'message-yank-prefix 'custom-variable)
+nil means use indentation."
+ :type 'string
+ :group 'message-insertion)
(defcustom message-indentation-spaces 3
"*Number of spaces to insert at the beginning of each cited line.
Used by `message-yank-original' via `message-yank-cite'."
- :group 'message
+ :group 'message-insertion
:type 'integer)
;;;###autoload
-(defvar message-cite-function
+(defcustom message-cite-function
(if (and (boundp 'mail-citation-hook)
mail-citation-hook)
mail-citation-hook
'message-cite-original)
- "*Function for citing an original message.")
-(put 'message-cite-function
- 'custom-type '(radio (function-item message-cite-original)
- (function-item sc-cite-original)
- (function :tag "Other")))
-(put 'message-cite-function
- 'factory-value '('message-cite-original))
-(custom-add-to-group 'message 'message-cite-function 'custom-variable)
+ "*Function for citing an original message."
+ :type '(radio (function-item message-cite-original)
+ (function-item sc-cite-original)
+ (function :tag "Other"))
+ :group 'message-insertion)
;;;###autoload
-(defvar message-indent-citation-function 'message-indent-citation
+(defcustom message-indent-citation-function 'message-indent-citation
"*Function for modifying a citation just inserted in the mail buffer.
This can also be a list of functions. Each function can find the
citation between (point) and (mark t). And each function should leave
-point and mark around the citation text as modified.")
-(put 'message-indent-citation-function
- 'custom-type 'function)
-(put 'message-indent-citation-function
- 'factory-value '('message-indent-citation))
-(custom-add-to-group 'message 'message-indent-citation-function 'custom-variable)
+point and mark around the citation text as modified."
+ :type 'function
+ :group 'message-insertion)
(defvar message-abbrevs-loaded nil)
;;;###autoload
-(defvar message-signature t
+(defcustom message-signature t
"*String to be inserted at the end of the message buffer.
If t, the `message-signature-file' file will be inserted instead.
If a function, the result from the function will be used instead.
-If a form, the result from the form will be used instead.")
-(put 'message-signature
- 'custom-type 'sexp)
-(put 'message-signature
- 'factory-value '(t))
-(custom-add-to-group 'message 'message-signature 'custom-variable)
+If a form, the result from the form will be used instead."
+ :type 'sexp
+ :group 'message-insertion)
;;;###autoload
-(defvar message-signature-file "~/.signature"
- "*File containing the text inserted at end of message buffer.")
-(put 'message-signature-file
- 'custom-type 'file)
-(put 'message-signature-file
- 'factory-value '("~/.signature"))
-(custom-add-to-group 'message 'message-signature-file 'custom-variable)
+(defcustom message-signature-file "~/.signature"
+ "*File containing the text inserted at end of message buffer."
+ :type 'file
+ :group 'message-insertion)
(defcustom message-distribution-function nil
"*Function called to return a Distribution header."
- :group 'message
+ :group 'message-news
+ :group 'message-headers
:type 'function)
(defcustom message-expires 14
- "*Number of days before your article expires."
- :group 'message
+ "Number of days before your article expires."
+ :group 'message-news
+ :group 'message-headers
+ :link '(custom-manual "(message)News Headers")
:type 'integer)
(defcustom message-user-path nil
"If nil, use the NNTP server name in the Path header.
If stringp, use this; if non-nil, use no host name (user name only)."
- :group 'message
+ :group 'message-news
+ :group 'message-headers
+ :link '(custom-manual "(message)News Headers")
:type '(choice (const :tag "nntp" nil)
(string :tag "name")
(sexp :tag "none" :format "%t" t)))
"*A string containing header lines to be inserted in outgoing messages.
It is inserted before you edit the message, so you can edit or delete
these lines."
- :group 'message
+ :group 'message-headers
:type 'string)
(defcustom message-default-mail-headers ""
"*A string of header lines to be inserted in outgoing mails."
- :group 'message
+ :group 'message-headers
+ :group 'message-mail
:type 'string)
(defcustom message-default-news-headers ""
"*A string of header lines to be inserted in outgoing news
articles."
- :group 'message
+ :group 'message-headers
+ :group 'message-news
:type 'string)
;; Note: could use /usr/ucb/mail instead of sendmail;
;; options -t, and -v if not interactive.
(defcustom message-mailer-swallows-blank-line
- (if (and (string-match "sparc-sun-sunos\\(\\'\\|[^5]\\)"
+ (if (and (string-match "sparc-sun-sunos\\(\\'\\|[^5]\\)"
system-configuration)
(file-readable-p "/etc/sendmail.cf")
(let ((buffer (get-buffer-create " *temp*")))
\(This problem exists on Sunos 4 when sendmail is run in remote mode.)
The value should be an expression to test whether the problem will
actually occur."
- :group 'message
+ :group 'message-sending
:type 'sexp)
+(ignore-errors
+ (define-mail-user-agent 'message-user-agent
+ 'message-mail 'message-send-and-exit
+ 'message-kill-buffer 'message-send-hook))
+
+(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.
-(defvar message-mode-syntax-table
+(defvar message-mode-syntax-table
(let ((table (copy-syntax-table text-mode-syntax-table)))
(modify-syntax-entry ?% ". " table)
table)
(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)
+ (background dark))
+ (:foreground "green2" :bold t))
+ (((class color)
+ (background light))
+ (:foreground "MidnightBlue" :bold t))
+ (t
+ (:bold t :italic t)))
+ "Face used for displaying From headers."
+ :group 'message-faces)
+
+(defface message-header-cc-face
+ '((((class color)
+ (background dark))
+ (:foreground "green4" :bold t))
+ (((class color)
+ (background light))
+ (:foreground "MidnightBlue"))
+ (t
+ (:bold t)))
+ "Face used for displaying Cc headers."
+ :group 'message-faces)
+
+(defface message-header-subject-face
+ '((((class color)
+ (background dark))
+ (:foreground "green3"))
+ (((class color)
+ (background light))
+ (:foreground "navy blue" :bold t))
+ (t
+ (:bold t)))
+ "Face used for displaying subject headers."
+ :group 'message-faces)
+
+(defface message-header-newsgroups-face
+ '((((class color)
+ (background dark))
+ (:foreground "yellow" :bold t :italic t))
+ (((class color)
+ (background light))
+ (:foreground "blue4" :bold t :italic t))
+ (t
+ (:bold t :italic t)))
+ "Face used for displaying newsgroups headers."
+ :group 'message-faces)
+
+(defface message-header-other-face
+ '((((class color)
+ (background dark))
+ (:foreground "red4"))
+ (((class color)
+ (background light))
+ (:foreground "steel blue"))
+ (t
+ (:bold t :italic t)))
+ "Face used for displaying newsgroups headers."
+ :group 'message-faces)
+
+(defface message-header-name-face
+ '((((class color)
+ (background dark))
+ (:foreground "DarkGreen"))
+ (((class color)
+ (background light))
+ (:foreground "cornflower blue"))
+ (t
+ (:bold t)))
+ "Face used for displaying header names."
+ :group 'message-faces)
+
+(defface message-header-xheader-face
+ '((((class color)
+ (background dark))
+ (:foreground "blue"))
+ (((class color)
+ (background light))
+ (:foreground "blue"))
+ (t
+ (:bold t)))
+ "Face used for displaying X-Header headers."
+ :group 'message-faces)
+
+(defface message-separator-face
+ '((((class color)
+ (background dark))
+ (:foreground "blue4"))
+ (((class color)
+ (background light))
+ (:foreground "brown"))
+ (t
+ (:bold t)))
+ "Face used for displaying the separator."
+ :group 'message-faces)
+
+(defface message-cited-text-face
+ '((((class color)
+ (background dark))
+ (:foreground "red"))
+ (((class color)
+ (background light))
+ (:foreground "red"))
+ (t
+ (:bold t)))
+ "Face used for displaying cited text names."
+ :group 'message-faces)
(defvar message-font-lock-keywords
- (let* ((cite-prefix "A-Za-z") (cite-suffix (concat cite-prefix "0-9_.@-")))
- (list '("^To:" . font-lock-function-name-face)
- '("^[GBF]?[Cc][Cc]:\\|^Reply-To:" . font-lock-keyword-face)
- '("^\\(Subject:\\)[ \t]*\\(.+\\)?"
- (1 font-lock-comment-face) (2 font-lock-type-face nil t))
- (list (concat "^\\(" (regexp-quote mail-header-separator) "\\)$")
- 1 'font-lock-comment-face)
- (cons (concat "^[ \t]*"
- "\\([" cite-prefix "]+[" cite-suffix "]*\\)?"
- "[>|}].*")
- 'font-lock-reference-face)
- '("^\\(X-[A-Za-z0-9-]+\\|In-Reply-To\\):.*"
- . font-lock-string-face)))
+ (let* ((cite-prefix "A-Za-z")
+ (cite-suffix (concat cite-prefix "0-9_.@-"))
+ (content "[ \t]*\\(.+\\(\n[ \t].*\\)*\\)"))
+ `((,(concat "^\\([Tt]o:\\)" content)
+ (1 'message-header-name-face)
+ (2 'message-header-to-face nil t))
+ (,(concat "^\\(^[GBF]?[Cc][Cc]:\\|^[Rr]eply-[Tt]o:\\)" content)
+ (1 'message-header-name-face)
+ (2 'message-header-cc-face nil t))
+ (,(concat "^\\([Ss]ubject:\\)" content)
+ (1 'message-header-name-face)
+ (2 'message-header-subject-face nil t))
+ (,(concat "^\\([Nn]ewsgroups:\\|Followup-[Tt]o:\\)" content)
+ (1 'message-header-name-face)
+ (2 'message-header-newsgroups-face nil t))
+ (,(concat "^\\([^: \n\t]+:\\)" content)
+ (1 'message-header-name-face)
+ (2 'message-header-other-face nil t))
+ (,(concat "^\\(X-[A-Za-z0-9-]+\\|In-Reply-To\\):" content)
+ (1 'message-header-name-face)
+ (2 'message-header-name-face))
+ (,(concat "^\\(" (regexp-quote mail-header-separator) "\\)$")
+ 1 'message-separator-face)
+ (,(concat "^[ \t]*"
+ "\\([" cite-prefix "]+[" cite-suffix "]*\\)?"
+ "[>|}].*")
+ (0 'message-cited-text-face))))
"Additional expressions to highlight in Message mode.")
(defvar message-face-alist
'((bold . bold-region)
(underline . underline-region)
- (default . (lambda (b e)
+ (default . (lambda (b e)
(unbold-region b e)
(ununderline-region b e))))
"Alist of mail and news faces for facemenu.
(defcustom message-send-hook nil
"Hook run before sending messages."
- :group 'message
+ :group 'message-various
:options '(ispell-message)
:type 'hook)
(defcustom message-send-mail-hook nil
"Hook run before sending mail messages."
- :group 'message
+ :group 'message-various
:type 'hook)
(defcustom message-send-news-hook nil
"Hook run before sending news messages."
- :group 'message
+ :group 'message-various
:type 'hook)
(defcustom message-sent-hook nil
"Hook run after sending messages."
- :group 'message
+ :group 'message-various
:type 'hook)
;;; Internal variables.
(defvar message-buffer-list nil)
+(defvar message-this-is-news nil)
+(defvar message-this-is-mail nil)
;; Byte-compiler warning
(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 ^.
+;;; (UNIX From lines), minus the initial ^.
(defvar message-unix-mail-delimiter
(let ((time-zone-regexp
(concat "\\([A-Z]?[A-Z]?[A-Z][A-Z]\\( DST\\)?"
"^|? *---+ +Message text follows: +---+ *|?$")
"A regexp that matches the separator before the text of a failed message.")
-(defvar message-header-format-alist
+(defvar message-header-format-alist
`((Newsgroups)
- (To . message-fill-address)
+ (To . message-fill-address)
(Cc . message-fill-address)
(Subject)
(In-Reply-To)
(eval-and-compile
(autoload 'message-setup-toolbar "messagexmas")
- (autoload 'mh-send-letter "mh-comp"))
+ (autoload 'mh-send-letter "mh-comp")
+ (autoload 'gnus-point-at-eol "gnus-util")
+ (autoload 'gnus-point-at-bol "gnus-util")
+ (autoload 'gnus-output-to-mail "gnus-util")
+ (autoload 'gnus-output-to-rmail "gnus-util")
+ (autoload 'mail-abbrev-in-expansion-header-p "mailabbrev"))
\f
-;;;
+;;;
;;; Utility functions.
;;;
-(defun message-point-at-bol ()
- "Return point at the beginning of the line."
- (let ((p (point)))
- (beginning-of-line)
- (prog1
- (point)
- (goto-char p))))
-
-(defun message-point-at-eol ()
- "Return point at the end of the line."
- (let ((p (point)))
- (end-of-line)
- (prog1
- (point)
- (goto-char p))))
-
(defmacro message-y-or-n-p (question show &rest text)
"Ask QUESTION, displaying the rest of the arguments in a temp. buffer if SHOW"
`(message-talkative-question 'y-or-n-p ,question ,show ,@text))
(setq paren nil))))
(nreverse elems)))))
-(defun message-fetch-field (header)
+(defun message-mail-file-mbox-p (file)
+ "Say whether FILE looks like a Unix mbox file."
+ (when (and (file-exists-p file)
+ (file-readable-p file)
+ (file-regular-p file))
+ (nnheader-temp-write nil
+ (nnheader-insert-file-contents file)
+ (goto-char (point-min))
+ (looking-at message-unix-mail-delimiter))))
+
+(defun message-fetch-field (header &optional not-all)
"The same as `mail-fetch-field', only remove all newlines."
- (let ((value (mail-fetch-field header)))
+ (let ((value (mail-fetch-field header nil (not not-all))))
(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
(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."
(defun message-news-p ()
"Say whether the current buffer contains a news message."
- (save-excursion
- (save-restriction
- (message-narrow-to-headers)
- (message-fetch-field "newsgroups"))))
+ (or message-this-is-news
+ (save-excursion
+ (save-restriction
+ (message-narrow-to-headers)
+ (message-fetch-field "newsgroups")))))
(defun message-mail-p ()
"Say whether the current buffer contains a mail message."
- (save-excursion
- (save-restriction
- (message-narrow-to-headers)
- (or (message-fetch-field "to")
- (message-fetch-field "cc")
- (message-fetch-field "bcc")))))
+ (or message-this-is-mail
+ (save-excursion
+ (save-restriction
+ (message-narrow-to-headers)
+ (or (message-fetch-field "to")
+ (message-fetch-field "cc")
+ (message-fetch-field "bcc"))))))
(defun message-next-header ()
"Go to the beginning of the next header."
(not (if (re-search-forward "^[^ \t]" nil t)
(beginning-of-line)
(goto-char (point-max)))))
-
+
(defun message-sort-headers-1 ()
"Sort the buffer as headers using `message-rank' text props."
(goto-char (point-min))
- (sort-subr
- nil 'message-next-header
+ (sort-subr
+ nil 'message-next-header
(lambda ()
(message-next-header)
(unless (bobp)
(define-key message-mode-map "\C-c\C-t" 'message-insert-to)
(define-key message-mode-map "\C-c\C-n" 'message-insert-newsgroups)
-
+
(define-key message-mode-map "\C-c\C-y" 'message-yank-original)
(define-key message-mode-map "\C-c\C-q" 'message-fill-yanked-message)
(define-key message-mode-map "\C-c\C-w" 'message-insert-signature)
(define-key message-mode-map "\C-c\C-k" 'message-kill-buffer)
(define-key message-mode-map "\C-c\C-d" 'message-dont-send)
+ (define-key message-mode-map "\C-c\C-e" 'message-elide-region)
+
(define-key message-mode-map "\t" 'message-tab))
-(easy-menu-define message-mode-menu message-mode-map
- "Message Menu."
- '("Message"
- "Go to Field:"
- "----"
- ["To" message-goto-to t]
- ["Subject" message-goto-subject t]
- ["Cc" message-goto-cc t]
- ["Reply-To" message-goto-reply-to t]
- ["Summary" message-goto-summary t]
- ["Keywords" message-goto-keywords t]
- ["Newsgroups" message-goto-newsgroups t]
- ["Followup-To" message-goto-followup-to t]
- ["Distribution" message-goto-distribution t]
- ["Body" message-goto-body t]
- ["Signature" message-goto-signature t]
- "----"
- "Miscellaneous Commands:"
- "----"
- ["Sort Headers" message-sort-headers t]
- ["Yank Original" message-yank-original t]
- ["Fill Yanked Message" message-fill-yanked-message t]
- ["Insert Signature" message-insert-signature t]
- ["Caesar (rot13) Message" message-caesar-buffer-body t]
- ["Rename buffer" message-rename-buffer t]
- ["Spellcheck" ispell-message t]
- "----"
- ["Send Message" message-send-and-exit t]
- ["Abort Message" message-dont-send t]))
+(easy-menu-define
+ message-mode-menu message-mode-map "Message Menu."
+ '("Message"
+ ["Sort Headers" message-sort-headers t]
+ ["Yank Original" message-yank-original t]
+ ["Fill Yanked Message" message-fill-yanked-message t]
+ ["Insert Signature" message-insert-signature t]
+ ["Caesar (rot13) Message" message-caesar-buffer-body t]
+ ["Caesar (rot13) Region" message-caesar-region (mark t)]
+ ["Elide Region" message-elide-region (mark t)]
+ ["Rename buffer" message-rename-buffer t]
+ ["Spellcheck" ispell-message t]
+ "----"
+ ["Send Message" message-send-and-exit t]
+ ["Abort Message" message-dont-send t]))
+
+(easy-menu-define
+ message-mode-field-menu message-mode-map ""
+ '("Field"
+ ["Fetch To" message-insert-to t]
+ ["Fetch Newsgroups" message-insert-newsgroups t]
+ "----"
+ ["To" message-goto-to t]
+ ["Subject" message-goto-subject t]
+ ["Cc" message-goto-cc t]
+ ["Reply-To" message-goto-reply-to t]
+ ["Summary" message-goto-summary t]
+ ["Keywords" message-goto-keywords t]
+ ["Newsgroups" message-goto-newsgroups t]
+ ["Followup-To" message-goto-followup-to t]
+ ["Distribution" message-goto-distribution t]
+ ["Body" message-goto-body t]
+ ["Signature" message-goto-signature t]))
(defvar facemenu-add-face-function)
(defvar facemenu-remove-face-function)
C-c C-w message-insert-signature (insert `message-signature-file' file).
C-c C-y message-yank-original (insert current message, if any).
C-c C-q message-fill-yanked-message (fill what was yanked).
+C-c C-e message-elide-region (elide the text between point and mark).
C-c C-r message-caesar-buffer-body (rot13 the message body)."
(interactive)
(kill-all-local-variables)
(make-local-variable 'message-reply-buffer)
(setq message-reply-buffer nil)
- (make-local-variable 'message-send-actions)
- (make-local-variable 'message-exit-actions)
+ (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)
(when (string-match "XEmacs\\|Lucid" emacs-version)
(message-setup-toolbar))
(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
\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")
(defun message-insert-signature (&optional force)
"Insert a signature. See documentation for the `message-signature' variable."
(interactive (list 0))
- (let* ((signature
+ (let* ((signature
(cond
((and (null message-signature)
(eq force 0))
(goto-char (point-max))
(or (bolp) (insert "\n")))))
+(defun message-elide-region (b e)
+ "Elide the text between point and mark. An ellipsis (from
+message-elide-elipsis) will be inserted where the text was killed."
+ (interactive "r")
+ (kill-region b e)
+ (unless (bolp)
+ (insert "\n"))
+ (insert message-elide-elipsis))
+
(defvar message-caesar-translation-table nil)
(defun message-caesar-region (b e &optional n)
;; We build the table, if necessary.
(when (or (not message-caesar-translation-table)
(/= (aref message-caesar-translation-table ?a) (+ ?a n)))
- (let ((i -1)
- (table (make-string 256 0)))
- (while (< (incf i) 256)
- (aset table i i))
- (setq table
- (concat
- (substring table 0 ?A)
- (substring table (+ ?A n) (+ ?A n (- 26 n)))
- (substring table ?A (+ ?A n))
- (substring table (+ ?A 26) ?a)
- (substring table (+ ?a n) (+ ?a n (- 26 n)))
- (substring table ?a (+ ?a n))
- (substring table (+ ?a 26) 255)))
- (setq message-caesar-translation-table table)))
- ;; Then we translate the region. Do it this way to retain
+ (setq message-caesar-translation-table
+ (message-make-caesar-translation-table n)))
+ ;; Then we translate the region. Do it this way to retain
;; text properties.
(while (< b e)
- (subst-char-in-region
+ (subst-char-in-region
b (1+ b) (char-after b)
(aref message-caesar-translation-table (char-after b)))
(incf b))))
+(defun message-make-caesar-translation-table (n)
+ "Create a rot table with offset N."
+ (let ((i -1)
+ (table (make-string 256 0)))
+ (while (< (incf i) 256)
+ (aset table i i))
+ (concat
+ (substring table 0 ?A)
+ (substring table (+ ?A n) (+ ?A n (- 26 n)))
+ (substring table ?A (+ ?A n))
+ (substring table (+ ?A 26) ?a)
+ (substring table (+ ?a n) (+ ?a n (- 26 n)))
+ (substring table ?a (+ ?a n))
+ (substring table (+ ?a 26) 255))))
+
(defun message-caesar-buffer-body (&optional rotnum)
"Caesar rotates all letters in the current buffer by 13 places.
Used to encode/decode possibly offensive messages (commonly in net.jokes).
(narrow-to-region (point) (point-max)))
(message-caesar-region (point-min) (point-max) rotnum))))
+(defun message-pipe-buffer-body (program)
+ "Pipe the message body in the current buffer through PROGRAM."
+ (save-excursion
+ (save-restriction
+ (when (message-goto-body)
+ (narrow-to-region (point) (point-max)))
+ (let ((body (buffer-substring (point-min) (point-max))))
+ (unless (equal 0 (call-process-region
+ (point-min) (point-max) program t t))
+ (insert body)
+ (message "%s failed." program))))))
+
(defun message-rename-buffer (&optional enter-string)
- "Rename the *message* buffer to \"*message* RECIPIENT\".
+ "Rename the *message* buffer to \"*message* RECIPIENT\".
If the function is run with a prefix, it will ask for a new buffer
name, rather than giving an automatic name."
(interactive "Pbuffer name: ")
(save-excursion
(save-restriction
(goto-char (point-min))
- (narrow-to-region (point)
+ (narrow-to-region (point)
(search-forward mail-header-separator nil 'end))
- (let* ((mail-to (if (message-news-p) (message-fetch-field "Newsgroups")
- (message-fetch-field "To")))
+ (let* ((mail-to (or
+ (if (message-news-p) (message-fetch-field "Newsgroups")
+ (message-fetch-field "To"))
+ ""))
(mail-trimmed-to
(if (string-match "," mail-to)
(concat (substring mail-to 0 (match-beginning 0)) ", ...")
(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)
(goto-char start)
(while (< (point) (mark t))
(insert message-yank-prefix)
- (forward-line 1)))
- (goto-char start))))
+ (forward-line 1))))
+ (goto-char start)))
(defun message-yank-original (&optional arg)
"Insert the message being replied to, if any.
(defun message-cite-original ()
"Cite function in the standard Message manner."
(let ((start (point))
- (functions
+ (functions
(when message-indent-citation-function
(if (listp message-indent-citation-function)
message-indent-citation-function
(narrow-to-region
(goto-char (point-min))
(progn
- (re-search-forward
+ (re-search-forward
(concat "^" (regexp-quote mail-header-separator) "$"))
(match-beginning 0)))
(goto-char (point-min))
(skip-chars-backward "\n")
t)
(while (and afters
- (not (re-search-forward
+ (not (re-search-forward
(concat "^" (regexp-quote (car afters)) ":")
nil t)))
(pop afters))
(defun message-kill-buffer ()
"Kill the current buffer."
(interactive)
- (let ((actions message-kill-actions))
- (kill-buffer (current-buffer))
- (message-do-actions actions)))
+ (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))))
(defun message-bury (buffer)
"Bury this mail buffer."
(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."
;; Now perform actions on successful sending.
(while actions
(ignore-errors
- (cond
+ (cond
;; A simple function.
((message-functionp (car actions))
(funcall (car actions)))
(set-buffer tembuf)
(erase-buffer)
;; Avoid copying text props.
- (insert (format
+ (insert (format
"%s" (save-excursion
(set-buffer mailbuf)
(buffer-string))))
nil errbuf nil "-oi")
;; Always specify who from,
;; since some systems have broken sendmails.
- (list "-f" (user-login-name))
+ ;; But some systems are more broken with -f, so
+ ;; we'll let users override this.
+ (if (null message-sendmail-f-is-evil)
+ (list "-f" (user-login-name)))
;; These mean "report errors by mail"
;; and "deliver in background".
(if (null message-interactive) '("-oem" "-odb"))
;;
;; qmail also has the advantage of not having been raped by
;; various vendors, so we don't have to allow for that, either --
- ;; compare this with message-send-mail-with-sendmail and weep
+ ;; compare this with message-send-mail-with-sendmail and weep
;; for sendmail's lost innocence.
;;
;; all this is way cool coz it lets us keep the arguments entirely
"Send the prepared message buffer with mh."
(let ((mh-previous-window-config nil)
(name (make-temp-name
- (concat (file-name-as-directory message-autosave-directory)
+ (concat (file-name-as-directory
+ (expand-file-name message-autosave-directory))
"msg."))))
(setq buffer-file-name name)
;; MH wants to generate these headers itself.
- (let ((headers message-deletable-headers))
- (while headers
- (goto-char (point-min))
- ;;(message "Deleting header %s" (car headers)) (sit-for 5)
- (and (re-search-forward
- (concat "^" (symbol-name (car headers)) ": *") nil t)
- (message-delete-line))
- (pop headers)))
+ (when message-mh-deletable-headers
+ (let ((headers message-mh-deletable-headers))
+ (while headers
+ (goto-char (point-min))
+ (and (re-search-forward
+ (concat "^" (symbol-name (car headers)) ": *") nil t)
+ (message-delete-line))
+ (pop headers))))
(run-hooks 'message-send-mail-hook)
;; Pass it on to mh.
(mh-send-letter)))
(save-excursion
(set-buffer tembuf)
(buffer-disable-undo (current-buffer))
- (erase-buffer)
+ (erase-buffer)
;; Avoid copying text props.
- (insert (format
+ (insert (format
"%s" (save-excursion
(set-buffer messbuf)
(buffer-string))))
(save-excursion
(save-restriction
(widen)
- (and
+ (and
;; We narrow to the headers and check them first.
(save-excursion
(save-restriction
(message-check-news-body-syntax)))))
(defun message-check-news-header-syntax ()
- (and
+ (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"))
;; Check for multiple identical headers.
(message-check 'multiple-headers
(let (found)
- (while (and (not found)
+ (while (and (not found)
(re-search-forward "^[^ \t:]+: " nil t))
(save-excursion
- (or (re-search-forward
- (concat "^"
+ (or (re-search-forward
+ (concat "^"
(regexp-quote
(setq found
(buffer-substring
(if (re-search-forward "^Sendsys:\\|^Version:" nil t)
(y-or-n-p
(format "The article contains a %s command. Really post? "
- (buffer-substring (match-beginning 0)
+ (buffer-substring (match-beginning 0)
(1- (match-end 0)))))
t))
;; See whether we can shorten Followup-To.
(not
(zerop
(length
- (setq to (completing-read
- "Followups to: (default all groups) "
+ (setq to (completing-read
+ "Followups to: (default all groups) "
(mapcar (lambda (g) (list g))
- (cons "poster"
- (message-tokenize-header
+ (cons "poster"
+ (message-tokenize-header
newsgroups)))))))))
(goto-char (point-min))
(insert "Followup-To: " to "\n"))
;; Check "Shoot me".
(message-check 'shoot
(if (re-search-forward
- "Message-ID.*.i-have-a-misconfigured-system-so-shoot-me" nil t)
+ "Message-ID.*.i-did-not-set--mail-host-address--so-shoot-me" nil t)
(y-or-n-p "You appear to have a misconfigured system. Really post? ")
t))
;; Check for Approved.
;; Check the Message-ID header.
(message-check 'message-id
(let* ((case-fold-search t)
- (message-id (message-fetch-field "message-id")))
+ (message-id (message-fetch-field "message-id" t)))
(or (not message-id)
(and (string-match "@" message-id)
(string-match "@[^\\.]*\\." message-id))
(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)
(hashtb (and (boundp 'gnus-active-hashtb)
gnus-active-hashtb))
errors)
- (if (not hashtb)
+ (if (or (not hashtb)
+ (not (boundp 'gnus-read-active-file))
+ (not gnus-read-active-file)
+ (eq gnus-read-active-file 'some))
t
(while groups
(when (and (not (boundp (intern (car groups) hashtb)))
(while (and headers (not error))
(when (setq header (mail-fetch-field (car headers)))
(if (or
- (not
+ (not
(string-match
"\\`\\([-+_&.a-zA-Z0-9]+\\)?\\(,[-+_&.a-zA-Z0-9]+\\)*\\'"
header))
- (memq
- nil (mapcar
+ (memq
+ nil (mapcar
(lambda (g)
(not (string-match "\\.\\'\\|\\.\\." g)))
(message-tokenize-header header ","))))
;; Check for control characters.
(message-check 'control-chars
(if (re-search-forward "[\000-\007\013\015-\037\200-\237]" nil t)
- (y-or-n-p
+ (y-or-n-p
"The article contains control characters. Really post? ")
t))
;; Check excessive size.
(not (eq message-fcc-handler-function 'rmail-output)))
(funcall message-fcc-handler-function file)
(if (and (file-readable-p file) (mail-file-babyl-p file))
- (rmail-output file 1)
+ (rmail-output file 1 nil t)
(let ((mail-use-rfc822 t))
(rmail-output file 1 t t))))))
+
(kill-buffer (current-buffer)))))
+(defun message-output (filename)
+ "Append this article to Unix/babyl mail file.."
+ (if (and (file-readable-p filename)
+ (mail-file-babyl-p filename))
+ (gnus-output-to-rmail filename t)
+ (gnus-output-to-mail filename t)))
+
(defun message-cleanup-headers ()
"Do various automatic cleanups of the headers."
;; Remove empty lines in the header.
(defun message-make-date ()
"Make a valid data header."
(let ((now (current-time)))
- (timezone-make-date-arpa-standard
+ (timezone-make-date-arpa-standard
(current-time-string now) (current-time-zone now))))
(defun message-make-message-id ()
"Make a unique Message-ID."
- (concat "<" (message-unique-id)
+ (concat "<" (message-unique-id)
(let ((psubject (save-excursion (message-fetch-field "subject"))))
(if (and message-reply-headers
(mail-header-references message-reply-headers)
(mail-header-subject message-reply-headers)
psubject
(mail-header-subject message-reply-headers)
- (not (string=
+ (not (string=
(message-strip-subject-re
(mail-header-subject message-reply-headers))
(message-strip-subject-re psubject))))
(aset user (match-beginning 0) ?_))
user)
(message-number-base36 (user-uid) -1))
- (message-number-base36 (+ (car tm)
+ (message-number-base36 (+ (car tm)
(lsh (% message-unique-id-char 25) 16)) 4)
(message-number-base36 (+ (nth 1 tm)
(lsh (/ message-unique-id-char 25) 16)) 4)
".fsf")))
(defun message-number-base36 (num len)
- (if (if (< len 0) (<= num 0) (= len 0))
+ (if (if (< len 0)
+ (<= num 0)
+ (= len 0))
""
(concat (message-number-base36 (/ num 36) (1- len))
(char-to-string (aref "zyxwvutsrqponmlkjihgfedcba9876543210"
(defun message-make-organization ()
"Make an Organization header."
- (let* ((organization
+ (let* ((organization
(or (getenv "ORGANIZATION")
(when message-user-organization
(if (message-functionp message-user-organization)
(save-restriction
(widen)
(goto-char (point-min))
- (re-search-forward
+ (re-search-forward
(concat "^" (regexp-quote mail-header-separator) "$"))
(forward-line 1)
(int-to-string (count-lines (point) (point-max))))))
(let ((from (mail-header-from message-reply-headers))
(date (mail-header-date message-reply-headers)))
(when from
- (let ((stop-pos
+ (let ((stop-pos
(string-match " *at \\| *@ \\| *(\\| *<" from)))
(concat (if stop-pos (substring from 0 stop-pos) from)
- "'s message of "
+ "'s message of "
(if (or (not date) (string= date ""))
"(unknown date)" date)))))))
(setcar current (+ (car current) (round (/ future (expt 2 16)))))
(setcar (cdr current) (+ (nth 1 current) (% (round future) (expt 2 16))))
;; Return the date in the future in UT.
- (timezone-make-date-arpa-standard
+ (timezone-make-date-arpa-standard
(current-time-string current) (current-time-zone current) '(0 "UT"))))
(defun message-make-path ()
(defun message-make-from ()
"Make a From header."
- (let* ((login (message-make-address))
- (fullname
+ (let* ((style message-from-style)
+ (login (message-make-address))
+ (fullname
(or (and (boundp 'user-full-name)
user-full-name)
(user-full-name))))
(setq fullname (user-login-name)))
(save-excursion
(message-set-work-buffer)
- (cond
- ((or (null message-from-style)
+ (cond
+ ((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))
;; ... then undo escaping of matching parentheses,
;; including matching nested parentheses.
(goto-char fullname-start)
- (while (re-search-forward
+ (while (re-search-forward
"\\(\\=\\|[^\\]\\(\\\\\\\\\\)*\\)\\\\(\\(\\([^\\]\\|\\\\\\\\\\)*\\)\\\\)"
nil 1)
(replace-match "\\1(\\3)" t)
(defun message-make-sender ()
"Return the \"real\" user address.
-This function tries to ignore all user modifications, and
+This function tries to ignore all user modifications, and
give as trustworthy answer as possible."
(concat (user-login-name) "@" (system-name)))
(defun message-user-mail-address ()
"Return the pertinent part of `user-mail-address'."
(when user-mail-address
- (nth 1 (mail-extract-address-components user-mail-address))))
+ (if (string-match " " user-mail-address)
+ (nth 1 (mail-extract-address-components user-mail-address))
+ user-mail-address)))
(defun message-make-fqdn ()
"Return user's fully qualified domain name."
(let ((system-name (system-name))
(user-mail (message-user-mail-address)))
- (cond
+ (cond
((string-match "[^.]\\.[^.]" system-name)
;; `system-name' returned the right result.
system-name)
(match-string 1 user-mail))
;; Default to this bogus thing.
(t
- (concat system-name ".i-have-a-misconfigured-system-so-shoot-me")))))
+ (concat system-name ".i-did-not-set--mail-host-address--so-shoot-me")))))
(defun message-make-host-name ()
"Return the name of the host."
(let ((headers message-deletable-headers))
(while headers
(goto-char (point-min))
- (and (re-search-forward
+ (and (re-search-forward
(concat "^" (symbol-name (car headers)) ": *") nil t)
(get-text-property (1+ (match-beginning 0)) 'message-deletable)
(message-delete-line))
;; Go through all the required headers and see if they are in the
;; articles already. If they are not, or are empty, they are
;; inserted automatically - except for Subject, Newsgroups and
- ;; Distribution.
+ ;; Distribution.
(while headers
(goto-char (point-min))
(setq elem (pop headers))
(setq header (cdr elem))
(setq header (car elem)))
(setq header elem))
- (when (or (not (re-search-forward
- (concat "^" (downcase (symbol-name header)) ":")
+ (when (or (not (re-search-forward
+ (concat "^" (downcase (symbol-name header)) ":")
nil t))
(progn
;; The header was found. We insert a space after the
(looking-at "[ \t]*$")))
;; So we find out what value we should insert.
(setq value
- (cond
+ (cond
((and (consp elem) (eq (car elem) 'optional))
;; This is an optional header. If the cdr of this
;; is something that is nil, then we do not insert
(read-from-minibuffer
(format "Empty header for %s; enter value: " header)))))
;; Finally insert the header.
- (when (and value
+ (when (and value
(not (equal value "")))
(save-excursion
(if (bolp)
(forward-line -1))
;; The value of this header was empty, so we clear
;; totally and insert the new value.
- (delete-region (point) (message-point-at-eol))
+ (delete-region (point) (gnus-point-at-eol))
(insert value))
;; Add the deletable property to the headers that require it.
(and (memq header message-deletable-headers)
(progn (beginning-of-line) (looking-at "[^:]+: "))
- (add-text-properties
+ (add-text-properties
(point) (match-end 0)
'(message-deletable t face italic) (current-buffer)))))))
- ;; Insert new Sender if the From is strange.
+ ;; Insert new Sender if the From is strange.
(let ((from (message-fetch-field "from"))
(sender (message-fetch-field "sender"))
(secure-sender (message-make-sender)))
- (when (and from
+ (when (and from
(not (message-check-element 'sender))
(not (string=
(downcase
(cadr (mail-extract-address-components from)))
(downcase secure-sender)))
(or (null sender)
- (not
+ (not
(string=
(downcase
(cadr (mail-extract-address-components sender)))
(downcase secure-sender)))))
- (goto-char (point-min))
+ (goto-char (point-min))
;; Rename any old Sender headers to Original-Sender.
(when (re-search-forward "^\\(Original-\\)*Sender:" nil t)
(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."
(insert (format message-courtesy-message newsgroups)))
(t
(insert message-courtesy-message)))))))
-
+
;;;
;;; Setting up a message buffer
;;;
(defun message-position-point ()
"Move point to where the user probably wants to find it."
(message-narrow-to-headers)
- (cond
+ (cond
((re-search-forward "^[^:]+:[ \t]*$" nil t)
(search-backward ":" )
(widen)
(defun message-buffer-name (type &optional to group)
"Return a new (unique) buffer name based on TYPE and TO."
(cond
- ;; Check whether `message-generate-new-buffers' is a function,
+ ;; Check whether `message-generate-new-buffers' is a function,
;; and if so, call it.
((message-functionp message-generate-new-buffers)
(funcall message-generate-new-buffers type to group))
;; list of buffers.
(setq message-buffer-list (delq (current-buffer) message-buffer-list))
(while (and message-max-buffers
+ message-buffer-list
(>= (length message-buffer-list) message-max-buffers))
;; Kill the oldest buffer -- unless it has been changed.
(let ((buffer (pop message-buffer-list)))
(if message-send-rename-function
(funcall message-send-rename-function)
(when (string-match "\\`\\*" (buffer-name))
- (rename-buffer
+ (rename-buffer
(concat "*sent " (substring (buffer-name) (match-end 0))) t)))
;; Push the current buffer onto the list.
(when message-max-buffers
- (setq message-buffer-list
+ (setq message-buffer-list
(nconc message-buffer-list (list (current-buffer))))))
(defvar mc-modes-alist)
(setq message-reply-buffer replybuffer)
(goto-char (point-min))
;; Insert all the headers.
- (mail-header-format
+ (mail-header-format
(let ((h headers)
(alist message-header-format-alist))
(while h
(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))
(unless (file-exists-p message-autosave-directory)
(make-directory message-autosave-directory t))
(let ((name (make-temp-name
- (concat (file-name-as-directory message-autosave-directory)
- "msg."))))
+ (expand-file-name
+ (concat (file-name-as-directory message-autosave-directory)
+ "msg.")))))
(setq buffer-auto-save-file-name
(save-excursion
(prog1
;;;
;;;###autoload
-(defun message-mail (&optional to subject)
+(defun message-mail (&optional to subject
+ other-headers continue switch-function
+ yank-action send-actions)
"Start editing a mail message to be sent."
(interactive)
- (message-pop-to-buffer (message-buffer-name "mail" to))
- (message-setup `((To . ,(or to "")) (Subject . ,(or subject "")))))
+ (let ((message-this-is-mail t))
+ (message-pop-to-buffer (message-buffer-name "mail" to))
+ (message-setup
+ (nconc
+ `((To . ,(or to "")) (Subject . ,(or subject "")))
+ (when other-headers other-headers)))))
;;;###autoload
(defun message-news (&optional newsgroups subject)
"Start editing a news article to be sent."
(interactive)
- (message-pop-to-buffer (message-buffer-name "news" nil newsgroups))
- (message-setup `((Newsgroups . ,(or newsgroups ""))
- (Subject . ,(or subject "")))))
+ (let ((message-this-is-news t))
+ (message-pop-to-buffer (message-buffer-name "news" nil newsgroups))
+ (message-setup `((Newsgroups . ,(or newsgroups ""))
+ (Subject . ,(or subject ""))))))
;;;###autoload
(defun message-reply (&optional to-address wide ignore-reply-to)
(interactive)
(let ((cur (current-buffer))
from subject date reply-to to cc
- references message-id follow-to
+ references message-id follow-to
(inhibit-point-motion-hooks t)
mct never-mct gnus-warning)
(save-restriction
(funcall message-wide-reply-to-function)))))
;; Find all relevant headers we need.
(setq from (message-fetch-field "from")
- date (message-fetch-field "date")
+ date (message-fetch-field "date")
subject (or (message-fetch-field "subject") "none")
to (message-fetch-field "to")
cc (message-fetch-field "cc")
mct (message-fetch-field "mail-copies-to")
reply-to (unless ignore-reply-to (message-fetch-field "reply-to"))
references (message-fetch-field "references")
- message-id (message-fetch-field "message-id"))
+ message-id (message-fetch-field "message-id" t))
;; Remove any (buggy) Re:'s that are present and make a
;; proper one.
(when (string-match "^[ \t]*[Rr][Ee]:[ \t]*" subject)
(when (and (setq gnus-warning (message-fetch-field "gnus-warning"))
(string-match "<[^>]+>" gnus-warning))
(setq message-id (match-string 0 gnus-warning)))
-
+
;; Handle special values of Mail-Copies-To.
(when mct
(cond ((equal (downcase mct) "never")
(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) ""))
- ;; Remove addresses that match `rmail-dont-reply-to-names'.
+ (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)))
(goto-char (point-min))
+ ;; Perhaps Mail-Copies-To: never removed the only address?
+ (when (eobp)
+ (insert (or reply-to from "")))
(setq ccalist
(mapcar
(lambda (addr)
(setq ccalist (delq (assoc (car (pop s)) s) ccalist)))))
(setq follow-to (list (cons 'To (cdr (pop ccalist)))))
(when ccalist
- (let ((ccs (cons 'Cc (mapconcat
+ (let ((ccs (cons 'Cc (mapconcat
(lambda (addr) (cdr addr)) ccalist ", "))))
(when (string-match "^ +" (cdr ccs))
(setcdr ccs (substring (cdr ccs) (match-end 0))))
(message-setup
`((Subject . ,subject)
- ,@follow-to
+ ,@follow-to
,@(if (or references message-id)
`((References . ,(concat (or references "") (and references " ")
(or message-id ""))))
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)
(interactive)
(let ((cur (current-buffer))
from subject date reply-to mct
- references message-id follow-to
+ references message-id follow-to
(inhibit-point-motion-hooks t)
- followup-to distribution newsgroups gnus-warning)
+ (message-this-is-news t)
+ followup-to distribution newsgroups gnus-warning posted-to)
(save-restriction
(narrow-to-region
(goto-char (point-min))
(setq follow-to
(funcall message-followup-to-function)))
(setq from (message-fetch-field "from")
- date (message-fetch-field "date")
+ date (message-fetch-field "date")
subject (or (message-fetch-field "subject") "none")
references (message-fetch-field "references")
- message-id (message-fetch-field "message-id")
+ message-id (message-fetch-field "message-id" t)
followup-to (message-fetch-field "followup-to")
newsgroups (message-fetch-field "newsgroups")
+ posted-to (message-fetch-field "posted-to")
reply-to (message-fetch-field "reply-to")
distribution (message-fetch-field "distribution")
mct (message-fetch-field "mail-copies-to"))
(string-match "<[^>]+>" gnus-warning))
(setq message-id (match-string 0 gnus-warning)))
;; Remove bogus distribution.
- (and (stringp distribution)
- (string-match "world" distribution)
- (setq distribution nil))
+ (when (and (stringp distribution)
+ (let ((case-fold-search t))
+ (string-match "world" distribution)))
+ (setq distribution nil))
;; Remove any (buggy) Re:'s that are present and make a
;; proper one.
(when (string-match "^[ \t]*[Rr][Ee]:[ \t]*" subject)
(message-setup
`((Subject . ,subject)
- ,@(cond
+ ,@(cond
(to-newsgroups
(list (cons 'Newsgroups to-newsgroups)))
(follow-to follow-to)
((and followup-to message-use-followup-to)
(list
- (cond
+ (cond
((equal (downcase followup-to) "poster")
(if (or (eq message-use-followup-to 'use)
(message-y-or-n-p "Obey Followup-To: poster? " t "\
A typical situation where `Followup-To: poster' is used is when the poster
does not read the newsgroup, so he wouldn't see any replies sent to it."))
- (cons 'To (or reply-to from ""))
+ (progn
+ (setq message-this-is-news nil)
+ (cons 'To (or reply-to from "")))
(cons 'Newsgroups newsgroups)))
(t
(if (or (equal followup-to newsgroups)
responses here are directed to other newsgroups."))
(cons 'Newsgroups followup-to)
(cons 'Newsgroups newsgroups))))))
+ (posted-to
+ `((Newsgroups . ,posted-to)))
(t
`((Newsgroups . ,newsgroups))))
,@(and distribution (list (cons 'Distribution distribution)))
(message-narrow-to-head)
(setq from (message-fetch-field "from")
newsgroups (message-fetch-field "newsgroups")
- message-id (message-fetch-field "message-id")
+ message-id (message-fetch-field "message-id" t)
distribution (message-fetch-field "distribution")))
;; Make sure that this article was written by the user.
(unless (string-equal
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
header line with the old Message-ID."
(interactive)
(let ((cur (current-buffer)))
- ;; Check whether the user owns the article that is to be superseded.
+ ;; Check whether the user owns the article that is to be superseded.
(unless (string-equal
(downcase (cadr (mail-extract-address-components
(message-fetch-field "from"))))
(defun message-make-forward-subject ()
"Return a Subject header suitable for the message in the current buffer."
- (concat "[" (or (message-fetch-field
- (if (message-news-p) "newsgroups" "from"))
- "(nowhere)")
- "] " (or (message-fetch-field "Subject") "")))
+ (save-excursion
+ (save-restriction
+ (current-buffer)
+ (message-narrow-to-head)
+ (concat "[" (or (message-fetch-field
+ (if (message-news-p) "newsgroups" "from"))
+ "(nowhere)")
+ "] " (or (message-fetch-field "Subject") "")))))
;;;###autoload
(defun message-forward (&optional news)
- "Forward the current message via mail.
+ "Forward the current message via mail.
Optional NEWS will use news to forward instead of mail."
(interactive "P")
(let ((cur (current-buffer))
art-beg)
(if news (message-news nil subject) (message-mail nil subject))
;; Put point where we want it before inserting the forwarded
- ;; message.
+ ;; message.
(if message-signature-before-forwarded-message
(goto-char (point-max))
(message-goto-body))
(while (re-search-backward "^\\(Also-\\)?Resent-" beg t)
(beginning-of-line)
(insert "Also-"))
+ ;; Quote any "From " lines at the beginning.
+ (goto-char beg)
+ (when (looking-at "From ")
+ (replace-match "X-From-Line: "))
;; Send it.
(message-send-mail)
(kill-buffer (current-buffer)))
(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
+ (delete-region
(point-min)
(if (re-search-forward "^[^ \n\t]+:" nil t)
(match-beginning 0)
(same-window-buffer-names nil)
(same-window-regexps nil))
(message-pop-to-buffer (message-buffer-name "news" nil newsgroups)))
- (message-setup `((Newsgroups . ,(or newsgroups ""))
+ (message-setup `((Newsgroups . ,(or newsgroups ""))
(Subject . ,(or subject "")))))
;;;###autoload
(same-window-buffer-names nil)
(same-window-regexps nil))
(message-pop-to-buffer (message-buffer-name "news" nil newsgroups)))
- (message-setup `((Newsgroups . ,(or newsgroups ""))
+ (message-setup `((Newsgroups . ,(or newsgroups ""))
(Subject . ,(or subject "")))))
;;; underline.el
-;; This code should be moved to underline.el (from which it is stolen).
+;; This code should be moved to underline.el (from which it is stolen).
;;;###autoload
(defun bold-region (start end)
(save-excursion
(let ((end1 (make-marker)))
(move-marker end1 (max start end))
- (goto-char (min start end))
+ (goto-char (min start end))
(while (re-search-forward "\b" end1 t)
(if (eq (following-char) (char-after (- (point) 2)))
(delete-char -2))))))
;;; Group name completion.
(defvar message-newgroups-header-regexp
- "^\\(Newsgroups\\|Followup-To\\|Posted-To\\):"
+ "^\\(Newsgroups\\|Followup-To\\|Posted-To\\|Gcc\\):"
"Regexp that match headers that lists groups.")
(defun message-tab ()
(defvar gnus-active-hashtb)
(defun message-expand-group ()
- (let* ((b (save-excursion (skip-chars-backward "^, :\t\n") (point)))
+ (let* ((b (save-excursion
+ (save-restriction
+ (narrow-to-region
+ (save-excursion
+ (beginning-of-line)
+ (skip-chars-forward "^:")
+ (1+ (point)))
+ (point))
+ (skip-chars-backward "^, \t\n") (point))))
(completion-ignore-case t)
(string (buffer-substring b (point)))
(hashtb (and (boundp 'gnus-active-hashtb) gnus-active-hashtb))
(cur (current-buffer))
comp)
(delete-region b (point))
- (cond
+ (cond
((= (length completions) 1)
(if (string= (car completions) string)
(progn
;;; Help stuff.
(defun message-talkative-question (ask question show &rest text)
- "Call FUNCTION with argument QUESTION, displaying the rest of the arguments in a temporary buffer if SHOW.
+ "Call FUNCTION with argument QUESTION, displaying the rest of the arguments in a temporary buffer if SHOW.
The following arguments may contain lists of values."
(if (and show
(setq text (message-flatten-list text)))
(funcall ask question))
(funcall ask question)))
-(defun message-flatten-list (&rest list)
- (message-flatten-list-1 list))
+(defun message-flatten-list (list)
+ "Return a new, flat list that contains all elements of LIST.
-(defun message-flatten-list-1 (list)
- (cond ((consp list)
- (apply 'append (mapcar 'message-flatten-list-1 list)))
+\(message-flatten-list '(1 (2 3 (4 5 (6))) 7))
+=> (1 2 3 4 5 6 7)"
+ (cond ((consp list)
+ (apply 'append (mapcar 'message-flatten-list list)))
(list
(list list))))