;;; message.el --- composing mail and news messages
-;; Copyright (C) 1996,97,98 Free Software Foundation, Inc.
+;; Copyright (C) 1996,97,98,99 Free Software Foundation, Inc.
;; 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 <kei-suzu@mail.wbs.ne.jp>
;; Tatsuya Ichikawa <t-ichi@po.shiojiri.ne.jp>
-;; Katsumi Yamaoka <yamaoka@jpl.org>
+;; Katsumi Yamaoka <yamaoka@jpl.org>
+;; Kiyokazu SUTO <suto@merry.xmath.ous.ac.jp>
;; Keywords: mail, news, MIME
;; This file is part of GNU Emacs.
(require 'mail-abbrevs)
(require 'mailabbrev))
(require 'mime-edit)
+(eval-when-compile (require 'static))
;; Avoid byte-compile warnings.
(eval-when-compile
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 buffer-file-name unchanged."
+shorten-followup-to existing-newsgroups buffer-file-name unchanged
+newsgroups."
:group 'message-news)
(defcustom message-required-news-headers
: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-buffers
:type '(choice (const :tag "off" nil)
(const :tag "unique" unique)
- (const :tag "unsuniqueent" unsent)
+ (const :tag "unsent" unsent)
(function fun)))
(defcustom message-kill-buffer-on-exit nil
:group 'message-forwarding
:type 'regexp)
-(defcustom message-make-forward-subject-function
+(defcustom message-make-forward-subject-function
'message-forward-subject-author-subject
"*A list of functions that are called to generate a subject header for forwarded messages.
The subject generated by the previous function is passed into each
:group 'message-forwarding
:type 'boolean)
-(defcustom message-ignored-resent-headers "^Return-receipt\\|^X-Gnus"
+(defcustom message-ignored-resent-headers "^Return-receipt\\|^X-Gnus\\|^Gnus-Warning:"
"*All headers that match this regexp will be deleted when resending a message."
:group 'message-interface
:type 'regexp)
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),
+Valid values include `message-send-mail-with-sendmail' (the default),
`message-send-mail-with-mh', `message-send-mail-with-qmail' and
`message-send-mail-with-smtp'."
:type '(radio (function-item message-send-mail-with-sendmail)
:group 'message-various
:type 'hook)
+(defcustom message-cancel-hook nil
+ "Hook run when cancelling articles."
+ :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
:type 'hook)
(defcustom message-bounce-setup-hook nil
- "Normal hook, run each time a a re-sending bounced message is initialized.
+ "Normal hook, run each time 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
:type 'hook)
-(defcustom message-header-hook '(eword-encode-header)
+(defcustom message-header-hook '((lambda () (eword-encode-header t)))
"Hook run in a message mode buffer narrowed to the headers."
:group 'message-various
:type 'hook)
;;;###autoload
(defcustom message-yank-prefix "> "
- "*Prefix inserted on the lines of yanked messages.
-nil means use indentation."
+ "*Prefix inserted on the lines of yanked messages."
: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 'boolean
+ :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'."
`message-cite-original-without-signature'.
Note that `message-cite-original' uses `mail-citation-hook' if that is non-nil."
:type '(radio (function-item message-cite-original)
+ (function-item message-cite-original-without-signature)
+ (function-item mu-cite/cite-original)
(function-item sc-cite-original)
(function :tag "Other"))
:group 'message-insertion)
;; Ignore errors in case this is used in Emacs 19.
;; Don't use ignore-errors because this is copied into loaddefs.el.
;;;###autoload
-(condition-case nil
- (define-mail-user-agent 'message-user-agent
- 'message-mail 'message-send-and-exit
- 'message-kill-buffer 'message-send-hook)
- (error nil))
+(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.")
:type '(choice (const :tag "unique" unique)
(const :tag "unsent" unsent)))
+(defcustom message-default-charset nil
+ "Default charset used in non-MULE XEmacsen."
+ :group 'message
+ :type 'symbol)
+
;;; Internal variables.
;;; Well, not really internal.
"\\([" cite-prefix "]+[" cite-suffix "]*\\)?"
"[:>|}].*")
(0 'message-cited-text-face))
- ("<#/?\\(multi\\)part.*>"
+ ("<#/?\\(multipart\\|part\\|external\\).*>"
(0 'message-mml-face))))
"Additional expressions to highlight in Message mode.")
(const :tag "always" t)
(const :tag "ask" ask)))
-(defvar message-send-coding-system 'binary
- "Coding system to encode outgoing mail.")
+(defvar message-draft-coding-system
+ (cond
+ ((boundp 'MULE) '*junet*)
+ ((not (fboundp 'find-coding-system)) nil)
+ ((find-coding-system 'emacs-mule)
+ (if (string-match "nt\\|windows" system-configuration)
+ 'emacs-mule-dos 'emacs-mule))
+ ((find-coding-system 'escape-quoted) 'escape-quoted)
+ ((find-coding-system 'no-conversion) 'no-conversion)
+ (t nil))
+ "Coding system to compose mail.")
;;; Internal variables.
-(defvar message-default-charset nil)
(defvar message-buffer-list nil)
(defvar message-this-is-news nil)
(defvar message-this-is-mail nil)
(defvar message-draft-article nil)
(defvar message-mime-part nil)
+(defvar message-posting-charset nil)
;; Byte-compiler warning
(defvar gnus-active-hashtb)
(Lines)
(Expires)
(Message-ID)
- (References . message-fill-header)
+ (References . message-shorten-references)
(User-Agent))
"Alist used for formatting headers.")
(autoload 'gnus-request-post "gnus-int")
(autoload 'gnus-copy-article-buffer "gnus-msg")
(autoload 'gnus-alive-p "gnus-util")
- (autoload 'rmail-output "rmail"))
+ (autoload 'rmail-output "rmail")
+ (autoload 'mu-cite/cite-original "mu-cite"))
\f
;;;
;;; 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"
(when value
(while (string-match "\n[\t ]+" value)
(setq value (replace-match " " t t value)))
- value)))
+ ;; We remove all text props.delete-region
+ (format "%s" value))))
(defun message-narrow-to-field ()
"Narrow the buffer to the header on the current line."
(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
(forward-line 1)
(if (re-search-forward "^[^ \t]" nil t)
(goto-char (match-beginning 0))
- (point-max))))
+ (goto-char (point-max)))))
number))
+(defun message-remove-first-header (header)
+ "Remove the first instance of HEADER if there is more than one."
+ (let ((count 0)
+ (regexp (concat "^" (regexp-quote header) ":")))
+ (save-excursion
+ (goto-char (point-min))
+ (while (re-search-forward regexp nil t)
+ (incf count)))
+ (while (> count 1)
+ (message-remove-header header nil t)
+ (decf count))))
+
(defun message-narrow-to-headers ()
"Narrow the buffer to the head of the message."
(widen)
(defun message-sort-headers-1 ()
"Sort the buffer as headers using `message-rank' text props."
(goto-char (point-min))
+ (require 'sort)
(sort-subr
nil 'message-next-header
(lambda ()
(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 "\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\M-h" 'message-insert-headers)
(define-key message-mode-map "\C-c\C-r" 'message-caesar-buffer-body)
(define-key message-mode-map "\C-c\C-o" 'message-sort-headers)
(define-key message-mode-map "\C-c\M-r" 'message-rename-buffer)
["Newline and Reformat" message-newline-and-reformat t]
["Rename buffer" message-rename-buffer t]
["Spellcheck" ispell-message t]
+ ["Attach file as MIME" mime-edit-insert-file t]
"----"
["Send Message" message-send-and-exit t]
["Abort Message" message-dont-send t]
"Major mode for editing mail and news to be sent.
Like Text Mode but with these additional commands:
C-c C-s message-send (send the message) C-c C-c message-send-and-exit
+C-c C-d Pospone sending the message C-c C-k Kill the message
C-c C-f move to a header field (and create it if there isn't):
C-c C-f C-t move to To C-c C-f C-s move to Subject
C-c C-f C-c move to Cc C-c C-f C-b move to Bcc
(interactive)
(kill-all-local-variables)
(set (make-local-variable '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)
(make-local-variable 'message-draft-article)
(setq adaptive-fill-first-line-regexp
(concat "[ \t]*[-a-z0-9A-Z]*>+[ \t]*\\|"
adaptive-fill-first-line-regexp))
+ (make-local-variable 'indent-tabs-mode) ;Turn off tabs for indentation.
+ (setq indent-tabs-mode nil)
(run-hooks 'text-mode-hook 'message-mode-hook))
\f
(interactive)
(if (looking-at "[ \t]*\n") (expand-abbrev))
(goto-char (point-min))
- (search-forward (concat "\n" mail-header-separator "\n") nil t))
+ (or (search-forward (concat "\n" mail-header-separator "\n") nil t)
+ (search-forward "\n\n" nil t)))
(defun message-goto-eoh ()
"Move point to the end of the headers."
(eq force 0))
(save-excursion
(goto-char (point-max))
- (not (re-search-backward
- message-signature-separator nil t))))
+ (not (re-search-backward message-signature-separator nil t))))
((and (null message-signature)
force)
t)
(goto-char (point-min))
(search-forward (concat "\n" mail-header-separator "\n") nil t)
(let ((fill-prefix message-yank-prefix))
- (fill-individual-paragraphs (point) (point-max) justifyp t))))
+ (fill-individual-paragraphs (point) (point-max) justifyp))))
(defun message-indent-citation ()
"Modify text just inserted from a message to be cited.
(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)
+ (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))))))
+ 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."
(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
+ (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)
(if (listp message-indent-citation-function)
message-indent-citation-function
(list message-indent-citation-function)))))
- (goto-char start)
- ;; Quote parts.
- (while (re-search-forward "<#/?!*\\(multi\\|part\\)>" end t)
- (goto-char (match-beginning 1))
- (insert "!"))
(goto-char end)
- (when (re-search-backward "^-- $" start t)
+ (when (re-search-backward message-signature-separator start t)
;; Also peel off any blank lines before the signature.
(forward-line -1)
(while (looking-at "^[ \t]*$")
(insert "\n"))
(funcall message-citation-line-function))))
-(defvar mail-citation-hook) ;Compiler directive
+(defvar mail-citation-hook) ;Compiler directive
(defun message-cite-original ()
"Cite function in the standard Message manner."
(if (and (boundp 'mail-citation-hook)
message-indent-citation-function
(list message-indent-citation-function)))))
(goto-char start)
- ;; Quote parts.
- (while (re-search-forward "<#/?!*\\(multi\\|part\\)>" end t)
- (goto-char (match-beginning 1))
- (insert "!"))
- (goto-char start)
(while functions
(funcall (pop functions)))
(when message-citation-line-function
(bufname (read-buffer (format "Kill buffer: (default %s) "
(buffer-name)))))
(if (or (not bufname)
+ (string-equal bufname "")
(string-equal bufname (buffer-name)))
(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)
- (not (eq 'tty (device-type))))
- window-system
+ (when (and (or (static-if (featurep 'xemacs)
+ (device-on-window-system-p)
+ window-system)
(>= emacs-major-version 20))
(or (and (eq message-delete-frame-on-exit t)
(select-frame frame)
(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))))
+ (unless sent
+ (error "No methods specified to send by"))
+ (prog1
+ (when (and success sent)
+ (message-do-fcc)
+ ;;(when (fboundp 'mail-hist-put-headers-into-history)
+ ;; (mail-hist-put-headers-into-history))
+ (save-excursion
+ (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)
+ (kill-buffer message-encoding-buffer)))))
(defun message-send-via-mail (arg)
"Send the current message via mail."
(message-check 'invisible-text
(when (text-property-any (point-min) (point-max) 'invisible t)
(put-text-property (point-min) (point-max) 'invisible nil)
- (unless (yes-or-no-p "Invisible text found and made visible; continue posting? ")
+ (unless (yes-or-no-p
+ "Invisible text found and made visible; continue posting? ")
(error "Invisible text found and made visible")))))
(defun message-add-action (action &rest types)
;; Remove some headers.
(save-restriction
(message-narrow-to-headers)
+;; We Semi-gnus people have no use for it.
+;; ;; We (re)generate the Lines header.
+;; (when (memq 'Lines message-required-mail-headers)
+;; (message-generate-headers '(Lines)))
;; Remove some headers.
(message-remove-header message-ignored-mail-headers t))
(goto-char (point-max))
(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)
;; Remove some headers.
(save-restriction
(message-narrow-to-headers)
+;; We Semi-gnus people have no use for it.
+;; ;; We (re)generate the Lines header.
+;; (when (memq 'Lines message-required-mail-headers)
+;; (message-generate-headers '(Lines)))
;; Remove some headers.
(message-remove-header message-ignored-news-headers t))
(goto-char (point-max))
(replace-match "\n")
(backward-char 1)
(run-hooks 'message-send-news-hook)
- ;;(require (car method))
- ;;(funcall (intern (format "%s-open-server" (car method)))
- ;;(cadr method) (cddr method))
- ;;(setq result
- ;; (funcall (intern (format "%s-request-post" (car method)))
- ;; (cadr method)))
(gnus-open-server method)
(gnus-request-post method)
))
(defun message-check-news-header-syntax ()
(and
+ ;; Check Newsgroups header.
+ (message-check 'newsgroyps
+ (let ((group (message-fetch-field "newsgroups")))
+ (or
+ (and group
+ (not (string-match "\\`[ \t]*\\'" group)))
+ (ignore
+ (message
+ "The newsgroups field is empty or missing. Posting is denied.")))))
;; Check the Subject header.
(message-check 'subject
(let* ((case-fold-search t)
(message-check 'from
(let* ((case-fold-search t)
(from (message-fetch-field "from"))
- (ad (nth 1 (std11-extract-address-components from))))
+ ad)
(cond
((not from)
(message "There is no From line. Posting is denied.")
nil)
- ((or (not (string-match "@[^\\.]*\\." ad)) ;larsi@ifi
+ ((or (not (string-match
+ "@[^\\.]*\\."
+ (setq ad (nth 1 (mail-extract-address-components
+ from))))) ;larsi@ifi
(string-match "\\.\\." ad) ;larsi@ifi..uio
(string-match "@\\." ad) ;larsi@.ifi.uio
(string-match "\\.$" ad) ;larsi@ifi.uio.
;; Check the length of the signature.
(message-check 'signature
(goto-char (point-max))
- (if (or (not (re-search-backward message-signature-separator nil t))
- (search-forward message-forward-end-separator nil t))
- t
- (if (> (count-lines (point) (point-max)) 5)
- (y-or-n-p
- (format
- "Your .sig is %d lines; it should be max 4. Really post? "
- (1- (count-lines (point) (point-max)))))
- t)))))
+ (if (> (count-lines (point) (point-max)) 5)
+ (y-or-n-p
+ (format
+ "Your .sig is %d lines; it should be max 4. Really post? "
+ (1- (count-lines (point) (point-max)))))
+ t))))
(defun message-check-mail-syntax ()
"Check the syntax of the message."
"Process Fcc headers in the current buffer."
(let ((case-fold-search t)
(coding-system-for-write 'raw-text)
+ (output-coding-system 'raw-text)
list file)
(save-excursion
(set-buffer (get-buffer-create " *message temp*"))
;; colon, if there is none.
(if (/= (char-after) ? ) (insert " ") (forward-char 1))
;; Find out whether the header is empty...
- (looking-at "[ \t]*$")))
+ (looking-at "[ \t]*\n[^ \t]")))
;; So we find out what value we should insert.
(setq value
(cond
(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,
- ;; and if so, call it.
- ((message-functionp message-generate-new-buffers)
- (funcall message-generate-new-buffers type to group))
;; Generate a new buffer name The Message Way.
((eq message-generate-new-buffers 'unique)
(generate-new-buffer-name
"")
(if (and group (not (string= group ""))) (concat " on " group) "")
"*")))
+ ;; 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))
((eq message-generate-new-buffers 'unsent)
(generate-new-buffer-name
(concat "*unsent " type
(t
(format "*%s message*" type))))
+(defmacro message-pop-to-buffer-1 (buffer)
+ `(if pop-up-frames
+ (let (special-display-buffer-names
+ special-display-regexps
+ same-window-buffer-names
+ same-window-regexps)
+ (pop-to-buffer ,buffer))
+ (pop-to-buffer ,buffer)))
+
(defun message-pop-to-buffer (name)
"Pop to buffer NAME, and warn if it already exists and is modified."
- (let ((pop-up-frames pop-up-frames)
- (special-display-buffer-names special-display-buffer-names)
- (special-display-regexps special-display-regexps)
- (same-window-buffer-names same-window-buffer-names)
- (same-window-regexps same-window-regexps)
- (buffer (get-buffer name))
- (cur (current-buffer)))
- (if (or (and (featurep 'xemacs)
- (not (eq 'tty (device-type))))
- window-system
- (>= emacs-major-version 20))
- (when message-use-multi-frames
- (setq pop-up-frames t
- special-display-buffer-names nil
- special-display-regexps nil
- same-window-buffer-names nil
- same-window-regexps nil))
- (setq pop-up-frames nil))
+ (let ((buffer (get-buffer name))
+ (pop-up-frames (and (or (static-if (featurep 'xemacs)
+ (device-on-window-system-p)
+ window-system)
+ (>= emacs-major-version 20))
+ message-use-multi-frames)))
(if (and buffer
(buffer-name buffer))
(progn
- (set-buffer (pop-to-buffer buffer))
+ (message-pop-to-buffer-1 buffer)
(when (and (buffer-modified-p)
(not (y-or-n-p
"Message already being composed; erase? ")))
(error "Message being composed")))
- (set-buffer (pop-to-buffer name)))
+ (message-pop-to-buffer-1 name))
(erase-buffer)
(message-mode)
(when pop-up-frames
- (make-local-variable 'message-original-frame)
- (setq message-original-frame (selected-frame)))))
+ (set (make-local-variable 'message-original-frame) (selected-frame)))))
(defun message-do-send-housekeeping ()
"Kill old message buffers."
(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.
(setq buffer-file-name (expand-file-name "*message*"
message-auto-save-directory))
(setq buffer-auto-save-file-name (make-auto-save-file-name)))
- (clear-visited-file-modtime)))
+ (clear-visited-file-modtime)
+ (static-if (boundp 'MULE)
+ (set-file-coding-system message-draft-coding-system)
+ (setq buffer-file-coding-system message-draft-coding-system))))
(defun message-disassociate-draft ()
"Disassociate the message buffer from the drafts directory."
(nndraft-request-expire-articles
(list message-draft-article) "drafts" nil t)))
+(defun message-insert-headers ()
+ "Generate the headers for the article."
+ (interactive)
+ (save-excursion
+ (save-restriction
+ (message-narrow-to-headers)
+ (when (message-news-p)
+ (message-generate-headers
+ (delq 'Lines
+ (delq 'Subject
+ (copy-sequence message-required-news-headers)))))
+ (when (message-mail-p)
+ (message-generate-headers
+ (delq 'Lines
+ (delq 'Subject
+ (copy-sequence message-required-mail-headers))))))))
+
\f
;;;
from subject date to cc
references message-id follow-to
(inhibit-point-motion-hooks t)
- mct never-mct mft mrt gnus-warning)
+ (message-this-is-mail t)
+ mct never-mct mft mrt gnus-warning in-reply-to)
(save-restriction
(message-narrow-to-head)
;; Allow customizations to have their say.
gnus-warning (message-fetch-field "gnus-warning"))
(when (and gnus-warning (string-match "<[^>]+>" gnus-warning))
(setq message-id (match-string 0 gnus-warning)))
+ ;; 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.
(setq subject (message-make-followup-subject subject))
sends a copy of your response to the author.")))
(setq mct (or mrt from)))
((and (eq message-use-mail-copies-to 'ask)
- (not
+ (not
(message-y-or-n-p
(concat "Obey Mail-Copies-To: " mct " ? ") t "\
You should normally obey the Mail-Copies-To: header.
sends a copy of your response to the author.")))
(setq mct (or mrt from)))
((and (eq message-use-mail-copies-to 'ask)
- (not
+ (not
(message-y-or-n-p
(concat "Obey Mail-Copies-To: " mct " ? ") t "\
You should normally obey the Mail-Copies-To: header.
"")
mail-header-separator "\n"
message-cancel-message)
+ (run-hooks 'message-cancel-hook)
(message "Canceling your article...")
(if (let ((message-syntax-checks
'dont-check-for-anything-just-trust-me)
(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 ()
(replace-match ""))
(buffer-string)))
-
+
;;; Forwarding messages.
(defun message-forward-subject-author-subject (subject)
(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))
(let ((cur (current-buffer))
(subject (message-make-forward-subject))
art-beg)
- (if news (message-news nil subject) (message-mail nil subject))
+ (if news
+ (message-news nil subject)
+ (message-mail nil subject))
;; Put point where we want it before inserting the forwarded
;; message.
(if message-signature-before-forwarded-message
(message "Resending message to %s...done" address)))
(defun message-bounce-setup-for-mime-edit ()
- (goto-char (point-min))
- (when (search-forward (concat "\n" mail-header-separator "\n") nil t)
- (replace-match "\n\n"))
(set (make-local-variable 'message-setup-hook) nil)
(mime-edit-again))
(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!")
(defun message-mime-insert-article (&optional full-headers)
(interactive "P")
(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
;;; MIME functions
;;;
-(defun message-insert-mime-part (file type)
- "Insert a multipart/alternative part into the buffer."
- (interactive
- (let* ((file (read-file-name "Insert file: " nil nil t))
- (type (mm-default-file-encoding file)))
- (list file
- (completing-read
- (format "MIME type for %s: " file)
- (mapcar (lambda (m) (list (cdr m))) mailcap-mime-extensions)
- nil nil type))))
- (insert (format "<#part type=%s filename=\"%s\"><#/part>\n"
- type file)))
+(defvar message-inhibit-body-encoding t)
(defun message-encode-message-body ()
- (message-goto-body)
- (save-restriction
- (narrow-to-region (point) (point-max))
- (let ((new (mml-generate-mime)))
- (delete-region (point-min) (point-max))
- (insert new)
- (goto-char (point-min))
- (widen)
- (forward-line -1)
- (let ((beg (point))
- (line (buffer-substring (point) (progn (forward-line 1) (point)))))
- (delete-region beg (point))
- (insert "Mime-Version: 1.0\n")
- (search-forward "\n\n")
- (forward-char -1)
- (insert line)
- (when (save-excursion
- (re-search-backward "^Content-Type: multipart/" nil t))
- (insert "This is a MIME multipart message. If you are reading\n")
- (insert "this, you shouldn't.\n"))))))
+ (unless message-inhibit-body-encoding
+ (let ((mail-parse-charset (or mail-parse-charset
+ message-default-charset
+ message-posting-charset))
+ (case-fold-search t)
+ lines content-type-p)
+ (message-goto-body)
+ (save-restriction
+ (narrow-to-region (point) (point-max))
+ (let ((new (mml-generate-mime)))
+ (when new
+ (delete-region (point-min) (point-max))
+ (insert new)
+ (goto-char (point-min))
+ (if (eq (aref new 0) ?\n)
+ (delete-char 1)
+ (search-forward "\n\n")
+ (setq lines (buffer-substring (point-min) (1- (point))))
+ (delete-region (point-min) (point))))))
+ (save-restriction
+ (message-narrow-to-headers-or-head)
+ (message-remove-header "Mime-Version")
+ (goto-char (point-max))
+ (insert "MIME-Version: 1.0\n")
+ (when lines
+ (insert lines))
+ (setq content-type-p
+ (re-search-backward "^Content-Type:" nil t)))
+ (save-restriction
+ (message-narrow-to-headers-or-head)
+ (message-remove-first-header "Content-Type")
+ (message-remove-first-header "Content-Transfer-Encoding"))
+ ;; We always make sure that the message has a Content-Type header.
+ ;; This is because some broken MTAs and MUAs get awfully confused
+ ;; when confronted with a message with a MIME-Version header and
+ ;; without a Content-Type header. For instance, Solaris'
+ ;; /usr/bin/mail.
+ (unless content-type-p
+ (goto-char (point-min))
+ (re-search-forward "^MIME-Version:")
+ (forward-line 1)
+ (insert "Content-Type: text/plain; charset=us-ascii\n")))))
(defvar message-save-buffer " *encoding")
(defun message-save-drafts ()
(set-buffer buffer)
(set-buffer-modified-p nil)))
-(run-hooks 'message-load-hook)
-
(provide 'message)
+(run-hooks 'message-load-hook)
+
;;; message.el ends here