;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
;; MORIOKA Tomohiko <morioka@jaist.ac.jp>
;; Shuhei KOBAYASHI <shuhei-k@jaist.ac.jp>
-;; Keiichi Suzuki <kei-suzu@mail.wbs.ne.jp>
+;; Keiichi Suzuki <keiichi@nanap.org>
+;; Tatsuya Ichikawa <t-ichi@po.shiojiri.ne.jp>
+;; Katsumi Yamaoka <yamaoka@jpl.org>
+;; Kiyokazu SUTO <suto@merry.xmath.ous.ac.jp>
;; Keywords: mail, news, MIME
;; This file is part of GNU Emacs.
(function :tag "Other"))
:group 'message-sending)
-(defcustom message-encode-function 'message-maybe-encode
+(autoload 'message-locale-maybe-encode "mess-lcl")
+(defcustom message-encode-function 'message-locale-maybe-encode
"*A function called to encode messages."
- :group 'message-sending
- :type 'function)
+ :type '(radio (function-item message-locale-maybe-encode)
+ (function-item message-maybe-encode)
+ (function :tag "Other"))
+ :group 'message-sending)
-(defcustom message-default-encoding "7bit"
- "*Default content transfer encoding type."
+(defcustom message-8bit-encoding-list '(8bit binary)
+ "*8bit encoding type in Content-Transfer-Encoding field."
:group 'message-sending
- :type 'string)
+ :type '(repeat (symbol :tag "Type")))
(defcustom message-courtesy-message
"The following message is a courtesy copy of an article\nthat has been posted to %s as well.\n\n"
:group 'message-interface
:type 'regexp)
+(defcustom message-bounce-setup-function 'message-bounce-setup-for-mime-edit
+ "Function to setup a re-sending bounced message."
+ :group 'message-sending
+ :type 'function)
+
;;;###autoload
(defcustom message-from-style 'default
"*Specifies how \"From\" headers look.
:group 'message-interface
:type 'regexp)
+(defcustom message-supersede-setup-function
+ 'message-supersede-setup-for-mime-edit
+ "Function to setup a supersede message."
+ :group 'message-sending
+ :type 'function)
+
(defcustom message-subject-re-regexp "^[ \t]*\\([Rr][Ee]:[ \t]*\\)*[ \t]*"
"*Regexp matching \"Re: \" in the subject line."
:group 'message-various
:group 'message-forwarding
:type 'boolean)
-(defcustom message-ignored-resent-headers "^Return-receipt"
+(defcustom message-ignored-resent-headers "^Return-Receipt"
"*All headers that match this regexp will be deleted when resending a message."
:group 'message-interface
:type 'regexp)
:group 'message-headers
:type 'boolean)
-(defcustom message-setup-hook '(turn-on-mime-edit)
+(defcustom message-setup-hook '(message-mime-setup)
"Normal hook, run each time a new outgoing message is initialized.
The function `message-setup' runs this hook."
:group 'message-various
:group 'message-various
:type 'hook)
+(defcustom message-bounce-setup-hook nil
+ "Normal hook, run each time a a re-sending bounced message is initialized.
+The function `message-bounce' runs this hook."
+ :group 'message-various
+ :type 'hook)
+
+(defcustom message-supersede-setup-hook nil
+ "Normal hook, run each time a supersede message is initialized.
+The function `message-supersede' runs this hook."
+ :group 'message-various
+ :type 'hook)
+
(defcustom message-mode-hook nil
"Hook run in message mode buffers."
:group 'message-various
(defcustom message-header-hook nil
"Hook run in a message mode before header encode. Buffer narrowed
to the headers."
- :group 'message-various
- :type 'hook)
+ :group 'message-various
+ :type 'hook)
(defcustom message-header-encode-function
- 'eword-encode-header
+ '(lambda () (eword-encode-header t))
"A function called to encode header."
:group 'message-various
:type 'function)
:type 'string
:group 'message-insertion)
+(defcustom message-yank-add-new-references t
+ "*Non-nil means new IDs will be added to \"References\" field when an
+article is yanked by the command `message-yank-original' interactively."
+ :type '(radio (const :tag "Do not add anything" nil)
+ (const :tag "From Message-Id, References and In-Reply-To fields" t)
+ (const :tag "From only Message-Id field." message-id-only))
+ :group 'message-insertion)
+
+(defcustom message-list-references-add-position nil
+ "*Integer value means position for adding to \"References\" field when
+an article is yanked by the command `message-yank-original' interactively."
+ :type '(radio (const :tag "Add to last" nil)
+ (integer :tag "Position from last ID"))
+ :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'."
(defvar message-reply-buffer nil)
(defvar message-reply-headers nil)
-(defvar message-user-agent nil) ; XXX: This symbol is overloaded! See below.
(defvar message-sent-message-via nil)
(defvar message-checksum nil)
(defvar message-send-actions nil
:group 'message-news
:type 'message-header-lines)
+(defcustom message-mail-follow-up-address-checker nil
+ "A function of check follow up mail address."
+ :group 'message-mail
+ :type 'function)
+
;; Note: could use /usr/ucb/mail instead of sendmail;
;; options -t, and -v if not interactive.
(defcustom message-mailer-swallows-blank-line
The default is `abbrev', which uses mailabbrev. nil switches
mail aliases off.")
-(defcustom message-autosave-directory
+(defcustom message-auto-save-directory
(nnheader-concat message-directory "drafts/")
- "*Directory where Message autosaves buffers if Gnus isn't running.
-If nil, Message won't autosave."
+ "*Directory where Message auto-saves buffers if Gnus isn't running.
+If nil, Message won't auto-save."
:group 'message-buffers
:type 'directory)
(const :tag "always" t)
(const :tag "ask" ask)))
-(defvar message-send-coding-system 'binary
- "Coding system to encode outgoing mail.")
-
;;; Internal variables.
(defvar message-buffer-list nil)
;;;
;;; Utility functions.
;;;
+(defun message-eval-parameter (parameter)
+ (condition-case ()
+ (if (symbolp parameter)
+ (if (functionp parameter)
+ (funcall parameter)
+ (eval parameter))
+ parameter)
+ (error nil)))
+
+(defsubst message-get-parameter (key &optional alist)
+ (unless alist
+ (setq alist message-parameter-alist))
+ (cdr (assq key alist)))
+
+(defmacro message-get-parameter-with-eval (key &optional alist)
+ `(message-eval-parameter (message-get-parameter ,key ,alist)))
(defmacro message-y-or-n-p (question show &rest text)
"Ask QUESTION, displaying the rest of the arguments in a temp. buffer if SHOW"
(not paren))))
(push (buffer-substring beg (point)) elems)
(setq beg (match-end 0)))
- ((= (following-char) ?\")
+ ((eq (char-after) ?\")
(setq quoted (not quoted)))
- ((and (= (following-char) ?\()
+ ((and (eq (char-after) ?\()
(not quoted))
(setq paren t))
- ((and (= (following-char) ?\))
+ ((and (eq (char-after) ?\))
(not quoted))
(setq paren nil))))
(nreverse elems)))))
(defun message-fetch-reply-field (header)
"Fetch FIELD from the message we're replying to."
- (let ((buffer (message-get-reply-buffer)))
+ (let ((buffer (message-eval-parameter message-reply-buffer)))
(when (and buffer
(buffer-name buffer))
(save-excursion
(- max rank)
(1+ max)))))
(message-sort-headers-1))))
-
-(defun message-eval-parameter (parameter)
- (condition-case ()
- (if (symbolp parameter)
- (if (functionp parameter)
- (funcall parameter)
- (eval parameter))
- parameter)
- (error nil)))
-
-(defun message-get-reply-buffer ()
- (message-eval-parameter message-reply-buffer))
-
-(defun message-get-original-reply-buffer ()
- (message-eval-parameter
- (cdr (assq 'original-buffer message-parameter-alist))))
\f
;;;
(define-key message-mode-map "\t" 'message-tab)
- (define-key message-mode-map "\C-xk" 'message-kill-buffer))
+ (define-key message-mode-map "\C-xk" 'message-mimic-kill-buffer))
(easy-menu-define
message-mode-menu message-mode-map "Message Menu."
"Move point to the end of the headers."
(interactive)
(message-goto-body)
- (forward-line -2))
+ (forward-line -1))
(defun message-goto-signature ()
"Move point to the beginning of the message signature.
(forward-line 1))))
(goto-char start)))
+(defun message-list-references (refs-list &rest refs-strs)
+ "Add `Message-ID's which appear in REFS-STRS but not in REFS-LIST,
+to REFS-LIST."
+ (let (refs ref id saved-id)
+ (when (and refs-list
+ (integerp message-list-references-add-position))
+ (let ((pos message-list-references-add-position))
+ (while (and refs-list
+ (> pos 0))
+ (setq saved-id (cons (car refs-list) saved-id)
+ refs-list (cdr refs-list)
+ pos (1- pos)))))
+ (while refs-strs
+ (setq refs (car refs-strs)
+ refs-strs (cdr refs-strs))
+ (when refs
+ (setq refs (std11-parse-msg-ids (std11-lexical-analyze refs)))
+ (while refs
+ (setq ref (car refs)
+ refs (cdr refs))
+ (when (eq (car ref) 'msg-id)
+ (setq id (concat "<"
+ (mapconcat
+ (function (lambda (p) (cdr p)))
+ (cdr ref) "")
+ ">"))
+ (or (member id refs-list)
+ (push id refs-list))))))
+ (while saved-id
+ (setq refs-list (cons (car saved-id) refs-list)
+ saved-id (cdr saved-id)))
+ refs-list))
+
(defvar gnus-article-copy)
(defun message-yank-original (&optional arg)
"Insert the message being replied to, if any.
This function uses `message-cite-function' to do the actual citing.
Just \\[universal-argument] as argument means don't indent, insert no
-prefix, and don't delete any headers."
+prefix, and don't delete any headers.
+
+In addition, if `message-yank-add-new-references' is non-nil and this
+command is called interactively, new IDs from the yanked article will
+be added to \"References\" field.
+\(See also `message-yank-add-new-references'.)"
(interactive "P")
(let ((modified (buffer-modified-p))
- (buffer (message-get-reply-buffer)))
+ (buffer (message-eval-parameter message-reply-buffer))
+ start end refs)
(when (and buffer
message-cite-function)
(delete-windows-on buffer t)
- (insert-buffer buffer)
+ (insert-buffer buffer) ; mark will be set at the end of article.
+ (setq start (point)
+ end (mark t))
+
+ ;; Add new IDs to References field.
+ (when (and message-yank-add-new-references (interactive-p))
+ (save-excursion
+ (save-restriction
+ (message-narrow-to-headers)
+ (setq refs (message-list-references
+ nil
+ (message-fetch-field "References")))
+ (widen)
+ (narrow-to-region start end)
+ (std11-narrow-to-header)
+ (when (setq refs (message-list-references
+ refs
+ (unless (eq message-yank-add-new-references
+ 'message-id-only)
+ (or (message-fetch-field "References")
+ (message-fetch-field "In-Reply-To")))
+ (message-fetch-field "Message-ID")))
+ (widen)
+ (message-narrow-to-headers)
+ (goto-char (point-min))
+ (let ((case-fold-search t))
+ (if (re-search-forward "^References:\\([\t ]+.+\n\\)+" nil t)
+ (replace-match "")
+ (goto-char (point-max))))
+ (mail-header-format
+ (list (or (assq 'References message-header-format-alist)
+ '(References . message-fill-references)))
+ (list (cons 'References
+ (mapconcat 'identity (nreverse refs) " "))))
+ (backward-delete-char 1)))))
+
(funcall message-cite-function)
(message-exchange-point-and-mark)
(unless (bolp)
(interactive)
(set-buffer-modified-p t)
(save-buffer)
- (let ((actions message-postpone-actions))
+ (let ((actions message-postpone-actions)
+ (frame (selected-frame))
+ (org-frame message-original-frame))
(message-bury (current-buffer))
- (message-do-actions actions)))
+ (message-do-actions actions)
+ (message-delete-frame frame org-frame)))
(defun message-kill-buffer ()
"Kill the current buffer."
(message-delete-frame frame org-frame)))
(message ""))
+(defun message-mimic-kill-buffer ()
+ "Kill the current buffer with query."
+ (interactive)
+ (unless (eq 'message-mode major-mode)
+ (error "%s must be invoked from a message buffer." this-command))
+ (let ((command this-command)
+ (bufname (read-buffer (format "Kill buffer: (default %s) "
+ (buffer-name)))))
+ (if (or (not bufname)
+ (string-equal bufname "")
+ (string-equal bufname (buffer-name)))
+ (let ((message-delete-frame-on-exit nil))
+ (message-kill-buffer))
+ (message "%s must be invoked only for the current buffer." command))))
+
(defun message-delete-frame (frame org-frame)
"Delete frame for editing message."
(when (and (or (and (featurep 'xemacs)
Otherwise any failure is reported in a message back to
the user from the mailer."
(interactive "P")
- ;; Disabled test.
- (when (or (buffer-modified-p)
- (message-check-element 'unchanged)
- (y-or-n-p "No changes in the buffer; really send? "))
- ;; Make it possible to undo the coming changes.
- (undo-boundary)
- (let ((inhibit-read-only t))
- (put-text-property (point-min) (point-max) 'read-only nil))
- (run-hooks 'message-send-hook)
- (message "Sending...")
- (let ((message-encoding-buffer
- (message-generate-new-buffer-clone-locals " message encoding"))
- (message-edit-buffer (current-buffer))
- (message-mime-mode mime-edit-mode-flag)
- (alist message-send-method-alist)
- (success t)
- elem sent)
- (save-excursion
- (set-buffer message-encoding-buffer)
- (erase-buffer)
- (insert-buffer message-edit-buffer)
- (funcall message-encode-function)
- (message-fix-before-sending)
- (while (and success
- (setq elem (pop alist)))
- (when (and (or (not (funcall (cadr elem)))
- (and (or (not (memq (car elem)
- message-sent-message-via))
- (y-or-n-p
- (format
- "Already sent message via %s; resend? "
- (car elem))))
- (setq success (funcall (caddr elem) arg)))))
- (setq sent t))))
- (when (and success 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")
- ;; Mark the buffer as unmodified and delete autosave.
- (set-buffer-modified-p nil)
- (delete-auto-save-file-if-necessary t)
- (message-disassociate-draft)
- ;; Delete other mail buffers and stuff.
- (message-do-send-housekeeping)
- (message-do-actions message-send-actions)
- ;; Return success.
- t))))
+ (if (catch 'message-sending-cancel
+ ;; Disabled test.
+ (unless (or (buffer-modified-p)
+ (message-check-element 'unchanged)
+ (y-or-n-p "No changes in the buffer; really send? "))
+ (throw 'message-sending-cancel t))
+ ;; Make it possible to undo the coming changes.
+ (undo-boundary)
+ (let ((inhibit-read-only t))
+ (put-text-property (point-min) (point-max) 'read-only nil))
+ (run-hooks 'message-send-hook)
+ (message "Sending...")
+ (let ((message-encoding-buffer
+ (message-generate-new-buffer-clone-locals " message encoding"))
+ (message-edit-buffer (current-buffer))
+ (message-mime-mode mime-edit-mode-flag)
+ (alist message-send-method-alist)
+ (success t)
+ elem sent)
+ (unwind-protect
+ (if (save-excursion
+ (set-buffer message-encoding-buffer)
+ (erase-buffer)
+ (insert-buffer message-edit-buffer)
+ (funcall message-encode-function)
+ (message-fix-before-sending)
+ (while (and success
+ (setq elem (pop alist)))
+ (and (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))))
+ (setq success (funcall (caddr elem) arg)))
+ (setq sent t)))
+ (not (and success sent)))
+ (throw 'message-sending-cancel t)
+ (message-do-fcc)
+ (run-hooks 'message-sent-hook)
+ (message "Sending...done")
+ ;; Mark the buffer as unmodified and delete auto-save.
+ (set-buffer-modified-p nil)
+ (delete-auto-save-file-if-necessary t)
+ (message-disassociate-draft)
+ ;; Delete other mail buffers and stuff.
+ (message-do-send-housekeeping)
+ (message-do-actions message-send-actions)
+ nil)
+ (kill-buffer message-encoding-buffer))))
+ (progn
+ (message "Canceled")
+ nil)
+ ;; Return success.
+ t))
(defun message-send-via-mail (arg)
"Send the current message via mail."
(eval (car actions)))))
(pop actions)))
+(defsubst message-maybe-split-and-send-mail ()
+ "Split a message if necessary, and send it via mail.
+Returns nil if sending succeeded, returns any string if sending failed.
+This sub function is for exclusive use of `message-send-mail'."
+ (let ((mime-edit-split-ignored-field-regexp
+ mime-edit-split-ignored-field-regexp)
+ (case-fold-search t)
+ failure)
+ (while (string-match "Message-ID" mime-edit-split-ignored-field-regexp)
+ (setq mime-edit-split-ignored-field-regexp
+ (concat (substring mime-edit-split-ignored-field-regexp
+ 0 (match-beginning 0))
+ "Hey_MIME-Edit,_there_is_an_inviolable_Message_ID"
+ "_so_don't_rape_it!"
+ (substring mime-edit-split-ignored-field-regexp
+ (match-end 0)))))
+ (setq failure
+ (or
+ (catch 'message-sending-mail-failure
+ (mime-edit-maybe-split-and-send
+ (function
+ (lambda ()
+ (interactive)
+ (save-restriction
+ (std11-narrow-to-header mail-header-separator)
+ (goto-char (point-min))
+ (when (re-search-forward "^Message-ID:" nil t)
+ (delete-region (match-end 0) (std11-field-end))
+ (insert " " (message-make-message-id))))
+ (condition-case err
+ (funcall message-send-mail-function)
+ (error
+ (throw 'message-sending-mail-failure err))))))
+ nil)
+ (condition-case err
+ (progn
+ (funcall message-send-mail-function)
+ nil)
+ (error err))))
+ (when failure
+ (if (eq 'error (car failure))
+ (cadr failure)
+ (prin1-to-string failure)))))
+
(defun message-send-mail (&optional arg)
(require 'mail-utils)
(let ((tembuf (message-generate-new-buffer-clone-locals " message temp"))
(case-fold-search nil)
- (news (message-news-p)))
+ (news (message-news-p))
+ failure)
(save-restriction
(message-narrow-to-headers)
;; Insert some headers.
(if (not (message-check-mail-syntax))
(progn
(message "")
- ;;(message "Posting not performed")
nil)
(unwind-protect
(save-excursion
;; Remove some headers.
(save-restriction
(message-narrow-to-headers)
+ ;; Remove some headers.
(message-remove-header message-ignored-mail-headers t))
(goto-char (point-max))
;; require one newline at the end.
- (or (= (preceding-char) ?\n)
+ (or (eq (char-before) ?\n)
(insert ?\n))
(when (and news
(or (message-fetch-field "cc")
(message-fetch-field "to")))
(message-insert-courtesy-copy))
- (mime-edit-maybe-split-and-send
- (function
- (lambda ()
- (interactive)
- (funcall message-send-mail-function)
- )))
- (funcall message-send-mail-function))
+ (setq failure (message-maybe-split-and-send-mail)))
(kill-buffer tembuf))
(set-buffer message-edit-buffer)
- (push 'mail message-sent-message-via))))
+ (if failure
+ (progn
+ (message "Couldn't send message via mail: %s" failure)
+ nil)
+ (push 'mail message-sent-message-via)))))
(defun message-send-mail-with-sendmail ()
"Send off the prepared buffer with sendmail."
(save-excursion
(set-buffer errbuf)
(erase-buffer))))
- (let ((default-directory "/")
- (coding-system-for-write message-send-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.
- ;; 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"))
- ;; 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.
- (if resend-to-addresses
- (list resend-to-addresses)
- '("-t")))))
+ (let ((default-directory "/"))
+ (as-binary-process
+ (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.
+ ;; 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"))
+ ;; 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.
+ (if resend-to-addresses
+ (list resend-to-addresses)
+ '("-t"))))))
(when message-interactive
(save-excursion
(set-buffer errbuf)
(run-hooks 'message-send-mail-hook)
;; send the message
(case
- (let ((coding-system-for-write message-send-coding-system))
- (apply
- 'call-process-region 1 (point-max) message-qmail-inject-program
- nil nil nil
- ;; qmail-inject's default behaviour is to look for addresses on the
- ;; command line; if there're none, it scans the headers.
- ;; yes, it does The Right Thing w.r.t. Resent-To and it's kin.
- ;;
- ;; in general, ALL of qmail-inject's defaults are perfect for simply
- ;; reading a formatted (i. e., at least a To: or Resent-To header)
- ;; message from stdin.
- ;;
- ;; 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
- ;; for sendmail's lost innocence.
- ;;
- ;; all this is way cool coz it lets us keep the arguments entirely
- ;; free for -inject-arguments -- a big win for the user and for us
- ;; since we don't have to play that double-guessing game and the user
- ;; gets full control (no gestapo'ish -f's, for instance). --sj
- message-qmail-inject-args))
+ (as-binary-process
+ (apply
+ 'call-process-region 1 (point-max) message-qmail-inject-program
+ nil nil nil
+ ;; qmail-inject's default behaviour is to look for addresses on the
+ ;; command line; if there're none, it scans the headers.
+ ;; yes, it does The Right Thing w.r.t. Resent-To and it's kin.
+ ;;
+ ;; in general, ALL of qmail-inject's defaults are perfect for simply
+ ;; reading a formatted (i. e., at least a To: or Resent-To header)
+ ;; message from stdin.
+ ;;
+ ;; 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
+ ;; for sendmail's lost innocence.
+ ;;
+ ;; all this is way cool coz it lets us keep the arguments entirely
+ ;; free for -inject-arguments -- a big win for the user and for us
+ ;; since we don't have to play that double-guessing game and the user
+ ;; gets full control (no gestapo'ish -f's, for instance). --sj
+ message-qmail-inject-args))
;; qmail-inject doesn't say anything on it's stdout/stderr,
;; we have to look at the retval instead
(0 nil)
(mh-send-letter)))
(defun message-send-mail-with-smtp ()
- "Send the prepared message buffer with SMTP."
- (require 'smtp)
- (let ((errbuf (if mail-interactive
- (generate-new-buffer " smtp errors")
- 0))
- (case-fold-search nil)
- resend-to-addresses
- delimline)
- (unwind-protect
- (save-excursion
- (goto-char (point-max))
- ;; require one newline at the end.
- (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)
- (setq delimline (point-marker))
- (run-hooks 'message-send-mail-hook)
- ;; (sendmail-synch-aliases)
- ;; (if mail-aliases
- ;; (expand-mail-aliases (point-min) delimline))
- (goto-char (point-min))
- ;; ignore any blank lines in the header
- (while (and (re-search-forward "\n\n\n*" delimline t)
- (< (point) delimline))
- (replace-match "\n"))
- (let ((case-fold-search t))
- (goto-char (point-min))
- (goto-char (point-min))
- (while (re-search-forward "^Resent-to:" delimline t)
- (setq resend-to-addresses
- (save-restriction
- (narrow-to-region (point)
- (save-excursion
- (end-of-line)
- (point)))
- (append (mail-parse-comma-list)
- resend-to-addresses))))
-;;; Apparently this causes a duplicate Sender.
-;;; ;; If the From is different than current user, insert Sender.
-;;; (goto-char (point-min))
-;;; (and (re-search-forward "^From:" delimline t)
-;;; (progn
-;;; (require 'mail-utils)
-;;; (not (string-equal
-;;; (mail-strip-quoted-names
-;;; (save-restriction
-;;; (narrow-to-region (point-min) delimline)
-;;; (mail-fetch-field "From")))
-;;; (user-login-name))))
-;;; (progn
-;;; (forward-line 1)
-;;; (insert "Sender: " (user-login-name) "\n")))
- ;; Don't send out a blank subject line
- (goto-char (point-min))
- (if (re-search-forward "^Subject:[ \t]*\n" delimline t)
- (replace-match ""))
- ;; Put the "From:" field in unless for some odd reason
- ;; they put one in themselves.
- (goto-char (point-min))
- (if (not (re-search-forward "^From:" delimline t))
- (let* ((login user-mail-address)
- (fullname (user-full-name)))
- (cond ((eq mail-from-style 'angles)
- (insert "From: " fullname)
- (let ((fullname-start (+ (point-min) 6))
- (fullname-end (point-marker)))
- (goto-char fullname-start)
- ;; Look for a character that cannot appear unquoted
- ;; according to RFC 822.
- (if (re-search-forward "[^- !#-'*+/-9=?A-Z^-~]"
- fullname-end 1)
- (progn
- ;; Quote fullname, escaping specials.
- (goto-char fullname-start)
- (insert "\"")
- (while (re-search-forward "[\"\\]"
- fullname-end 1)
- (replace-match "\\\\\\&" t))
- (insert "\""))))
- (insert " <" login ">\n"))
- ((eq mail-from-style 'parens)
- (insert "From: " login " (")
- (let ((fullname-start (point)))
- (insert fullname)
- (let ((fullname-end (point-marker)))
- (goto-char fullname-start)
- ;; RFC 822 says \ and nonmatching parentheses
- ;; must be escaped in comments.
- ;; Escape every instance of ()\ ...
- (while (re-search-forward "[()\\]" fullname-end 1)
- (replace-match "\\\\\\&" t))
- ;; ... then undo escaping of matching parentheses,
- ;; including matching nested parentheses.
- (goto-char fullname-start)
- (while (re-search-forward
- "\\(\\=\\|[^\\]\\(\\\\\\\\\\)*\\)\\\\(\\(\\([^\\]\\|\\\\\\\\\\)*\\)\\\\)"
- fullname-end 1)
- (replace-match "\\1(\\3)" t)
- (goto-char fullname-start))))
- (insert ")\n"))
- ((null mail-from-style)
- (insert "From: " login "\n")))))
- ;; Insert an extra newline if we need it to work around
- ;; Sun's bug that swallows newlines.
- (goto-char (1+ delimline))
- (if (eval mail-mailer-swallows-blank-line)
- (newline))
- ;; Find and handle any FCC fields.
- (goto-char (point-min))
- (if (re-search-forward "^FCC:" delimline t)
- (mail-do-fcc delimline))
- (if mail-interactive
- (save-excursion
- (set-buffer errbuf)
- (erase-buffer))))
- ;;
- ;;
- ;;
- (let ((recipient-address-list
- (or resend-to-addresses
- (smtp-deduce-address-list (current-buffer)
- (point-min) delimline))))
- (smtp-do-bcc delimline)
-
- (if recipient-address-list
- (if (not (smtp-via-smtp recipient-address-list
- (current-buffer)))
- (error "Sending failed; SMTP protocol error"))
- (error "Sending failed; no recipients"))
- ))
- (if (bufferp errbuf)
- (kill-buffer errbuf)))))
+ "Send off the prepared buffer with SMTP."
+ (require 'smtp) ; XXX
+ (let ((case-fold-search t)
+ recipients)
+ (save-restriction
+ (message-narrow-to-headers)
+ (setq recipients
+ ;; XXX: Should be replaced by better one.
+ (smtp-deduce-address-list (current-buffer)
+ (point-min) (point-max)))
+ ;; Remove BCC lines.
+ (message-remove-header "bcc"))
+ ;; replace the header delimiter with a blank line.
+ (goto-char (point-min))
+ (re-search-forward
+ (concat "^" (regexp-quote mail-header-separator) "\n"))
+ (replace-match "\n")
+ (backward-char 1)
+ (run-hooks 'message-send-mail-hook)
+ (if recipients
+ (let ((result (smtp-via-smtp user-mail-address
+ recipients
+ (current-buffer))))
+ (unless (eq result t)
+ (error "Sending failed; " result)))
+ (error "Sending failed; no recipients"))))
+
+(defsubst message-maybe-split-and-send-news (method)
+ "Split a message if necessary, and send it via news.
+Returns nil if sending succeeded, returns t if sending failed.
+This sub function is for exclusive use of `message-send-news'."
+ (let ((mime-edit-split-ignored-field-regexp
+ mime-edit-split-ignored-field-regexp)
+ (case-fold-search t))
+ (while (string-match "Message-ID" mime-edit-split-ignored-field-regexp)
+ (setq mime-edit-split-ignored-field-regexp
+ (concat (substring mime-edit-split-ignored-field-regexp
+ 0 (match-beginning 0))
+ "Hey_MIME-Edit,_there_is_an_inviolable_Message_ID"
+ "_so_don't_rape_it!"
+ (substring mime-edit-split-ignored-field-regexp
+ (match-end 0)))))
+ (or
+ (catch 'message-sending-news-failure
+ (mime-edit-maybe-split-and-send
+ (function
+ (lambda ()
+ (interactive)
+ (save-restriction
+ (std11-narrow-to-header mail-header-separator)
+ (goto-char (point-min))
+ (when (re-search-forward "^Message-ID:" nil t)
+ (delete-region (match-end 0) (std11-field-end))
+ (insert " " (message-make-message-id))))
+ (unless (funcall message-send-news-function method)
+ (throw 'message-sending-news-failure t)))))
+ nil)
+ (not (funcall message-send-news-function method)))))
(defun message-send-news (&optional arg)
(let ((tembuf (message-generate-new-buffer-clone-locals " *message temp*"))
(run-hooks 'message-header-encoded-hook))
(message-cleanup-headers)
(if (not (message-check-news-syntax))
- (progn
- (message "")
- ;;(message "Posting not performed")
- nil)
+ nil
(unwind-protect
(save-excursion
(set-buffer tembuf)
(message-remove-header message-ignored-news-headers t))
(goto-char (point-max))
;; require one newline at the end.
- (or (= (preceding-char) ?\n)
+ (or (eq (char-before) ?\n)
(insert ?\n))
- (mime-edit-maybe-split-and-send
- (function
- (lambda ()
- (interactive)
- (save-restriction
- (std11-narrow-to-header mail-header-separator)
- (goto-char (point-min))
- (when (re-search-forward "^Message-Id:" nil t)
- (delete-region (match-end 0)(std11-field-end))
- (insert (concat " " (message-make-message-id)))
- ))
- (funcall message-send-news-function method)
- )))
- (setq result (funcall message-send-news-function method)))
+ (setq result (message-maybe-split-and-send-news method)))
(kill-buffer tembuf))
(set-buffer message-edit-buffer)
(if result
- (push 'news message-sent-message-via)
- (message "Couldn't send message via news: %s"
- (nnheader-get-report (car method)))
- nil))))
+ (progn
+ (message "Couldn't send message via news: %s"
+ (nnheader-get-report (car method)))
+ nil)
+ (push 'news message-sent-message-via)))))
;; 1997-09-29 by MORIOKA Tomohiko
(defun message-send-news-with-gnus (method)
(y-or-n-p
"The article contains control characters. Really post? ")
t))
- ;; Check content transfer encoding.
- (message-check 'encoding
- (message-check-encoding))
+ ;; Check 8bit characters.
+ (message-check '8bit
+ (message-check-8bit))
;; Check excessive size.
(message-check 'size
(if (> (buffer-size) 60000)
(defun message-check-mail-body-syntax ()
(and
- ;; Check content transfer encoding.
- (message-check 'encoding
- (message-check-encoding)
+ ;; Check 8bit characters.
+ (message-check '8bit
+ (message-check-8bit)
)))
-(defun message-check-encoding ()
- "Check content encoding type."
+(defun message-check-8bit ()
+ "Check the article contains 8bit characters."
(save-excursion
(set-buffer message-encoding-buffer)
(message-narrow-to-headers)
(let* ((case-fold-search t)
- (encoding-string
- (message-fetch-field "content-transfer-encoding"))
- (encoding (or encoding-string
- message-default-encoding)))
- (message "%s %s" encoding-string encoding)
- (if (string-match "^8bit" encoding)
+ (field-value (message-fetch-field "content-transfer-encoding")))
+ (if (and field-value
+ (member (downcase field-value) message-8bit-encoding-list))
t
(widen)
(set-buffer (get-buffer-create " message syntax"))
(erase-buffer)
+ (goto-char (point-min))
(set-buffer-multibyte nil)
(insert-buffer message-encoding-buffer)
(goto-char (point-min))
- (if (re-search-forward "[\200-\377]" nil t)
+ (if (re-search-forward "[^\x00-\x7f]" nil t)
(y-or-n-p
"The article contains 8bit characters. Really post? ")
t)))))
(concat "^" (regexp-quote mail-header-separator) "$"))
(while (not (eobp))
(when (not (looking-at "[ \t\n]"))
- (setq sum (logxor (ash sum 1) (if (natnump sum) 0 1)
- (following-char))))
+ (setq sum (logxor (ash sum 1) (if (natnump sum) 0 1)
+ (char-after))))
(forward-char 1)))
sum))
(defun message-do-fcc ()
"Process Fcc headers in the current buffer."
(let ((case-fold-search t)
- (coding-system-for-write 'raw-text)
+ (coding-system-for-write nnheader-message-coding-system-for-write)
list file)
(save-excursion
(set-buffer (get-buffer-create " *message temp*"))
(message-narrow-to-headers)
(while (setq file (message-fetch-field "fcc"))
(push file list)
- (message-remove-header "fcc" nil t))
- (run-hooks 'message-header-hook)
- (when (functionp message-header-encode-function)
- (funcall message-header-encode-function))
- (run-hooks 'message-header-encoded-hook))
- (run-hooks 'message-before-do-fcc-hook)
+ (message-remove-header "fcc" nil t)))
(goto-char (point-min))
(re-search-forward (concat "^" (regexp-quote mail-header-separator) "$"))
(replace-match "" t t)
(progn
;; The header was found. We insert a space after the
;; colon, if there is none.
- (if (/= (following-char) ? ) (insert " ") (forward-char 1))
+ (if (eq (char-after) ? )
+ (forward-char 1)
+ (insert " "))
;; Find out whether the header is empty...
(looking-at "[ \t]*$")))
;; So we find out what value we should insert.
(goto-char (point-min))
(while (not (eobp))
(skip-chars-forward "^,\"" (point-max))
- (if (or (= (following-char) ?,)
+ (if (or (eq (char-after) ?,)
(eobp))
(when (not quoted)
(if (and (> (current-column) 78)
(search-backward ":" )
(widen)
(forward-char 1)
- (if (= (following-char) ? )
+ (if (eq (char-after) ? )
(forward-char 1)
(insert " ")))
(t
(when actions
(setq message-send-actions actions))
(setq message-reply-buffer
- (or (cdr (assq 'reply-buffer message-parameter-alist))
+ (or (message-get-parameter 'reply-buffer)
replybuffer))
(goto-char (point-min))
;; Insert all the headers.
(run-hooks 'message-header-setup-hook))
(set-buffer-modified-p nil)
(setq buffer-undo-list nil)
+ (add-to-list 'buffer-file-format 'message)
+ (set-buffer-file-coding-system nnheader-message-coding-system-for-write)
(run-hooks 'message-setup-hook)
(message-position-point)
(undo-boundary))
(defun message-set-auto-save-file-name ()
"Associate the message buffer with a file in the drafts directory."
- (when message-autosave-directory
+ (when message-auto-save-directory
(if (gnus-alive-p)
(setq message-draft-article
(nndraft-request-associate-buffer "drafts"))
(setq buffer-file-name (expand-file-name "*message*"
- message-autosave-directory))
+ message-auto-save-directory))
(setq buffer-auto-save-file-name (make-auto-save-file-name)))
(clear-visited-file-modtime)))
from subject date reply-to to cc
references message-id follow-to
(inhibit-point-motion-hooks t)
- mct never-mct gnus-warning)
+ mct never-mct gnus-warning in-reply-to)
(save-restriction
(message-narrow-to-head)
;; Allow customizations to have their say.
reply-to (message-fetch-field "reply-to")
references (message-fetch-field "references")
message-id (message-fetch-field "message-id" t))
+ ;; Get the references from "In-Reply-To" field if there were
+ ;; no references and "In-Reply-To" field looks promising.
+ (unless references
+ (when (and (setq in-reply-to (message-fetch-field "in-reply-to"))
+ (string-match "<[^>]+>" in-reply-to))
+ (setq references (match-string 0 in-reply-to))))
;; Remove any (buggy) Re:'s that are present and make a
;; proper one.
(when (string-match message-subject-re-regexp subject)
(message-tokenize-header (buffer-string))))
(let ((s ccalist))
(while s
- (setq ccalist (delq (assoc (car (pop s)) s) ccalist)))))
+ (setq ccalist (delq (assoc (car (pop s)) s) ccalist))))
+ (when (functionp message-mail-follow-up-address-checker)
+ (setq ccalist (funcall message-mail-follow-up-address-checker
+ ccalist))))
(setq follow-to (list (cons 'To (cdr (pop ccalist)))))
(when ccalist
(let ((ccs (cons 'Cc (mapconcat
(message "Canceling your article...done"))
(kill-buffer buf)))))
+(defun message-supersede-setup-for-mime-edit ()
+ (set (make-local-variable 'message-setup-hook) nil)
+ (mime-edit-again))
+
;;;###autoload
(defun message-supersede ()
"Start composing a message to supersede the current message.
(goto-char (point-max))
(insert mail-header-separator)
(widen)
- (forward-line 1)))
+ (when message-supersede-setup-function
+ (funcall message-supersede-setup-function))
+ (run-hooks 'message-supersede-setup-hook)
+ (goto-char (point-min))
+ (search-forward (concat "\n" mail-header-separator "\n") nil t)))
;;;###autoload
(defun message-recover ()
(current-buffer)
(message-narrow-to-head)
(let ((funcs message-make-forward-subject-function)
- (subject (if message-wash-forwarded-subjects
- (message-wash-subject
- (or (nnheader-decode-subject
- (message-fetch-field "Subject"))
- ""))
- (or (nnheader-decode-subject
- (message-fetch-field "Subject"))
- ""))))
+ (subject (message-fetch-field "Subject")))
+ (setq subject
+ (if subject
+ (if message-wash-forwarded-subjects
+ (message-wash-subject
+ (nnheader-decode-subject subject))
+ (nnheader-decode-subject subject))
+ "(none)"))
;; Make sure funcs is a list.
(and funcs
(not (listp funcs))
(kill-buffer (current-buffer)))
(message "Resending message to %s...done" address)))
+(defun message-bounce-setup-for-mime-edit ()
+ (set (make-local-variable 'message-setup-hook) nil)
+ (mime-edit-again))
+
;;;###autoload
(defun message-bounce ()
"Re-mail the current message.
(message-remove-header message-ignored-bounced-headers t)
(goto-char (point-max))
(insert mail-header-separator))
+ (when message-bounce-setup-function
+ (funcall message-bounce-setup-function))
+ (run-hooks 'message-bounce-setup-hook)
(message-position-point)))
;;;
(goto-char (min start end))
(while (< (point) end1)
(or (looking-at "[_\^@- ]")
- (insert (following-char) "\b"))
+ (insert (char-after) "\b"))
(forward-char 1)))))
;;;###autoload
(move-marker end1 (max start end))
(goto-char (min start end))
(while (re-search-forward "\b" end1 t)
- (if (eq (following-char) (char-after (- (point) 2)))
+ (if (eq (char-after) (char-after (- (point) 2)))
(delete-char -2))))))
(defalias 'message-exchange-point-and-mark 'exchange-point-and-mark)
(cdr local)))))
locals)))
-;;; @ for MIME Edit mode
+;; @ For `message-mail-follow-up-address-checker'.
+
+(defcustom message-mailing-list-address-list nil
+ "*Regexp matching addresses that are mailing lists.
+It must be a simple regexp string or a list of regexp strings.
+This variable is used by \`message-check-mailing-list-with-address-list\'."
+ :group 'message-mail
+ :type '(repeat regexp))
+
+(defun message-check-mailing-list-with-address-list (alist)
+ (let ((s alist)
+ (regexp (if (stringp message-mailing-list-address-list)
+ message-mailing-list-address-list
+ (mapconcat
+ (lambda (x)
+ x)
+ message-mailing-list-address-list
+ "\\|")))
+ address non-mailing-list mailing-list)
+ (while (setq address (car (pop s)))
+ (if (string-match regexp address)
+ (setq mailing-list t)
+ (setq non-mailing-list
+ (append non-mailing-list (list address)))))
+ (if (or (not non-mailing-list)
+ (not mailing-list)
+ (not (y-or-n-p "Do you want to remove private address? ")))
+ alist
+ (setq s non-mailing-list)
+ (while s
+ (setq alist (delq (assoc (pop s) alist) alist)))
+ alist)
+ ))
+
+(defcustom message-mailing-list-address-p nil
+ "*The function return t if address is a mailing list.
+It must be function, and interface is (ADDRESS).
+ADDRESS is a string of mail address.
+This variable is used by \`message-check-mailing-list-with-function\'."
+ :group 'message-mail
+ :type 'function)
+
+(defun message-check-mailing-list-with-function (alist)
+ (let ((s alist)
+ address non-mailing-list mailing-list)
+ (while (setq address (car (pop s)))
+ (if (funcall message-mailing-list-address-p address)
+ (setq mailing-list t)
+ (setq non-mailing-list
+ (append non-mailing-list (list address)))))
+ (if (or (not non-mailing-list)
+ (not mailing-list)
+ (not (y-or-n-p "Do you want to remove private address? ")))
+ alist
+ (setq s non-mailing-list)
+ (while s
+ (setq alist (delq (assoc (pop s) alist) alist)))
+ alist)
+ ))
+
+;;; @ Encode buffer.
;;;
(defun message-maybe-encode ()
(when message-mime-mode
+ ;; Inherit the buffer local variable `mime-edit-pgp-processing'.
+ (let ((pgp-processing (with-current-buffer message-edit-buffer
+ mime-edit-pgp-processing)))
+ (setq mime-edit-pgp-processing pgp-processing))
(run-hooks 'mime-edit-translate-hook)
(if (catch 'mime-edit-error
(save-excursion
+ (mime-edit-pgp-enclose-buffer)
(mime-edit-translate-body)
))
(error "Translation error!")
- )
- (end-of-invisible)
- (run-hooks 'mime-edit-exit-hook)
+ ))
+ (end-of-invisible)
+ (run-hooks 'mime-edit-exit-hook)
+ )
+
+;;; @ for saving buffer
+;;;
+
+(defvar message-save-encoder 'message-mime-save-encoder)
+
+(defun message-save-formatter (start end &optional orig-buf)
+ "Format message, when save message buffer. [message.el]"
+ (save-restriction
+ (narrow-to-region start end)
+ (and message-save-encoder
+ (funcall message-save-encoder orig-buf))
+ (goto-char start)
+ (and (search-forward (concat "\n" mail-header-separator "\n") nil t)
+ (replace-match "\n\n"))
))
+(set-alist 'format-alist
+ 'message
+ '("Message."
+ "1\\(^\\)"
+ nil
+ message-save-formatter
+ t nil))
+
+;;; @ for MIME Edit mode
+;;;
+
(defun message-mime-insert-article (&optional message)
(interactive)
(let ((message-cite-function 'mime-edit-inserted-message-filter)
- (message-reply-buffer (message-get-original-reply-buffer))
+ (message-reply-buffer
+ (message-get-parameter-with-eval 'original-buffer))
(start (point)))
(message-yank-original nil)
(save-excursion
(set-alist 'mime-edit-message-inserter-alist
'message-mode (function message-mime-insert-article))
-;;; Miscellaneous functions
-
-;; stolen (and renamed) from nnheader.el
-(defun message-replace-chars-in-string (string from to)
- "Replace characters in STRING from FROM to TO."
- (let ((string (substring string 0)) ;Copy string.
- (len (length string))
- (idx 0))
- ;; Replace all occurrences of FROM with TO.
- (while (< idx len)
- (when (= (aref string idx) from)
- (aset string idx to))
- (setq idx (1+ idx)))
- string))
-
-(defvar message-save-buffer " *encoding")
-(defun message-save-drafts ()
- (interactive)
- (if (not (get-buffer message-save-buffer))
- (get-buffer-create message-save-buffer))
- (let ((filename buffer-file-name)
- (buffer (current-buffer)))
- (set-buffer message-save-buffer)
- (erase-buffer)
- (insert-buffer buffer)
- (mime-edit-translate-buffer)
- (write-region (point-min) (point-max) filename)
- (set-buffer buffer)
- (set-buffer-modified-p nil)))
+(defun message-mime-save-encoder (orig-buf)
+ (when (with-current-buffer orig-buf mime-edit-mode-flag)
+ (mime-edit-translate-body)
+ (mime-edit-translate-header)
+ ))
+
+(defun message-mime-after-save-hook ()
+ (set-buffer-file-coding-system nnheader-message-coding-system-for-write)
+ (set-buffer-modified-p nil)
+ )
+
+(defun message-mime-setup ()
+ (turn-on-mime-edit)
+ (make-local-hook 'after-save-hook)
+ (add-hook 'after-save-hook 'message-mime-after-save-hook nil t)
+ )
(run-hooks 'message-load-hook)