;;; sendmail.el --- mail sending commands for Emacs.
-;; Copyright (C) 1985, 86, 92, 93, 94, 95, 96 Free Software Foundation, Inc.
+;; Copyright (C) 1985, 86, 92, 93, 94, 95, 96, 1998 Free Software Foundation, Inc.
;; Maintainer: FSF
;; Keywords: mail
If `parens', they look like:
king@grassland.com (Elvis Parsley)
If `angles', they look like:
- Elvis Parsley <king@grassland.com>"
- :type '(choice (const nil) (const parens) (const angles))
+ Elvis Parsley <king@grassland.com>
+If `system-default', allows the mailer to insert its default From field
+derived from the envelope-from address.
+
+In old versions of Emacs, the `system-default' setting also caused
+Emacs to pass the proper email address from `user-mail-address'
+to the mailer to specify the envelope-from address. But that is now
+controlled by a separate variable, `mail-specify-envelope-from'."
+ :type '(choice (const nil) (const parens) (const angles)
+ (const system-default))
+ :version "20.3"
+ :group 'sendmail)
+
+;;;###autoload
+(defcustom mail-specify-envelope-from t
+ "*If non-nil, specify the envelope-from address when sending mail.
+The value used to specify it is whatever is found in `user-mail-address'.
+
+On most systems, specifying the envelope-from address
+is a privileged operation."
+ :version "21.1"
+ :type 'boolean
:group 'sendmail)
;;;###autoload
;;;###autoload
(defvar send-mail-function 'sendmail-send-it "\
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'.")
+The headers should be delimited by a line which is
+not a valid RFC822 header or continuation line.")
;;;###autoload
(defcustom mail-header-separator "--text follows this line--" "\
(defcustom mail-citation-hook nil
"*Hook for modifying a citation just inserted in the mail buffer.
-Each hook function can find the citation between (point) and (mark t).
-And each hook function should leave point and mark around the citation
-text as modified.
+Each hook function can find the citation between (point) and (mark t),
+and should leave point and mark around the citation text as modified.
+The hook functions can find the header of the cited message
+in the variable `mail-citation-header', whether or not this is included
+in the cited portion of the message.
If this hook is entirely empty (nil), a default action is taken
instead of no action."
:type 'hook
:group 'sendmail)
+(defvar mail-citation-header nil
+ "While running `mail-citation-hook', this variable holds the message header.
+This enables the hook functions to see the whole message header
+regardless of what part of it (if any) is included in the cited text.")
+
+(defcustom mail-citation-prefix-regexp "[ \t]*[-a-z0-9A-Z]*>+[ \t]*\\|[ \t]*"
+ "*Regular expression to match a citation prefix plus whitespace.
+It should match whatever sort of citation prefixes you want to handle,
+with whitespace before and after; it should also match just whitespace.
+The default value matches citations like `foo-bar>' plus whitespace."
+ :type 'regexp
+ :group 'sendmail
+ :version "20.3")
+
(defvar mail-abbrevs-loaded nil)
(defvar mail-mode-map nil)
;;;###autoload
(defcustom mail-signature nil
"*Text inserted at end of mail buffer when a message is initialized.
-If t, it means to insert the contents of the file `mail-signature-file'."
- :type '(choice (const nil) (const t) string)
+If t, it means to insert the contents of the file `mail-signature-file'.
+If a string, that string is inserted.
+ (To make a proper signature, the string should begin with \\n\\n-- \\n,
+ which is the standard way to delimit a signature in a message.)
+Otherwise, it should be an expression; it is evaluated
+and should insert whatever you want to insert."
+ :type '(choice (const "None" nil)
+ (const :tag "Use `.signature' file" t)
+ (string :tag "String to insert")
+ (sexp :tag "Expression to evaluate"))
:group 'sendmail)
+(put 'mail-signature 'risky-local-variable t)
(defcustom mail-signature-file "~/.signature"
"*File containing the text inserted at end of mail buffer."
(let ((fill-prefix "\t")
(address-start (point)))
(insert to "\n")
- (fill-region-as-paragraph address-start (point-max)))
+ (fill-region-as-paragraph address-start (point-max))
+ (goto-char (point-max))
+ (unless (bolp)
+ (newline)))
(newline))
(if cc
(let ((fill-prefix "\t")
(address-start (progn (insert "CC: ") (point))))
(insert cc "\n")
- (fill-region-as-paragraph address-start (point-max))))
+ (fill-region-as-paragraph address-start (point-max))
+ (goto-char (point-max))
+ (unless (bolp)
+ (newline))))
(if in-reply-to
- (let ((fill-prefix "\t")
+ (let ((fill-prefix "\t")
(fill-column 78)
(address-start (point)))
(insert "In-reply-to: " in-reply-to "\n")
- (fill-region-as-paragraph address-start (point-max))))
+ (fill-region-as-paragraph address-start (point-max))
+ (goto-char (point-max))
+ (unless (bolp)
+ (newline))))
(insert "Subject: " (or subject "") "\n")
(if mail-default-headers
(insert mail-default-headers))
(progn
(insert "\n\n-- \n")
(insert-file-contents mail-signature-file))))
- (mail-signature
- (insert mail-signature)))
+ ((stringp mail-signature)
+ (insert mail-signature))
+ (t
+ (eval mail-signature)))
(goto-char (point-max))
(or (bolp) (newline)))
(if to (goto-char to))
(setq fill-paragraph-function 'mail-mode-fill-paragraph)
(make-local-variable 'adaptive-fill-regexp)
(setq adaptive-fill-regexp
- (concat "[ \t]*[-a-z0-9A-Z]*>+[ \t]*\\|" adaptive-fill-regexp))
+ (concat "[ \t]*\\([-|#;>*]+ *\\|(?[0-9]+[.)] *\\)+"
+ "\\|[ \t]*[-a-z0-9A-Z]*>+[ \t]*"
+ "\\|[ \t]*"))
(make-local-variable 'adaptive-fill-first-line-regexp)
(setq adaptive-fill-first-line-regexp
- (concat "[ \t]*[-a-z0-9A-Z]*>+[ \t]*\\|" adaptive-fill-first-line-regexp))
+ (concat adaptive-fill-first-line-regexp
+ "\\|[ \t]*[-a-z0-9A-Z]*>+[ \t]*"))
;; `-- ' precedes the signature. `-----' appears at the start of the
;; lines that delimit forwarded messages.
;; Lines containing just >= 3 dashes, perhaps after whitespace,
;; are also sometimes used and should be separators.
(setq paragraph-start (concat (regexp-quote mail-header-separator)
- "$\\|[ \t]*[a-z0-9A-Z]*>+[ \t]*$\\|[ \t]*$\\|"
+ "$\\|\t*\\([-|#;>* ]\\|(?[0-9]+[.)]\\)+$"
+ "\\|[ \t]*[a-z0-9A-Z]*>+[ \t]*$\\|[ \t]*$\\|"
+ "-- $\\|---+$\\|"
page-delimiter))
(setq paragraph-separate paragraph-start)
(run-hooks 'text-mode-hook 'mail-mode-hook))
+
+(defun mail-header-end ()
+ "Return the buffer location of the end of headers, as a number."
+ (save-restriction
+ (widen)
+ (save-excursion
+ (rfc822-goto-eoh)
+ (point))))
+
+(defun mail-text-start ()
+ "Return the buffer location of the start of text, as a number."
+ (save-restriction
+ (widen)
+ (save-excursion
+ (rfc822-goto-eoh)
+ (forward-line 1)
+ (point))))
+
+(defun mail-sendmail-delimit-header ()
+ "Set up whatever header delimiter convention sendmail will use.
+Concretely: replace the first blank line in the header with the separator."
+ (rfc822-goto-eoh)
+ (insert mail-header-separator)
+ (point))
+
+(defun mail-sendmail-undelimit-header ()
+ "Remove header separator to put the message in correct form for sendmail.
+Leave point at the start of the delimiter line."
+ (rfc822-goto-eoh)
+ (delete-region (point) (progn (end-of-line) (point))))
+
(defun mail-mode-auto-fill ()
"Carry out Auto Fill for Mail mode.
If within the headers, this makes the new lines into continuation lines."
- (if (< (point)
- (save-excursion
- (goto-char (point-min))
- (if (re-search-forward
- (concat "^" (regexp-quote mail-header-separator) "$")
- nil t)
- (point)
- 0)))
+ (if (< (point) (mail-header-end))
(let ((old-line-start (save-excursion (beginning-of-line) (point))))
(if (do-auto-fill)
(save-excursion
(defun mail-mode-fill-paragraph (arg)
;; Do something special only if within the headers.
- (if (< (point)
- (save-excursion
- (goto-char (point-min))
- (if (re-search-forward
- (concat "^" (regexp-quote mail-header-separator) "$")
- nil t)
- (point)
- 0)))
+ (if (< (point) (mail-header-end))
(let (beg end fieldname)
(re-search-backward "^[-a-zA-Z]+:" nil 'yes)
(setq beg (point))
(error "Message contains non-ASCII characters"))))
;; Complain about any invalid line.
(goto-char (point-min))
- (while (not (looking-at (regexp-quote mail-header-separator)))
+ (while (< (point) (mail-header-end))
(unless (looking-at "[ \t]\\|.*:\\|$")
(push-mark opoint)
(error "Invalid header line (maybe a continuation line lacks initial whitespace)"))
(error))
(setq mail-send-actions (cdr mail-send-actions)))
(message "Sending...done")
- ;; If buffer has no file, mark it as unmodified and delete autosave.
+ ;; If buffer has no file, mark it as unmodified and delete auto-save.
(if (not buffer-file-name)
(progn
(set-buffer-modified-p nil)
;;;###autoload
(defvar sendmail-coding-system nil
- "Coding system to encode the outgoing mail.")
+ "*Coding system for encoding the outgoing mail.
+This has higher priority than `default-buffer-file-coding-system'
+and `default-sendmail-coding-system',
+but lower priority than the local value of `buffer-file-coding-system'.
+See also the function `select-message-coding-system'.")
+
+;;;###autoload
+(defvar default-sendmail-coding-system 'iso-latin-1
+ "Default coding system for encoding the outgoing mail.
+This variable is used only when `sendmail-coding-system' is nil.
+
+This variable is set/changed by the command set-language-environment.
+User should not set this variable manually,
+instead use sendmail-coding-system to get a constant encoding
+of outgoing mails regardless of the current language environment.
+See also the function `select-message-coding-system'.")
(defun sendmail-send-it ()
(require 'mail-utils)
resend-to-addresses
delimline
fcc-was-found
- (mailbuf (current-buffer))
- (sendmail-coding-system
- (if (local-variable-p 'buffer-file-coding-system)
- buffer-file-coding-system
- (or sendmail-coding-system
- default-buffer-file-coding-system
- 'iso-latin-1))))
- (if (fboundp select-safe-coding-system-function)
- (setq sendmail-coding-system
- (funcall select-safe-coding-system-function
- (point-min) (point-max) sendmail-coding-system)))
+ (mailbuf (current-buffer)))
(unwind-protect
(save-excursion
(set-buffer tembuf)
(or (= (preceding-char) ?\n)
(insert ?\n))
;; Change header-delimiter to be what sendmail expects.
- (goto-char (point-min))
- (re-search-forward
- (concat "^" (regexp-quote mail-header-separator) "\n"))
- (replace-match "\n")
- (backward-char 1)
+ (goto-char (mail-header-end))
+ (delete-region (point) (progn (end-of-line) (point)))
(setq delimline (point-marker))
(sendmail-sync-aliases)
(if mail-aliases
(goto-char fullname-start))))
(insert ")\n"))
((null mail-from-style)
- (insert "From: " login "\n")))))
+ (insert "From: " login "\n"))
+ ((eq mail-from-style 'system-default)
+ nil)
+ (t (error "Invalid value for `mail-from-style'")))))
;; Insert an extra newline if we need it to work around
;; Sun's bug that swallows newlines.
(goto-char (1+ delimline))
(re-search-forward "^To:\\|^cc:\\|^bcc:\\|^resent-to:\
\\|^resent-cc:\\|^resent-bcc:"
delimline t))
- (let ((default-directory "/")
- (coding-system-for-write sendmail-coding-system))
- (apply 'call-process-region
- (append (list (point-min) (point-max)
- (if (boundp 'sendmail-program)
- sendmail-program
- "/usr/lib/sendmail")
- nil errbuf nil "-oi")
- ;; Always specify who from,
- ;; since some systems have broken sendmails.
- (list "-f" (user-login-name))
- ;;; ;; Don't say "from root" if running under su.
- ;;; (and (equal (user-real-login-name) "root")
- ;;; (list "-f" (user-login-name)))
- (and mail-alias-file
- (list (concat "-oA" mail-alias-file)))
- (if mail-interactive
- ;; These mean "report errors to terminal"
- ;; and "deliver interactively"
- '("-oep" "-odi")
- ;; These mean "report errors by mail"
- ;; and "deliver in background".
- '("-oem" "-odb"))
- ;; Get the addresses from the message
- ;; unless this is a resend.
- ;; We must not do that for a resend
- ;; because we would find the original addresses.
- ;; For a resend, include the specific addresses.
- (or resend-to-addresses
- '("-t")))))
+ (let* ((default-directory "/")
+ (coding-system-for-write (select-message-coding-system))
+ (args
+ (append (list (point-min) (point-max)
+ (if (boundp 'sendmail-program)
+ sendmail-program
+ "/usr/lib/sendmail")
+ nil errbuf nil "-oi")
+ (and mail-specify-envelope-from
+ (list "-f" user-mail-address))
+;;; ;; Don't say "from root" if running under su.
+;;; (and (equal (user-real-login-name) "root")
+;;; (list "-f" (user-login-name)))
+ (and mail-alias-file
+ (list (concat "-oA" mail-alias-file)))
+ (if mail-interactive
+ ;; These mean "report errors to terminal"
+ ;; and "deliver interactively"
+ '("-oep" "-odi")
+ ;; These mean "report errors by mail"
+ ;; and "deliver in background".
+ '("-oem" "-odb"))
+ ;; Get the addresses from the message
+ ;; unless this is a resend.
+ ;; We must not do that for a resend
+ ;; because we would find the original addresses.
+ ;; For a resend, include the specific addresses.
+ (or resend-to-addresses
+ '("-t"))))
+ (exit-value (apply 'call-process-region args)))
+ (or (null exit-value) (zerop exit-value)
+ (error "Sending...failed with exit value %d" exit-value)))
(or fcc-was-found
(error "No recipients")))
(if mail-interactive
"Make a Sent-via header line from each To or CC header line."
(interactive)
(save-excursion
- (goto-char (point-min))
- ;; find the header-separator
- (search-forward (concat "\n" mail-header-separator "\n"))
- (forward-line -1)
;; put a marker at the end of the header
- (let ((end (point-marker))
+ (let ((end (copy-marker (mail-header-end)))
(case-fold-search t)
to-line)
(goto-char (point-min))
(defun mail-position-on-field (field &optional soft)
(let (end
(case-fold-search t))
- (goto-char (point-min))
- (re-search-forward (concat "^" (regexp-quote mail-header-separator) "$"))
- (setq end (match-beginning 0))
+ (setq end (mail-header-end))
(goto-char (point-min))
(if (re-search-forward (concat "^" (regexp-quote field) ":") end t)
(progn
"Move point to beginning of message text."
(interactive)
(expand-abbrev)
- (goto-char (point-min))
- (search-forward (concat "\n" mail-header-separator "\n")))
+ (goto-char (mail-text-start)))
\f
(defun mail-signature (atpoint)
"Sign letter with contents of the file `mail-signature-file'.
Numeric argument means justify as well."
(interactive "P")
(save-excursion
- (goto-char (point-min))
- (search-forward (concat "\n" mail-header-separator "\n") nil t)
+ (goto-char (mail-text-start))
(fill-individual-paragraphs (point)
(point-max)
justifyp
- t)))
+ mail-citation-prefix-regexp)))
(defun mail-indent-citation ()
"Modify text just inserted from a message to be cited.
;; delete that window to save screen space.
;; t means don't alter other frames.
(delete-windows-on original t)
- (insert-buffer original))
+ (insert-buffer original)
+ (set-text-properties (point) (mark t) nil))
(if (consp arg)
nil
(goto-char start)
(let ((mail-indentation-spaces (if arg (prefix-numeric-value arg)
mail-indentation-spaces))
+ ;; Avoid error in Transient Mark mode
+ ;; on account of mark's being inactive.
(mark-even-if-inactive t))
(if mail-citation-hook
- (run-hooks 'mail-citation-hook)
+ ;; Bind mail-citation-hook to the inserted message's header.
+ (let ((mail-citation-header
+ (buffer-substring-no-properties
+ start
+ (save-excursion
+ (save-restriction
+ (narrow-to-region start (point-max))
+ (goto-char start)
+ (rfc822-goto-eoh)
+ (point))))))
+ (run-hooks 'mail-citation-hook))
(if mail-yank-hooks
(run-hooks 'mail-yank-hooks)
(mail-indent-citation)))))
(interactive "P")
(and (consp mail-reply-action)
(eq (car mail-reply-action) 'insert-buffer)
+ (with-current-buffer (nth 1 mail-reply-action)
+ (or (mark t)
+ (error "No mark set: %S" (current-buffer))))
(let ((buffer (nth 1 mail-reply-action))
- (start (point)))
+ (start (point))
+ ;; Avoid error in Transient Mark mode
+ ;; on account of mark's being inactive.
+ (mark-even-if-inactive t))
;; Insert the citation text.
(insert (with-current-buffer buffer
- (buffer-substring (point) (mark))))
+ (buffer-substring-no-properties (point) (mark))))
(push-mark start)
;; Indent or otherwise annotate the citation text.
(if (consp arg)
(let ((mail-indentation-spaces (if arg (prefix-numeric-value arg)
mail-indentation-spaces)))
(if mail-citation-hook
- (run-hooks 'mail-citation-hook)
+ ;; Bind mail-citation-hook to the original message's header.
+ (let ((mail-citation-header
+ (with-current-buffer buffer
+ (buffer-substring-no-properties
+ (point-min)
+ (save-excursion
+ (goto-char (point-min))
+ (rfc822-goto-eoh)
+ (point))))))
+ (run-hooks 'mail-citation-hook))
(if mail-yank-hooks
(run-hooks 'mail-yank-hooks)
(mail-indent-citation))))))))
;; to avoid any danger that it can't be written.
(if (file-exists-p (expand-file-name "~/"))
(setq default-directory (expand-file-name "~/")))
- (auto-save-mode auto-save-default)
+ ;; Only call auto-save-mode if necessary, to avoid changing auto-save file.
+ (if (or (and auto-save-default (not buffer-auto-save-file-name))
+ (and (not auto-save-default) buffer-auto-save-file-name))
+ (auto-save-mode auto-save-default))
(mail-mode)
;; Disconnect the buffer from its visited file
;; (in case the user has actually visited a file *mail*).