*** empty log message ***
[gnus] / lisp / gnus-msg.el
1 ;;; gnus-msg.el --- mail and post interface for Gnus
2 ;; Copyright (C) 1995,96,97,98 Free Software Foundation, Inc.
3
4 ;; Author: Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>
5 ;;      Lars Magne Ingebrigtsen <larsi@gnus.org>
6 ;; Keywords: news
7
8 ;; This file is part of GNU Emacs.
9
10 ;; GNU Emacs is free software; you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 2, or (at your option)
13 ;; any later version.
14
15 ;; GNU Emacs is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 ;; GNU General Public License for more details.
19
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
22 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 ;; Boston, MA 02111-1307, USA.
24
25 ;;; Commentary:
26
27 ;;; Code:
28
29 (eval-when-compile (require 'cl))
30
31 (require 'gnus)
32 (require 'gnus-ems)
33 (require 'message)
34 (require 'gnus-art)
35
36 (defcustom gnus-post-method nil
37   "*Preferred method for posting USENET news.
38
39 If this variable is `current', Gnus will use the \"current\" select
40 method when posting.  If it is nil (which is the default), Gnus will
41 use the native posting method of the server.
42
43 This method will not be used in mail groups and the like, only in
44 \"real\" newsgroups.
45
46 If not nil nor `native', the value must be a valid method as discussed
47 in the documentation of `gnus-select-method'. It can also be a list of
48 methods. If that is the case, the user will be queried for what select
49 method to use when posting."
50   :group 'gnus-group-foreign
51   :type `(choice (const nil)
52                  (const current)
53                  (const native)
54                  (sexp :tag "Methods" ,gnus-select-method)))
55
56 (defvar gnus-outgoing-message-group nil
57   "*All outgoing messages will be put in this group.
58 If you want to store all your outgoing mail and articles in the group
59 \"nnml:archive\", you set this variable to that value.  This variable
60 can also be a list of group names.
61
62 If you want to have greater control over what group to put each
63 message in, you can set this variable to a function that checks the
64 current newsgroup name and then returns a suitable group name (or list
65 of names).")
66
67 (defvar gnus-mailing-list-groups nil
68   "*Regexp matching groups that are really mailing lists.
69 This is useful when you're reading a mailing list that has been
70 gatewayed to a newsgroup, and you want to followup to an article in
71 the group.")
72
73 (defvar gnus-add-to-list nil
74   "*If non-nil, add a `to-list' parameter automatically.")
75
76 (defvar gnus-crosspost-complaint
77   "Hi,
78
79 You posted the article below with the following Newsgroups header:
80
81 Newsgroups: %s
82
83 The %s group, at least, was an inappropriate recipient
84 of this message.  Please trim your Newsgroups header to exclude this
85 group before posting in the future.
86
87 Thank you.
88
89 "
90   "Format string to be inserted when complaining about crossposts.
91 The first %s will be replaced by the Newsgroups header;
92 the second with the current group name.")
93
94 (defvar gnus-message-setup-hook nil
95   "Hook run after setting up a message buffer.")
96
97 (defvar gnus-bug-create-help-buffer t
98   "*Should we create the *Gnus Help Bug* buffer?")
99
100 (defvar gnus-posting-styles nil
101   "*Alist of styles to use when posting.")
102
103 (defvar gnus-posting-style-alist
104   '((organization . message-user-organization)
105     (signature . message-signature)
106     (signature-file . message-signature-file)
107     (address . user-mail-address)
108     (name . user-full-name))
109   "*Mapping from style parameters to variables.")
110
111 ;;; Internal variables.
112
113 (defvar gnus-message-buffer "*Mail Gnus*")
114 (defvar gnus-article-copy nil)
115 (defvar gnus-last-posting-server nil)
116 (defvar gnus-message-group-art nil)
117
118 (defconst gnus-bug-message
119   "Sending a bug report to the Gnus Towers.
120 ========================================
121
122 The buffer below is a mail buffer.  When you press `C-c C-c', it will
123 be sent to the Gnus Bug Exterminators.
124
125 At the bottom of the buffer you'll see lots of variable settings.
126 Please do not delete those.  They will tell the Bug People what your
127 environment is, so that it will be easier to locate the bugs.
128
129 If you have found a bug that makes Emacs go \"beep\", set
130 debug-on-error to t (`M-x set-variable RET debug-on-error RET t RET')
131 and include the backtrace in your bug report.
132
133 Please describe the bug in annoying, painstaking detail.
134
135 Thank you for your help in stamping out bugs.
136 ")
137
138 (eval-and-compile
139   (autoload 'gnus-uu-post-news "gnus-uu" nil t)
140   (autoload 'news-setup "rnewspost")
141   (autoload 'news-reply-mode "rnewspost")
142   (autoload 'rmail-dont-reply-to "mail-utils")
143   (autoload 'rmail-output "rmailout"))
144
145 \f
146 ;;;
147 ;;; Gnus Posting Functions
148 ;;;
149
150 (gnus-define-keys (gnus-summary-send-map "S" gnus-summary-mode-map)
151   "p" gnus-summary-post-news
152   "f" gnus-summary-followup
153   "F" gnus-summary-followup-with-original
154   "c" gnus-summary-cancel-article
155   "s" gnus-summary-supersede-article
156   "r" gnus-summary-reply
157   "R" gnus-summary-reply-with-original
158   "w" gnus-summary-wide-reply
159   "W" gnus-summary-wide-reply-with-original
160   "n" gnus-summary-followup-to-mail
161   "N" gnus-summary-followup-to-mail-with-original
162   "m" gnus-summary-mail-other-window
163   "u" gnus-uu-post-news
164   "\M-c" gnus-summary-mail-crosspost-complaint
165   "om" gnus-summary-mail-forward
166   "op" gnus-summary-post-forward
167   "Om" gnus-uu-digest-mail-forward
168   "Op" gnus-uu-digest-post-forward)
169
170 (gnus-define-keys (gnus-send-bounce-map "D" gnus-summary-send-map)
171   "b" gnus-summary-resend-bounced-mail
172   ;; "c" gnus-summary-send-draft
173   "r" gnus-summary-resend-message)
174
175 ;;; Internal functions.
176
177 (defvar gnus-article-reply nil)
178 (defmacro gnus-setup-message (config &rest forms)
179   (let ((winconf (make-symbol "gnus-setup-message-winconf"))
180         (buffer (make-symbol "gnus-setup-message-buffer"))
181         (article (make-symbol "gnus-setup-message-article"))
182         (group (make-symbol "gnus-setup-message-group")))
183     `(let ((,winconf (current-window-configuration))
184            (,buffer (buffer-name (current-buffer)))
185            (,article (and gnus-article-reply (gnus-summary-article-number)))
186            (,group gnus-newsgroup-name)
187            (message-header-setup-hook
188             (copy-sequence message-header-setup-hook)))
189        (add-hook 'message-header-setup-hook 'gnus-inews-insert-gcc)
190        (add-hook 'message-header-setup-hook 'gnus-inews-insert-archive-gcc)
191        (add-hook 'message-mode-hook 'gnus-configure-posting-styles)
192        (unwind-protect
193            (progn
194              ,@forms)
195          (gnus-inews-add-send-actions ,winconf ,buffer ,article)
196          (setq gnus-message-buffer (current-buffer))
197          (set (make-local-variable 'gnus-message-group-art)
198               (cons ,group ,article))
199          (make-local-variable 'gnus-newsgroup-name)
200          (gnus-run-hooks 'gnus-message-setup-hook))
201        (gnus-add-buffer)
202        (gnus-configure-windows ,config t)
203        (set-buffer-modified-p nil))))
204
205 (defun gnus-inews-add-send-actions (winconf buffer article)
206   (make-local-hook 'message-sent-hook)
207   (add-hook 'message-sent-hook 'gnus-inews-do-gcc nil t)
208   (setq message-post-method
209         `(lambda (arg)
210            (gnus-post-method arg ,gnus-newsgroup-name)))
211   (setq message-newsreader (setq message-mailer (gnus-extended-version)))
212   (message-add-action
213    `(set-window-configuration ,winconf) 'exit 'postpone 'kill)
214   (message-add-action
215    `(when (gnus-buffer-exists-p ,buffer)
216       (save-excursion
217         (set-buffer ,buffer)
218         ,(when article
219            `(gnus-summary-mark-article-as-replied ,article))))
220    'send))
221
222 (put 'gnus-setup-message 'lisp-indent-function 1)
223 (put 'gnus-setup-message 'edebug-form-spec '(form body))
224
225 ;;; Post news commands of Gnus group mode and summary mode
226
227 (defun gnus-group-mail ()
228   "Start composing a mail."
229   (interactive)
230   (gnus-setup-message 'message
231     (message-mail)))
232
233 (defun gnus-group-post-news (&optional arg)
234   "Start composing a news message.
235 If ARG, post to the group under point.
236 If ARG is 1, prompt for a group name."
237   (interactive "P")
238   ;; Bind this variable here to make message mode hooks work ok.
239   (let ((gnus-newsgroup-name
240          (if arg
241              (if (= 1 (prefix-numeric-value arg))
242                  (completing-read "Newsgroup: " gnus-active-hashtb nil
243                                   (gnus-read-active-file-p))
244                (gnus-group-group-name))
245            "")))
246     (gnus-post-news 'post gnus-newsgroup-name)))
247
248 (defun gnus-summary-post-news ()
249   "Start composing a news message."
250   (interactive)
251   (gnus-post-news 'post gnus-newsgroup-name))
252
253 (defun gnus-summary-followup (yank &optional force-news)
254   "Compose a followup to an article.
255 If prefix argument YANK is non-nil, original article is yanked automatically."
256   (interactive
257    (list (and current-prefix-arg
258               (gnus-summary-work-articles 1))))
259   (when yank
260     (gnus-summary-goto-subject (car yank)))
261   (save-window-excursion
262     (gnus-summary-select-article))
263   (let ((headers (gnus-summary-article-header (gnus-summary-article-number)))
264         (gnus-newsgroup-name gnus-newsgroup-name))
265     ;; Send a followup.
266     (gnus-post-news nil gnus-newsgroup-name
267                     headers gnus-article-buffer
268                     yank nil force-news)))
269
270 (defun gnus-summary-followup-with-original (n &optional force-news)
271   "Compose a followup to an article and include the original article."
272   (interactive "P")
273   (gnus-summary-followup (gnus-summary-work-articles n) force-news))
274
275 (defun gnus-summary-followup-to-mail (&optional arg)
276   "Followup to the current mail message via news."
277   (interactive
278    (list (and current-prefix-arg
279               (gnus-summary-work-articles 1))))
280   (gnus-summary-followup arg t))
281
282 (defun gnus-summary-followup-to-mail-with-original (&optional arg)
283   "Followup to the current mail message via news."
284   (interactive "P")
285   (gnus-summary-followup (gnus-summary-work-articles arg) t))
286
287 (defun gnus-inews-yank-articles (articles)
288   (let (beg article)
289     (message-goto-body)
290     (while (setq article (pop articles))
291       (save-window-excursion
292         (set-buffer gnus-summary-buffer)
293         (gnus-summary-select-article nil nil nil article)
294         (gnus-summary-remove-process-mark article))
295       (gnus-copy-article-buffer)
296       (let ((message-reply-buffer gnus-article-copy)
297             (message-reply-headers gnus-current-headers))
298         (message-yank-original)
299         (setq beg (or beg (mark t))))
300       (when articles
301         (insert "\n")))
302     (push-mark)
303     (goto-char beg)))
304
305 (defun gnus-summary-cancel-article (&optional n symp)
306   "Cancel an article you posted.
307 Uses the process-prefix convention.  If given the symbolic
308 prefix `a', cancel using the standard posting method; if not
309 post using the current select method."
310   (interactive (gnus-interactive "P\ny"))
311   (let ((articles (gnus-summary-work-articles n))
312         (message-post-method
313          `(lambda (arg)
314             (gnus-post-method (not (eq symp 'a)) ,gnus-newsgroup-name)))
315         article)
316     (while (setq article (pop articles))
317       (when (gnus-summary-select-article t nil nil article)
318         (when (gnus-eval-in-buffer-window gnus-original-article-buffer
319                 (message-cancel-news))
320           (gnus-summary-mark-as-read article gnus-canceled-mark)
321           (gnus-cache-remove-article 1))
322         (gnus-article-hide-headers-if-wanted))
323       (gnus-summary-remove-process-mark article))))
324
325 (defun gnus-summary-supersede-article ()
326   "Compose an article that will supersede a previous article.
327 This is done simply by taking the old article and adding a Supersedes
328 header line with the old Message-ID."
329   (interactive)
330   (let ((article (gnus-summary-article-number)))
331     (gnus-setup-message 'reply-yank
332       (gnus-summary-select-article t)
333       (set-buffer gnus-original-article-buffer)
334       (message-supersede)
335       (push
336        `((lambda ()
337            (when (gnus-buffer-exists-p ,gnus-summary-buffer)
338              (save-excursion
339                (set-buffer ,gnus-summary-buffer)
340                (gnus-cache-possibly-remove-article ,article nil nil nil t)
341                (gnus-summary-mark-as-read ,article gnus-canceled-mark)))))
342        message-send-actions))))
343
344 \f
345
346 (defun gnus-copy-article-buffer (&optional article-buffer)
347   ;; make a copy of the article buffer with all text properties removed
348   ;; this copy is in the buffer gnus-article-copy.
349   ;; if ARTICLE-BUFFER is nil, gnus-article-buffer is used
350   ;; this buffer should be passed to all mail/news reply/post routines.
351   (setq gnus-article-copy (gnus-get-buffer-create " *gnus article copy*"))
352   (buffer-disable-undo gnus-article-copy)
353   (let ((article-buffer (or article-buffer gnus-article-buffer))
354         end beg contents)
355     (if (not (and (get-buffer article-buffer)
356                   (gnus-buffer-exists-p article-buffer)))
357         (error "Can't find any article buffer")
358       (save-excursion
359         (set-buffer article-buffer)
360         (save-restriction
361           ;; Copy over the (displayed) article buffer, delete
362           ;; hidden text and remove text properties.
363           (widen)
364           (copy-to-buffer gnus-article-copy (point-min) (point-max))
365           (set-buffer gnus-article-copy)
366           (gnus-article-delete-text-of-type 'annotation)
367           (gnus-remove-text-with-property 'gnus-prev)
368           (gnus-remove-text-with-property 'gnus-next)
369           (insert
370            (prog1
371                (format "%s" (buffer-string))
372              (erase-buffer)))
373           ;; Find the original headers.
374           (set-buffer gnus-original-article-buffer)
375           (goto-char (point-min))
376           (while (looking-at message-unix-mail-delimiter)
377             (forward-line 1))
378           (setq beg (point))
379           (setq end (or (search-forward "\n\n" nil t) (point)))
380           ;; Delete the headers from the displayed articles.
381           (set-buffer gnus-article-copy)
382           (delete-region (goto-char (point-min))
383                          (or (search-forward "\n\n" nil t) (point)))
384           ;; Insert the original article headers.
385           (insert-buffer-substring gnus-original-article-buffer beg end)
386           (gnus-article-decode-rfc1522)))
387       gnus-article-copy)))
388
389 (defun gnus-post-news (post &optional group header article-buffer yank subject
390                             force-news)
391   (when article-buffer
392     (gnus-copy-article-buffer))
393   (let ((gnus-article-reply article-buffer)
394         (add-to-list gnus-add-to-list))
395     (gnus-setup-message (cond (yank 'reply-yank)
396                               (article-buffer 'reply)
397                               (t 'message))
398       (let* ((group (or group gnus-newsgroup-name))
399              (pgroup group)
400              to-address to-group mailing-list to-list
401              newsgroup-p)
402         (when group
403           (setq to-address (gnus-group-find-parameter group 'to-address)
404                 to-group (gnus-group-find-parameter group 'to-group)
405                 to-list (gnus-group-find-parameter group 'to-list)
406                 newsgroup-p (gnus-group-find-parameter group 'newsgroup)
407                 mailing-list (when gnus-mailing-list-groups
408                                (string-match gnus-mailing-list-groups group))
409                 group (gnus-group-real-name group)))
410         (if (or (and to-group
411                      (gnus-news-group-p to-group))
412                 newsgroup-p
413                 force-news
414                 (and (gnus-news-group-p
415                       (or pgroup gnus-newsgroup-name)
416                       (if header (mail-header-number header)
417                         gnus-current-article))
418                      (not mailing-list)
419                      (not to-list)
420                      (not to-address)))
421             ;; This is news.
422             (if post
423                 (message-news (or to-group group))
424               (set-buffer gnus-article-copy)
425               (gnus-msg-treat-broken-reply-to)
426               (message-followup (if (or newsgroup-p force-news) nil to-group)))
427           ;; The is mail.
428           (if post
429               (progn
430                 (message-mail (or to-address to-list))
431                 ;; Arrange for mail groups that have no `to-address' to
432                 ;; get that when the user sends off the mail.
433                 (when (and (not to-list)
434                            (not to-address)
435                            add-to-list)
436                   (push (list 'gnus-inews-add-to-address pgroup)
437                         message-send-actions)))
438             (set-buffer gnus-article-copy)
439             (gnus-msg-treat-broken-reply-to)
440             (message-wide-reply to-address)))
441         (when yank
442           (gnus-inews-yank-articles yank))))))
443
444 (defun gnus-msg-treat-broken-reply-to ()
445   "Remove the Reply-to header iff broken-reply-to."
446   (when (gnus-group-find-parameter
447          gnus-newsgroup-name 'broken-reply-to)
448     (save-restriction
449       (message-narrow-to-head)
450       (message-remove-header "reply-to"))))
451
452 (defun gnus-post-method (arg group &optional silent)
453   "Return the posting method based on GROUP and ARG.
454 If SILENT, don't prompt the user."
455   (let ((group-method (gnus-find-method-for-group group)))
456     (cond
457      ;; If the group-method is nil (which shouldn't happen) we use
458      ;; the default method.
459      ((null group-method)
460       (or (and (null (eq gnus-post-method 'active)) gnus-post-method)
461                gnus-select-method message-post-method))
462      ;; We want the inverse of the default
463      ((and arg (not (eq arg 0)))
464       (if (eq gnus-post-method 'active)
465           gnus-select-method
466         group-method))
467      ;; We query the user for a post method.
468      ((or arg
469           (and gnus-post-method
470                (not (eq gnus-post-method 'current))
471                (listp (car gnus-post-method))))
472       (let* ((methods
473               ;; Collect all methods we know about.
474               (append
475                (when (and gnus-post-method
476                           (not (eq gnus-post-method 'current)))
477                  (if (listp (car gnus-post-method))
478                      gnus-post-method
479                    (list gnus-post-method)))
480                gnus-secondary-select-methods
481                (mapcar 'cdr gnus-server-alist)
482                (list gnus-select-method)
483                (list group-method)))
484              method-alist post-methods method)
485         ;; Weed out all mail methods.
486         (while methods
487           (setq method (gnus-server-get-method "" (pop methods)))
488           (when (or (gnus-method-option-p method 'post)
489                     (gnus-method-option-p method 'post-mail))
490             (push method post-methods)))
491         ;; Create a name-method alist.
492         (setq method-alist
493               (mapcar
494                (lambda (m)
495                  (list (concat (cadr m) " (" (symbol-name (car m)) ")") m))
496                post-methods))
497         ;; Query the user.
498         (cadr
499          (assoc
500           (setq gnus-last-posting-server
501                 (if (and silent
502                          gnus-last-posting-server)
503                     ;; Just use the last value.
504                     gnus-last-posting-server
505                   (completing-read
506                    "Posting method: " method-alist nil t
507                    (cons (or gnus-last-posting-server "") 0))))
508           method-alist))))
509      ;; Override normal method.
510      ((and (eq gnus-post-method 'current)
511            (not (eq (car group-method) 'nndraft))
512            (not arg))
513       group-method) 
514      (gnus-post-method
515       gnus-post-method)
516      ;; Use the normal select method.
517      (t gnus-select-method))))
518
519 \f
520
521 ;; Dummy to avoid byte-compile warning.
522 (defvar nnspool-rejected-article-hook)
523 (defvar xemacs-codename)
524
525 ;;; Since the X-Newsreader/X-Mailer are ``vanity'' headers, they might
526 ;;; as well include the Emacs version as well.
527 ;;; The following function works with later GNU Emacs, and XEmacs.
528 (defun gnus-extended-version ()
529   "Stringified Gnus version and Emacs version."
530   (interactive)
531   (concat
532    gnus-version
533    "/"
534    (cond
535     ((string-match "^\\([0-9]+\\.[0-9]+\\)\\.[.0-9]+$" emacs-version)
536      (concat "Emacs " (substring emacs-version
537                                  (match-beginning 1)
538                                  (match-end 1))))
539     ((string-match "\\([A-Z]*[Mm][Aa][Cc][Ss]\\)[^(]*\\(\\((beta.*)\\|'\\)\\)?"
540                    emacs-version)
541      (concat (substring emacs-version
542                         (match-beginning 1)
543                         (match-end 1))
544              (format " %d.%d" emacs-major-version emacs-minor-version)
545              (if (match-beginning 3)
546                  (substring emacs-version
547                             (match-beginning 3)
548                             (match-end 3))
549                "")
550              (if (boundp 'xemacs-codename)
551                  (concat " - \"" xemacs-codename "\""))))
552     (t emacs-version))))
553
554 ;; Written by "Mr. Per Persson" <pp@gnu.ai.mit.edu>.
555 (defun gnus-inews-insert-mime-headers ()
556   "Insert MIME headers.
557 Assumes ISO-Latin-1 is used iff 8-bit characters are present."
558   (goto-char (point-min))
559   (let ((mail-header-separator
560          (progn
561            (goto-char (point-min))
562            (if (and (search-forward (concat "\n" mail-header-separator "\n")
563                                     nil t)
564                     (not (search-backward "\n\n" nil t)))
565                mail-header-separator
566              ""))))
567     (or (mail-position-on-field "Mime-Version")
568         (insert "1.0")
569         (cond ((save-restriction
570                  (widen)
571                  (goto-char (point-min))
572                  (re-search-forward "[^\000-\177]" nil t))
573                (or (mail-position-on-field "Content-Type")
574                    (insert "text/plain; charset=ISO-8859-1"))
575                (or (mail-position-on-field "Content-Transfer-Encoding")
576                    (insert "8bit")))
577               (t (or (mail-position-on-field "Content-Type")
578                      (insert "text/plain; charset=US-ASCII"))
579                  (or (mail-position-on-field "Content-Transfer-Encoding")
580                      (insert "7bit")))))))
581
582 (custom-add-option 'message-header-hook 'gnus-inews-insert-mime-headers)
583
584 \f
585 ;;;
586 ;;; Gnus Mail Functions
587 ;;;
588
589 ;;; Mail reply commands of Gnus summary mode
590
591 (defun gnus-summary-reply (&optional yank wide)
592   "Start composing a reply mail to the current message.
593 If prefix argument YANK is non-nil, the original article is yanked
594 automatically."
595   (interactive
596    (list (and current-prefix-arg
597               (gnus-summary-work-articles 1))))
598   ;; Stripping headers should be specified with mail-yank-ignored-headers.
599   (when yank
600     (gnus-summary-goto-subject (car yank)))
601   (let ((gnus-article-reply t))
602     (gnus-setup-message (if yank 'reply-yank 'reply)
603       (gnus-summary-select-article)
604       (set-buffer (gnus-copy-article-buffer))
605       (gnus-msg-treat-broken-reply-to)
606       (message-reply nil wide)
607       (when yank
608         (gnus-inews-yank-articles yank)))))
609
610 (defun gnus-summary-reply-with-original (n &optional wide)
611   "Start composing a reply mail to the current message.
612 The original article will be yanked."
613   (interactive "P")
614   (gnus-summary-reply (gnus-summary-work-articles n) wide))
615
616 (defun gnus-summary-wide-reply (&optional yank)
617   "Start composing a wide reply mail to the current message.
618 If prefix argument YANK is non-nil, the original article is yanked
619 automatically."
620   (interactive
621    (list (and current-prefix-arg
622               (gnus-summary-work-articles 1))))
623   (gnus-summary-reply yank t))
624
625 (defun gnus-summary-wide-reply-with-original (n)
626   "Start composing a wide reply mail to the current message.
627 The original article will be yanked."
628   (interactive "P")
629   (gnus-summary-reply-with-original n t))
630
631 (defun gnus-summary-mail-forward (&optional full-headers post)
632   "Forward the current message to another user.
633 If FULL-HEADERS (the prefix), include full headers when forwarding."
634   (interactive "P")
635   (gnus-setup-message 'forward
636     (gnus-summary-select-article)
637     (set-buffer gnus-original-article-buffer)
638     (let ((message-included-forward-headers
639            (if full-headers "" message-included-forward-headers)))
640       (message-forward post))))
641
642 (defun gnus-summary-resend-message (address n)
643   "Resend the current article to ADDRESS."
644   (interactive "sResend message(s) to: \nP")
645   (let ((articles (gnus-summary-work-articles n))
646         article)
647     (while (setq article (pop articles))
648       (gnus-summary-select-article nil nil nil article)
649       (save-excursion
650         (set-buffer gnus-original-article-buffer)
651         (message-resend address)))))
652
653 (defun gnus-summary-post-forward (&optional full-headers)
654   "Forward the current article to a newsgroup.
655 If FULL-HEADERS (the prefix), include full headers when forwarding."
656   (interactive "P")
657   (gnus-summary-mail-forward full-headers t))
658
659 (defvar gnus-nastygram-message
660   "The following article was inappropriately posted to %s.\n\n"
661   "Format string to insert in nastygrams.
662 The current group name will be inserted at \"%s\".")
663
664 (defun gnus-summary-mail-nastygram (n)
665   "Send a nastygram to the author of the current article."
666   (interactive "P")
667   (when (or gnus-expert-user
668             (gnus-y-or-n-p
669              "Really send a nastygram to the author of the current article? "))
670     (let ((group gnus-newsgroup-name))
671       (gnus-summary-reply-with-original n)
672       (set-buffer gnus-message-buffer)
673       (message-goto-body)
674       (insert (format gnus-nastygram-message group))
675       (message-send-and-exit))))
676
677 (defun gnus-summary-mail-crosspost-complaint (n)
678   "Send a complaint about crossposting to the current article(s)."
679   (interactive "P")
680   (let ((articles (gnus-summary-work-articles n))
681         article)
682     (while (setq article (pop articles))
683       (set-buffer gnus-summary-buffer)
684       (gnus-summary-goto-subject article)
685       (let ((group (gnus-group-real-name gnus-newsgroup-name))
686             newsgroups followup-to)
687         (gnus-summary-select-article)
688         (set-buffer gnus-original-article-buffer)
689         (if (and (<= (length (message-tokenize-header
690                               (setq newsgroups (mail-fetch-field "newsgroups"))
691                               ", "))
692                      1)
693                  (or (not (setq followup-to (mail-fetch-field "followup-to")))
694                      (not (member group (message-tokenize-header
695                                          followup-to ", ")))))
696             (if followup-to
697                 (gnus-message 1 "Followup-to restricted")
698               (gnus-message 1 "Not a crossposted article"))
699           (set-buffer gnus-summary-buffer)
700           (gnus-summary-reply-with-original 1)
701           (set-buffer gnus-message-buffer)
702           (message-goto-body)
703           (insert (format gnus-crosspost-complaint newsgroups group))
704           (message-goto-subject)
705           (re-search-forward " *$")
706           (replace-match " (crosspost notification)" t t)
707           (gnus-deactivate-mark)
708           (when (gnus-y-or-n-p "Send this complaint? ")
709             (message-send-and-exit)))))))
710
711 (defun gnus-summary-mail-other-window ()
712   "Compose mail in other window."
713   (interactive)
714   (gnus-setup-message 'message
715     (message-mail)))
716
717 (defun gnus-mail-parse-comma-list ()
718   (let (accumulated
719         beg)
720     (skip-chars-forward " ")
721     (while (not (eobp))
722       (setq beg (point))
723       (skip-chars-forward "^,")
724       (while (zerop
725               (save-excursion
726                 (save-restriction
727                   (let ((i 0))
728                     (narrow-to-region beg (point))
729                     (goto-char beg)
730                     (logand (progn
731                               (while (search-forward "\"" nil t)
732                                 (incf i))
733                               (if (zerop i) 2 i))
734                             2)))))
735         (skip-chars-forward ",")
736         (skip-chars-forward "^,"))
737       (skip-chars-backward " ")
738       (push (buffer-substring beg (point))
739             accumulated)
740       (skip-chars-forward "^,")
741       (skip-chars-forward ", "))
742     accumulated))
743
744 (defun gnus-inews-add-to-address (group)
745   (let ((to-address (mail-fetch-field "to")))
746     (when (and to-address
747                (gnus-alive-p))
748       ;; This mail group doesn't have a `to-list', so we add one
749       ;; here.  Magic!
750       (when (gnus-y-or-n-p
751              (format "Do you want to add this as `to-list': %s " to-address))
752         (gnus-group-add-parameter group (cons 'to-list to-address))))))
753
754 (defun gnus-put-message ()
755   "Put the current message in some group and return to Gnus."
756   (interactive)
757   (let ((reply gnus-article-reply)
758         (winconf gnus-prev-winconf)
759         (group gnus-newsgroup-name))
760
761     (or (and group (not (gnus-group-read-only-p group)))
762         (setq group (read-string "Put in group: " nil
763                                  (gnus-writable-groups))))
764     (when (gnus-gethash group gnus-newsrc-hashtb)
765       (error "No such group: %s" group))
766
767     (save-excursion
768       (save-restriction
769         (widen)
770         (message-narrow-to-headers)
771         (let (gnus-deletable-headers)
772           (if (message-news-p)
773               (message-generate-headers message-required-news-headers)
774             (message-generate-headers message-required-mail-headers)))
775         (goto-char (point-max))
776         (insert "Gcc: " group "\n")
777         (widen)))
778
779     (gnus-inews-do-gcc)
780
781     (when (get-buffer gnus-group-buffer)
782       (when (gnus-buffer-exists-p (car-safe reply))
783         (set-buffer (car reply))
784         (and (cdr reply)
785              (gnus-summary-mark-article-as-replied
786               (cdr reply))))
787       (when winconf
788         (set-window-configuration winconf)))))
789
790 (defun gnus-article-mail (yank)
791   "Send a reply to the address near point.
792 If YANK is non-nil, include the original article."
793   (interactive "P")
794   (let ((address
795          (buffer-substring
796           (save-excursion (re-search-backward "[ \t\n]" nil t) (1+ (point)))
797           (save-excursion (re-search-forward "[ \t\n]" nil t) (1- (point))))))
798     (when address
799       (message-reply address)
800       (when yank
801         (gnus-inews-yank-articles (list (cdr gnus-article-current)))))))
802
803 (defvar nntp-server-type)
804 (defun gnus-bug ()
805   "Send a bug report to the Gnus maintainers."
806   (interactive)
807   (unless (gnus-alive-p)
808     (error "Gnus has been shut down"))
809   (gnus-setup-message 'bug
810     (delete-other-windows)
811     (when gnus-bug-create-help-buffer
812       (switch-to-buffer "*Gnus Help Bug*")
813       (erase-buffer)
814       (insert gnus-bug-message)
815       (goto-char (point-min)))
816     (message-pop-to-buffer "*Gnus Bug*")
817     (message-setup `((To . ,gnus-maintainer) (Subject . "")))
818     (when gnus-bug-create-help-buffer
819       (push `(gnus-bug-kill-buffer) message-send-actions))
820     (goto-char (point-min))
821     (re-search-forward (concat "^" (regexp-quote mail-header-separator) "$"))
822     (forward-line 1)
823     (insert (gnus-version) "\n"
824             (emacs-version) "\n")
825     (when (and (boundp 'nntp-server-type)
826                (stringp nntp-server-type))
827       (insert nntp-server-type))
828     (insert "\n\n\n\n\n")
829     (gnus-debug)
830     (goto-char (point-min))
831     (search-forward "Subject: " nil t)
832     (message "")))
833
834 (defun gnus-bug-kill-buffer ()
835   (when (get-buffer "*Gnus Help Bug*")
836     (kill-buffer "*Gnus Help Bug*")))
837
838 (defun gnus-debug ()
839   "Attempts to go through the Gnus source file and report what variables have been changed.
840 The source file has to be in the Emacs load path."
841   (interactive)
842   (let ((files '("gnus.el" "gnus-sum.el" "gnus-group.el"
843                  "gnus-art.el" "gnus-start.el" "gnus-async.el"
844                  "gnus-msg.el" "gnus-score.el" "gnus-win.el" "gnus-topic.el"
845                  "nnmail.el" "message.el"))
846         (point (point))
847         file expr olist sym)
848     (gnus-message 4 "Please wait while we snoop your variables...")
849     (sit-for 0)
850     ;; Go through all the files looking for non-default values for variables.
851     (save-excursion
852       (set-buffer (gnus-get-buffer-create " *gnus bug info*"))
853       (buffer-disable-undo (current-buffer))
854       (while files
855         (erase-buffer)
856         (when (and (setq file (locate-library (pop files)))
857                    (file-exists-p file))
858           (insert-file-contents file)
859           (goto-char (point-min))
860           (if (not (re-search-forward "^;;* *Internal variables" nil t))
861               (gnus-message 4 "Malformed sources in file %s" file)
862             (narrow-to-region (point-min) (point))
863             (goto-char (point-min))
864             (while (setq expr (ignore-errors (read (current-buffer))))
865               (ignore-errors
866                 (and (or (eq (car expr) 'defvar)
867                          (eq (car expr) 'defcustom))
868                      (stringp (nth 3 expr))
869                      (or (not (boundp (nth 1 expr)))
870                          (not (equal (eval (nth 2 expr))
871                                      (symbol-value (nth 1 expr)))))
872                      (push (nth 1 expr) olist)))))))
873       (kill-buffer (current-buffer)))
874     (when (setq olist (nreverse olist))
875       (insert "------------------ Environment follows ------------------\n\n"))
876     (while olist
877       (if (boundp (car olist))
878           (condition-case ()
879               (pp `(setq ,(car olist)
880                          ,(if (or (consp (setq sym (symbol-value (car olist))))
881                                   (and (symbolp sym)
882                                        (not (or (eq sym nil)
883                                                 (eq sym t)))))
884                               (list 'quote (symbol-value (car olist)))
885                             (symbol-value (car olist))))
886                   (current-buffer))
887             (error
888              (format "(setq %s 'whatever)\n" (car olist))))
889         (insert ";; (makeunbound '" (symbol-name (car olist)) ")\n"))
890       (setq olist (cdr olist)))
891     (insert "\n\n")
892     ;; Remove any control chars - they seem to cause trouble for some
893     ;; mailers.  (Byte-compiled output from the stuff above.)
894     (goto-char point)
895     (while (re-search-forward "[\000-\010\013-\037\200-\237]" nil t)
896       (replace-match (format "\\%03o" (string-to-char (match-string 0)))
897                      t t))))
898
899 ;;; Treatment of rejected articles.
900 ;;; Bounced mail.
901
902 (defun gnus-summary-resend-bounced-mail (&optional fetch)
903   "Re-mail the current message.
904 This only makes sense if the current message is a bounce message than
905 contains some mail you have written which has been bounced back to
906 you.
907 If FETCH, try to fetch the article that this is a reply to, if indeed
908 this is a reply."
909   (interactive "P")
910   (gnus-summary-select-article t)
911   (set-buffer gnus-original-article-buffer)
912   (gnus-setup-message 'compose-bounce
913     (let* ((references (mail-fetch-field "references"))
914            (parent (and references (gnus-parent-id references))))
915       (message-bounce)
916       ;; If there are references, we fetch the article we answered to.
917       (and fetch parent
918            (gnus-summary-refer-article parent)
919            (gnus-summary-show-all-headers)))))
920
921 ;;; Gcc handling.
922
923 ;; Do Gcc handling, which copied the message over to some group.
924 (defun gnus-inews-do-gcc (&optional gcc)
925   (interactive)
926   (when (gnus-alive-p)
927     (save-excursion
928       (save-restriction
929         (message-narrow-to-headers)
930         (let ((gcc (or gcc (mail-fetch-field "gcc" nil t)))
931               (cur (current-buffer))
932               groups group method)
933           (when gcc
934             (message-remove-header "gcc")
935             (widen)
936             (setq groups (message-tokenize-header gcc " ,"))
937             ;; Copy the article over to some group(s).
938             (while (setq group (pop groups))
939               (gnus-check-server
940                (setq method
941                      (cond ((and (null (gnus-get-info group))
942                                  (eq (car gnus-message-archive-method)
943                                      (car
944                                       (gnus-server-to-method
945                                        (gnus-group-method group)))))
946                             ;; If the group doesn't exist, we assume
947                             ;; it's an archive group...
948                             gnus-message-archive-method)
949                            ;; Use the method.
950                            ((gnus-info-method (gnus-get-info group))
951                             (gnus-info-method (gnus-get-info group)))
952                            ;; Find the method.
953                            (t (gnus-group-method group)))))
954               (gnus-check-server method)
955               (unless (gnus-request-group group t method)
956                 (gnus-request-create-group group method))
957               (save-excursion
958                 (nnheader-set-temp-buffer " *acc*")
959                 (insert-buffer-substring cur)
960                 (goto-char (point-min))
961                 (when (re-search-forward
962                        (concat "^" (regexp-quote mail-header-separator) "$")
963                        nil t)
964                   (replace-match "" t t ))
965                 (unless (gnus-request-accept-article group method t)
966                   (gnus-message 1 "Couldn't store article in group %s: %s"
967                                 group (gnus-status-message method))
968                   (sit-for 2))
969                 (kill-buffer (current-buffer))))))))))
970
971 (defun gnus-inews-insert-gcc ()
972   "Insert Gcc headers based on `gnus-outgoing-message-group'."
973   (save-excursion
974     (save-restriction
975       (message-narrow-to-headers)
976       (let* ((group gnus-outgoing-message-group)
977              (gcc (cond
978                    ((gnus-functionp group)
979                     (funcall group))
980                    ((or (stringp group) (list group))
981                     group))))
982         (when gcc
983           (insert "Gcc: "
984                   (if (stringp gcc) gcc
985                     (mapconcat 'identity gcc " "))
986                   "\n"))))))
987
988 (defun gnus-inews-insert-archive-gcc (&optional group)
989   "Insert the Gcc to say where the article is to be archived."
990   (let* ((var gnus-message-archive-group)
991          (group (or group gnus-newsgroup-name ""))
992          (gcc-self-val
993           (and gnus-newsgroup-name
994                (gnus-group-find-parameter
995                 gnus-newsgroup-name 'gcc-self)))
996          result 
997          (groups
998           (cond
999            ((null gnus-message-archive-method)
1000             ;; Ignore.
1001             nil)
1002            ((stringp var)
1003             ;; Just a single group.
1004             (list var))
1005            ((null var)
1006             ;; We don't want this.
1007             nil)
1008            ((and (listp var) (stringp (car var)))
1009             ;; A list of groups.
1010             var)
1011            ((gnus-functionp var)
1012             ;; A function.
1013             (funcall var group))
1014            (t
1015             ;; An alist of regexps/functions/forms.
1016             (while (and var
1017                         (not
1018                          (setq result
1019                                (cond
1020                                 ((stringp (caar var))
1021                                  ;; Regexp.
1022                                  (when (string-match (caar var) group)
1023                                    (cdar var)))
1024                                 ((gnus-functionp (car var))
1025                                  ;; Function.
1026                                  (funcall (car var) group))
1027                                 (t
1028                                  (eval (car var)))))))
1029               (setq var (cdr var)))
1030             result)))
1031          name)
1032     (when (or groups gcc-self-val)
1033       (when (stringp groups)
1034         (setq groups (list groups)))
1035       (save-excursion
1036         (save-restriction
1037           (message-narrow-to-headers)
1038           (goto-char (point-max))
1039           (insert "Gcc: ")
1040           (if gcc-self-val
1041               ;; Use the `gcc-self' param value instead.
1042               (progn
1043                 (insert
1044                  (if (stringp gcc-self-val)
1045                      gcc-self-val
1046                    group))
1047                 (if (not (eq gcc-self-val 'none))
1048                     (insert "\n")
1049                   (progn
1050                     (beginning-of-line)
1051                     (kill-line))))
1052             ;; Use the list of groups.
1053             (while (setq name (pop groups))
1054               (insert (if (string-match ":" name)
1055                           name
1056                         (gnus-group-prefixed-name
1057                          name gnus-message-archive-method)))
1058               (when groups
1059                 (insert " ")))
1060             (insert "\n")))))))
1061
1062 ;;; Posting styles.
1063
1064 (defun gnus-configure-posting-styles ()
1065   "Configure posting styles according to `gnus-posting-styles'."
1066   (let ((styles gnus-posting-styles)
1067         (gnus-newsgroup-name (or gnus-newsgroup-name ""))
1068         style match variable attribute value value-value)
1069     ;; Go through all styles and look for matches.
1070     (while styles
1071       (setq style (pop styles)
1072             match (pop style))
1073       (when (cond ((stringp match)
1074                    ;; Regexp string match on the group name.
1075                    (string-match match gnus-newsgroup-name))
1076                   ((or (symbolp match)
1077                        (gnus-functionp match))
1078                    (cond ((gnus-functionp match)
1079                           ;; Function to be called.
1080                           (funcall match))
1081                          ((boundp match)
1082                           ;; Variable to be checked.
1083                           (symbol-value match))))
1084                   ((listp match)
1085                    ;; This is a form to be evaled.
1086                    (eval match)))
1087         ;; We have a match, so we set the variables.
1088         (while style
1089           (setq attribute (pop style)
1090                 value (cadr attribute)
1091                 variable nil)
1092           ;; We find the variable that is to be modified.
1093           (if (and (not (stringp (car attribute)))
1094                    (not (eq 'body (car attribute)))
1095                    (not (setq variable (cdr (assq (car attribute) 
1096                                                   gnus-posting-style-alist)))))
1097               (message "Couldn't find attribute %s" (car attribute))
1098             ;; We get the value.
1099             (setq value-value
1100                   (cond ((stringp value)
1101                          value)
1102                         ((or (symbolp value)
1103                              (gnus-functionp value))
1104                          (cond ((gnus-functionp value)
1105                                 (funcall value))
1106                                ((boundp value)
1107                                 (symbol-value value))))
1108                         ((listp value)
1109                          (eval value))))
1110             (if variable
1111                 ;; This is an ordinary variable.
1112                 (set (make-local-variable variable) value-value)
1113               ;; This is either a body or a header to be inserted in the
1114               ;; message
1115               (when value-value
1116                 (let ((attr (car attribute)))
1117                   (if (eq 'body attr)
1118                       (save-excursion
1119                         (goto-char (point-max))
1120                         (insert value-value))
1121                     (save-excursion
1122                       (message-goto-eoh)
1123                       (insert (if (stringp attr) attr (symbol-name attr))
1124                               ": " value-value "\n"))))))))))))
1125
1126 ;;; Allow redefinition of functions.
1127
1128 (gnus-ems-redefine)
1129
1130 (provide 'gnus-msg)
1131
1132 ;;; gnus-msg.el ends here