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