X-Git-Url: http://git.chise.org/gitweb/?a=blobdiff_plain;f=lisp%2Fgnus-msg.el;h=ad0fef60efdc5e865bf3e20ae678499880db5de0;hb=8d5b94488b8fe507a83eb5475ecaa54afb8a98b8;hp=7ad418543b57347d775999383503d2413541a6d2;hpb=d14bd1a9c69a77dfcb3c52b9960cf1d73610f9ed;p=elisp%2Fgnus.git- diff --git a/lisp/gnus-msg.el b/lisp/gnus-msg.el index 7ad4185..ad0fef6 100644 --- a/lisp/gnus-msg.el +++ b/lisp/gnus-msg.el @@ -1,5 +1,6 @@ ;;; gnus-msg.el --- mail and post interface for Semi-gnus -;; Copyright (C) 1995,96,97,98,99 Free Software Foundation, Inc. +;; Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001 +;; Free Software Foundation, Inc. ;; Author: Masanobu UMEDA ;; Lars Magne Ingebrigtsen @@ -31,33 +32,34 @@ ;;; Code: (eval-when-compile (require 'cl)) +(eval-when-compile (require 'static)) (require 'gnus) (require 'gnus-ems) (require 'message) (require 'gnus-art) -(defcustom gnus-post-method nil +(defcustom gnus-post-method 'current "*Preferred method for posting USENET news. -If this variable is `current', Gnus will use the \"current\" select -method when posting. If it is nil (which is the default), Gnus will -use the native posting method of the server. +If this variable is `current' (which is the default), Gnus will use +the \"current\" select method when posting. If it is nil, Gnus will +use the native select method when posting. This method will not be used in mail groups and the like, only in \"real\" newsgroups. If not nil nor `native', the value must be a valid method as discussed -in the documentation of `gnus-select-method'. It can also be a list of -methods. If that is the case, the user will be queried for what select +in the documentation of `gnus-select-method'. It can also be a list of +methods. If that is the case, the user will be queried for what select method to use when posting." :group 'gnus-group-foreign :type `(choice (const nil) - (const current) + (const current) (const native) (sexp :tag "Methods" ,gnus-select-method))) -(defvar gnus-outgoing-message-group nil +(defcustom gnus-outgoing-message-group nil "*All outgoing messages will be put in this group. If you want to store all your outgoing mail and articles in the group \"nnml:archive\", you set this variable to that value. This variable @@ -66,18 +68,25 @@ can also be a list of group names. If you want to have greater control over what group to put each message in, you can set this variable to a function that checks the current newsgroup name and then returns a suitable group name (or list -of names).") +of names)." + :group 'gnus-message + :type '(choice (string :tag "Group") + (function))) -(defvar gnus-mailing-list-groups nil +(defcustom gnus-mailing-list-groups nil "*Regexp matching groups that are really mailing lists. This is useful when you're reading a mailing list that has been gatewayed to a newsgroup, and you want to followup to an article in -the group.") +the group." + :group 'gnus-message + :type 'regexp) -(defvar gnus-add-to-list nil - "*If non-nil, add a `to-list' parameter automatically.") +(defcustom gnus-add-to-list nil + "*If non-nil, add a `to-list' parameter automatically." + :group 'gnus-message + :type 'boolean) -(defvar gnus-crosspost-complaint +(defcustom gnus-crosspost-complaint "Hi, You posted the article below with the following Newsgroups header: @@ -93,25 +102,77 @@ Thank you. " "Format string to be inserted when complaining about crossposts. The first %s will be replaced by the Newsgroups header; -the second with the current group name.") - -(defvar gnus-message-setup-hook '(gnus-maybe-setup-default-charset) - "Hook run after setting up a message buffer.") - -(defvar gnus-bug-create-help-buffer t - "*Should we create the *Gnus Help Bug* buffer?") - -(defvar gnus-posting-styles nil - "*Alist of styles to use when posting.") +the second with the current group name." + :group 'gnus-message + :type 'string) + +(defcustom gnus-message-setup-hook '(gnus-maybe-setup-default-charset) + "Hook run after setting up a message buffer." + :group 'gnus-message + :type 'hook) + +(defcustom gnus-bug-create-help-buffer t + "*Should we create the *Gnus Help Bug* buffer?" + :group 'gnus-message + :type 'boolean) + +(defcustom gnus-posting-styles nil + "*Alist of styles to use when posting." + :group 'gnus-message + :type '(repeat (cons (choice (regexp) + (function) + (variable) + (sexp)) + (repeat (list + (choice (const signature) + (const signature-file) + (const organization) + (const address) + (const name) + (const body) + (string :tag "Header")) + (choice (string) + (function) + (variable) + (sexp))))))) + +(defcustom gnus-inews-mark-gcc-as-read nil + "If non-nil, automatically mark Gcc articles as read." + :group 'gnus-message + :type 'boolean) (defcustom gnus-group-posting-charset-alist - '(("^no\\." iso-8859-1) - (message-this-is-mail nil) - (".*" iso-8859-1) - (message-this-is-news iso-8859-1)) - "Alist of regexps (to match group names) and default charsets to be unencoded when posting." - :type '(repeat (list (regexp :tag "Group") - (symbol :tag "Charset"))) + '(("^\\(no\\|fr\\)\\.[^,]*\\(,[ \t\n]*\\(no\\|fr\\)\\.[^,]*\\)*$" iso-8859-1 (iso-8859-1)) + ("^\\(fido7\\|relcom\\)\\.[^,]*\\(,[ \t\n]*\\(fido7\\|relcom\\)\\.[^,]*\\)*$" koi8-r (koi8-r)) + (message-this-is-mail nil nil) + (message-this-is-news nil t)) + "Alist of regexps and permitted unencoded charsets for posting. +Each element of the alist has the form (TEST HEADER BODY-LIST), where +TEST is either a regular expression matching the newsgroup header or a +variable to query, +HEADER is the charset which may be left unencoded in the header (nil +means encode all charsets), +BODY-LIST is a list of charsets which may be encoded using 8bit +content-transfer encoding in the body, or one of the special values +nil (always encode using quoted-printable) or t (always use 8bit). + +Note that any value other than nil for HEADER infringes some RFCs, so +use this option with care." + :type '(repeat (list :tag "Permitted unencoded charsets" + (choice :tag "Where" + (regexp :tag "Group") + (const :tag "Mail message" + :value message-this-is-mail) + (const :tag "News article" + :value message-this-is-news)) + (choice :tag "Header" + (const :tag "None" nil) + (symbol :tag "Charset")) + (choice :tag "Body" + (const :tag "Any" :value t) + (const :tag "None" :value nil) + (repeat :tag "Charsets" + (symbol :tag "Charset"))))) :group 'gnus-charset) ;;; Internal variables. @@ -121,9 +182,12 @@ the second with the current group name.") (defvar gnus-message-buffer "*Mail Gnus*") (defvar gnus-article-copy nil) +(defvar gnus-check-before-posting nil) (defvar gnus-last-posting-server nil) (defvar gnus-message-group-art nil) +(defvar gnus-msg-force-broken-reply-to nil) + (defconst gnus-bug-message (format "Sending a bug report to the Gnus Towers. ======================================== @@ -174,18 +238,23 @@ Thank you for your help in stamping out bugs. "c" gnus-summary-cancel-article "s" gnus-summary-supersede-article "r" gnus-summary-reply + "y" gnus-summary-yank-message "R" gnus-summary-reply-with-original "w" gnus-summary-wide-reply "W" gnus-summary-wide-reply-with-original + "v" gnus-summary-very-wide-reply + "V" gnus-summary-very-wide-reply-with-original "n" gnus-summary-followup-to-mail "N" gnus-summary-followup-to-mail-with-original "m" gnus-summary-mail-other-window "u" gnus-uu-post-news "\M-c" gnus-summary-mail-crosspost-complaint + "Br" gnus-summary-reply-broken-reply-to + "BR" gnus-summary-reply-broken-reply-to-with-original "om" gnus-summary-mail-forward "op" gnus-summary-post-forward - "Om" gnus-summary-mail-digest - "Op" gnus-summary-post-digest) + "Om" gnus-summary-digest-mail-forward + "Op" gnus-summary-digest-post-forward) (gnus-define-keys (gnus-send-bounce-map "D" gnus-summary-send-map) "b" gnus-summary-resend-bounced-mail @@ -202,7 +271,7 @@ Thank you for your help in stamping out bugs. (group (make-symbol "gnus-setup-message-group"))) `(let ((,winconf (current-window-configuration)) (,buffer (buffer-name (current-buffer))) - (,article (and gnus-article-reply (gnus-summary-article-number))) + (,article gnus-article-reply) (,group gnus-newsgroup-name) (message-header-setup-hook (copy-sequence message-header-setup-hook)) @@ -217,18 +286,68 @@ Thank you for your help in stamping out bugs. (unwind-protect (progn ,@forms) - (gnus-inews-add-send-actions ,winconf ,buffer ,article) + (gnus-inews-add-send-actions ,winconf ,buffer ,article ,config) + (gnus-inews-insert-draft-meta-information ,group ,article) (setq gnus-message-buffer (current-buffer)) (set (make-local-variable 'gnus-message-group-art) (cons ,group ,article)) (set (make-local-variable 'gnus-newsgroup-name) ,group) - (set (make-local-variable 'message-posting-charset) - (gnus-setup-posting-charset ,group)) (gnus-run-hooks 'gnus-message-setup-hook)) (gnus-add-buffer) (gnus-configure-windows ,config t) (set-buffer-modified-p nil)))) +(defun gnus-inews-insert-draft-meta-information (group article) + (save-excursion + (when (and group + (not (string= group "")) + (not (message-fetch-field gnus-draft-meta-information-header))) + (goto-char (point-min)) + (insert gnus-draft-meta-information-header ": (\"" group "\" " + (if article (number-to-string + (if (listp article) + (car article) + article)) "\"\"") + ")\n")))) + +;;;###autoload +(defun gnus-msg-mail (&optional to subject other-headers continue + switch-action yank-action send-actions) + "Start editing a mail message to be sent. +Like `message-mail', but with Gnus paraphernalia, particularly the +Gcc: header for archiving purposes." + (interactive) + (let ((buf (current-buffer)) + mail-buf) + (gnus-setup-message 'message + (message-mail to subject other-headers continue + nil yank-action send-actions)) + (when switch-action + (setq mail-buf (current-buffer)) + (switch-to-buffer buf) + (apply switch-action mail-buf nil))) + ;; COMPOSEFUNC should return t if succeed. Undocumented ??? + t) + +;;;###autoload +(defun gnus-button-mailto (address) + "Mail to ADDRESS." + (set-buffer (gnus-copy-article-buffer)) + (gnus-setup-message 'message + (message-reply address))) + +;;;###autoload +(defun gnus-button-reply (&optional to-address wide) + "Like `message-reply'." + (interactive) + (gnus-setup-message 'message + (message-reply to-address wide))) + +;;;###autoload +(define-mail-user-agent 'gnus-user-agent + 'gnus-msg-mail 'message-send-and-exit + 'message-kill-buffer 'message-send-hook) + (defun gnus-setup-posting-charset (group) (let ((alist gnus-group-posting-charset-alist) (group (or group "")) @@ -242,11 +361,15 @@ Thank you for your help in stamping out bugs. (funcall (car elem) group)) (and (symbolp (car elem)) (symbol-value (car elem)))) - (throw 'found (cadr elem)))))))) + (throw 'found (cons (cadr elem) (caddr elem))))))))) -(defun gnus-inews-add-send-actions (winconf buffer article) +(defun gnus-inews-add-send-actions (winconf buffer article &optional config) (make-local-hook 'message-sent-hook) - (add-hook 'message-sent-hook 'gnus-inews-do-gcc nil t) + (add-hook 'message-sent-hook (if gnus-agent 'gnus-agent-possibly-do-gcc + 'gnus-inews-do-gcc) nil t) + (when gnus-agent + (make-local-hook 'message-header-hook) + (add-hook 'message-header-hook 'gnus-agent-possibly-save-gcc nil t)) (setq message-post-method `(lambda (arg) (gnus-post-method arg ,gnus-newsgroup-name))) @@ -259,7 +382,9 @@ Thank you for your help in stamping out bugs. (save-excursion (set-buffer ,buffer) ,(when article - `(gnus-summary-mark-article-as-replied ,article)))) + (if (eq config 'forward) + `(gnus-summary-mark-article-as-forwarded ',article) + `(gnus-summary-mark-article-as-replied ',article))))) 'send)) (put 'gnus-setup-message 'lisp-indent-function 1) @@ -346,29 +471,32 @@ If prefix argument YANK is non-nil, original article is yanked automatically." (gnus-summary-followup (gnus-summary-work-articles arg) t)) (defun gnus-inews-yank-articles (articles) - (let* ((more-than-one (cdr articles)) - (frame (when (and message-use-multi-frames more-than-one) - (window-frame (get-buffer-window (current-buffer))))) - refs beg article) + (let ((more-than-one (cdr articles)) + (cur (current-buffer)) + refs beg article window) (message-goto-body) (while (setq article (pop articles)) (save-window-excursion (set-buffer gnus-summary-buffer) (gnus-summary-select-article nil nil nil article) (gnus-summary-remove-process-mark article)) - (when frame - (select-frame frame)) ;; Gathering references. (when more-than-one (setq refs (message-list-references refs (mail-header-references gnus-current-headers) - (mail-header-message-id gnus-current-headers)))) + (mail-header-message-id gnus-current-headers))) + (when message-use-multi-frames + (when (setq window (get-buffer-window cur t)) + (select-frame (window-frame window))))) (gnus-copy-article-buffer) (let ((message-reply-buffer gnus-article-copy) - (message-reply-headers gnus-current-headers)) + (message-reply-headers + (with-current-buffer gnus-article-copy + ;; The headers are decoded. + (nnheader-parse-head t)))) (message-yank-original) (setq beg (or beg (mark t)))) (when articles @@ -452,59 +580,75 @@ header line with the old Message-ID." (error "Can't find any article buffer") (save-excursion (set-buffer article-buffer) - (save-restriction - ;; Copy over the (displayed) article buffer, delete - ;; hidden text and remove text properties. - (widen) - (let ((inhibit-read-only t)) - (copy-to-buffer gnus-article-copy (point-min) (point-max)) + (let ((gnus-newsgroup-charset (or gnus-article-charset + gnus-newsgroup-charset)) + (gnus-newsgroup-ignored-charsets + (or gnus-article-ignored-charsets + gnus-newsgroup-ignored-charsets))) + (save-restriction + ;; Copy over the (displayed) article buffer, delete + ;; hidden text and remove text properties. + (widen) + (let ((inhibit-read-only t)) + (copy-to-buffer gnus-article-copy (point-min) (point-max)) + (set-buffer gnus-article-copy) + ;; Encode bitmap smileys to ordinary text. + ;; Possibly, the original text might be restored. + (static-unless (featurep 'xemacs) + (when (featurep 'smiley-mule) + (smiley-encode-buffer))) + (gnus-article-delete-text-of-type 'annotation) + (gnus-remove-text-with-property 'gnus-prev) + (gnus-remove-text-with-property 'gnus-next) + (gnus-remove-text-with-property 'x-face-mule-bitmap-image) + (insert + (prog1 + (static-if (featurep 'xemacs) + ;; Revome smiley extents for (possibly) XEmacs 21.1. + (format "%s" + (buffer-substring-no-properties (point-min) + (point-max))) + (buffer-substring-no-properties (point-min) (point-max))) + (erase-buffer)))) + ;; Find the original headers. + (set-buffer gnus-original-article-buffer) + (goto-char (point-min)) + (while (looking-at message-unix-mail-delimiter) + (forward-line 1)) + (setq beg (point)) + (setq end (or (message-goto-body) beg)) + ;; Delete the headers from the displayed articles. (set-buffer gnus-article-copy) - (gnus-article-delete-text-of-type 'annotation) - (gnus-remove-text-with-property 'gnus-prev) - (gnus-remove-text-with-property 'gnus-next) - (gnus-remove-text-with-property 'x-face-mule-bitmap-image) - (insert - (prog1 - (format "%s" (buffer-string)) - (erase-buffer))) - ) - ;; Find the original headers. - (set-buffer gnus-original-article-buffer) - (goto-char (point-min)) - (while (looking-at message-unix-mail-delimiter) - (forward-line 1)) - (setq beg (point)) - (setq end (or (search-forward "\n\n" nil t) (point))) - ;; Delete the headers from the displayed articles. - (set-buffer gnus-article-copy) - (delete-region (goto-char (point-min)) - (or (search-forward "\n\n" nil t) (point-max))) - ;; Insert the original article headers. - (insert-buffer-substring gnus-original-article-buffer beg end) - (article-decode-encoded-words))) + (delete-region (goto-char (point-min)) + (or (message-goto-body) (point-max))) + ;; Insert the original article headers. + (insert-buffer-substring gnus-original-article-buffer beg end) + (article-decode-encoded-words)))) gnus-article-copy))) (defun gnus-post-news (post &optional group header article-buffer yank subject force-news) (when article-buffer (gnus-copy-article-buffer)) - (let ((gnus-article-reply article-buffer) + (let ((gnus-article-reply (and article-buffer (gnus-summary-article-number))) (add-to-list gnus-add-to-list)) (gnus-setup-message (cond (yank 'reply-yank) (article-buffer 'reply) (t 'message)) (let* ((group (or group gnus-newsgroup-name)) + (charset (gnus-group-name-charset nil group)) (pgroup group) to-address to-group mailing-list to-list newsgroup-p) (when group - (setq to-address (gnus-group-find-parameter group 'to-address) + (setq to-address (gnus-parameter-to-address group) to-group (gnus-group-find-parameter group 'to-group) - to-list (gnus-group-find-parameter group 'to-list) + to-list (gnus-parameter-to-list group) newsgroup-p (gnus-group-find-parameter group 'newsgroup) mailing-list (when gnus-mailing-list-groups (string-match gnus-mailing-list-groups group)) - group (gnus-group-real-name group))) + group (gnus-group-name-decode (gnus-group-real-name group) + charset))) (if (or (and to-group (gnus-news-group-p to-group)) newsgroup-p @@ -521,7 +665,13 @@ header line with the old Message-ID." (message-news (or to-group group)) (set-buffer gnus-article-copy) (gnus-msg-treat-broken-reply-to) - (message-followup (if (or newsgroup-p force-news) nil to-group))) + (message-followup (if (or newsgroup-p force-news) + (if (save-restriction + (article-narrow-to-head) + (message-fetch-field "newsgroups")) + nil + "") + to-group))) ;; The is mail. (if post (progn @@ -539,10 +689,11 @@ header line with the old Message-ID." (when yank (gnus-inews-yank-articles yank)))))) -(defun gnus-msg-treat-broken-reply-to () +(defun gnus-msg-treat-broken-reply-to (&optional force) "Remove the Reply-to header iff broken-reply-to." - (when (gnus-group-find-parameter - gnus-newsgroup-name 'broken-reply-to) + (when (or force + (gnus-group-find-parameter + gnus-newsgroup-name 'broken-reply-to)) (save-restriction (message-narrow-to-head) (message-remove-header "reply-to")))) @@ -550,13 +701,15 @@ header line with the old Message-ID." (defun gnus-post-method (arg group &optional silent) "Return the posting method based on GROUP and ARG. If SILENT, don't prompt the user." - (let ((group-method (gnus-find-method-for-group group))) + (let ((gnus-post-method (or (gnus-parameter-post-method group) + gnus-post-method)) + (group-method (gnus-find-method-for-group group))) (cond ;; If the group-method is nil (which shouldn't happen) we use ;; the default method. ((null group-method) (or (and (null (eq gnus-post-method 'active)) gnus-post-method) - gnus-select-method message-post-method)) + gnus-select-method message-post-method)) ;; We want the inverse of the default ((and arg (not (eq arg 0))) (if (eq gnus-post-method 'active) @@ -592,7 +745,9 @@ If SILENT, don't prompt the user." (setq method-alist (mapcar (lambda (m) - (list (concat (cadr m) " (" (symbol-name (car m)) ")") m)) + (if (equal (cadr m) "") + (list (symbol-name (car m)) m) + (list (concat (cadr m) " (" (symbol-name (car m)) ")") m))) post-methods)) ;; Query the user. (cadr @@ -609,6 +764,7 @@ If SILENT, don't prompt the user." ;; Override normal method. ((and (eq gnus-post-method 'current) (not (eq (car group-method) 'nndraft)) + (gnus-get-function group-method 'request-post t) (not arg)) group-method) ((and gnus-post-method @@ -618,52 +774,91 @@ If SILENT, don't prompt the user." (t gnus-select-method)))) - -(defun gnus-extended-version () - "Stringified gnus version." - (concat gnus-product-name "/" gnus-version-number " (based on " - gnus-original-product-name " v" gnus-original-version-number ")")) - -(defun gnus-message-make-user-agent (&optional include-mime-info max-column) - "Return user-agent info. -INCLUDE-MIME-INFO the optional first argument if it is non-nil and the variable - `mime-edit-user-agent-value' exists, the return value will include it. -MAX-COLUMN the optional second argument if it is specified, the return value - will be folded up in the proper way." +(defun gnus-message-make-user-agent (&optional include-mime-info max-column + newline-product) + "Return a user-agent info. If INCLUDE-MIME-INFO is non-nil and the +variable `mime-edit-user-agent-value' is bound, the value will be +included in the return value. If MAX-COLUMN is specified, the return +value will be folded up as it were filled. NEWLINE-PRODUCT specifies +whether a newline should be inserted in front of each product-token. +If the value is t or `hard', it works strictly. Otherwise, if it is +non-nil (e.g. `soft'), it works semi-strictly. + +Here is an example of how to use this function: + +\(add-hook 'gnus-message-setup-hook + (lambda nil + (setq message-user-agent nil) + (save-excursion + (save-restriction + (message-narrow-to-headers) + (goto-char (point-max)) + (insert \"User-Agent: \" + (gnus-message-make-user-agent t 76 'soft) + \"\\n\"))))) +" (let ((user-agent (if (and include-mime-info (boundp 'mime-edit-user-agent-value)) (concat (gnus-extended-version) " " mime-edit-user-agent-value) (gnus-extended-version)))) - (if max-column - (let (boundary) - (unless (natnump max-column) (setq max-column 76)) - (with-temp-buffer - (insert " " user-agent) - (goto-char 13) - (while (re-search-forward "[\n\t ]+" nil t) - (replace-match " ")) - (goto-char 13) - (while (re-search-forward "[^ ()/]+\\(/[^ ()/]+\\)? ?" nil t) - (while (eq ?\( (char-after (point))) - (forward-list) - (skip-chars-forward " ")) - (skip-chars-backward " ") - (if (> (current-column) max-column) - (progn - (if (or (not boundary) (eq ?\n (char-after boundary))) - (progn - (setq boundary (point)) - (unless (eobp) - (delete-char 1) - (insert "\n "))) - (goto-char boundary) - (delete-char 1) - (insert "\n "))) - (setq boundary (point)))) - (buffer-substring 13 (point-max)))) - user-agent))) + (when max-column + (unless (natnump max-column) + (setq max-column 76)) + (with-temp-buffer + (set-buffer-multibyte t) + (insert (mapconcat 'identity (split-string user-agent) " ")) + (goto-char (point-min)) + (let ((bol t) + start agent agents width element swidth) + (while (re-search-forward "\\([^ ]+\\) ?" nil t) + (setq start (match-beginning 0)) + (if (eq (char-after start) ?\() + (progn + (goto-char start) + (forward-list) + (push (buffer-substring start (point)) agent)) + (when agent + (push (nreverse agent) agents)) + (setq agent (list (match-string 1))))) + (when agent + (push (nreverse agent) agents)) + (setq agents (nreverse agents)) + (if (> (+ 12 (string-width (caar agents))) max-column) + (setq user-agent "\n" + width 0) + (setq user-agent "" + width 11)) + (while agents + (setq agent (car agents) + agents (cdr agents)) + (when (and (not bol) + (or (memq newline-product '(t hard)) + (and newline-product + (> (+ width 1 + (string-width (mapconcat 'identity + agent " "))) + max-column)))) + (setq user-agent (concat user-agent "\n") + width 0 + bol t)) + (while agent + (setq element (car agent) + swidth (string-width element) + agent (cdr agent)) + (if bol + (setq user-agent (if (member user-agent '("" "\n")) + (concat user-agent element) + (concat user-agent " " element)) + width (+ width 1 swidth) + bol nil) + (if (> (+ width 1 swidth) max-column) + (setq user-agent (concat user-agent "\n " element) + width (1+ swidth)) + (setq user-agent (concat user-agent " " element) + width (+ width 1 swidth))))))))) + user-agent)) ;;; @@ -672,21 +867,39 @@ MAX-COLUMN the optional second argument if it is specified, the return value ;;; Mail reply commands of Gnus summary mode -(defun gnus-summary-reply (&optional yank wide) - "Start composing a reply mail to the current message. +(defun gnus-summary-reply (&optional yank wide very-wide) + "Start composing a mail reply to the current message. If prefix argument YANK is non-nil, the original article is yanked -automatically." +automatically. +If WIDE, make a wide reply. +If VERY-WIDE, make a very wide reply." (interactive (list (and current-prefix-arg (gnus-summary-work-articles 1)))) ;; Stripping headers should be specified with mail-yank-ignored-headers. (when yank (gnus-summary-goto-subject (car yank))) - (let ((gnus-article-reply t)) + (let ((gnus-article-reply (or yank (gnus-summary-article-number))) + (headers "")) (gnus-setup-message (if yank 'reply-yank 'reply) - (gnus-summary-select-article) + (if (not very-wide) + (gnus-summary-select-article) + (dolist (article very-wide) + (gnus-summary-select-article nil nil nil article) + (save-excursion + (set-buffer (gnus-copy-article-buffer)) + (gnus-msg-treat-broken-reply-to) + (save-restriction + (message-narrow-to-head) + (setq headers (concat headers (buffer-string))))))) (set-buffer (gnus-copy-article-buffer)) - (gnus-msg-treat-broken-reply-to) + (gnus-msg-treat-broken-reply-to gnus-msg-force-broken-reply-to) + (save-restriction + (message-narrow-to-head) + (when very-wide + (erase-buffer) + (insert headers)) + (goto-char (point-max))) (message-reply nil wide) (when yank (gnus-inews-yank-articles yank))))) @@ -697,6 +910,24 @@ The original article will be yanked." (interactive "P") (gnus-summary-reply (gnus-summary-work-articles n) wide)) +(defun gnus-summary-reply-broken-reply-to (&optional yank wide very-wide) + "Like `gnus-summary-reply' except removing reply-to field. +If prefix argument YANK is non-nil, the original article is yanked +automatically. +If WIDE, make a wide reply. +If VERY-WIDE, make a very wide reply." + (interactive + (list (and current-prefix-arg + (gnus-summary-work-articles 1)))) + (let ((gnus-msg-force-broken-reply-to t)) + (gnus-summary-reply yank wide very-wide))) + +(defun gnus-summary-reply-broken-reply-to-with-original (n &optional wide) + "Like `gnus-summary-reply-with-original' except removing reply-to field. +The original article will be yanked." + (interactive "P") + (gnus-summary-reply-broken-reply-to (gnus-summary-work-articles n) wide)) + (defun gnus-summary-wide-reply (&optional yank) "Start composing a wide reply mail to the current message. If prefix argument YANK is non-nil, the original article is yanked @@ -712,6 +943,22 @@ The original article will be yanked." (interactive "P") (gnus-summary-reply-with-original n t)) +(defun gnus-summary-very-wide-reply (&optional yank) + "Start composing a very wide reply mail to the current message. +If prefix argument YANK is non-nil, the original article is yanked +automatically." + (interactive + (list (and current-prefix-arg + (gnus-summary-work-articles 1)))) + (gnus-summary-reply yank t (gnus-summary-work-articles yank))) + +(defun gnus-summary-very-wide-reply-with-original (n) + "Start composing a very wide reply mail to the current message. +The original article will be yanked." + (interactive "P") + (gnus-summary-reply + (gnus-summary-work-articles n) t (gnus-summary-work-articles n))) + (defun gnus-summary-mail-forward (&optional full-headers post) "Forward the current message to another user. If FULL-HEADERS (the prefix), include full headers when forwarding." @@ -721,53 +968,82 @@ If FULL-HEADERS (the prefix), include full headers when forwarding." (let ((charset default-mime-charset)) (set-buffer gnus-original-article-buffer) (make-local-variable 'default-mime-charset) - (setq default-mime-charset charset) - ) + (setq default-mime-charset charset)) (let ((message-included-forward-headers (if full-headers "" message-included-forward-headers))) (message-forward post)))) -;;; XXX: generate Subject and ``Topics''? -(defun gnus-summary-mail-digest (&optional n post) - "Digests and forwards all articles in this series." +(defun gnus-summary-digest-mail-forward (&optional n post) + "Digests and forwards all articles in this series. +If N is a positive number, forward the N next articles. +If N is a negative number, forward the N previous articles. +If N is nil and any articles have been marked with the process mark, +forward those articles instead. +Optional POST will use news to forward instead of mail." (interactive "P") - (let ((subject "Digested Articles") - (articles (gnus-summary-work-articles n)) - article frame) - (gnus-setup-message 'forward - (gnus-summary-select-article) - (if post (message-news nil subject) (message-mail nil subject)) - (when (and message-use-multi-frames (cdr articles)) - (setq frame (window-frame (get-buffer-window (current-buffer))))) - (message-goto-body) - (while (setq article (pop articles)) - (save-window-excursion - (set-buffer gnus-summary-buffer) - (gnus-summary-select-article nil nil nil article) - (gnus-summary-remove-process-mark article)) - (when frame - (select-frame frame)) - (insert (mime-make-tag "message" "rfc822") "\n") - (insert-buffer-substring gnus-original-article-buffer)) - (push-mark) - (message-goto-body) - (mime-edit-enclose-digest-region (point)(mark t))))) - -(defun gnus-summary-post-digest (&optional n) - "Digest and forwards all articles in this series to a newsgroup." + (let ((articles (gnus-summary-work-articles n)) + (topics "Topics:\n") + subject article frame) + (when (car articles) + (gnus-setup-message 'forward + (gnus-summary-select-article) + (if (cdr articles) + (setq articles (sort articles '<) + subject "Digested Articles") + (with-current-buffer gnus-original-article-buffer + (setq subject (message-make-forward-subject)))) + (if post + (message-news nil subject) + (message-mail nil subject)) + (when (and message-use-multi-frames (cdr articles)) + (setq frame (window-frame (get-buffer-window (current-buffer))))) + (message-goto-body) + (while (setq article (pop articles)) + (save-window-excursion + (set-buffer gnus-summary-buffer) + (gnus-summary-select-article nil nil nil article) + (setq topics (concat topics " " + (mail-header-subject gnus-current-headers) + "\n")) + (gnus-summary-remove-process-mark article)) + (when frame + (select-frame frame)) + (insert (mime-make-tag "message" "rfc822") "\n") + (narrow-to-region (point) (point)) + (insert-buffer-substring gnus-original-article-buffer) + (save-restriction + (article-narrow-to-head) + (message-remove-header message-included-forward-headers t nil t)) + (goto-char (point-max)) + (widen)) + (push-mark) + (message-goto-body) + (insert topics) + (message-goto-body) + (mime-edit-enclose-digest-region (point)(mark t)))))) + +(defun gnus-summary-digest-post-forward (&optional n) + "Digest and forwards all articles in this series to a newsgroup. +If N is a positive number, forward the N next articles. +If N is a negative number, forward the N previous articles. +If N is nil and any articles have been marked with the process mark, +forward those articles instead." (interactive "P") - (gnus-summary-mail-digest n t)) + (gnus-summary-digest-mail-forward n t)) (defun gnus-summary-resend-message (address n) "Resend the current article to ADDRESS." - (interactive "sResend message(s) to: \nP") + (interactive + (list (message-read-from-minibuffer "Resend message(s) to: ") + current-prefix-arg)) (let ((articles (gnus-summary-work-articles n)) article) (while (setq article (pop articles)) (gnus-summary-select-article nil nil nil article) (save-excursion (set-buffer gnus-original-article-buffer) - (message-resend address))))) + (message-resend address)) + (gnus-summary-mark-article-as-forwarded article)))) (defun gnus-summary-post-forward (&optional full-headers) "Forward the current article to a newsgroup. @@ -877,35 +1153,32 @@ The current group name will be inserted at \"%s\".") (let ((reply gnus-article-reply) (winconf gnus-prev-winconf) (group gnus-newsgroup-name)) + (unless (and group + (not (gnus-group-read-only-p group))) + (setq group (read-string "Put in group: " nil (gnus-writable-groups)))) - (or (and group (not (gnus-group-read-only-p group))) - (setq group (read-string "Put in group: " nil - (gnus-writable-groups)))) (when (gnus-gethash group gnus-newsrc-hashtb) (error "No such group: %s" group)) - (save-excursion (save-restriction (widen) (message-narrow-to-headers) - (let (gnus-deletable-headers) - (if (message-news-p) - (message-generate-headers message-required-news-headers) - (message-generate-headers message-required-mail-headers))) + (let ((gnus-deletable-headers nil)) + (message-generate-headers + (if (message-news-p) + message-required-news-headers + message-required-mail-headers))) (goto-char (point-max)) (insert "Gcc: " group "\n") (widen))) - (gnus-inews-do-gcc) - - (when (get-buffer gnus-group-buffer) - (when (gnus-buffer-exists-p (car-safe reply)) - (set-buffer (car reply)) - (and (cdr reply) - (gnus-summary-mark-article-as-replied - (cdr reply)))) - (when winconf - (set-window-configuration winconf))))) + (when (and (get-buffer gnus-group-buffer) + (gnus-buffer-exists-p (car-safe reply)) + (cdr reply)) + (set-buffer (car reply)) + (gnus-summary-mark-article-as-replied (cdr reply))) + (when winconf + (set-window-configuration winconf)))) (defun gnus-article-mail (yank) "Send a reply to the address near point. @@ -926,31 +1199,40 @@ If YANK is non-nil, include the original article." (interactive) (unless (gnus-alive-p) (error "Gnus has been shut down")) - (gnus-setup-message 'bug - (delete-other-windows) - (when gnus-bug-create-help-buffer - (switch-to-buffer "*Gnus Help Bug*") - (erase-buffer) - (insert gnus-bug-message) - (goto-char (point-min))) - (message-pop-to-buffer "*Gnus Bug*") - (message-setup - `((To . ,gnus-maintainer) (Cc . ,semi-gnus-developers) (Subject . ""))) + (gnus-setup-message (if (message-mail-user-agent) 'message 'bug) + (unless (message-mail-user-agent) + (message-pop-to-buffer "*Gnus Bug*") + (delete-other-windows) + (when gnus-bug-create-help-buffer + (switch-to-buffer "*Gnus Help Bug*") + (erase-buffer) + (insert gnus-bug-message) + (goto-char (point-min)) + (sit-for 0) + (set-buffer "*Gnus Bug*"))) + (let ((message-this-is-mail t)) + (message-setup `((To . ,gnus-maintainer) (Subject . "")))) (when gnus-bug-create-help-buffer (push `(gnus-bug-kill-buffer) message-send-actions)) (goto-char (point-min)) (re-search-forward (concat "^" (regexp-quote mail-header-separator) "$")) (forward-line 1) - (insert (gnus-version) "\n" + (insert gnus-product-name " " gnus-version-number + " (r" gnus-revision-number ") " + "based on " gnus-original-product-name " v" + gnus-original-version-number "\n" (emacs-version) "\n") (when (and (boundp 'nntp-server-type) (stringp nntp-server-type)) (insert nntp-server-type)) (insert "\n\n\n\n\n") - (save-excursion - (set-buffer (gnus-get-buffer-create " *gnus environment info*")) - (gnus-debug)) - (insert "<#part type=application/x-emacs-lisp buffer=\" *gnus environment info*\" disposition=inline description=\"User settings\"><#/part>") + (let (mime-content-types) + (mime-edit-insert-tag "text" "plain" "; type=emacs-lisp")) + (insert (with-temp-buffer + (gnus-debug) + (buffer-string))) + (let (mime-content-types) + (mime-edit-insert-tag "text" "plain")) (goto-char (point-min)) (search-forward "Subject: " nil t) (message ""))) @@ -959,6 +1241,39 @@ If YANK is non-nil, include the original article." (when (get-buffer "*Gnus Help Bug*") (kill-buffer "*Gnus Help Bug*"))) +(defun gnus-summary-yank-message (buffer n) + "Yank the current article into a composed message." + (interactive + (list (completing-read "Buffer: " (mapcar 'list (message-buffers)) nil t) + current-prefix-arg)) + (when (gnus-buffer-live-p buffer) + (let ((summary-frame (selected-frame)) + (message-frame (when (static-if (featurep 'xemacs) + (device-on-window-system-p) + window-system) + (let ((window (get-buffer-window buffer t))) + (when window + (window-frame window))))) + (separator (concat "^" (regexp-quote mail-header-separator) + "\n"))) + (gnus-summary-iterate n + (gnus-summary-select-article) + (gnus-copy-article-buffer) + (when (frame-live-p message-frame) + (raise-frame message-frame) + (select-frame message-frame)) + (with-current-buffer buffer + (when (save-excursion + (beginning-of-line) + (let (case-fold-search) + (and (not (re-search-backward separator nil t)) + (re-search-forward separator nil t)))) + (goto-char (match-end 0))) + (message-yank-buffer gnus-article-copy)) + (select-frame summary-frame)) + (when (frame-live-p message-frame) + (select-frame message-frame))))) + (defun gnus-debug () "Attempts to go through the Gnus source file and report what variables have been changed. The source file has to be in the Emacs load path." @@ -966,13 +1281,13 @@ The source file has to be in the Emacs load path." (let ((files '("gnus.el" "gnus-sum.el" "gnus-group.el" "gnus-art.el" "gnus-start.el" "gnus-async.el" "gnus-msg.el" "gnus-score.el" "gnus-win.el" "gnus-topic.el" - "nnmail.el" "message.el")) + "nnmail.el" "nntp.el" "message.el")) (point (point)) file expr olist sym) (gnus-message 4 "Please wait while we snoop your variables...") - (sit-for 0) ;; Go through all the files looking for non-default values for variables. (save-excursion + (sit-for 0) (set-buffer (gnus-get-buffer-create " *gnus bug info*")) (while files (erase-buffer) @@ -986,8 +1301,7 @@ The source file has to be in the Emacs load path." (goto-char (point-min)) (while (setq expr (ignore-errors (read (current-buffer)))) (ignore-errors - (and (or (eq (car expr) 'defvar) - (eq (car expr) 'defcustom)) + (and (memq (car expr) '(defvar defcustom defvoo)) (stringp (nth 3 expr)) (or (not (boundp (nth 1 expr))) (not (equal (eval (nth 2 expr)) @@ -995,7 +1309,7 @@ The source file has to be in the Emacs load path." (push (nth 1 expr) olist))))))) (kill-buffer (current-buffer))) (when (setq olist (nreverse olist)) - (insert "------------------ Environment follows ------------------\n\n")) + (insert ";----------------- Environment follows ------------------\n\n")) (while olist (if (boundp (car olist)) (condition-case () @@ -1011,13 +1325,17 @@ The source file has to be in the Emacs load path." (format "(setq %s 'whatever)\n" (car olist)))) (insert ";; (makeunbound '" (symbol-name (car olist)) ")\n")) (setq olist (cdr olist))) - (insert "\n\n") ;; Remove any control chars - they seem to cause trouble for some ;; mailers. (Byte-compiled output from the stuff above.) (goto-char point) (while (re-search-forward "[\000-\010\013-\037\200-\237]" nil t) (replace-match (format "\\%03o" (string-to-char (match-string 0))) - t t)))) + t t)) + ;; Break MIME tags purposely. + (goto-char point) + (while (re-search-forward mime-edit-tag-regexp nil t) + (goto-char (1+ (match-beginning 0))) + (insert "X")))) ;;; Treatment of rejected articles. ;;; Bounced mail. @@ -1044,54 +1362,60 @@ this is a reply." ;;; Gcc handling. +(defun gnus-inews-group-method (group) + (cond + ;; If the group doesn't exist, we assume + ;; it's an archive group... + ((and (null (gnus-get-info group)) + (eq (car (gnus-server-to-method gnus-message-archive-method)) + (car (gnus-server-to-method (gnus-group-method group))))) + gnus-message-archive-method) + ;; Use the method. + ((gnus-info-method (gnus-get-info group)) + (gnus-info-method (gnus-get-info group))) + ;; Find the method. + (t (gnus-server-to-method (gnus-group-method group))))) + ;; Do Gcc handling, which copied the message over to some group. (defun gnus-inews-do-gcc (&optional gcc) (interactive) - (when (gnus-alive-p) - (save-excursion - (save-restriction - (message-narrow-to-headers) - (let ((gcc (or gcc (mail-fetch-field "gcc" nil t))) - (coding-system-for-write 'raw-text) - groups group method) - (when gcc - (message-remove-header "gcc") - (widen) - (setq groups (message-tokenize-header gcc " ,")) - ;; Copy the article over to some group(s). - (while (setq group (pop groups)) - (gnus-check-server - (setq method - (cond ((and (null (gnus-get-info group)) - (eq (car gnus-message-archive-method) - (car - (gnus-server-to-method - (gnus-group-method group))))) - ;; If the group doesn't exist, we assume - ;; it's an archive group... - gnus-message-archive-method) - ;; Use the method. - ((gnus-info-method (gnus-get-info group)) - (gnus-info-method (gnus-get-info group))) - ;; Find the method. - (t (gnus-group-method group))))) - (gnus-check-server method) - (unless (gnus-request-group group t method) - (gnus-request-create-group group method)) - (save-excursion - (nnheader-set-temp-buffer " *acc*") - (insert-buffer-substring message-encoding-buffer) - (gnus-run-hooks 'gnus-before-do-gcc-hook) - (goto-char (point-min)) - (when (re-search-forward - (concat "^" (regexp-quote mail-header-separator) "$") - nil t) - (replace-match "" t t )) - (unless (gnus-request-accept-article group method t t) - (gnus-message 1 "Couldn't store article in group %s: %s" - group (gnus-status-message method)) - (sit-for 2)) - (kill-buffer (current-buffer)))))))))) + (save-excursion + (save-restriction + (message-narrow-to-headers) + (let ((gcc (or gcc (mail-fetch-field "gcc" nil t))) + (coding-system-for-write 'raw-text) + (output-coding-system 'raw-text) + groups group method group-art) + (when gcc + (message-remove-header "gcc") + (widen) + (setq groups (message-unquote-tokens + (message-tokenize-header gcc " ,"))) + ;; Copy the article over to some group(s). + (while (setq group (pop groups)) + (unless (gnus-check-server + (setq method (gnus-inews-group-method group))) + (error "Can't open server %s" (if (stringp method) method + (car method)))) + (unless (gnus-request-group group nil method) + (gnus-request-create-group group method)) + (save-excursion + (nnheader-set-temp-buffer " *acc*") + (insert-buffer-substring message-encoding-buffer) + (gnus-run-hooks 'gnus-before-do-gcc-hook) + (goto-char (point-min)) + (when (re-search-forward + (concat "^" (regexp-quote mail-header-separator) "$") + nil t) + (replace-match "" t t )) + (unless (setq group-art + (gnus-request-accept-article group method t t)) + (gnus-message 1 "Couldn't store article in group %s: %s" + group (gnus-status-message method)) + (sit-for 2)) + (when (and group-art gnus-inews-mark-gcc-as-read) + (gnus-group-mark-article-read group (cdr group-art))) + (kill-buffer (current-buffer))))))))) (defun gnus-inews-insert-gcc () "Insert Gcc headers based on `gnus-outgoing-message-group'." @@ -1116,6 +1440,7 @@ this is a reply." (group (or group gnus-newsgroup-name "")) (gcc-self-val (and gnus-newsgroup-name + (not (equal gnus-newsgroup-name "")) (gnus-group-find-parameter gnus-newsgroup-name 'gcc-self))) result @@ -1191,7 +1516,7 @@ this is a reply." (unless gnus-inhibit-posting-styles (let ((group (or gnus-newsgroup-name "")) (styles gnus-posting-styles) - style match variable attribute value v styles results + style match variable attribute value v results filep name address element) ;; If the group has a posting-style parameter, add it at the end with a ;; regexp matching everything, to be sure it takes precedence over all @@ -1203,10 +1528,17 @@ this is a reply." ;; Go through all styles and look for matches. (dolist (style styles) (setq match (pop style)) + (goto-char (point-min)) (when (cond ((stringp match) ;; Regexp string match on the group name. - (string-match match gnus-newsgroup-name)) + (string-match match group)) + ((eq match 'header) + (and (gnus-buffer-live-p gnus-article-copy) + (with-current-buffer gnus-article-copy + (let ((header (message-fetch-field (pop style)))) + (and header + (string-match (pop style) header)))))) ((or (symbolp match) (gnus-functionp match)) (cond @@ -1226,7 +1558,7 @@ this is a reply." filep nil) (setq value (cond - ((eq (car attribute) :file) + ((eq (car attribute) ':file) (setq filep t) (cadr attribute)) ((eq (car attribute) :value) @@ -1251,48 +1583,55 @@ this is a reply." (setq element 'signature filep t)) ;; Get the contents of file elems. - (when filep + (when (and filep v) (setq v (with-temp-buffer (insert-file-contents v) (buffer-string)))) (setq results (delq (assoc element results) results)) - (push (cons element - v) results)))) + (push (cons element v) results)))) ;; Now we have all the styles, so we insert them. (setq name (assq 'name results) address (assq 'address results)) (setq results (delq name (delq address results))) (make-local-variable 'message-setup-hook) (dolist (result results) - (when (cdr result) - (add-hook 'message-setup-hook - (cond - ((eq 'body (car result)) + (add-hook 'message-setup-hook + (cond + ((eq 'eval (car result)) + 'ignore) + ((eq 'body (car result)) + `(lambda () + (save-excursion + (message-goto-body) + (insert ,(cdr result))))) + ((eq 'signature (car result)) + (set (make-local-variable 'message-signature) nil) + (set (make-local-variable 'message-signature-file) nil) + (if (not (cdr result)) + 'ignore `(lambda () (save-excursion - (message-goto-body) - (insert ,(cdr result))))) - ((eq 'signature (car result)) - (set (make-local-variable 'message-signature) nil) - (set (make-local-variable 'message-signature-file) nil) + (let ((message-signature ,(cdr result))) + (when message-signature + (message-insert-signature))))))) + (t + (let ((header + (if (symbolp (car result)) + (capitalize (symbol-name (car result))) + (car result)))) `(lambda () (save-excursion - (let ((message-signature ,(cdr result))) - (message-insert-signature))))) - (t - (let ((header - (if (symbolp (car result)) - (capitalize (symbol-name (car result))) - (car result)))) - `(lambda () - (save-excursion - (message-remove-header ,header) - (message-goto-eoh) - (insert ,header ": " ,(cdr result) "\n"))))))))) + (message-remove-header ,header) + (let ((value ,(cdr result))) + (when value + (message-goto-eoh) + (insert ,header ": " value "\n")))))))))) (when (or name address) (add-hook 'message-setup-hook `(lambda () - (let ((user-full-name ,(or (cdr name) user-full-name)) + (set (make-local-variable 'user-mail-address) + ,(or (cdr address) user-mail-address)) + (let ((user-full-name ,(or (cdr name) (user-full-name))) (user-mail-address ,(or (cdr address) user-mail-address))) (save-excursion @@ -1307,7 +1646,7 @@ this is a reply." (defun gnus-maybe-setup-default-charset () (let ((charset (and (boundp 'gnus-summary-buffer) - (buffer-live-p gnus-summary-buffer) + (buffer-live-p gnus-summary-buffer) (save-excursion (set-buffer gnus-summary-buffer) default-mime-charset)))) @@ -1318,6 +1657,21 @@ this is a reply." )))) +;;; @ for MIME view mode +;;; + +(defun gnus-following-method (buf) + (gnus-setup-message 'reply-yank + (set-buffer buf) + (if (message-news-p) + (message-followup) + (message-reply nil 'wide)) + (let ((message-reply-buffer buf)) + (message-yank-original)) + (message-goto-body)) + (kill-buffer buf)) + + ;;; Allow redefinition of functions. (gnus-ems-redefine)