Synch with Oort Gnus.
[elisp/gnus.git-] / lisp / gnus-msg.el
1 ;;; gnus-msg.el --- mail and post interface for Semi-gnus
2 ;; Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001
3 ;;        Free Software Foundation, Inc.
4
5 ;; Author: Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>
6 ;;      Lars Magne Ingebrigtsen <larsi@gnus.org>
7 ;;      MORIOKA Tomohiko <morioka@jaist.ac.jp>
8 ;;      Shuhei KOBAYASHI <shuhei-k@jaist.ac.jp>
9 ;;      Katsumi Yamaoka  <yamaoka@jpl.org>
10 ;;      Kiyokazu SUTO    <suto@merry.xmath.ous.ac.jp>
11 ;; Keywords: mail, news, MIME
12
13 ;; This file is part of GNU Emacs.
14
15 ;; GNU Emacs is free software; you can redistribute it and/or modify
16 ;; it under the terms of the GNU General Public License as published by
17 ;; the Free Software Foundation; either version 2, or (at your option)
18 ;; any later version.
19
20 ;; GNU Emacs is distributed in the hope that it will be useful,
21 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
22 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23 ;; GNU General Public License for more details.
24
25 ;; You should have received a copy of the GNU General Public License
26 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
27 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
28 ;; Boston, MA 02111-1307, USA.
29
30 ;;; Commentary:
31
32 ;;; Code:
33
34 (eval-when-compile (require 'cl))
35 (eval-when-compile (require 'static))
36
37 (require 'gnus)
38 (require 'gnus-ems)
39 (require 'message)
40 (require 'gnus-art)
41
42 (defcustom gnus-post-method 'current
43   "*Preferred method for posting USENET news.
44
45 If this variable is `current' (which is the default), Gnus will use
46 the \"current\" select method when posting.  If it is `native', Gnus
47 will use the native select method when posting.
48
49 This method will not be used in mail groups and the like, only in
50 \"real\" newsgroups.
51
52 If not `native' nor `current', the value must be a valid method as discussed
53 in the documentation of `gnus-select-method'.  It can also be a list of
54 methods.  If that is the case, the user will be queried for what select
55 method to use when posting."
56   :group 'gnus-group-foreign
57   :link '(custom-manual "(gnus)Posting Server")
58   :type `(choice (const native)
59                  (const current)
60                  (sexp :tag "Methods" ,gnus-select-method)))
61
62 (defcustom gnus-outgoing-message-group nil
63   "*All outgoing messages will be put in this group.
64 If you want to store all your outgoing mail and articles in the group
65 \"nnml:archive\", you set this variable to that value.  This variable
66 can also be a list of group names.
67
68 If you want to have greater control over what group to put each
69 message in, you can set this variable to a function that checks the
70 current newsgroup name and then returns a suitable group name (or list
71 of names)."
72   :group 'gnus-message
73   :type '(choice (string :tag "Group")
74                  (function)))
75
76 (defcustom gnus-mailing-list-groups nil
77   "*Regexp matching groups that are really mailing lists.
78 This is useful when you're reading a mailing list that has been
79 gatewayed to a newsgroup, and you want to followup to an article in
80 the group."
81   :group 'gnus-message
82   :type 'regexp)
83
84 (defcustom gnus-add-to-list nil
85   "*If non-nil, add a `to-list' parameter automatically."
86   :group 'gnus-message
87   :type 'boolean)
88
89 (defcustom gnus-crosspost-complaint
90   "Hi,
91
92 You posted the article below with the following Newsgroups header:
93
94 Newsgroups: %s
95
96 The %s group, at least, was an inappropriate recipient
97 of this message.  Please trim your Newsgroups header to exclude this
98 group before posting in the future.
99
100 Thank you.
101
102 "
103   "Format string to be inserted when complaining about crossposts.
104 The first %s will be replaced by the Newsgroups header;
105 the second with the current group name."
106   :group 'gnus-message
107   :type 'string)
108
109 (defcustom gnus-message-setup-hook '(gnus-maybe-setup-default-charset)
110   "Hook run after setting up a message buffer."
111   :group 'gnus-message
112   :type 'hook)
113
114 (defcustom gnus-bug-create-help-buffer t
115   "*Should we create the *Gnus Help Bug* buffer?"
116   :group 'gnus-message
117   :type 'boolean)
118
119 (defcustom gnus-posting-styles nil
120   "*Alist of styles to use when posting.
121 See Info node `(gnus)Posting Styles'."
122   :group 'gnus-message
123   :type '(repeat (cons (choice (regexp)
124                                (function)
125                                (variable)
126                                (sexp))
127                        (repeat (list
128                                 (choice (const signature)
129                                         (const signature-file)
130                                         (const organization)
131                                         (const address)
132                                         (const name)
133                                         (const body)
134                                         (string :tag "Header"))
135                                 (choice (string)
136                                         (function)
137                                         (variable)
138                                         (sexp)))))))
139
140 (defcustom gnus-inews-mark-gcc-as-read nil
141   "If non-nil, automatically mark Gcc articles as read."
142   :group 'gnus-message
143   :type 'boolean)
144
145 (defcustom gnus-group-posting-charset-alist
146   '(("^\\(no\\|fr\\)\\.[^,]*\\(,[ \t\n]*\\(no\\|fr\\)\\.[^,]*\\)*$" iso-8859-1 (iso-8859-1))
147     ("^\\(fido7\\|relcom\\)\\.[^,]*\\(,[ \t\n]*\\(fido7\\|relcom\\)\\.[^,]*\\)*$" koi8-r (koi8-r))
148     (message-this-is-mail nil nil)
149     (message-this-is-news nil t))
150   "Alist of regexps and permitted unencoded charsets for posting.
151 Each element of the alist has the form (TEST HEADER BODY-LIST), where
152 TEST is either a regular expression matching the newsgroup header or a
153 variable to query,
154 HEADER is the charset which may be left unencoded in the header (nil
155 means encode all charsets),
156 BODY-LIST is a list of charsets which may be encoded using 8bit
157 content-transfer encoding in the body, or one of the special values
158 nil (always encode using quoted-printable) or t (always use 8bit).
159
160 Note that any value other than nil for HEADER infringes some RFCs, so
161 use this option with care."
162   :type '(repeat (list :tag "Permitted unencoded charsets"
163                        (choice :tag "Where"
164                                (regexp :tag "Group")
165                                (const :tag "Mail message"
166                                       :value message-this-is-mail)
167                                (const :tag "News article"
168                                       :value message-this-is-news))
169                        (choice :tag "Header"
170                                (const :tag "None" nil)
171                                (symbol :tag "Charset"))
172                        (choice :tag "Body"
173                                (const :tag "Any" :value t)
174                                (const :tag "None" :value nil)
175                                (repeat :tag "Charsets"
176                                        (symbol :tag "Charset")))))
177   :group 'gnus-charset)
178
179 ;;; Internal variables.
180
181 (defvar gnus-inhibit-posting-styles nil
182   "Inhibit the use of posting styles.")
183
184 (defvar gnus-message-buffer "*Mail Gnus*")
185 (defvar gnus-article-copy nil)
186 (defvar gnus-check-before-posting nil)
187 (defvar gnus-last-posting-server nil)
188 (defvar gnus-message-group-art nil)
189
190 (defvar gnus-msg-force-broken-reply-to nil)
191
192 (defconst gnus-bug-message
193   (format "Sending a bug report to the Gnus Towers.
194 ========================================
195
196 This gnus is the %s%s.
197 If you think the bug is a Semi-gnus bug, send a bug report to Semi-gnus
198 Developers. (the addresses below are mailing list addresses)
199
200 ========================================
201
202 The buffer below is a mail buffer.  When you press `C-c C-c', it will
203 be sent to the Gnus Bug Exterminators.
204
205 The thing near the bottom of the buffer is how the environment
206 settings will be included in the mail.  Please do not delete that.
207 They will tell the Bug People what your environment is, so that it
208 will be easier to locate the bugs.
209
210 If you have found a bug that makes Emacs go \"beep\", set
211 debug-on-error to t (`M-x set-variable RET debug-on-error RET t RET')
212 and include the backtrace in your bug report.
213
214 Please describe the bug in annoying, painstaking detail.
215
216 Thank you for your help in stamping out bugs.
217 "
218           gnus-product-name
219           (if (string= gnus-product-name "Semi-gnus")
220               ""
221             ", a modified version of Semi-gnus")))
222
223 (eval-and-compile
224   (autoload 'gnus-uu-post-news "gnus-uu" nil t)
225   (autoload 'news-setup "rnewspost")
226   (autoload 'news-reply-mode "rnewspost")
227   (autoload 'rmail-dont-reply-to "mail-utils")
228   (autoload 'rmail-output "rmailout"))
229
230 \f
231 ;;;
232 ;;; Gnus Posting Functions
233 ;;;
234
235 (gnus-define-keys (gnus-summary-send-map "S" gnus-summary-mode-map)
236   "p" gnus-summary-post-news
237   "i" gnus-summary-news-other-window
238   "f" gnus-summary-followup
239   "F" gnus-summary-followup-with-original
240   "c" gnus-summary-cancel-article
241   "s" gnus-summary-supersede-article
242   "r" gnus-summary-reply
243   "y" gnus-summary-yank-message
244   "R" gnus-summary-reply-with-original
245   "w" gnus-summary-wide-reply
246   "W" gnus-summary-wide-reply-with-original
247   "v" gnus-summary-very-wide-reply
248   "V" gnus-summary-very-wide-reply-with-original
249   "n" gnus-summary-followup-to-mail
250   "N" gnus-summary-followup-to-mail-with-original
251   "m" gnus-summary-mail-other-window
252   "u" gnus-uu-post-news
253   "\M-c" gnus-summary-mail-crosspost-complaint
254   "Br" gnus-summary-reply-broken-reply-to
255   "BR" gnus-summary-reply-broken-reply-to-with-original
256   "om" gnus-summary-mail-forward
257   "op" gnus-summary-post-forward
258   "Om" gnus-summary-digest-mail-forward
259   "Op" gnus-summary-digest-post-forward)
260
261 (gnus-define-keys (gnus-send-bounce-map "D" gnus-summary-send-map)
262   "b" gnus-summary-resend-bounced-mail
263   ;; "c" gnus-summary-send-draft
264   "r" gnus-summary-resend-message)
265
266 ;;; Internal functions.
267
268 (defvar gnus-article-reply nil)
269 (defmacro gnus-setup-message (config &rest forms)
270   (let ((winconf (make-symbol "gnus-setup-message-winconf"))
271         (buffer (make-symbol "gnus-setup-message-buffer"))
272         (article (make-symbol "gnus-setup-message-article"))
273         (group (make-symbol "gnus-setup-message-group")))
274     `(let ((,winconf (current-window-configuration))
275            (,buffer (buffer-name (current-buffer)))
276            (,article gnus-article-reply)
277            (,group gnus-newsgroup-name)
278            (message-header-setup-hook
279             (copy-sequence message-header-setup-hook))
280            (message-mode-hook (copy-sequence message-mode-hook))
281            (message-startup-parameter-alist
282             '((reply-buffer . gnus-copy-article-buffer)
283               (original-buffer . gnus-original-article-buffer)
284               (user-agent . Gnus))))
285        (add-hook 'message-header-setup-hook 'gnus-inews-insert-gcc)
286        (add-hook 'message-header-setup-hook 'gnus-inews-insert-archive-gcc)
287        ;; #### FIXME: for a reason that I did not manage to identify yet,
288        ;; the variable `gnus-newsgroup-name' does not honor a dynamically
289        ;; scoped or setq'ed value from a caller like `C-u gnus-summary-mail'.
290        ;; After evaluation of @forms below, it gets the value we actually want
291        ;; to override, and the posting styles are used. For that reason, I've
292        ;; added an optional argument to `gnus-configure-posting-styles' to
293        ;; make sure that the correct value for the group name is used. -- drv
294        (add-hook 'message-mode-hook
295                  (lambda ()
296                    (gnus-configure-posting-styles ,group)))
297        (unwind-protect
298            (progn
299              ,@forms)
300          (gnus-inews-add-send-actions ,winconf ,buffer ,article ,config)
301          (gnus-inews-insert-draft-meta-information ,group ,article)
302          (setq gnus-message-buffer (current-buffer))
303          (set (make-local-variable 'gnus-message-group-art)
304               (cons ,group ,article))
305          (set (make-local-variable 'gnus-newsgroup-name) ,group)
306          (gnus-run-hooks 'gnus-message-setup-hook))
307        (gnus-add-buffer)
308        (gnus-configure-windows ,config t)
309        (run-hooks 'post-command-hook)
310        (set-buffer-modified-p nil))))
311
312 (defun gnus-inews-insert-draft-meta-information (group article)
313   (save-excursion
314     (when (and group
315                (not (string= group ""))
316                (not (message-fetch-field gnus-draft-meta-information-header)))
317       (goto-char (point-min))
318       (insert gnus-draft-meta-information-header ": (\"" group "\" "
319               (if article (number-to-string
320                            (if (listp article)
321                                (car article)
322                              article)) "\"\"")
323               ")\n"))))
324
325 ;;;###autoload
326 (defun gnus-msg-mail (&optional to subject other-headers continue
327                                 switch-action yank-action send-actions)
328   "Start editing a mail message to be sent.
329 Like `message-mail', but with Gnus paraphernalia, particularly the
330 Gcc: header for archiving purposes."
331   (interactive)
332   (let ((buf (current-buffer))
333         mail-buf)
334     (gnus-setup-message 'message
335       (message-mail to subject other-headers continue
336                     nil yank-action send-actions))
337     (when switch-action
338       (setq mail-buf (current-buffer))
339       (switch-to-buffer buf)
340       (apply switch-action mail-buf nil)))
341   ;; COMPOSEFUNC should return t if succeed.  Undocumented ???
342   t)
343
344 (defvar save-selected-window-window)
345
346 ;;;###autoload
347 (defun gnus-button-mailto (address)
348   "Mail to ADDRESS."
349   (set-buffer (gnus-copy-article-buffer))
350   (gnus-setup-message 'message
351     (message-reply address))
352   (and (boundp 'save-selected-window-window)
353        (not (window-live-p save-selected-window-window))
354        (setq save-selected-window-window (selected-window))))
355
356 ;;;###autoload
357 (defun gnus-button-reply (&optional to-address wide)
358   "Like `message-reply'."
359   (interactive)
360   (gnus-setup-message 'message
361     (message-reply to-address wide))
362   (and (boundp 'save-selected-window-window)
363        (not (window-live-p save-selected-window-window))
364        (setq save-selected-window-window (selected-window))))
365
366 ;;;###autoload
367 (define-mail-user-agent 'gnus-user-agent
368   'gnus-msg-mail 'message-send-and-exit
369   'message-kill-buffer 'message-send-hook)
370
371 (defun gnus-setup-posting-charset (group)
372   (let ((alist gnus-group-posting-charset-alist)
373         (group (or group ""))
374         elem)
375     (when group
376       (catch 'found
377         (while (setq elem (pop alist))
378           (when (or (and (stringp (car elem))
379                          (string-match (car elem) group))
380                     (and (gnus-functionp (car elem))
381                          (funcall (car elem) group))
382                     (and (symbolp (car elem))
383                          (symbol-value (car elem))))
384             (throw 'found (cons (cadr elem) (caddr elem)))))))))
385
386 (defun gnus-inews-add-send-actions (winconf buffer article &optional config)
387   (make-local-hook 'message-sent-hook)
388   (add-hook 'message-sent-hook (if gnus-agent 'gnus-agent-possibly-do-gcc
389                                  'gnus-inews-do-gcc) nil t)
390   (when gnus-agent
391     (make-local-hook 'message-header-hook)
392     (add-hook 'message-header-hook 'gnus-agent-possibly-save-gcc nil t))
393   (setq message-post-method
394         `(lambda (arg)
395            (gnus-post-method arg ,gnus-newsgroup-name)))
396   (setq message-user-agent (gnus-extended-version))
397   (when (not message-use-multi-frames)
398     (message-add-action
399      `(set-window-configuration ,winconf) 'exit 'postpone 'kill))
400   (message-add-action
401    `(when (gnus-buffer-exists-p ,buffer)
402       (save-excursion
403         (set-buffer ,buffer)
404         ,(when article
405            (if (eq config 'forward)
406                `(gnus-summary-mark-article-as-forwarded ',article)
407              `(gnus-summary-mark-article-as-replied ',article)))))
408    'send))
409
410 (put 'gnus-setup-message 'lisp-indent-function 1)
411 (put 'gnus-setup-message 'edebug-form-spec '(form body))
412
413 ;;; Post news commands of Gnus group mode and summary mode
414
415 (defun gnus-group-mail (&optional arg)
416   "Start composing a mail.
417 If ARG, use the group under the point to find a posting style.
418 If ARG is 1, prompt for a group name to find the posting style."
419   (interactive "P")
420   ;; We can't `let' gnus-newsgroup-name here, since that leads
421   ;; to local variables leaking.
422   (let ((group gnus-newsgroup-name)
423         (buffer (current-buffer)))
424     (unwind-protect
425         (progn
426           (setq gnus-newsgroup-name
427                 (if arg
428                     (if (= 1 (prefix-numeric-value arg))
429                         (completing-read "Use posting style of group: "
430                                          gnus-active-hashtb nil
431                                          (gnus-read-active-file-p))
432                       (gnus-group-group-name))
433                   ""))
434           ;; #### see comment in gnus-setup-message -- drv
435           (gnus-setup-message 'message (message-mail)))
436       (save-excursion
437         (set-buffer buffer)
438         (setq gnus-newsgroup-name group)))))
439
440 (defun gnus-group-news (&optional arg)
441   "Start composing a news.
442 If ARG, post to group under point.
443 If ARG is 1, prompt for group name to post to.
444
445 This function prepares a news even when using mail groups.  This is useful
446 for posting messages to mail groups without actually sending them over the
447 network.  The corresponding backend must have a 'request-post method."
448   (interactive "P")
449   ;; We can't `let' gnus-newsgroup-name here, since that leads
450   ;; to local variables leaking.
451   (let ((group gnus-newsgroup-name)
452         (buffer (current-buffer)))
453     (unwind-protect
454         (progn
455           (setq gnus-newsgroup-name
456                 (if arg
457                     (if (= 1 (prefix-numeric-value arg))
458                         (completing-read "Use group: "
459                                          gnus-active-hashtb nil
460                                          (gnus-read-active-file-p))
461                       (gnus-group-group-name))
462                   ""))
463           ;; #### see comment in gnus-setup-message -- drv
464           (gnus-setup-message 'message
465             (message-news (gnus-group-real-name gnus-newsgroup-name))))
466       (save-excursion
467         (set-buffer buffer)
468         (setq gnus-newsgroup-name group)))))
469
470 (defun gnus-group-post-news (&optional arg)
471   "Start composing a message (a news by default).
472 If ARG, post to group under point.  If ARG is 1, prompt for group name.
473 Depending on the selected group, the message might be either a mail or
474 a news."
475   (interactive "P")
476   ;; Bind this variable here to make message mode hooks work ok.
477   (let ((gnus-newsgroup-name
478          (if arg
479              (if (= 1 (prefix-numeric-value arg))
480                  (completing-read "Newsgroup: " gnus-active-hashtb nil
481                                   (gnus-read-active-file-p))
482                (gnus-group-group-name))
483            "")))
484     (gnus-post-news 'post gnus-newsgroup-name)))
485
486 (defun gnus-summary-mail-other-window (&optional arg)
487   "Start composing a mail in another window.
488 Use the posting of the current group by default.
489 If ARG, don't do that.  If ARG is 1, prompt for group name to find the
490 posting style."
491   (interactive "P")
492   ;; We can't `let' gnus-newsgroup-name here, since that leads
493   ;; to local variables leaking.
494   (let ((group gnus-newsgroup-name)
495         (buffer (current-buffer)))
496     (unwind-protect
497         (progn
498           (setq gnus-newsgroup-name
499                 (if arg
500                     (if (= 1 (prefix-numeric-value arg))
501                         (completing-read "Use group: "
502                                          gnus-active-hashtb nil
503                                          (gnus-read-active-file-p))
504                       "")
505                   gnus-newsgroup-name))
506           ;; #### see comment in gnus-setup-message -- drv
507           (gnus-setup-message 'message (message-mail)))
508       (save-excursion
509         (set-buffer buffer)
510         (setq gnus-newsgroup-name group)))))
511
512 (defun gnus-summary-news-other-window (&optional arg)
513   "Start composing a news in another window.
514 Post to the current group by default.
515 If ARG, don't do that.  If ARG is 1, prompt for group name to post to.
516
517 This function prepares a news even when using mail groups.  This is useful
518 for posting messages to mail groups without actually sending them over the
519 network.  The corresponding backend must have a 'request-post method."
520   (interactive "P")
521   ;; We can't `let' gnus-newsgroup-name here, since that leads
522   ;; to local variables leaking.
523   (let ((group gnus-newsgroup-name)
524         (buffer (current-buffer)))
525     (unwind-protect
526         (progn
527           (setq gnus-newsgroup-name
528                 (if arg
529                     (if (= 1 (prefix-numeric-value arg))
530                         (completing-read "Use group: "
531                                          gnus-active-hashtb nil
532                                          (gnus-read-active-file-p))
533                       "")
534                   gnus-newsgroup-name))
535           ;; #### see comment in gnus-setup-message -- drv
536           (gnus-setup-message 'message
537             (message-news (gnus-group-real-name gnus-newsgroup-name))))
538       (save-excursion
539         (set-buffer buffer)
540         (setq gnus-newsgroup-name group)))))
541
542 (defun gnus-summary-post-news (&optional arg)
543   "Start composing a message.  Post to the current group by default.
544 If ARG, don't do that.  If ARG is 1, prompt for a group name to post to.
545 Depending on the selected group, the message might be either a mail or
546 a news."
547   (interactive "P")
548   ;; Bind this variable here to make message mode hooks work ok.
549   (let ((gnus-newsgroup-name
550          (if arg
551              (if (= 1 (prefix-numeric-value arg))
552                  (completing-read "Newsgroup: " gnus-active-hashtb nil
553                                   (gnus-read-active-file-p))
554                "")
555            gnus-newsgroup-name)))
556     (gnus-post-news 'post gnus-newsgroup-name)))
557
558
559 (defun gnus-summary-followup (yank &optional force-news)
560   "Compose a followup to an article.
561 If prefix argument YANK is non-nil, original article is yanked automatically."
562   (interactive
563    (list (and current-prefix-arg
564               (gnus-summary-work-articles 1))))
565   (when yank
566     (gnus-summary-goto-subject (car yank)))
567   (save-window-excursion
568     (gnus-summary-select-article))
569   (let ((headers (gnus-summary-article-header (gnus-summary-article-number)))
570         (gnus-newsgroup-name gnus-newsgroup-name))
571     ;; Send a followup.
572     (gnus-post-news nil gnus-newsgroup-name
573                     headers gnus-article-buffer
574                     yank nil force-news)))
575
576 (defun gnus-summary-followup-with-original (n &optional force-news)
577   "Compose a followup to an article and include the original article."
578   (interactive "P")
579   (gnus-summary-followup (gnus-summary-work-articles n) force-news))
580
581 (defun gnus-summary-followup-to-mail (&optional arg)
582   "Followup to the current mail message via news."
583   (interactive
584    (list (and current-prefix-arg
585               (gnus-summary-work-articles 1))))
586   (gnus-summary-followup arg t))
587
588 (defun gnus-summary-followup-to-mail-with-original (&optional arg)
589   "Followup to the current mail message via news."
590   (interactive "P")
591   (gnus-summary-followup (gnus-summary-work-articles arg) t))
592
593 (defun gnus-inews-yank-articles (articles)
594   (let ((more-than-one (cdr articles))
595         (cur (current-buffer))
596         refs beg article window)
597     (message-goto-body)
598     (while (setq article (pop articles))
599       (save-window-excursion
600         (set-buffer gnus-summary-buffer)
601         (gnus-summary-select-article nil nil nil article)
602         (gnus-summary-remove-process-mark article))
603
604       ;; Gathering references.
605       (when more-than-one
606         (setq refs (message-list-references
607                     refs
608                     (mail-header-references gnus-current-headers)
609                     (mail-header-message-id gnus-current-headers)))
610         (when message-use-multi-frames
611           (when (setq window (get-buffer-window cur t))
612             (select-frame (window-frame window)))))
613
614       (gnus-copy-article-buffer)
615       (let ((message-reply-buffer gnus-article-copy)
616             (message-reply-headers
617              (with-current-buffer gnus-article-copy
618                ;; The headers are decoded.
619                (nnheader-parse-head t))))
620         (message-yank-original)
621         (setq beg (or beg (mark t))))
622       (when articles
623         (insert "\n")))
624     (push-mark)
625
626     ;; Replace with the gathered references.
627     (when refs
628       (push-mark beg)
629       (save-restriction
630         (message-narrow-to-headers)
631         (let ((case-fold-search t))
632           (if (re-search-forward "^References:\\([\t ]+.+\n\\)+" nil t)
633               (replace-match "")
634             (goto-char (point-max))))
635         (mail-header-format
636          (list (or (assq 'References message-header-format-alist)
637                    '(References . message-shorten-references)))
638          (list (cons 'References
639                      (mapconcat 'identity (nreverse refs) " "))))
640         (backward-delete-char 1))
641       (setq beg (mark t))
642       (pop-mark))
643
644     (goto-char beg)))
645
646 (defun gnus-summary-cancel-article (&optional n symp)
647   "Cancel an article you posted.
648 Uses the process-prefix convention.  If given the symbolic
649 prefix `a', cancel using the standard posting method; if not
650 post using the current select method."
651   (interactive (gnus-interactive "P\ny"))
652   (let ((articles (gnus-summary-work-articles n))
653         (message-post-method
654          `(lambda (arg)
655             (gnus-post-method (not (eq symp 'a)) ,gnus-newsgroup-name)))
656         article)
657     (while (setq article (pop articles))
658       (when (gnus-summary-select-article t nil nil article)
659         (when (gnus-eval-in-buffer-window gnus-article-buffer
660                 (save-excursion
661                   (set-buffer gnus-original-article-buffer)
662                   (message-cancel-news)))
663           (gnus-summary-mark-as-read article gnus-canceled-mark)
664           (gnus-cache-remove-article 1))
665         (gnus-article-hide-headers-if-wanted))
666       (gnus-summary-remove-process-mark article))))
667
668 (defun gnus-summary-supersede-article ()
669   "Compose an article that will supersede a previous article.
670 This is done simply by taking the old article and adding a Supersedes
671 header line with the old Message-ID."
672   (interactive)
673   (let ((article (gnus-summary-article-number))
674         (gnus-message-setup-hook '(gnus-maybe-setup-default-charset)))
675     (gnus-setup-message 'reply-yank
676       (gnus-summary-select-article t)
677       (set-buffer gnus-original-article-buffer)
678       (message-supersede)
679       (push
680        `((lambda ()
681            (when (gnus-buffer-exists-p ,gnus-summary-buffer)
682              (save-excursion
683                (set-buffer ,gnus-summary-buffer)
684                (gnus-cache-possibly-remove-article ,article nil nil nil t)
685                (gnus-summary-mark-as-read ,article gnus-canceled-mark)))))
686        message-send-actions))))
687
688 \f
689
690 (defun gnus-copy-article-buffer (&optional article-buffer)
691   ;; make a copy of the article buffer with all text properties removed
692   ;; this copy is in the buffer gnus-article-copy.
693   ;; if ARTICLE-BUFFER is nil, gnus-article-buffer is used
694   ;; this buffer should be passed to all mail/news reply/post routines.
695   (setq gnus-article-copy (gnus-get-buffer-create " *gnus article copy*"))
696   (let ((article-buffer (or article-buffer gnus-article-buffer))
697         end beg)
698     (if (not (and (get-buffer article-buffer)
699                   (gnus-buffer-exists-p article-buffer)))
700         (error "Can't find any article buffer")
701       (save-excursion
702         (set-buffer article-buffer)
703         (let ((gnus-newsgroup-charset (or gnus-article-charset
704                                           gnus-newsgroup-charset))
705               (gnus-newsgroup-ignored-charsets
706                (or gnus-article-ignored-charsets
707                    gnus-newsgroup-ignored-charsets)))
708           (save-restriction
709             ;; Copy over the (displayed) article buffer, delete
710             ;; hidden text and remove text properties.
711             (widen)
712             (let ((inhibit-read-only t))
713               (copy-to-buffer gnus-article-copy (point-min) (point-max))
714               (set-buffer gnus-article-copy)
715               ;; Encode bitmap smileys to ordinary text.
716               ;; Possibly, the original text might be restored.
717               (static-unless (featurep 'xemacs)
718                 (when (featurep 'smiley-mule)
719                   (smiley-encode-buffer)))
720               (gnus-article-delete-text-of-type 'annotation)
721               (gnus-remove-text-with-property 'gnus-prev)
722               (gnus-remove-text-with-property 'gnus-next)
723               (gnus-remove-text-with-property 'x-face-mule-bitmap-image)
724               (insert
725                (prog1
726                    (static-if (featurep 'xemacs)
727                        ;; Revome smiley extents for (possibly) XEmacs 21.1.
728                        (format "%s"
729                                (buffer-substring-no-properties (point-min)
730                                                                (point-max)))
731                      (buffer-substring-no-properties (point-min) (point-max)))
732                  (erase-buffer))))
733             ;; Find the original headers.
734             (set-buffer gnus-original-article-buffer)
735             (goto-char (point-min))
736             (while (looking-at message-unix-mail-delimiter)
737               (forward-line 1))
738             (setq beg (point))
739             (setq end (or (message-goto-body) beg))
740             ;; Delete the headers from the displayed articles.
741             (set-buffer gnus-article-copy)
742             (delete-region (goto-char (point-min))
743                            (or (message-goto-body) (point-max)))
744             ;; Insert the original article headers.
745             (insert-buffer-substring gnus-original-article-buffer beg end)
746             ;; Decode charsets.
747             (let ((gnus-article-decode-hook
748                    (delq 'article-decode-charset
749                          (copy-sequence gnus-article-decode-hook))))
750               ;; Needed for T-gnus.
751               (add-hook 'gnus-article-decode-hook
752                         'article-decode-encoded-words)
753               (run-hooks 'gnus-article-decode-hook)))))
754       gnus-article-copy)))
755
756 (defun gnus-post-news (post &optional group header article-buffer yank subject
757                             force-news)
758   (when article-buffer
759     (gnus-copy-article-buffer))
760   (let ((gnus-article-reply (and article-buffer (gnus-summary-article-number)))
761         (add-to-list gnus-add-to-list))
762     (gnus-setup-message (cond (yank 'reply-yank)
763                               (article-buffer 'reply)
764                               (t 'message))
765       (let* ((group (or group gnus-newsgroup-name))
766              (charset (gnus-group-name-charset nil group))
767              (pgroup group)
768              to-address to-group mailing-list to-list
769              newsgroup-p)
770         (when group
771           (setq to-address (gnus-parameter-to-address group)
772                 to-group (gnus-group-find-parameter group 'to-group)
773                 to-list (gnus-parameter-to-list group)
774                 newsgroup-p (gnus-group-find-parameter group 'newsgroup)
775                 mailing-list (when gnus-mailing-list-groups
776                                (string-match gnus-mailing-list-groups group))
777                 group (gnus-group-name-decode (gnus-group-real-name group)
778                                               charset)))
779         (if (or (and to-group
780                      (gnus-news-group-p to-group))
781                 newsgroup-p
782                 force-news
783                 (and (gnus-news-group-p
784                       (or pgroup gnus-newsgroup-name)
785                       (or header gnus-current-article))
786                      (not mailing-list)
787                      (not to-list)
788                      (not to-address)))
789             ;; This is news.
790             (if post
791                 (message-news (or to-group group))
792               (set-buffer gnus-article-copy)
793               (gnus-msg-treat-broken-reply-to)
794               (message-followup (if (or newsgroup-p force-news)
795                                     (if (save-restriction
796                                           (article-narrow-to-head)
797                                           (message-fetch-field "newsgroups"))
798                                         nil
799                                       "")
800                                   to-group)))
801           ;; The is mail.
802           (if post
803               (progn
804                 (message-mail (or to-address to-list))
805                 ;; Arrange for mail groups that have no `to-address' to
806                 ;; get that when the user sends off the mail.
807                 (when (and (not to-list)
808                            (not to-address)
809                            add-to-list)
810                   (push (list 'gnus-inews-add-to-address pgroup)
811                         message-send-actions)))
812             (set-buffer gnus-article-copy)
813             (gnus-msg-treat-broken-reply-to)
814             (message-wide-reply to-address)))
815         (when yank
816           (gnus-inews-yank-articles yank))))))
817
818 (defun gnus-msg-treat-broken-reply-to (&optional force)
819   "Remove the Reply-to header iff broken-reply-to."
820   (when (or force
821             (gnus-group-find-parameter
822              gnus-newsgroup-name 'broken-reply-to))
823     (save-restriction
824       (message-narrow-to-head)
825       (message-remove-header "reply-to"))))
826
827 (defun gnus-post-method (arg group &optional silent)
828   "Return the posting method based on GROUP and ARG.
829 If SILENT, don't prompt the user."
830   (let ((gnus-post-method (or (gnus-parameter-post-method group)
831                               gnus-post-method))
832         (group-method (gnus-find-method-for-group group)))
833     (cond
834      ;; If the group-method is nil (which shouldn't happen) we use
835      ;; the default method.
836      ((null group-method)
837       (or (and (listp gnus-post-method) ;If not current/native/nil
838                (not (listp (car gnus-post-method))) ; and not a list of methods
839                gnus-post-method)        ;then use it.
840           gnus-select-method
841           message-post-method))
842      ;; We want the inverse of the default
843      ((and arg (not (eq arg 0)))
844       (if (eq gnus-post-method 'current)
845           gnus-select-method
846         group-method))
847      ;; We query the user for a post method.
848      ((or arg
849           (and (listp gnus-post-method)
850                (listp (car gnus-post-method))))
851       (let* ((methods
852               ;; Collect all methods we know about.
853               (append
854                (when (listp gnus-post-method)
855                  (if (listp (car gnus-post-method))
856                      gnus-post-method
857                    (list gnus-post-method)))
858                gnus-secondary-select-methods
859                (mapcar 'cdr gnus-server-alist)
860                (mapcar 'car gnus-opened-servers)
861                (list gnus-select-method)
862                (list group-method)))
863              method-alist post-methods method)
864         ;; Weed out all mail methods.
865         (while methods
866           (setq method (gnus-server-get-method "" (pop methods)))
867           (when (and (or (gnus-method-option-p method 'post)
868                          (gnus-method-option-p method 'post-mail))
869                      (not (member method post-methods)))
870             (push method post-methods)))
871         ;; Create a name-method alist.
872         (setq method-alist
873               (mapcar
874                (lambda (m)
875                  (if (equal (cadr m) "")
876                      (list (symbol-name (car m)) m)
877                    (list (concat (cadr m) " (" (symbol-name (car m)) ")") m)))
878                post-methods))
879         ;; Query the user.
880         (cadr
881          (assoc
882           (setq gnus-last-posting-server
883                 (if (and silent
884                          gnus-last-posting-server)
885                     ;; Just use the last value.
886                     gnus-last-posting-server
887                   (completing-read
888                    "Posting method: " method-alist nil t
889                    (cons (or gnus-last-posting-server "") 0))))
890           method-alist))))
891      ;; Override normal method.
892      ((and (eq gnus-post-method 'current)
893            (not (eq (car group-method) 'nndraft))
894            (gnus-get-function group-method 'request-post t))
895       (assert (not arg))
896       group-method)
897      ;; Use gnus-post-method.
898      ((listp gnus-post-method)          ;A method...
899       (assert (not (listp (car gnus-post-method)))) ;... not a list of methods.
900       gnus-post-method)
901      ;; Use the normal select method (nil or native).
902      (t gnus-select-method))))
903
904 \f
905 (defun gnus-message-make-user-agent (&optional include-mime-info max-column
906                                                  newline-product)
907   "Return a user-agent info.  If INCLUDE-MIME-INFO is non-nil and the
908 variable `mime-edit-user-agent-value' is bound, the value will be
909 included in the return value.  If MAX-COLUMN is specified, the return
910 value will be folded up as it were filled.  NEWLINE-PRODUCT specifies
911 whether a newline should be inserted in front of each product-token.
912 If the value is t or `hard', it works strictly.  Otherwise, if it is
913 non-nil (e.g. `soft'), it works semi-strictly.
914
915 Here is an example of how to use this function:
916
917 \(add-hook 'gnus-message-setup-hook
918           (lambda nil
919             (setq message-user-agent nil)
920             (save-excursion
921               (save-restriction
922                 (message-narrow-to-headers)
923                 (goto-char (point-max))
924                 (insert \"User-Agent: \"
925                         (gnus-message-make-user-agent t 76 'soft)
926                         \"\\n\")))))
927 "
928   (let ((user-agent (if (and include-mime-info
929                              (boundp 'mime-edit-user-agent-value))
930                         (concat (gnus-extended-version)
931                                 " "
932                                 mime-edit-user-agent-value)
933                       (gnus-extended-version))))
934     (when max-column
935       (unless (natnump max-column)
936         (setq max-column 76))
937       (with-temp-buffer
938         (set-buffer-multibyte t)
939         (insert (mapconcat 'identity (split-string user-agent) " "))
940         (goto-char (point-min))
941         (let ((bol t)
942               start agent agents width element swidth)
943           (while (re-search-forward "\\([^ ]+\\) ?" nil t)
944             (setq start (match-beginning 0))
945             (if (eq (char-after start) ?\()
946                 (progn
947                   (goto-char start)
948                   (forward-list)
949                   (push (buffer-substring start (point)) agent))
950               (when agent
951                 (push (nreverse agent) agents))
952               (setq agent (list (match-string 1)))))
953           (when agent
954             (push (nreverse agent) agents))
955           (setq agents (nreverse agents))
956           (if (> (+ 12 (string-width (caar agents))) max-column)
957               (setq user-agent "\n"
958                     width 0)
959             (setq user-agent ""
960                   width 11))
961           (while agents
962             (setq agent (car agents)
963                   agents (cdr agents))
964             (when (and (not bol)
965                        (or (memq newline-product '(t hard))
966                            (and newline-product
967                                 (> (+ width 1
968                                       (string-width (mapconcat 'identity
969                                                                agent " ")))
970                                    max-column))))
971               (setq user-agent (concat user-agent "\n")
972                     width 0
973                     bol t))
974             (while agent
975               (setq element (car agent)
976                     swidth (string-width element)
977                     agent (cdr agent))
978               (if bol
979                   (setq user-agent (if (member user-agent '("" "\n"))
980                                        (concat user-agent element)
981                                      (concat user-agent " " element))
982                         width (+ width 1 swidth)
983                         bol nil)
984                 (if (> (+ width 1 swidth) max-column)
985                     (setq user-agent (concat user-agent "\n " element)
986                           width (1+ swidth))
987                   (setq user-agent (concat user-agent " " element)
988                         width (+ width 1 swidth)))))))))
989     user-agent))
990
991 \f
992 ;;;
993 ;;; Gnus Mail Functions
994 ;;;
995
996 ;;; Mail reply commands of Gnus summary mode
997
998 (defun gnus-summary-reply (&optional yank wide very-wide)
999   "Start composing a mail reply to the current message.
1000 If prefix argument YANK is non-nil, the original article is yanked
1001 automatically.
1002 If WIDE, make a wide reply.
1003 If VERY-WIDE, make a very wide reply."
1004   (interactive
1005    (list (and current-prefix-arg
1006               (gnus-summary-work-articles 1))))
1007   ;; Stripping headers should be specified with mail-yank-ignored-headers.
1008   (when yank
1009     (gnus-summary-goto-subject (car yank)))
1010   (let ((gnus-article-reply (or yank (gnus-summary-article-number)))
1011         (headers ""))
1012     (gnus-setup-message (if yank 'reply-yank 'reply)
1013       (if (not very-wide)
1014           (gnus-summary-select-article)
1015         (dolist (article very-wide)
1016           (gnus-summary-select-article nil nil nil article)
1017           (save-excursion
1018             (set-buffer (gnus-copy-article-buffer))
1019             (gnus-msg-treat-broken-reply-to)
1020             (save-restriction
1021               (message-narrow-to-head)
1022               (setq headers (concat headers (buffer-string)))))))
1023       (set-buffer (gnus-copy-article-buffer))
1024       (gnus-msg-treat-broken-reply-to gnus-msg-force-broken-reply-to)
1025       (save-restriction
1026         (message-narrow-to-head)
1027         (when very-wide
1028           (erase-buffer)
1029           (insert headers))
1030         (goto-char (point-max)))
1031       (message-reply nil wide)
1032       (when yank
1033         (gnus-inews-yank-articles yank)))))
1034
1035 (defun gnus-summary-reply-with-original (n &optional wide)
1036   "Start composing a reply mail to the current message.
1037 The original article will be yanked."
1038   (interactive "P")
1039   (gnus-summary-reply (gnus-summary-work-articles n) wide))
1040
1041 (defun gnus-summary-reply-broken-reply-to (&optional yank wide very-wide)
1042   "Like `gnus-summary-reply' except removing reply-to field.
1043 If prefix argument YANK is non-nil, the original article is yanked
1044 automatically.
1045 If WIDE, make a wide reply.
1046 If VERY-WIDE, make a very wide reply."
1047   (interactive
1048    (list (and current-prefix-arg
1049               (gnus-summary-work-articles 1))))
1050   (let ((gnus-msg-force-broken-reply-to t))
1051     (gnus-summary-reply yank wide very-wide)))
1052
1053 (defun gnus-summary-reply-broken-reply-to-with-original (n &optional wide)
1054   "Like `gnus-summary-reply-with-original' except removing reply-to field.
1055 The original article will be yanked."
1056   (interactive "P")
1057   (gnus-summary-reply-broken-reply-to (gnus-summary-work-articles n) wide))
1058
1059 (defun gnus-summary-wide-reply (&optional yank)
1060   "Start composing a wide reply mail to the current message.
1061 If prefix argument YANK is non-nil, the original article is yanked
1062 automatically."
1063   (interactive
1064    (list (and current-prefix-arg
1065               (gnus-summary-work-articles 1))))
1066   (gnus-summary-reply yank t))
1067
1068 (defun gnus-summary-wide-reply-with-original (n)
1069   "Start composing a wide reply mail to the current message.
1070 The original article will be yanked."
1071   (interactive "P")
1072   (gnus-summary-reply-with-original n t))
1073
1074 (defun gnus-summary-very-wide-reply (&optional yank)
1075   "Start composing a very wide reply mail to the current message.
1076 If prefix argument YANK is non-nil, the original article is yanked
1077 automatically."
1078   (interactive
1079    (list (and current-prefix-arg
1080               (gnus-summary-work-articles 1))))
1081   (gnus-summary-reply yank t (gnus-summary-work-articles yank)))
1082
1083 (defun gnus-summary-very-wide-reply-with-original (n)
1084   "Start composing a very wide reply mail to the current message.
1085 The original article will be yanked."
1086   (interactive "P")
1087   (gnus-summary-reply
1088    (gnus-summary-work-articles n) t (gnus-summary-work-articles n)))
1089
1090 (defun gnus-summary-mail-forward (&optional full-headers post)
1091   "Forward the current message to another user.
1092 If FULL-HEADERS (the prefix), include full headers when forwarding."
1093   (interactive "P")
1094   (gnus-setup-message 'forward
1095     (gnus-summary-select-article)
1096     (let ((charset default-mime-charset))
1097       (set-buffer gnus-original-article-buffer)
1098       (make-local-variable 'default-mime-charset)
1099       (setq default-mime-charset charset))
1100     (let ((message-included-forward-headers
1101            (if full-headers "" message-included-forward-headers)))
1102       (message-forward post))))
1103
1104 (defun gnus-summary-digest-mail-forward (&optional n post)
1105   "Digests and forwards all articles in this series.
1106 If N is a positive number, forward the N next articles.
1107 If N is a negative number, forward the N previous articles.
1108 If N is nil and any articles have been marked with the process mark,
1109 forward those articles instead.
1110 Optional POST will use news to forward instead of mail."
1111   (interactive "P")
1112   (let ((articles (gnus-summary-work-articles n))
1113         (topics "Topics:\n")
1114         subject article frame)
1115     (when (car articles)
1116       (gnus-setup-message 'forward
1117         (gnus-summary-select-article)
1118         (if (cdr articles)
1119             (setq articles (sort articles '<)
1120                   subject "Digested Articles")
1121           (with-current-buffer gnus-original-article-buffer
1122             (setq subject (message-make-forward-subject))))
1123         (if post
1124             (message-news nil subject)
1125           (message-mail nil subject))
1126         (when (and message-use-multi-frames (cdr articles))
1127           (setq frame (window-frame (get-buffer-window (current-buffer)))))
1128         (message-goto-body)
1129         (while (setq article (pop articles))
1130           (save-window-excursion
1131             (set-buffer gnus-summary-buffer)
1132             (gnus-summary-select-article nil nil nil article)
1133             (setq topics (concat topics "    "
1134                                  (mail-header-subject gnus-current-headers)
1135                                  "\n"))
1136             (gnus-summary-remove-process-mark article))
1137           (when frame
1138             (select-frame frame))
1139           (insert (mime-make-tag "message" "rfc822") "\n")
1140           (narrow-to-region (point) (point))
1141           (insert-buffer-substring gnus-original-article-buffer)
1142           (save-restriction
1143             (article-narrow-to-head)
1144             (message-remove-header message-included-forward-headers t nil t))
1145           (goto-char (point-max))
1146           (widen))
1147         (push-mark)
1148         (message-goto-body)
1149         (insert topics)
1150         (message-goto-body)
1151         (mime-edit-enclose-digest-region (point)(mark t))))))
1152
1153 (defun gnus-summary-digest-post-forward (&optional n)
1154   "Digest and forwards all articles in this series to a newsgroup.
1155 If N is a positive number, forward the N next articles.
1156 If N is a negative number, forward the N previous articles.
1157 If N is nil and any articles have been marked with the process mark,
1158 forward those articles instead."
1159   (interactive "P")
1160   (gnus-summary-digest-mail-forward n t))
1161
1162 (defun gnus-summary-resend-message (address n)
1163   "Resend the current article to ADDRESS."
1164   (interactive
1165    (list (message-read-from-minibuffer "Resend message(s) to: ")
1166          current-prefix-arg))
1167   (let ((articles (gnus-summary-work-articles n))
1168         article)
1169     (while (setq article (pop articles))
1170       (gnus-summary-select-article nil nil nil article)
1171       (save-excursion
1172         (set-buffer gnus-original-article-buffer)
1173         (message-resend address))
1174       (gnus-summary-mark-article-as-forwarded article))))
1175
1176 (defun gnus-summary-post-forward (&optional full-headers)
1177   "Forward the current article to a newsgroup.
1178 If FULL-HEADERS (the prefix), include full headers when forwarding."
1179   (interactive "P")
1180   (gnus-summary-mail-forward full-headers t))
1181
1182 (defvar gnus-nastygram-message
1183   "The following article was inappropriately posted to %s.\n\n"
1184   "Format string to insert in nastygrams.
1185 The current group name will be inserted at \"%s\".")
1186
1187 (defun gnus-summary-mail-nastygram (n)
1188   "Send a nastygram to the author of the current article."
1189   (interactive "P")
1190   (when (or gnus-expert-user
1191             (gnus-y-or-n-p
1192              "Really send a nastygram to the author of the current article? "))
1193     (let ((group gnus-newsgroup-name))
1194       (gnus-summary-reply-with-original n)
1195       (set-buffer gnus-message-buffer)
1196       (message-goto-body)
1197       (insert (format gnus-nastygram-message group))
1198       (message-send-and-exit))))
1199
1200 (defun gnus-summary-mail-crosspost-complaint (n)
1201   "Send a complaint about crossposting to the current article(s)."
1202   (interactive "P")
1203   (let ((articles (gnus-summary-work-articles n))
1204         article)
1205     (while (setq article (pop articles))
1206       (set-buffer gnus-summary-buffer)
1207       (gnus-summary-goto-subject article)
1208       (let ((group (gnus-group-real-name gnus-newsgroup-name))
1209             newsgroups followup-to)
1210         (gnus-summary-select-article)
1211         (set-buffer gnus-original-article-buffer)
1212         (if (and (<= (length (message-tokenize-header
1213                               (setq newsgroups
1214                                     (mail-fetch-field "newsgroups"))
1215                               ", "))
1216                      1)
1217                  (or (not (setq followup-to (mail-fetch-field "followup-to")))
1218                      (not (member group (message-tokenize-header
1219                                          followup-to ", ")))))
1220             (if followup-to
1221                 (gnus-message 1 "Followup-to restricted")
1222               (gnus-message 1 "Not a crossposted article"))
1223           (set-buffer gnus-summary-buffer)
1224           (gnus-summary-reply-with-original 1)
1225           (set-buffer gnus-message-buffer)
1226           (message-goto-body)
1227           (insert (format gnus-crosspost-complaint newsgroups group))
1228           (message-goto-subject)
1229           (re-search-forward " *$")
1230           (replace-match " (crosspost notification)" t t)
1231           (gnus-deactivate-mark)
1232           (when (gnus-y-or-n-p "Send this complaint? ")
1233             (message-send-and-exit)))))))
1234
1235 (defun gnus-mail-parse-comma-list ()
1236   (let (accumulated
1237         beg)
1238     (skip-chars-forward " ")
1239     (while (not (eobp))
1240       (setq beg (point))
1241       (skip-chars-forward "^,")
1242       (while (zerop
1243               (save-excursion
1244                 (save-restriction
1245                   (let ((i 0))
1246                     (narrow-to-region beg (point))
1247                     (goto-char beg)
1248                     (logand (progn
1249                               (while (search-forward "\"" nil t)
1250                                 (incf i))
1251                               (if (zerop i) 2 i))
1252                             2)))))
1253         (skip-chars-forward ",")
1254         (skip-chars-forward "^,"))
1255       (skip-chars-backward " ")
1256       (push (buffer-substring beg (point))
1257             accumulated)
1258       (skip-chars-forward "^,")
1259       (skip-chars-forward ", "))
1260     accumulated))
1261
1262 (defun gnus-inews-add-to-address (group)
1263   (let ((to-address (mail-fetch-field "to")))
1264     (when (and to-address
1265                (gnus-alive-p))
1266       ;; This mail group doesn't have a `to-list', so we add one
1267       ;; here.  Magic!
1268       (when (gnus-y-or-n-p
1269              (format "Do you want to add this as `to-list': %s " to-address))
1270         (gnus-group-add-parameter group (cons 'to-list to-address))))))
1271
1272 (defun gnus-put-message ()
1273   "Put the current message in some group and return to Gnus."
1274   (interactive)
1275   (let ((reply gnus-article-reply)
1276         (winconf gnus-prev-winconf)
1277         (group gnus-newsgroup-name))
1278     (unless (and group
1279                  (not (gnus-group-read-only-p group)))
1280       (setq group (read-string "Put in group: " nil (gnus-writable-groups))))
1281
1282     (when (gnus-gethash group gnus-newsrc-hashtb)
1283       (error "No such group: %s" group))
1284     (save-excursion
1285       (save-restriction
1286         (widen)
1287         (message-narrow-to-headers)
1288         (let ((gnus-deletable-headers nil))
1289           (message-generate-headers
1290            (if (message-news-p)
1291                message-required-news-headers
1292              message-required-mail-headers)))
1293         (goto-char (point-max))
1294         (insert "Gcc: " group "\n")
1295         (widen)))
1296     (gnus-inews-do-gcc)
1297     (when (and (get-buffer gnus-group-buffer)
1298                (gnus-buffer-exists-p (car-safe reply))
1299                (cdr reply))
1300       (set-buffer (car reply))
1301       (gnus-summary-mark-article-as-replied (cdr reply)))
1302     (when winconf
1303       (set-window-configuration winconf))))
1304
1305 (defun gnus-article-mail (yank)
1306   "Send a reply to the address near point.
1307 If YANK is non-nil, include the original article."
1308   (interactive "P")
1309   (let ((address
1310          (buffer-substring
1311           (save-excursion (re-search-backward "[ \t\n]" nil t) (1+ (point)))
1312           (save-excursion (re-search-forward "[ \t\n]" nil t) (1- (point))))))
1313     (when address
1314       (message-reply address)
1315       (when yank
1316         (gnus-inews-yank-articles (list (cdr gnus-article-current)))))))
1317
1318 (defvar nntp-server-type)
1319 (defun gnus-bug ()
1320   "Send a bug report to the Gnus maintainers."
1321   (interactive)
1322   (unless (gnus-alive-p)
1323     (error "Gnus has been shut down"))
1324   (gnus-setup-message (if (message-mail-user-agent) 'message 'bug)
1325     (unless (message-mail-user-agent)
1326       (message-pop-to-buffer "*Gnus Bug*")
1327       (delete-other-windows)
1328       (when gnus-bug-create-help-buffer
1329         (switch-to-buffer "*Gnus Help Bug*")
1330         (erase-buffer)
1331         (insert gnus-bug-message)
1332         (goto-char (point-min))
1333         (sit-for 0)
1334         (set-buffer "*Gnus Bug*")))
1335     (let ((message-this-is-mail t))
1336       (message-setup `((To . ,gnus-maintainer) (Subject . ""))))
1337     (when gnus-bug-create-help-buffer
1338       (push `(gnus-bug-kill-buffer) message-send-actions))
1339     (goto-char (point-min))
1340     (re-search-forward (concat "^" (regexp-quote mail-header-separator) "$"))
1341     (forward-line 1)
1342     (insert gnus-product-name " " gnus-version-number
1343             " (r" gnus-revision-number ") "
1344             "based on " gnus-original-product-name " v"
1345             gnus-original-version-number "\n"
1346             (emacs-version) "\n")
1347     (when (and (boundp 'nntp-server-type)
1348                (stringp nntp-server-type))
1349       (insert nntp-server-type))
1350     (insert "\n\n\n\n\n")
1351     (let (mime-content-types)
1352       (mime-edit-insert-tag "text" "plain" "; type=emacs-lisp"))
1353     (insert (with-temp-buffer
1354               (gnus-debug)
1355               (buffer-string)))
1356     (let (mime-content-types)
1357       (mime-edit-insert-tag "text" "plain"))
1358     (goto-char (point-min))
1359     (search-forward "Subject: " nil t)
1360     (message "")))
1361
1362 (defun gnus-bug-kill-buffer ()
1363   (when (get-buffer "*Gnus Help Bug*")
1364     (kill-buffer "*Gnus Help Bug*")))
1365
1366 (defun gnus-summary-yank-message (buffer n)
1367   "Yank the current article into a composed message."
1368   (interactive
1369    (list (completing-read "Buffer: " (mapcar 'list (message-buffers)) nil t)
1370          current-prefix-arg))
1371   (when (gnus-buffer-live-p buffer)
1372     (let ((summary-frame (selected-frame))
1373           (message-frame (when (static-if (featurep 'xemacs)
1374                                    (device-on-window-system-p)
1375                                  window-system)
1376                            (let ((window (get-buffer-window buffer t)))
1377                              (when window
1378                                (window-frame window)))))
1379           (separator (concat "^" (regexp-quote mail-header-separator)
1380                              "\n")))
1381       (gnus-summary-iterate n
1382         (gnus-summary-select-article)
1383         (gnus-copy-article-buffer)
1384         (when (frame-live-p message-frame)
1385           (raise-frame message-frame)
1386           (select-frame message-frame))
1387         (with-current-buffer buffer
1388           (when (save-excursion
1389                   (beginning-of-line)
1390                   (let (case-fold-search)
1391                     (and (not (re-search-backward separator nil t))
1392                          (re-search-forward separator nil t))))
1393             (goto-char (match-end 0)))
1394           (message-yank-buffer gnus-article-copy))
1395         (select-frame summary-frame))
1396       (when (frame-live-p message-frame)
1397         (select-frame message-frame)))))
1398
1399 (defun gnus-debug ()
1400   "Attempts to go through the Gnus source file and report what variables have been changed.
1401 The source file has to be in the Emacs load path."
1402   (interactive)
1403   (let ((files '("gnus.el" "gnus-sum.el" "gnus-group.el"
1404                  "gnus-art.el" "gnus-start.el" "gnus-async.el"
1405                  "gnus-msg.el" "gnus-score.el" "gnus-win.el" "gnus-topic.el"
1406                  "nnmail.el" "nntp.el" "message.el"))
1407         (point (point))
1408         file expr olist sym)
1409     (gnus-message 4 "Please wait while we snoop your variables...")
1410     ;; Go through all the files looking for non-default values for variables.
1411     (save-excursion
1412       (sit-for 0)
1413       (set-buffer (gnus-get-buffer-create " *gnus bug info*"))
1414       (while files
1415         (erase-buffer)
1416         (when (and (setq file (locate-library (pop files)))
1417                    (file-exists-p file))
1418           (insert-file-contents file)
1419           (goto-char (point-min))
1420           (if (not (re-search-forward "^;;* *Internal variables" nil t))
1421               (gnus-message 4 "Malformed sources in file %s" file)
1422             (narrow-to-region (point-min) (point))
1423             (goto-char (point-min))
1424             (while (setq expr (ignore-errors (read (current-buffer))))
1425               (ignore-errors
1426                 (and (memq (car expr) '(defvar defcustom defvoo))
1427                      (stringp (nth 3 expr))
1428                      (or (not (boundp (nth 1 expr)))
1429                          (not (equal (eval (nth 2 expr))
1430                                      (symbol-value (nth 1 expr)))))
1431                      (push (nth 1 expr) olist)))))))
1432       (kill-buffer (current-buffer)))
1433     (when (setq olist (nreverse olist))
1434       (insert ";----------------- Environment follows ------------------\n\n"))
1435     (while olist
1436       (if (boundp (car olist))
1437           (condition-case ()
1438               (pp `(setq ,(car olist)
1439                          ,(if (or (consp (setq sym (symbol-value (car olist))))
1440                                   (and (symbolp sym)
1441                                        (not (or (eq sym nil)
1442                                                 (eq sym t)))))
1443                               (list 'quote (symbol-value (car olist)))
1444                             (symbol-value (car olist))))
1445                   (current-buffer))
1446             (error
1447              (format "(setq %s 'whatever)\n" (car olist))))
1448         (insert ";; (makeunbound '" (symbol-name (car olist)) ")\n"))
1449       (setq olist (cdr olist)))
1450     ;; Remove any control chars - they seem to cause trouble for some
1451     ;; mailers.  (Byte-compiled output from the stuff above.)
1452     (goto-char point)
1453     (while (re-search-forward "[\000-\010\013-\037\200-\237]" nil t)
1454       (replace-match (format "\\%03o" (string-to-char (match-string 0)))
1455                      t t))
1456     ;; Break MIME tags purposely.
1457     (goto-char point)
1458     (while (re-search-forward mime-edit-tag-regexp nil t)
1459       (goto-char (1+ (match-beginning 0)))
1460       (insert "X"))))
1461
1462 ;;; Treatment of rejected articles.
1463 ;;; Bounced mail.
1464
1465 (defun gnus-summary-resend-bounced-mail (&optional fetch)
1466   "Re-mail the current message.
1467 This only makes sense if the current message is a bounce message than
1468 contains some mail you have written which has been bounced back to
1469 you.
1470 If FETCH, try to fetch the article that this is a reply to, if indeed
1471 this is a reply."
1472   (interactive "P")
1473   (gnus-summary-select-article t)
1474   (set-buffer gnus-original-article-buffer)
1475   (let ((gnus-message-setup-hook '(gnus-maybe-setup-default-charset)))
1476     (gnus-setup-message 'compose-bounce
1477       (let* ((references (mail-fetch-field "references"))
1478              (parent (and references (gnus-parent-id references))))
1479         (message-bounce)
1480         ;; If there are references, we fetch the article we answered to.
1481         (and fetch parent
1482              (gnus-summary-refer-article parent)
1483              (gnus-summary-show-all-headers))))))
1484
1485 ;;; Gcc handling.
1486
1487 (defun gnus-inews-group-method (group)
1488   (cond
1489    ;; If the group doesn't exist, we assume
1490    ;; it's an archive group...
1491    ((and (null (gnus-get-info group))
1492          (eq (car (gnus-server-to-method gnus-message-archive-method))
1493              (car (gnus-server-to-method (gnus-group-method group)))))
1494     gnus-message-archive-method)
1495    ;; Use the method.
1496    ((gnus-info-method (gnus-get-info group))
1497     (gnus-info-method (gnus-get-info group)))
1498    ;; Find the method.
1499    (t (gnus-server-to-method (gnus-group-method group)))))
1500
1501 ;; Do Gcc handling, which copied the message over to some group.
1502 (defun gnus-inews-do-gcc (&optional gcc)
1503   (interactive)
1504   (save-excursion
1505     (save-restriction
1506       (message-narrow-to-headers)
1507       (let ((gcc (or gcc (mail-fetch-field "gcc" nil t)))
1508             (coding-system-for-write 'raw-text)
1509             (output-coding-system 'raw-text)
1510             groups group method group-art)
1511         (when gcc
1512           (message-remove-header "gcc")
1513           (widen)
1514           (setq groups (message-unquote-tokens
1515                         (message-tokenize-header gcc " ,")))
1516           ;; Copy the article over to some group(s).
1517           (while (setq group (pop groups))
1518             (unless (gnus-check-server
1519                      (setq method (gnus-inews-group-method group)))
1520               (error "Can't open server %s" (if (stringp method) method
1521                                               (car method))))
1522             (unless (gnus-request-group group nil method)
1523               (gnus-request-create-group group method))
1524             (save-excursion
1525               (nnheader-set-temp-buffer " *acc*")
1526               (insert-buffer-substring message-encoding-buffer)
1527               (gnus-run-hooks 'gnus-before-do-gcc-hook)
1528               (goto-char (point-min))
1529               (when (re-search-forward
1530                      (concat "^" (regexp-quote mail-header-separator) "$")
1531                      nil t)
1532                 (replace-match "" t t ))
1533               (unless (setq group-art
1534                             (gnus-request-accept-article group method t t))
1535                 (gnus-message 1 "Couldn't store article in group %s: %s"
1536                               group (gnus-status-message method))
1537                 (sit-for 2))
1538               (when (and group-art gnus-inews-mark-gcc-as-read)
1539                 (gnus-group-mark-article-read group (cdr group-art)))
1540               (kill-buffer (current-buffer)))))))))
1541
1542 (defun gnus-inews-insert-gcc ()
1543   "Insert Gcc headers based on `gnus-outgoing-message-group'."
1544   (save-excursion
1545     (save-restriction
1546       (message-narrow-to-headers)
1547       (let* ((group gnus-outgoing-message-group)
1548              (gcc (cond
1549                    ((gnus-functionp group)
1550                     (funcall group))
1551                    ((or (stringp group) (list group))
1552                     group))))
1553         (when gcc
1554           (insert "Gcc: "
1555                   (if (stringp gcc) gcc
1556                     (mapconcat 'identity gcc " "))
1557                   "\n"))))))
1558
1559 (defun gnus-inews-insert-archive-gcc (&optional group)
1560   "Insert the Gcc to say where the article is to be archived."
1561   (let* ((var gnus-message-archive-group)
1562          (group (or group gnus-newsgroup-name ""))
1563          (gcc-self-val
1564           (and gnus-newsgroup-name
1565                (not (equal gnus-newsgroup-name ""))
1566                (gnus-group-find-parameter
1567                 gnus-newsgroup-name 'gcc-self)))
1568          result
1569          (groups
1570           (cond
1571            ((null gnus-message-archive-method)
1572             ;; Ignore.
1573             nil)
1574            ((stringp var)
1575             ;; Just a single group.
1576             (list var))
1577            ((null var)
1578             ;; We don't want this.
1579             nil)
1580            ((and (listp var) (stringp (car var)))
1581             ;; A list of groups.
1582             var)
1583            ((gnus-functionp var)
1584             ;; A function.
1585             (funcall var group))
1586            (t
1587             ;; An alist of regexps/functions/forms.
1588             (while (and var
1589                         (not
1590                          (setq result
1591                                (cond
1592                                 ((stringp (caar var))
1593                                  ;; Regexp.
1594                                  (when (string-match (caar var) group)
1595                                    (cdar var)))
1596                                 ((gnus-functionp (car var))
1597                                  ;; Function.
1598                                  (funcall (car var) group))
1599                                 (t
1600                                  (eval (car var)))))))
1601               (setq var (cdr var)))
1602             result)))
1603          name)
1604     (when (or groups gcc-self-val)
1605       (when (stringp groups)
1606         (setq groups (list groups)))
1607       (save-excursion
1608         (save-restriction
1609           (message-narrow-to-headers)
1610           (goto-char (point-max))
1611           (insert "Gcc: ")
1612           (if gcc-self-val
1613               ;; Use the `gcc-self' param value instead.
1614               (progn
1615                 (insert
1616                  (if (stringp gcc-self-val)
1617                      gcc-self-val
1618                    group))
1619                 (if (not (eq gcc-self-val 'none))
1620                     (insert "\n")
1621                   (progn
1622                     (beginning-of-line)
1623                     (kill-line))))
1624             ;; Use the list of groups.
1625             (while (setq name (pop groups))
1626               (insert (if (string-match ":" name)
1627                           name
1628                         (gnus-group-prefixed-name
1629                          name gnus-message-archive-method)))
1630               (when groups
1631                 (insert " ")))
1632             (insert "\n")))))))
1633
1634 ;;; Posting styles.
1635
1636 (defun gnus-configure-posting-styles (&optional group-name)
1637   "Configure posting styles according to `gnus-posting-styles'."
1638   (unless gnus-inhibit-posting-styles
1639     (let ((group (or group-name gnus-newsgroup-name ""))
1640           (styles gnus-posting-styles)
1641           style match variable attribute value v results
1642           filep name address element)
1643       ;; If the group has a posting-style parameter, add it at the end with a
1644       ;; regexp matching everything, to be sure it takes precedence over all
1645       ;; the others.
1646       (when gnus-newsgroup-name
1647         (let ((tmp-style (gnus-group-find-parameter group 'posting-style t)))
1648           (when tmp-style
1649             (setq styles (append styles (list (cons ".*" tmp-style)))))))
1650       ;; Go through all styles and look for matches.
1651       (dolist (style styles)
1652         (setq match (pop style))
1653         (goto-char (point-min))
1654         (when (cond
1655                ((stringp match)
1656                 ;; Regexp string match on the group name.
1657                 (string-match match group))
1658                ((eq match 'header)
1659                 (and (gnus-buffer-live-p gnus-article-copy)
1660                      (with-current-buffer gnus-article-copy
1661                        (let ((header (message-fetch-field (pop style))))
1662                          (and header
1663                               (string-match (pop style) header))))))
1664                ((or (symbolp match)
1665                     (gnus-functionp match))
1666                 (cond
1667                  ((gnus-functionp match)
1668                   ;; Function to be called.
1669                   (funcall match))
1670                  ((boundp match)
1671                   ;; Variable to be checked.
1672                   (symbol-value match))))
1673                ((listp match)
1674                 ;; This is a form to be evaled.
1675                 (eval match)))
1676           ;; We have a match, so we set the variables.
1677           (dolist (attribute style)
1678             (setq element (pop attribute)
1679                   variable nil
1680                   filep nil)
1681             (setq value
1682                   (cond
1683                    ((eq (car attribute) ':file)
1684                     (setq filep t)
1685                     (cadr attribute))
1686                    ((eq (car attribute) :value)
1687                     (cadr attribute))
1688                    (t
1689                     (car attribute))))
1690             ;; We get the value.
1691             (setq v
1692                   (cond
1693                    ((stringp value)
1694                     value)
1695                    ((or (symbolp value)
1696                         (gnus-functionp value))
1697                     (cond ((gnus-functionp value)
1698                            (funcall value))
1699                           ((boundp value)
1700                            (symbol-value value))))
1701                    ((listp value)
1702                     (eval value))))
1703             ;; Translate obsolescent value.
1704             (when (eq element 'signature-file)
1705               (setq element 'signature
1706                     filep t))
1707             ;; Get the contents of file elems.
1708             (when (and filep v)
1709               (setq v (with-temp-buffer
1710                         (insert-file-contents v)
1711                         (buffer-string))))
1712             (setq results (delq (assoc element results) results))
1713             (push (cons element v) results))))
1714       ;; Now we have all the styles, so we insert them.
1715       (setq name (assq 'name results)
1716             address (assq 'address results))
1717       (setq results (delq name (delq address results)))
1718       ;; make-local-hook is not obsolete in Emacs 20 or XEmacs.
1719       (make-local-hook 'message-setup-hook)
1720       (dolist (result results)
1721         (add-hook 'message-setup-hook
1722                   (cond
1723                    ((eq 'eval (car result))
1724                     'ignore)
1725                    ((eq 'body (car result))
1726                     `(lambda ()
1727                        (save-excursion
1728                          (message-goto-body)
1729                          (insert ,(cdr result)))))
1730                    ((eq 'signature (car result))
1731                     (set (make-local-variable 'message-signature) nil)
1732                     (set (make-local-variable 'message-signature-file) nil)
1733                     (if (not (cdr result))
1734                         'ignore
1735                       `(lambda ()
1736                          (save-excursion
1737                            (let ((message-signature ,(cdr result)))
1738                              (when message-signature
1739                                (message-insert-signature)))))))
1740                    (t
1741                     (let ((header
1742                            (if (symbolp (car result))
1743                                (capitalize (symbol-name (car result)))
1744                              (car result))))
1745                       `(lambda ()
1746                          (save-excursion
1747                            (message-remove-header ,header)
1748                            (let ((value ,(cdr result)))
1749                              (when value
1750                                (message-goto-eoh)
1751                                (insert ,header ": " value "\n"))))))))
1752                   nil 'local))
1753       (when (or name address)
1754         (add-hook 'message-setup-hook
1755                   `(lambda ()
1756                      (set (make-local-variable 'user-mail-address)
1757                           ,(or (cdr address) user-mail-address))
1758                      (let ((user-full-name ,(or (cdr name) (user-full-name)))
1759                            (user-mail-address
1760                             ,(or (cdr address) user-mail-address)))
1761                        (save-excursion
1762                          (message-remove-header "From")
1763                          (message-goto-eoh)
1764                          (insert "From: " (message-make-from) "\n"))))
1765                   nil 'local)))))
1766
1767
1768 ;;; @ for MIME Edit mode
1769 ;;;
1770
1771 (defun gnus-maybe-setup-default-charset ()
1772   (let ((charset
1773          (and (boundp 'gnus-summary-buffer)
1774               (buffer-live-p gnus-summary-buffer)
1775               (save-excursion
1776                 (set-buffer gnus-summary-buffer)
1777                 default-mime-charset))))
1778     (if charset
1779         (progn
1780           (make-local-variable 'default-mime-charset)
1781           (setq default-mime-charset charset)
1782           ))))
1783
1784
1785 ;;; @ for MIME view mode
1786 ;;;
1787
1788 (defun gnus-following-method (buf)
1789   (gnus-setup-message 'reply-yank
1790     (set-buffer buf)
1791     (if (message-news-p)
1792         (message-followup)
1793       (message-reply nil 'wide))
1794     (let ((message-reply-buffer buf))
1795       (message-yank-original))
1796     (message-goto-body))
1797   (kill-buffer buf))
1798
1799
1800 ;;; Allow redefinition of functions.
1801
1802 (gnus-ems-redefine)
1803
1804 (provide 'gnus-msg)
1805
1806 ;;; gnus-msg.el ends here