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