;; 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>
;; Keywords: mail, news, MIME
;; This file is part of GNU Emacs.
:group 'message
:group 'faces)
+(defgroup message-frames nil
+ "Message frames"
+ :group 'message)
+
(defcustom message-directory "~/Mail/"
"*Directory from which all other mail file variables are derived."
:group 'message-various
(defcustom message-required-news-headers
'(From Newsgroups Subject Date Message-ID
(optional . Organization) Lines
- (optional . X-Newsreader))
+ (optional . User-Agent))
"*Headers to be generated or prompted for when posting an article.
RFC977 and RFC1036 require From, Date, Newsgroups, Subject,
Message-ID. Organization, Lines, In-Reply-To, Expires, and
-X-Newsreader are optional. If don't you want message to insert some
+User-Agent are optional. If don't you want message to insert some
header, remove it from this list."
:group 'message-news
:group 'message-headers
(defcustom message-required-mail-headers
'(From Subject Date (optional . In-Reply-To) Message-ID Lines
- (optional . X-Mailer))
+ (optional . User-Agent))
"*Headers to be generated or prompted for when mailing a message.
RFC822 required that From, Date, To, Subject and Message-ID be
-included. Organization, Lines and X-Mailer are optional."
+included. Organization, Lines and User-Agent are optional."
:group 'message-mail
:group 'message-headers
:type '(repeat sexp))
:type 'string)
(defcustom message-forward-end-separator
- ""
+ (concat (mime-make-tag "text" "plain") "\n")
"*Delimiter inserted after forwarded messages."
:group 'message-forwarding
:type 'string)
:type 'boolean)
(defcustom message-included-forward-headers
- "^From:\\|^Newsgroups:\\|^Subject:\\|^Date:\\|^Followup-To:\\|^Reply-To:\\|^Organization:\\|^Summary:\\|^Keywords:\\|^To:\\|^Cc:\\|^Posted-To:\\|^Mail-Copies-To:\\|^Apparently-To:\\|^Gnus-Warning:\\|^Resent-\\|^Message-ID:\\|^References:\\|^Content-Transfer-Encoding:\\|^Content-Type:\\|^Mime-Version:"
+ "^From:\\|^Newsgroups:\\|^Subject:\\|^Date:\\|^Followup-To:\\|^Reply-To:\\|^Organization:\\|^Summary:\\|^Keywords:\\|^To:\\|^Cc:\\|^Posted-To:\\|^Mail-Copies-To:\\|^Apparently-To:\\|^Gnus-Warning:\\|^Resent-\\|^Message-ID:\\|^References:\\|^Content-Transfer-Encoding:\\|^Content-Type:\\|^MIME-Version:"
"*Regexp matching headers to be included in forwarded messages."
:group 'message-forwarding
:type 'regexp)
+(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
+successive function.
+
+The provided functions are:
+
+* message-forward-subject-author-subject (Source of article (author or
+ newsgroup)), in brackets followed by the subject
+* message-forward-subject-fwd (Subject of article with 'Fwd:' prepended
+ to it."
+ :group 'message-forwarding
+ :type '(radio (function-item message-forward-subject-author-subject)
+ (function-item message-forward-subject-fwd)))
+
+(defcustom message-wash-forwarded-subjects nil
+ "*If non-nil, try to remove as much old cruft as possible from the subject of messages before generating the new subject of a forward."
+ :group 'message-forwarding
+ :type 'boolean)
+
(defcustom message-ignored-resent-headers "^Return-receipt"
"*All headers that match this regexp will be deleted when resending a message."
:group 'message-interface
(defvar gnus-select-method)
(defcustom message-post-method
(cond ((and (boundp 'gnus-post-method)
+ (listp gnus-post-method)
gnus-post-method)
gnus-post-method)
((boundp 'gnus-select-method)
:group 'message-headers
:type 'boolean)
-(defcustom message-setup-hook
- '(message-maybe-setup-default-charset turn-on-mime-edit)
+(defcustom message-setup-hook '(turn-on-mime-edit)
"Normal hook, run each time a new outgoing message is initialized.
The function `message-setup' runs this hook."
:group 'message-various
(defvar message-reply-buffer nil)
(defvar message-reply-headers nil)
-(defvar message-newsreader nil)
-(defvar message-mailer 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
"A list of actions to be performed before killing a message buffer.")
(defvar message-postpone-actions nil
"A list of actions to be performed after postponing a message.")
+(defvar message-original-frame nil)
(define-widget 'message-header-lines 'text
"All header lines must be LFD terminated."
:group 'message-various
:type 'hook)
+(defcustom message-use-multi-frames nil
+ "Make new frame when sending messages."
+ :group 'message-frames
+ :type 'boolean)
+
+(defcustom message-delete-frame-on-exit nil
+ "Delete frame after sending messages."
+ :group 'message-frames
+ :type '(choice (const :tag "off" nil)
+ (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)
(Expires)
(Message-ID)
(References . message-fill-references)
- (X-Mailer)
- (X-Newsreader))
+ (User-Agent))
"Alist used for formatting headers.")
(eval-and-compile
(autoload 'nndraft-request-expire-articles "nndraft")
(autoload 'gnus-open-server "gnus-int")
(autoload 'gnus-request-post "gnus-int")
+ (autoload 'gnus-copy-article-buffer "gnus-msg")
(autoload 'gnus-alive-p "gnus-util")
(autoload 'rmail-output "rmail"))
(setq paragraph-separate paragraph-start)
(make-local-variable 'message-reply-headers)
(setq message-reply-headers nil)
- (make-local-variable 'message-newsreader)
- (make-local-variable 'message-mailer)
+ (make-local-variable 'message-user-agent)
(make-local-variable 'message-post-method)
(make-local-variable 'message-sent-message-via)
(setq message-sent-message-via nil)
(goto-char (point-min))
(search-forward (concat "\n" mail-header-separator "\n") nil t))
+(defun message-goto-eoh ()
+ "Move point to the end of the headers."
+ (interactive)
+ (message-goto-body)
+ (forward-line -2))
+
(defun message-goto-signature ()
- "Move point to the beginning of the message signature."
+ "Move point to the beginning of the message signature.
+If there is no signature in the article, go to the end and
+return nil."
(interactive)
(goto-char (point-min))
(if (re-search-forward message-signature-separator nil t)
(forward-line 1)
- (goto-char (point-max))))
+ (goto-char (point-max))
+ nil))
\f
(interactive "r")
(save-excursion
(goto-char end)
- (delete-region (point) (progn (message-goto-signature)
- (forward-line -2)
- (point)))
+ (delete-region (point) (if (not (message-goto-signature))
+ (point)
+ (forward-line -2)
+ (point)))
(insert "\n")
(goto-char beg)
(delete-region beg (progn (message-goto-body)
(forward-line 2)
(point))))
- (message-goto-signature)
- (forward-line -2))
+ (when (message-goto-signature)
+ (forward-line -2)))
(defun message-kill-to-signature ()
"Deletes all text up to the signature."
(forward-line 1))))
(goto-char start)))
+(defvar gnus-article-copy)
(defun message-yank-original (&optional arg)
"Insert the message being replied to, if any.
Puts point before the text and mark after.
(insert "\n"))
(funcall message-citation-line-function))))
+(defvar mail-citation-hook) ;Compiler directive
(defun message-cite-original ()
"Cite function in the standard Message manner."
(if (and (boundp 'mail-citation-hook)
;;; Sending messages
;;;
+;; Avoid byte-compile warning.
+(defvar message-encoding-buffer nil)
+(defvar message-edit-buffer nil)
+(defvar message-mime-mode nil)
+
(defun message-send-and-exit (&optional arg)
"Send message like `message-send', then, if no errors, exit from mail buffer."
(interactive "P")
(let ((buf (current-buffer))
- (actions message-exit-actions))
+ (actions message-exit-actions)
+ (frame (selected-frame))
+ (org-frame message-original-frame))
(when (and (message-send arg)
(buffer-name buf))
(if message-kill-buffer-on-exit
(bury-buffer buf)
(when (eq buf (current-buffer))
(message-bury buf)))
- (message-do-actions actions))))
+ (message-do-actions actions)
+ (message-delete-frame frame org-frame)
+ t)))
(defun message-dont-send ()
"Don't send the message you have been editing."
(interactive)
(when (or (not (buffer-modified-p))
(yes-or-no-p "Message modified; kill anyway? "))
- (let ((actions message-kill-actions))
+ (let ((actions message-kill-actions)
+ (frame (selected-frame))
+ (org-frame message-original-frame))
(setq buffer-file-name nil)
(kill-buffer (current-buffer))
- (message-do-actions actions))))
+ (message-do-actions actions)
+ (message-delete-frame frame org-frame))))
+
+(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
+ (>= emacs-major-version 20))
+ (or (and (eq message-delete-frame-on-exit t)
+ (select-frame frame)
+ (or (eq frame org-frame)
+ (prog1
+ (y-or-n-p "Delete this frame?")
+ (message ""))))
+ (and (eq message-delete-frame-on-exit 'ask)
+ (select-frame frame)
+ (prog1
+ (y-or-n-p "Delete this frame?")
+ (message "")))))
+ (delete-frame frame)))
(defun message-bury (buffer)
"Bury this mail buffer."
(undo-boundary)
(let ((inhibit-read-only t))
(put-text-property (point-min) (point-max) 'read-only nil))
- (message-fix-before-sending)
(run-hooks 'message-send-hook)
(message "Sending...")
(let ((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)))
;; Make sure there's a newline at the end of the message.
(goto-char (point-max))
(unless (bolp)
- (insert "\n")))
+ (insert "\n"))
+ ;; Make all invisible text visible.
+ ;;(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?")
+ ;; (error "Invisible text found and made visible")))
+ )
(defun message-add-action (action &rest types)
"Add ACTION to be performed when doing an exit of type TYPES."
(set-buffer errbuf)
(erase-buffer))))
(let ((default-directory "/")
- (coding-system-for-write 'binary))
+ (coding-system-for-write message-send-coding-system))
(apply 'call-process-region
(append (list (point-min) (point-max)
(if (boundp 'sendmail-program)
(run-hooks 'message-send-mail-hook)
;; send the message
(case
- (let ((coding-system-for-write 'binary))
+ (let ((coding-system-for-write message-send-coding-system))
(apply
'call-process-region 1 (point-max) message-qmail-inject-program
nil nil nil
(or mail-host-address
(message-make-fqdn)))
+(defun message-make-user-agent ()
+ "Return user-agent info."
+ (if message-user-agent
+ (save-excursion
+ (goto-char (point-min))
+ (let ((case-fold-search t)
+ user-agent beg p end)
+ (if (re-search-forward "^User-Agent:[ \t]*" nil t)
+ (progn
+ (setq beg (match-beginning 0)
+ p (match-end 0)
+ end (std11-field-end)
+ user-agent (buffer-substring p end))
+ (delete-region beg (1+ end))
+ (concat message-user-agent " " user-agent)
+ )
+ message-user-agent)))))
+
(defun message-generate-headers (headers)
"Prepare article HEADERS.
Headers already prepared in the buffer are not modified."
(To nil)
(Distribution (message-make-distribution))
(Lines (message-make-lines))
- (X-Newsreader message-newsreader)
- (X-Mailer (and (not (message-fetch-field "X-Newsreader"))
- message-mailer))
+ (User-Agent (message-make-user-agent))
(Expires (message-make-expires))
(case-fold-search t)
header value elem)
(defun message-pop-to-buffer (name)
"Pop to buffer NAME, and warn if it already exists and is modified."
- (let ((buffer (get-buffer name))
+ (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))
(if (and buffer
(buffer-name buffer))
(progn
(error "Message being composed")))
(set-buffer (pop-to-buffer name)))
(erase-buffer)
- (message-mode)))
+ (message-mode)
+ (when pop-up-frames
+ (make-local-variable 'message-original-frame)
+ (setq message-original-frame (selected-frame)))))
(defun message-do-send-housekeeping ()
"Kill old message buffers."
(defun message-mail (&optional to subject
other-headers continue switch-function
yank-action send-actions)
- "Start editing a mail message to be sent."
+ "Start editing a mail message to be sent.
+OTHER-HEADERS is an alist of header/value pairs."
(interactive)
(let ((message-this-is-mail t))
(message-pop-to-buffer (message-buffer-name "mail" to))
(if wide to-address nil)))
(setq message-reply-headers
- (vector 0 subject from date message-id references 0 0 ""))
+ (make-full-mail-header-from-decoded-header
+ 0 subject from date message-id references 0 0 ""))
(message-setup
`((Subject . ,subject)
cur)
(setq message-reply-headers
- (vector 0 subject from date message-id references 0 0 ""))))
+ (make-full-mail-header-from-decoded-header
+ 0 subject from date message-id references 0 0 ""))))
;;;###autoload
This is done simply by taking the old article and adding a Supersedes
header line with the old Message-ID."
(interactive)
- (let ((cur (current-buffer)))
+ (let ((cur (current-buffer))
+ (sender (message-fetch-field "sender"))
+ (from (message-fetch-field "from")))
;; Check whether the user owns the article that is to be superseded.
- (unless (string-equal
- (downcase (or (message-fetch-field "sender")
- (cadr (mail-extract-address-components
- (message-fetch-field "from")))))
- (downcase (message-make-sender)))
+ (unless (or (and sender
+ (string-equal
+ (downcase sender)
+ (downcase (message-make-sender))))
+ (string-equal
+ (downcase (cadr (mail-extract-address-components from)))
+ (downcase (cadr (mail-extract-address-components
+ (message-make-from))))))
(error "This article is not yours"))
;; Get a normal message buffer.
(message-pop-to-buffer (message-buffer-name "supersede"))
(insert-file-contents file-name nil)))
(t (error "message-recover cancelled")))))
+;;; Washing Subject:
+
+(defun message-wash-subject (subject)
+ "Remove junk like \"Re:\", \"(fwd)\", etc. that was added to the subject by previous forwarders, replyers, etc."
+ (nnheader-temp-write nil
+ (insert-string subject)
+ (goto-char (point-min))
+ ;; strip Re/Fwd stuff off the beginning
+ (while (re-search-forward
+ "\\([Rr][Ee]:\\|[Ff][Ww][Dd]\\(\\[[0-9]*\\]\\)?:\\|[Ff][Ww]:\\)" nil t)
+ (replace-match ""))
+
+ ;; and gnus-style forwards [foo@bar.com] subject
+ (goto-char (point-min))
+ (while (re-search-forward "\\[[^ \t]*\\(@\\|\\.\\)[^ \t]*\\]" nil t)
+ (replace-match ""))
+
+ ;; and off the end
+ (goto-char (point-max))
+ (while (re-search-backward "([Ff][Ww][Dd])" nil t)
+ (replace-match ""))
+
+ ;; and finally, any whitespace that was left-over
+ (goto-char (point-min))
+ (while (re-search-forward "^[ \t]+" nil t)
+ (replace-match ""))
+ (goto-char (point-max))
+ (while (re-search-backward "[ \t]+$" nil t)
+ (replace-match ""))
+
+ (buffer-string)))
+
;;; Forwarding messages.
+(defun message-forward-subject-author-subject (subject)
+ "Generate a subject for a forwarded message.
+The form is: [Source] Subject, where if the original message was mail,
+Source is the sender, and if the original message was news, Source is
+the list of newsgroups is was posted to."
+ (concat "["
+ (or (message-fetch-field
+ (if (message-news-p) "newsgroups" "from"))
+ "(nowhere)")
+ "] " subject))
+
+(defun message-forward-subject-fwd (subject)
+ "Generate a subject for a forwarded message.
+The form is: Fwd: Subject, where Subject is the original subject of
+the message."
+ (concat "Fwd: " subject))
+
(defun message-make-forward-subject ()
"Return a Subject header suitable for the message in the current buffer."
(save-excursion
(save-restriction
(current-buffer)
(message-narrow-to-head)
- (concat "[" (or (message-fetch-field
- (if (message-news-p) "newsgroups" "from"))
- "(nowhere)")
- "] " (or (eword-decode-unstructured-field-body
- (message-fetch-field "Subject") ""))))))
+ (let ((funcs message-make-forward-subject-function)
+ (subject (if message-wash-forwarded-subjects
+ (message-wash-subject
+ (or (eword-decode-field
+ 'Subject (message-fetch-field "Subject"))
+ ""))
+ (or (eword-decode-field
+ 'Subject (message-fetch-field "Subject"))
+ ""))))
+ ;; Make sure funcs is a list.
+ (and funcs
+ (not (listp funcs))
+ (setq funcs (list funcs)))
+ ;; Apply funcs in order, passing subject generated by previous
+ ;; func to the next one.
+ (while funcs
+ (when (message-functionp (car funcs))
+ (setq subject (funcall (car funcs) subject)))
+ (setq funcs (cdr funcs)))
+ subject))))
;;;###autoload
(defun message-forward (&optional news)
(insert-buffer-substring cur)
(undo-boundary)
(message-narrow-to-head)
- (if (and (message-fetch-field "Mime-Version")
+ (if (and (message-fetch-field "MIME-Version")
(setq boundary (message-fetch-field "Content-Type")))
(if (string-match "boundary=\"\\([^\"]+\\)\"" boundary)
(setq boundary (concat (match-string 1 boundary) " *\n"
(point))))
(hashtb (and (boundp 'gnus-active-hashtb) gnus-active-hashtb))
(completions (all-completions string hashtb))
- (cur (current-buffer))
comp)
(delete-region b (point))
(cond
(let ((locals (save-excursion
(set-buffer buffer)
(buffer-local-variables)))
- (regexp "^gnus\\|^nn\\|^message"))
+ (regexp "^\\(gnus\\|nn\\|message\\|user-\\(mail-address\\|full-name\\)\\)"))
(mapcar
(lambda (local)
(when (and (consp local)
;;; @ for MIME Edit mode
;;;
-(defun message-maybe-setup-default-charset ()
- (let ((charset
- (and (boundp 'gnus-summary-buffer)
- (buffer-live-p gnus-summary-buffer)
- (save-excursion
- (set-buffer gnus-summary-buffer)
- default-mime-charset))))
- (if charset
- (progn
- (make-local-variable 'default-mime-charset)
- (setq default-mime-charset charset)
- ))))
-
(defun message-maybe-encode ()
(when message-mime-mode
(run-hooks 'mime-edit-translate-hook)