Update FSF's address.
[bpt/emacs.git] / lisp / gnus-msg.el
CommitLineData
41487370 1;;; gnus-msg.el --- mail and post interface for Gnus
b578f267 2
41487370
LMI
3;; Copyright (C) 1995 Free Software Foundation, Inc.
4
5;; Author: Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>
6;; Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
7;; Keywords: news
8
9;; This file is part of GNU Emacs.
10
11;; GNU Emacs is free software; you can redistribute it and/or modify
12;; it under the terms of the GNU General Public License as published by
13;; the Free Software Foundation; either version 2, or (at your option)
14;; any later version.
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
b578f267
EN
22;; along with GNU Emacs; see the file COPYING. If not, write to the
23;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24;; Boston, MA 02111-1307, USA.
41487370
LMI
25
26;;; Commentary:
27
28;;; Code:
29
30(require 'gnus)
31(require 'sendmail)
32(require 'gnus-ems)
33
34(defvar gnus-organization-file "/usr/lib/news/organization"
35 "*Local news organization file.")
36
37(defvar gnus-prepare-article-hook (list 'gnus-inews-insert-signature)
38 "*A hook called after preparing body, but before preparing header headers.
39The default hook (`gnus-inews-insert-signature') inserts a signature
40file specified by the variable `gnus-signature-file'.")
41
42(defvar gnus-post-prepare-function nil
43 "*Function that is run after a post buffer has been prepared.
44It is called with the name of the newsgroup that is posted to. It
45might be used, for instance, for inserting signatures based on the
46newsgroup name. (In that case, `gnus-signature-file' and
47`mail-signature' should both be set to nil).")
48
49(defvar gnus-post-prepare-hook nil
50 "*Hook that is run after a post buffer has been prepared.
51If you want to insert the signature, you might put
52`gnus-inews-insert-signature' in this hook.")
53
54(defvar gnus-use-followup-to t
55 "*Specifies what to do with Followup-To header.
56If nil, ignore the header. If it is t, use its value, but ignore
57`poster'. If it is the symbol `ask', query the user before posting.
58If it is the symbol `use', always use the value.")
59
60(defvar gnus-followup-to-function nil
61 "*A variable that contains a function that returns a followup address.
62The function will be called in the buffer of the article that is being
63followed up. The buffer will be narrowed to the headers of the
64article. To pick header headers, one might use `mail-fetch-field'. The
65function will be called with the name of the current newsgroup as the
66argument.
67
68Here's an example `gnus-followup-to-function':
69
70(setq gnus-followup-to-function
71 (lambda (group)
72 (cond ((string= group \"mail.list\")
73 (or (mail-fetch-field \"sender\")
74 (mail-fetch-field \"from\")))
75 (t
76 (or (mail-fetch-field \"reply-to\")
77 (mail-fetch-field \"from\"))))))")
78
79(defvar gnus-reply-to-function nil
80 "*A variable that contains a function that returns a reply address.
81See the `gnus-followup-to-function' variable for an explanation of how
82this variable is used.
83
84This function should return a string that will be used to fill in the
85header. This function may also return a list. In that case, every
86list element should be a cons where the first car should be a string
87with the header name, and the cdr should be a string with the header
88value.")
89
90(defvar gnus-author-copy (getenv "AUTHORCOPY")
91 "*Save outgoing articles in this file.
92Initialized from the AUTHORCOPY environment variable.
93
94If this variable begins with the character \"|\", outgoing articles
95will be piped to the named program. It is possible to save an article
96in an MH folder as follows:
97
98\(setq gnus-author-copy \"|/usr/local/lib/mh/rcvstore +Article\")
99
100If the first character is not a pipe, articles are saved using the
101function specified by the `gnus-author-copy-saver' variable.")
102
103(defvar gnus-mail-self-blind nil
104 "*Non-nil means insert a BCC header in all outgoing articles.
105This will result in having a copy of the article mailed to yourself.
106The BCC header is inserted when the post buffer is initialized, so you
107can remove or alter the BCC header to override the default.")
108
109(defvar gnus-author-copy-saver (function rmail-output)
110 "*A function called to save outgoing articles.
111This function will be called with the same of the file to store the
112article in. The default function is `rmail-output' which saves in Unix
113mailbox format.")
114
115(defvar gnus-user-login-name nil
116 "*The login name of the user.
117Got from the function `user-login-name' if undefined.")
118
119(defvar gnus-user-full-name nil
120 "*The full name of the user.
121Got from the NAME environment variable if undefined.")
122
123(defvar gnus-user-from-line nil
124 "*Your full, complete e-mail address.
125Overrides the other Gnus variables if it is non-nil.
126
127Here are two example values of this variable:
128
129 \"Lars Magne Ingebrigtsen <larsi@ifi.uio.no>\"
130
131and
132
133 \"larsi@ifi.uio.no (Lars Magne Ingebrigtsen)\"
134
135The first version is recommended, but the name has to be quoted if it
136contains non-alphanumerical characters.")
137
138(defvar gnus-signature-file "~/.signature"
139 "*Your signature file.
140If the variable is a string that doesn't correspond to a file, the
141string itself is inserted.")
142
143(defvar gnus-signature-function nil
144 "*A function that should return a signature file name.
145The function will be called with the name of the newsgroup being
146posted to.
147If the function returns a string that doesn't correspond to a file, the
148string itself is inserted.
149If the function returns nil, the `gnus-signature-file' variable will
150be used instead.")
151
152(defvar gnus-required-headers
153 '(From Date Newsgroups Subject Message-ID Organization Lines X-Newsreader)
154 "*Headers to be generated or prompted for when posting an article.
155RFC977 and RFC1036 require From, Date, Newsgroups, Subject,
156Message-ID. Organization, Lines and X-Newsreader are optional. If
157you want Gnus not to insert some header, remove it from this list.")
158
159(defvar gnus-deletable-headers '(Message-ID Date)
160 "*Headers to be deleted if they already exists and were generated by Gnus previously.")
161
162(defvar gnus-removable-headers '(NNTP-Posting-Host Bcc Xref)
163 "*Headers to be removed unconditionally before posting.")
164
165(defvar gnus-check-before-posting
166 '(subject-cmsg multiple-headers sendsys message-id from
167 long-lines control-chars size new-text
168 signature)
169 "In non-nil, Gnus will attempt to run some checks on outgoing posts.
170If this variable is t, Gnus will check everything it can. If it is a
171list, then those elements in that list will be checked.")
172
173(defvar gnus-delete-supersedes-headers
174 "^Path:\\|^Date\\|^NNTP-Posting-Host:\\|^Supersedes:"
175 "*Header lines matching this regexp will be deleted before posting.
176It's best to delete old Path and Date headers before posting to avoid
177any confusion.")
178
179(defvar gnus-auto-mail-to-author nil
180 "*If non-nil, mail the authors of articles a copy of your follow-ups.
181If this variable is `ask', the user will be prompted for whether to
182mail a copy. The string given by `gnus-mail-courtesy-message' will be
183inserted at the beginning of the mail copy.
184
185Mail is sent using the function specified by the
186`gnus-mail-send-method' variable.")
187
188;; Added by Ethan Bradford <ethanb@ptolemy.astro.washington.edu>.
189(defvar gnus-mail-courtesy-message
190 "The following message is a courtesy copy of an article\nthat has been posted as well.\n\n"
191 "*This is inserted at the start of a mailed copy of a posted message.
192If this variable is nil, no such courtesy message will be added.")
193
194(defvar gnus-mail-reply-method (function gnus-mail-reply-using-mail)
195 "*Function to compose a reply.
196Three pre-made functions are `gnus-mail-reply-using-mail' (sendmail);
197`gnus-mail-reply-using-mhe' (MH-E); and `gnus-mail-reply-using-vm'.")
198
199(defvar gnus-mail-forward-method (function gnus-mail-forward-using-mail)
200 "*Function to forward the current message to another user.
201Three pre-made functions are `gnus-mail-forward-using-mail' (sendmail);
202`gnus-mail-forward-using-mhe' (MH-E); and `gnus-mail-forward-using-vm'.")
203
204(defvar gnus-mail-other-window-method 'gnus-mail-other-window-using-mail
205 "*Function to compose mail in the other window.
206Three pre-made functions are `gnus-mail-other-window-using-mail'
207(sendmail); `gnus-mail-other-window-using-mhe' (MH-E); and
208`gnus-mail-other-window-using-vm'.")
209
210(defvar gnus-mail-send-method send-mail-function
211 "*Function to mail a message which is also being posted as an article.
212The message must have To or Cc header. The default is copied from
213the variable `send-mail-function'.")
214
215(defvar gnus-inews-article-function 'gnus-inews-article
216 "*Function to post an article.")
217
218(defvar gnus-inews-article-hook (list 'gnus-inews-do-fcc)
219 "*A hook called before finally posting an article.
220The default hook (`gnus-inews-do-fcc') does FCC processing (ie. saves
221the article to a file).")
222
223(defvar gnus-inews-article-header-hook nil
224 "*A hook called after inserting the headers in an article to be posted.
225The hook is called from the *post-news* buffer, narrowed to the
226headers.")
227
228(defvar gnus-mail-hook nil
229 "*A hook called as the last thing after setting up a mail buffer.")
230
231;;; Internal variables.
232
233(defvar gnus-post-news-buffer "*post-news*")
234(defvar gnus-mail-buffer "*mail*")
235(defvar gnus-summary-send-map nil)
236(defvar gnus-article-copy nil)
237(defvar gnus-reply-subject nil)
238
239(eval-and-compile
240 (autoload 'gnus-uu-post-news "gnus-uu" nil t)
241 (autoload 'rmail-output "rmailout"))
242
243\f
244;;;
245;;; Gnus Posting Functions
246;;;
247
248(define-prefix-command 'gnus-summary-send-map)
249(define-key gnus-summary-mode-map "S" 'gnus-summary-send-map)
250(define-key gnus-summary-send-map "p" 'gnus-summary-post-news)
251(define-key gnus-summary-send-map "f" 'gnus-summary-followup)
252(define-key gnus-summary-send-map "F" 'gnus-summary-followup-with-original)
253(define-key gnus-summary-send-map "b" 'gnus-summary-followup-and-reply)
254(define-key gnus-summary-send-map "B" 'gnus-summary-followup-and-reply-with-original)
255(define-key gnus-summary-send-map "c" 'gnus-summary-cancel-article)
256(define-key gnus-summary-send-map "s" 'gnus-summary-supersede-article)
257(define-key gnus-summary-send-map "r" 'gnus-summary-reply)
258(define-key gnus-summary-send-map "R" 'gnus-summary-reply-with-original)
259(define-key gnus-summary-send-map "m" 'gnus-summary-mail-other-window)
260(define-key gnus-summary-send-map "u" 'gnus-uu-post-news)
261(define-key gnus-summary-send-map "om" 'gnus-summary-mail-forward)
262(define-key gnus-summary-send-map "op" 'gnus-summary-post-forward)
263(define-key gnus-summary-send-map "Om" 'gnus-uu-digest-mail-forward)
264(define-key gnus-summary-send-map "Op" 'gnus-uu-digest-post-forward)
265
266;;; Internal functions.
267
268(defun gnus-number-base36 (num len)
269 (if (if (< len 0) (<= num 0) (= len 0))
270 ""
271 (concat (gnus-number-base36 (/ num 36) (1- len))
272 (char-to-string (aref "zyxwvutsrqponmlkjihgfedcba9876543210"
273 (% num 36))))))
274
275;;; Post news commands of Gnus group mode and summary mode
276
277(defun gnus-group-mail ()
278 "Start composing a mail."
279 (interactive)
280 (funcall gnus-mail-other-window-method))
281
282(defun gnus-group-post-news ()
283 "Post an article."
284 (interactive)
285 (let ((gnus-newsgroup-name nil))
286 (gnus-post-news 'post nil nil gnus-article-buffer)))
287
288(defun gnus-summary-post-news ()
289 "Post an article."
290 (interactive)
291 (gnus-set-global-variables)
292 (gnus-post-news 'post gnus-newsgroup-name))
293
294(defun gnus-summary-followup (yank &optional yank-articles)
295 "Compose a followup to an article.
296If prefix argument YANK is non-nil, original article is yanked automatically."
297 (interactive "P")
298 (gnus-set-global-variables)
299 (if yank-articles (gnus-summary-goto-subject (car yank-articles)))
300 (save-window-excursion
301 (gnus-summary-select-article))
302 (let ((headers (gnus-get-header-by-number (gnus-summary-article-number)))
303 (gnus-newsgroup-name gnus-newsgroup-name))
304 ;; Check Followup-To: poster.
305 (set-buffer gnus-article-buffer)
306 (if (and gnus-use-followup-to
307 (string-equal "poster" (gnus-fetch-field "followup-to"))
308 (or (not (memq gnus-use-followup-to '(t ask)))
309 (not (gnus-y-or-n-p
310 "Do you want to ignore `Followup-To: poster'? "))))
311 ;; Mail to the poster.
312 (gnus-summary-reply yank)
313 (gnus-post-news nil gnus-newsgroup-name
314 headers gnus-article-buffer
315 (or yank-articles (not (not yank)))))))
316
317(defun gnus-summary-followup-with-original (n)
318 "Compose a followup to an article and include the original article."
319 (interactive "P")
320 (gnus-summary-followup t (gnus-summary-work-articles n)))
321
322;; Suggested by Daniel Quinlan <quinlan@best.com>.
323(defun gnus-summary-followup-and-reply (yank &optional yank-articles)
324 "Compose a followup and do an auto mail to author."
325 (interactive "P")
326 (gnus-set-global-variables)
327 (let ((gnus-auto-mail-to-author t))
328 (gnus-summary-followup yank yank-articles)))
329
330(defun gnus-summary-followup-and-reply-with-original (n)
331 "Compose a followup, include the original, and do an auto mail to author."
332 (interactive "P")
333 (gnus-summary-followup-and-reply t (gnus-summary-work-articles n)))
334
335(defun gnus-summary-cancel-article (n)
336 "Cancel an article you posted."
337 (interactive "P")
338 (gnus-set-global-variables)
339 (let ((articles (gnus-summary-work-articles n)))
340 (while articles
341 (gnus-summary-select-article t nil nil (car articles))
342 (and (gnus-eval-in-buffer-window gnus-article-buffer (gnus-cancel-news))
343 (gnus-summary-mark-as-read (car articles) gnus-canceled-mark))
344 (gnus-summary-remove-process-mark (car articles))
345 (gnus-article-hide-headers-if-wanted)
346 (setq articles (cdr articles)))))
347
348(defun gnus-summary-supersede-article ()
349 "Compose an article that will supersede a previous article.
350This is done simply by taking the old article and adding a Supersedes
351header line with the old Message-ID."
352 (interactive)
353 (gnus-set-global-variables)
354 (gnus-summary-select-article t)
355 (if (not
356 (string-equal
357 (downcase (mail-strip-quoted-names
358 (mail-header-from gnus-current-headers)))
359 (downcase (mail-strip-quoted-names (gnus-inews-user-name)))))
360 (error "This article is not yours."))
361 (save-excursion
362 (set-buffer gnus-article-buffer)
363 (let ((buffer-read-only nil))
364 (goto-char (point-min))
365 (search-forward "\n\n" nil t)
366 (if (not (re-search-backward "^Message-ID: " nil t))
367 (error "No Message-ID in this article"))))
368 (if (gnus-post-news 'post gnus-newsgroup-name)
369 (progn
370 (erase-buffer)
371 (insert-buffer gnus-article-buffer)
372 (if (search-forward "\n\n" nil t)
373 (forward-char -1)
374 (goto-char (point-max)))
375 (narrow-to-region (point-min) (point))
376 (goto-char (point-min))
377 (and gnus-delete-supersedes-headers
378 (delete-matching-lines gnus-delete-supersedes-headers))
379 (goto-char (point-min))
380 (if (not (re-search-forward "^Message-ID: " nil t))
381 (error "No Message-ID in this article")
382 (replace-match "Supersedes: " t t))
383 (goto-char (point-max))
384 (insert mail-header-separator)
385 (widen)
386 (forward-line 1))))
387
388\f
389;;;###autoload
390(defalias 'sendnews 'gnus-post-news)
391
392;;;###autoload
393(defalias 'postnews 'gnus-post-news)
394
395(defun gnus-copy-article-buffer (&optional article-buffer)
396 ;; make a copy of the article buffer with all text properties removed
397 ;; this copy is in the buffer gnus-article-copy.
398 ;; if ARTICLE-BUFFER is nil, gnus-article-buffer is used
399 ;; this buffer should be passed to all mail/news reply/post routines.
400 (setq gnus-article-copy (get-buffer-create " *gnus article copy*"))
401 (buffer-disable-undo gnus-article-copy)
402 (or (memq gnus-article-copy gnus-buffer-list)
403 (setq gnus-buffer-list (cons gnus-article-copy gnus-buffer-list)))
404 (let ((article-buffer (or article-buffer gnus-article-buffer)))
405 (if (and (get-buffer article-buffer)
406 (buffer-name (get-buffer article-buffer)))
407 (save-excursion
408 (set-buffer article-buffer)
409 (widen)
410 (copy-to-buffer gnus-article-copy (point-min) (point-max))
411 (set-text-properties (point-min) (point-max)
412 nil gnus-article-copy)))))
413
b747e8eb 414;;;###autoload
41487370
LMI
415(defun gnus-post-news (post &optional group header article-buffer yank subject)
416 "Begin editing a new USENET news article to be posted.
417Type \\[describe-mode] in the buffer to get a list of commands."
418 (interactive (list t))
419 (gnus-copy-article-buffer article-buffer)
420 (if (or (not gnus-novice-user)
421 gnus-expert-user
422 (not (eq 'post
423 (nth 1 (assoc
424 (format "%s" (car (gnus-find-method-for-group
425 gnus-newsgroup-name)))
426 gnus-valid-select-methods))))
427 (and group
428 (assq 'to-address
429 (nth 5 (nth 2 (gnus-gethash group gnus-newsrc-hashtb)))))
430 (gnus-y-or-n-p "Are you sure you want to post to all of USENET? "))
431 (let ((sumart (if (not post)
432 (save-excursion
433 (set-buffer gnus-summary-buffer)
434 (cons (current-buffer) gnus-current-article))))
435 (from (and header (mail-header-from header)))
436 (winconf (current-window-configuration))
437 real-group)
438 (and gnus-interactive-post
439 (not gnus-expert-user)
440 post (not group)
441 (progn
442 (setq gnus-newsgroup-name
443 (setq group
444 (completing-read "Group: " gnus-active-hashtb)))
445 (or subject
446 (setq subject (read-string "Subject: ")))))
447 (setq mail-reply-buffer gnus-article-copy)
448
449 (let ((newsgroup-name (or group gnus-newsgroup-name "")))
450 (setq real-group (and group (gnus-group-real-name group)))
451 (setq gnus-post-news-buffer
452 (gnus-request-post-buffer
453 post real-group subject header gnus-article-copy
454 (nth 2 (and group (gnus-gethash group gnus-newsrc-hashtb)))
455 (or (cdr (assq 'to-group
456 (nth 5 (nth 2 (gnus-gethash
457 newsgroup-name
458 gnus-newsrc-hashtb)))))
459 (if (and (boundp 'gnus-followup-to-function)
460 gnus-followup-to-function
461 gnus-article-copy)
462 (save-excursion
463 (set-buffer gnus-article-copy)
464 (funcall gnus-followup-to-function group))))
465 gnus-use-followup-to))
466 (if post
467 (gnus-configure-windows 'post 'force)
468 (if yank
469 (gnus-configure-windows 'followup-yank 'force)
470 (gnus-configure-windows 'followup 'force)))
471 (gnus-overload-functions)
472 (make-local-variable 'gnus-article-reply)
473 (make-local-variable 'gnus-article-check-size)
474 (make-local-variable 'gnus-reply-subject)
475 (setq gnus-reply-subject (and header (mail-header-subject header)))
476 (setq gnus-article-reply sumart)
477 ;; Handle `gnus-auto-mail-to-author'.
478 ;; Suggested by Daniel Quinlan <quinlan@best.com>.
479 ;; Revised to respect Reply-To by Ulrik Dickow <dickow@nbi.dk>.
480 (let ((to (and (not post)
481 (if (if (eq gnus-auto-mail-to-author 'ask)
482 (y-or-n-p "Also send mail to author? ")
483 gnus-auto-mail-to-author)
484 (or (save-excursion
485 (set-buffer gnus-article-copy)
486 (gnus-fetch-field "reply-to"))
487 from)))))
488 (if to
489 (if (mail-fetch-field "To")
490 (progn
491 (beginning-of-line)
492 (insert "Cc: " to "\n"))
493 (mail-position-on-field "To")
494 (insert to))))
495 ;; Handle author copy using BCC field.
496 (if (and gnus-mail-self-blind
497 (not (mail-fetch-field "bcc")))
498 (progn
499 (mail-position-on-field "Bcc")
500 (insert (if (stringp gnus-mail-self-blind)
501 gnus-mail-self-blind
502 (user-login-name)))))
503 ;; Handle author copy using FCC field.
504 (if gnus-author-copy
505 (progn
506 (mail-position-on-field "Fcc")
507 (insert gnus-author-copy)))
508 (goto-char (point-min))
509 (if post
510 (cond ((not group)
511 (re-search-forward "^Newsgroup:" nil t)
512 (end-of-line))
513 ((not subject)
514 (re-search-forward "^Subject:" nil t)
515 (end-of-line))
516 (t
517 (re-search-forward
518 (concat "^" (regexp-quote mail-header-separator) "$"))
519 (forward-line 1)))
520 (re-search-forward
521 (concat "^" (regexp-quote mail-header-separator) "$"))
522 (forward-line 1)
523 (if (not yank)
524 ()
525 (save-excursion
526 (if (not (listp yank))
527 (news-reply-yank-original nil)
528 (setq yank (reverse yank))
529 (while yank
530 (save-excursion
531 (save-window-excursion
532 (set-buffer gnus-summary-buffer)
533 (gnus-summary-select-article nil nil nil (car yank))
534 (gnus-summary-remove-process-mark (car yank)))
535 (let ((mail-reply-buffer gnus-article-copy))
536 (gnus-copy-article-buffer)
537 (let ((news-reply-yank-message-id
538 (save-excursion
539 (set-buffer gnus-article-copy)
540 (mail-fetch-field "message-id")))
541 (news-reply-yank-from
542 (save-excursion
543 (set-buffer gnus-article-copy)
544 (mail-fetch-field "from"))))
545 (news-reply-yank-original nil))
546 (setq yank (cdr yank)))))))))
547 (if gnus-post-prepare-function
548 (funcall gnus-post-prepare-function group))
549 (run-hooks 'gnus-post-prepare-hook)
550 (make-local-variable 'gnus-prev-winconf)
551 (setq gnus-prev-winconf winconf))))
552 (setq gnus-article-check-size (cons (buffer-size) (gnus-article-checksum)))
553 (message "")
554 t)
555
556(defun gnus-inews-news (&optional use-group-method)
557 "Send a news message.
558If given a prefix, and the group is a foreign group, this function
559will attempt to use the foreign server to post the article."
560 (interactive "P")
561 (or gnus-current-select-method
562 (setq gnus-current-select-method gnus-select-method))
563 (let* ((case-fold-search nil)
564 (server-running (gnus-server-opened gnus-current-select-method))
565 (reply gnus-article-reply)
566 error post-result)
567 (save-excursion
568 ;; Connect to default NNTP server if necessary.
569 ;; Suggested by yuki@flab.fujitsu.junet.
570 (gnus-start-news-server) ;Use default server.
571 ;; NNTP server must be opened before current buffer is modified.
572 (widen)
573 (goto-char (point-min))
574 (run-hooks 'news-inews-hook)
575 (save-restriction
576 (narrow-to-region
577 (point-min)
578 (progn
579 (goto-char (point-min))
580 (re-search-forward
581 (concat "^" (regexp-quote mail-header-separator) "$"))
582 (match-beginning 0)))
583
584 ;; Correct newsgroups field: change sequence of spaces to comma and
a7acbbe4 585 ;; eliminate spaces around commas. Eliminate embedded line breaks.
41487370
LMI
586 (goto-char (point-min))
587 (if (re-search-forward "^Newsgroups: +" nil t)
588 (save-restriction
589 (narrow-to-region
590 (point)
591 (if (re-search-forward "^[^ \t]" nil t)
592 (match-beginning 0)
593 (forward-line 1)
594 (point)))
595 (goto-char (point-min))
596 (while (re-search-forward "\n[ \t]+" nil t)
597 (replace-match " " t t)) ;No line breaks (too confusing)
598 (goto-char (point-min))
599 (while (re-search-forward "[ \t\n]*,[ \t\n]*\\|[ \t]+" nil t)
600 (replace-match "," t t))
601 (goto-char (point-min))
602 ;; Remove a trailing comma.
603 (if (re-search-forward ",$" nil t)
604 (replace-match "" t t))))
605
606 ;; Added by Per Abrahamsen <abraham@iesd.auc.dk>.
607 ;; Help save the the world!
608 (or
609 gnus-expert-user
610 (let ((newsgroups (mail-fetch-field "newsgroups"))
611 (followup-to (mail-fetch-field "followup-to"))
612 groups to)
613 (if (and newsgroups
614 (string-match "," newsgroups) (not followup-to))
615 (progn
616 (while (string-match "," newsgroups)
617 (setq groups
618 (cons (list (substring newsgroups
619 0 (match-beginning 0)))
620 groups))
621 (setq newsgroups (substring newsgroups (match-end 0))))
622 (setq groups (nreverse (cons (list newsgroups) groups)))
623
624 (setq to
625 (completing-read "Followups to: (default all groups) "
626 groups))
627 (if (> (length to) 0)
628 (progn
629 (goto-char (point-min))
630 (insert "Followup-To: " to "\n")))))))
631
632 ;; Cleanup Followup-To.
633 (goto-char (point-min))
634 (if (search-forward-regexp "^Followup-To: +" nil t)
635 (save-restriction
636 (narrow-to-region
637 (point)
638 (if (re-search-forward "^[^ \t]" nil 'end)
639 (match-beginning 0)
640 (point-max)))
641 (goto-char (point-min))
642 (replace-regexp "\n[ \t]+" " ") ;No line breaks (too confusing)
643 (goto-char (point-min))
644 (replace-regexp "[ \t\n]*,[ \t\n]*\\|[ \t]+" ",")))
645
646 ;; Mail the message too if To:, Bcc:. or Cc: exists.
647 (let* ((types '("to" "bcc" "cc"))
648 (ty types)
649 fcc-line)
650 (while ty
651 (or (mail-fetch-field (car ty) nil t)
652 (setq types (delete (car ty) types)))
653 (setq ty (cdr ty)))
654
655 (if (not types)
656 ;; We do not want to send mail.
657 ()
658 (if (not gnus-mail-send-method)
659 (progn
660 (ding)
661 (gnus-message
662 1 "No mailer defined. To: and/or Cc: fields ignored.")
663 (sit-for 1))
664 (save-excursion
665 ;; We want to remove Fcc, because we want to handle
666 ;; that one ourselves...
667
668 (goto-char (point-min))
669 (if (re-search-forward "^Fcc: " nil t)
670 (progn
671 (setq fcc-line
672 (buffer-substring
673 (progn (beginning-of-line) (point))
674 (progn (forward-line 1) (point))))
675 (forward-line -1)
676 (gnus-delete-line)))
677
678 ;; We generate a Message-ID so that the mail and the
679 ;; news copy of the message both get the same ID.
680 (or (mail-fetch-field "message-id")
681 (not (memq 'Message-ID gnus-required-headers))
682 (progn
683 (goto-char (point-max))
684 (insert "Message-ID: " (gnus-inews-message-id) "\n")))
685
686 (save-restriction
687 (widen)
688 (gnus-message 5 "Sending via mail...")
689
690 (if (and gnus-mail-courtesy-message
691 (or (member "to" types)
692 (member "cc" types)))
693 ;; We only want to insert the courtesy mail
694 ;; message if we use to or cc; bcc should not
695 ;; have one. Well, if both bcc and to are
696 ;; present, it will get one anyway.
697 (progn
698 ;; Insert "courtesy" mail message.
699 (goto-char (point-min))
700 (re-search-forward
701 (concat "^" (regexp-quote
702 mail-header-separator) "$"))
703 (forward-line 1)
704 (insert gnus-mail-courtesy-message)
705 (funcall gnus-mail-send-method)
706 (goto-char (point-min))
707 (search-forward gnus-mail-courtesy-message)
708 (replace-match "" t t))
709 (funcall gnus-mail-send-method))
710
711 (gnus-message 5 "Sending via mail...done")
712
713 (goto-char (point-min))
714 (narrow-to-region
715 (point)
716 (re-search-forward
717 (concat "^" (regexp-quote mail-header-separator) "$")))
718 (goto-char (point-min))
719 (while (re-search-forward "^BCC:" nil t)
720 (delete-region (match-beginning 0)
721 ;; There might be continuation headers.
722 (if (re-search-forward "^[^ \t]" nil t)
723 (match-beginning 0)
724 ;; Uhm... or something like this.
725 (forward-line 1)
726 (point)))))
727 (if fcc-line
728 (progn
729 (goto-char (point-max))
730 (insert fcc-line))))))))
731
732 ;; Send to server.
733 (gnus-message 5 "Posting to USENET...")
734 (setq post-result (funcall gnus-inews-article-function use-group-method))
735 (cond ((eq post-result 'illegal)
736 (setq error t)
737 (ding))
738 (post-result
739 (gnus-message 5 "Posting to USENET...done")
740 (if (gnus-buffer-exists-p (car-safe reply))
741 (progn
742 (save-excursion
743 (set-buffer gnus-summary-buffer)
744 (gnus-summary-mark-article-as-replied
745 (cdr reply)))))
746 (set-buffer-modified-p nil))
747 (t
748 ;; We cannot signal an error.
749 (setq error t)
750 (ding)
751 (gnus-message 1 "Article rejected: %s"
752 (gnus-status-message gnus-select-method)))))
753 ;; If NNTP server is opened by gnus-inews-news, close it by myself.
754 (or server-running
755 (gnus-close-server (gnus-find-method-for-group gnus-newsgroup-name)))
756 (let ((conf gnus-prev-winconf))
757 (if (not error)
758 (progn
759 (bury-buffer)
760 ;; Restore last window configuration.
761 (and conf (set-window-configuration conf)))))))
762
763(defun gnus-inews-check-post ()
764 "Check whether the post looks ok."
765 (or
766 (not gnus-check-before-posting)
767 (and
768 ;; We narrow to the headers and check them first.
769 (save-excursion
770 (save-restriction
771 (goto-char (point-min))
772 (narrow-to-region
773 (point)
774 (progn
775 (re-search-forward
776 (concat "^" (regexp-quote mail-header-separator) "$"))
777 (match-beginning 0)))
778 (goto-char (point-min))
779 (and
780 ;; Check for commands in Subject.
781 (or
782 (gnus-check-before-posting 'subject-cmsg)
783 (save-excursion
784 (if (string-match "^cmsg " (mail-fetch-field "subject"))
785 (gnus-y-or-n-p
786 "The control code \"cmsg \" is in the subject. Really post? ")
787 t)))
788 ;; Check for multiple identical headers.
789 (or (gnus-check-before-posting 'multiple-headers)
790 (save-excursion
791 (let (found)
792 (while (and (not found) (re-search-forward "^[^ \t:]+: "
793 nil t))
794 (save-excursion
795 (or (re-search-forward
796 (concat "^" (setq found
797 (buffer-substring
798 (match-beginning 0)
799 (- (match-end 0) 2))))
800 nil t)
801 (setq found nil))))
802 (if found
803 (gnus-y-or-n-p
804 (format "Multiple %s headers. Really post? " found))
805 t))))
806 ;; Check for version and sendsys.
807 (or (gnus-check-before-posting 'sendsys)
808 (save-excursion
809 (if (re-search-forward "^Sendsys:\\|^Version:" nil t)
810 (gnus-y-or-n-p
811 (format "The article contains a %s command. Really post? "
812 (buffer-substring (match-beginning 0)
813 (1- (match-end 0)))))
814 t)))
815 ;; Check the Message-Id header.
816 (or (gnus-check-before-posting 'message-id)
817 (save-excursion
818 (let* ((case-fold-search t)
819 (message-id (mail-fetch-field "message-id")))
820 (or (not message-id)
821 (and (string-match "@" message-id)
822 (string-match "@[^\\.]*\\." message-id))
823 (gnus-y-or-n-p
824 (format
825 "The Message-ID looks strange: \"%s\". Really post? "
826 message-id))))))
827 ;; Check the From header.
828 (or (gnus-check-before-posting 'from)
829 (save-excursion
830 (let* ((case-fold-search t)
831 (from (mail-fetch-field "from")))
832 (cond
833 ((not from)
834 (gnus-y-or-n-p "There is no From line. Really post? "))
835 ((not (string-match "@[^\\.]*\\." from))
836 (gnus-y-or-n-p
837 (format
838 "The address looks strange: \"%s\". Really post? " from)))
839 ((string-match "(.*).*(.*)" from)
840 (gnus-y-or-n-p
841 (format
842 "The From header looks strange: \"%s\". Really post? "
843 from)))
844 (t t)))))
845 )))
846 ;; Check for long lines.
847 (or (gnus-check-before-posting 'long-lines)
848 (save-excursion
849 (goto-char (point-min))
850 (re-search-forward
851 (concat "^" (regexp-quote mail-header-separator) "$"))
852 (while (and
853 (progn
854 (end-of-line)
855 (< (current-column) 80))
856 (zerop (forward-line 1))))
857 (or (bolp)
858 (eobp)
859 (gnus-y-or-n-p
860 (format
861 "You have lines longer than 79 characters. Really post? ")))))
862 ;; Check for control characters.
863 (or (gnus-check-before-posting 'control-chars)
864 (save-excursion
865 (if (re-search-forward "[\000-\007\013\015-\037\200-\237]" nil t)
866 (gnus-y-or-n-p
867 "The article contains control characters. Really post? ")
868 t)))
869 ;; Check excessive size.
870 (or (gnus-check-before-posting 'size)
871 (if (> (buffer-size) 60000)
872 (gnus-y-or-n-p
873 (format "The article is %d octets long. Really post? "
874 (buffer-size)))
875 t))
876 ;; Use the (size . checksum) variable to see whether the
877 ;; article is empty or has only quoted text.
878 (or
879 (gnus-check-before-posting 'new-text)
880 (if (and (= (buffer-size) (car gnus-article-check-size))
881 (= (gnus-article-checksum) (cdr gnus-article-check-size)))
882 (gnus-y-or-n-p
883 "It looks like there's no new text in your article. Really post? ")
884 t))
885 ;; Check the length of the signature.
886 (or (gnus-check-before-posting 'signature)
887 (progn
888 (goto-char (point-max))
889 (if (not (re-search-backward gnus-signature-separator nil t))
890 t
891 (if (> (count-lines (point) (point-max)) 5)
892 (gnus-y-or-n-p
893 (format
894 "Your .sig is %d lines; it should be max 4. Really post? "
895 (count-lines (point) (point-max))))
896 t)))))))
897
898(defun gnus-article-checksum ()
899 (let ((sum 0))
900 (save-excursion
901 (while (not (eobp))
902 (setq sum (logxor sum (following-char)))
903 (forward-char 1)))
904 sum))
905
906;; Returns non-nil if this type is not to be checked.
907(defun gnus-check-before-posting (type)
908 (not
909 (or (not gnus-check-before-posting)
910 (if (listp gnus-check-before-posting)
911 (memq type gnus-check-before-posting)
912 t))))
913
914(defun gnus-cancel-news ()
915 "Cancel an article you posted."
916 (interactive)
917 (if (or gnus-expert-user
918 (gnus-yes-or-no-p "Do you really want to cancel this article? "))
919 (let ((from nil)
920 (newsgroups nil)
921 (message-id nil)
922 (distribution nil))
923 (or (gnus-member-of-valid 'post gnus-newsgroup-name)
924 (error "This backend does not support canceling"))
925 (save-excursion
926 ;; Get header info. from original article.
927 (save-restriction
928 (gnus-article-show-all-headers)
929 (goto-char (point-min))
930 (search-forward "\n\n" nil 'move)
931 (narrow-to-region (point-min) (point))
932 (setq from (mail-fetch-field "from"))
933 (setq newsgroups (mail-fetch-field "newsgroups"))
934 (setq message-id (mail-fetch-field "message-id"))
935 (setq distribution (mail-fetch-field "distribution")))
936 ;; Verify if the article is absolutely user's by comparing
937 ;; user id with value of its From: field.
938 (if (not
939 (string-equal
940 (downcase (mail-strip-quoted-names from))
941 (downcase (mail-strip-quoted-names (gnus-inews-user-name)))))
942 (progn
943 (ding) (gnus-message 3 "This article is not yours.")
944 nil)
945 ;; Make control article.
946 (set-buffer (get-buffer-create " *Gnus-canceling*"))
947 (buffer-disable-undo (current-buffer))
948 (erase-buffer)
949 (insert "Newsgroups: " newsgroups "\n"
950 "Subject: cancel " message-id "\n"
951 "Control: cancel " message-id "\n"
952 (if distribution
953 (concat "Distribution: " distribution "\n")
954 "")
955 mail-header-separator "\n"
956 "This is a cancel message from " from ".\n")
957 ;; Send the control article to NNTP server.
958 (gnus-message 5 "Canceling your article...")
959 (prog1
960 (if (funcall gnus-inews-article-function)
961 (gnus-message 5 "Canceling your article...done")
962 (progn
963 (ding)
964 (gnus-message 1 "Cancel failed; %s"
965 (gnus-status-message gnus-newsgroup-name))
966 nil)
967 t)
968 ;; Kill the article buffer.
969 (kill-buffer (current-buffer))))))))
970
971\f
972;;; Lowlevel inews interface
973
974(defun gnus-inews-article (&optional use-group-method)
975 "Post an article in current buffer using NNTP protocol."
976 (let ((artbuf (current-buffer))
977 (tmpbuf (get-buffer-create " *Gnus-posting*")))
978 (widen)
979 (goto-char (point-max))
980 ;; require a newline at the end for inews to append .signature to
981 (or (= (preceding-char) ?\n)
982 (insert ?\n))
983 ;; Prepare article headers. All message body such as signature
984 ;; must be inserted before Lines: field is prepared.
985 (save-restriction
986 (goto-char (point-min))
987 (narrow-to-region
988 (point-min)
989 (save-excursion
990 (re-search-forward
991 (concat "^" (regexp-quote mail-header-separator) "$"))
992 (match-beginning 0)))
993 (gnus-inews-remove-headers)
994 (gnus-inews-insert-headers)
995 (run-hooks 'gnus-inews-article-header-hook)
996 (widen))
997 ;; Check whether the article is a good Net Citizen.
998 (if (and gnus-article-check-size
999 (not (gnus-inews-check-post)))
1000 ;; Aber nein!
1001 'illegal
1002 ;; Looks ok, so we do the nasty.
1003 (save-excursion
1004 (set-buffer tmpbuf)
1005 (buffer-disable-undo (current-buffer))
1006 (erase-buffer)
1007 (insert-buffer-substring artbuf)
1008 ;; Remove the header separator.
1009 (goto-char (point-min))
1010 (re-search-forward
1011 (concat "^" (regexp-quote mail-header-separator) "$"))
1012 (replace-match "" t t)
1013 ;; This hook may insert a signature.
1014 (save-excursion
1015 (goto-char (point-min))
1016 (let ((gnus-newsgroup-name (or (mail-fetch-field "newsgroups")
1017 gnus-newsgroup-name)))
1018 (run-hooks 'gnus-prepare-article-hook)))
1019 ;; Run final inews hooks. This hook may do FCC.
1020 ;; The article must be saved before being posted because
1021 ;; `gnus-request-post' modifies the buffer.
1022 (run-hooks 'gnus-inews-article-hook)
1023 ;; Post an article to NNTP server.
1024 ;; Return NIL if post failed.
1025 (prog1
1026 (gnus-request-post
1027 (if use-group-method
1028 (gnus-find-method-for-group gnus-newsgroup-name)
1029 gnus-select-method) use-group-method)
1030 (kill-buffer (current-buffer)))))))
1031
1032(defun gnus-inews-remove-headers ()
1033 (let ((case-fold-search t)
1034 (headers gnus-removable-headers))
1035 ;; Remove toxic headers.
1036 (while headers
1037 (goto-char (point-min))
1038 (and (re-search-forward
1039 (concat "^" (downcase (format "%s" (car headers))))
1040 nil t)
1041 (delete-region (progn (beginning-of-line) (point))
1042 (progn (forward-line 1) (point))))
1043 (setq headers (cdr headers)))))
1044
1045(defun gnus-inews-insert-headers ()
1046 "Prepare article headers.
1047Headers already prepared in the buffer are not modified.
1048Headers in `gnus-required-headers' will be generated."
1049 (let ((Date (gnus-inews-date))
1050 (Message-ID (gnus-inews-message-id))
1051 (Organization (gnus-inews-organization))
1052 (From (gnus-inews-user-name))
1053 (Path (gnus-inews-path))
1054 (Subject nil)
1055 (Newsgroups nil)
1056 (Distribution nil)
1057 (Lines (gnus-inews-lines))
1058 (X-Newsreader gnus-version)
1059 (headers gnus-required-headers)
1060 (case-fold-search t)
1061 header value elem)
1062 ;; First we remove any old generated headers.
1063 (let ((headers gnus-deletable-headers))
1064 (while headers
1065 (goto-char (point-min))
1066 (and (re-search-forward
1067 (concat "^" (symbol-name (car headers)) ": *") nil t)
1068 (get-text-property (1+ (match-beginning 0)) 'gnus-deletable)
1069 (gnus-delete-line))
1070 (setq headers (cdr headers))))
1071 ;; If there are References, and no "Re: ", then the thread has
1072 ;; changed name. See Son-of-1036.
1073 (if (and (mail-fetch-field "references")
1074 (get-buffer gnus-article-buffer))
1075 (let ((psubject (gnus-simplify-subject-re
1076 (mail-fetch-field "subject"))))
1077 (or (and psubject gnus-reply-subject
1078 (string= (gnus-simplify-subject-re gnus-reply-subject)
1079 psubject))
1080 (progn
1081 (string-match "@" Message-ID)
1082 (setq Message-ID
1083 (concat (substring Message-ID 0 (match-beginning 0))
1084 "_-_"
1085 (substring Message-ID (match-beginning 0))))))))
1086 ;; Go through all the required headers and see if they are in the
1087 ;; articles already. If they are not, or are empty, they are
1088 ;; inserted automatically - except for Subject, Newsgroups and
1089 ;; Distribution.
1090 (while headers
1091 (goto-char (point-min))
1092 (setq elem (car headers))
1093 (if (consp elem)
1094 (setq header (car elem))
1095 (setq header elem))
1096 (if (or (not (re-search-forward
1097 (concat "^" (downcase (symbol-name header)) ":") nil t))
1098 (progn
1099 ;; The header was found. We insert a space after the
1100 ;; colon, if there is none.
1101 (if (/= (following-char) ? ) (insert " "))
1102 ;; Find out whether the header is empty...
1103 (looking-at "[ \t]*$")))
1104 ;; So we find out what value we should insert.
1105 (progn
1106 (setq value
1107 (or (if (consp elem)
1108 ;; The element is a cons. Either the cdr is
1109 ;; a string to be inserted verbatim, or it
1110 ;; is a function, and we insert the value
1111 ;; returned from this function.
1112 (or (and (stringp (cdr elem)) (cdr elem))
1113 (and (fboundp (cdr elem)) (funcall (cdr elem))))
1114 ;; The element is a symbol. We insert the
1115 ;; value of this symbol, if any.
1116 (and (boundp header) (symbol-value header)))
1117 ;; We couldn't generate a value for this header,
1118 ;; so we just ask the user.
1119 (read-from-minibuffer
1120 (format "Empty header for %s; enter value: " header))))
1121 ;; Finally insert the header.
1122 (save-excursion
1123 (if (bolp)
1124 (progn
1125 (goto-char (point-max))
1126 (insert (symbol-name header) ": " value "\n")
1127 (forward-line -1))
1128 (replace-match value t t))
1129 ;; Add the deletable property to the headers that require it.
1130 (and (memq header gnus-deletable-headers)
1131 (progn (beginning-of-line) (looking-at "[^:]+: "))
1132 (add-text-properties
1133 (point) (match-end 0)
1134 '(gnus-deletable t face italic) (current-buffer))))))
1135 (setq headers (cdr headers)))
1136 ;; Insert new Sender if the From is strange.
1137 (let ((from (mail-fetch-field "from"))
1138 (sender (mail-fetch-field "sender")))
1139 (if (and from
1140 (not (string=
1141 (downcase (car (gnus-extract-address-components from)))
1142 (downcase (gnus-inews-real-user-address))))
1143 (or (null sender)
1144 (not
1145 (string=
1146 (downcase (car (gnus-extract-address-components sender)))
1147 (downcase (gnus-inews-real-user-address))))))
1148 (progn
1149 (goto-char (point-min))
1150 (and (re-search-forward "^Sender:" nil t)
1151 (progn
1152 (beginning-of-line)
1153 (insert "Original-")
1154 (beginning-of-line)))
1155 (insert "Sender: " (gnus-inews-real-user-address) "\n"))))))
1156
1157
1158(defun gnus-inews-insert-signature ()
1159 "Insert a signature file.
1160If `gnus-signature-function' is bound and returns a string, this
1161string is used instead of the variable `gnus-signature-file'.
1162In either case, if the string is a file name, this file is
1163inserted. If the string is not a file name, the string itself is
1164inserted.
1165
1166If you never want any signature inserted, set both of these variables to
1167nil."
1168 (save-excursion
1169 (let ((signature
1170 (or (and gnus-signature-function
1171 (funcall gnus-signature-function gnus-newsgroup-name))
1172 gnus-signature-file)))
1173 (if (and signature
1174 (or (file-exists-p signature)
1175 (string-match " " signature)
1176 (not (string-match
1177 "^/[^/]+/" (expand-file-name signature)))))
1178 (progn
1179 (goto-char (point-max))
1180 (if (and mail-signature (search-backward "\n-- \n" nil t))
1181 ()
1182 ;; Delete any previous signatures.
1183 (if (search-backward "\n-- \n" nil t)
1184 (delete-region (point) (point-max)))
1185 (or (eolp) (insert "\n"))
1186 (insert "-- \n")
1187 (if (file-exists-p signature)
1188 (insert-file-contents signature)
1189 (insert signature))
1190 (goto-char (point-max))
1191 (or (bolp) (insert "\n"))))))))
1192
1193;; Written by "Mr. Per Persson" <pp@solace.mh.se>.
1194(defun gnus-inews-insert-mime-headers ()
1195 (let ((mail-header-separator ""))
1196 (or (mail-position-on-field "Mime-Version")
1197 (insert "1.0")
1198 (cond ((save-excursion
1199 (beginning-of-buffer)
1200 (re-search-forward "[\200-\377]" nil t))
1201 (or (mail-position-on-field "Content-Type")
1202 (insert "text/plain; charset=ISO-8859-1"))
1203 (or (mail-position-on-field "Content-Transfer-Encoding")
1204 (insert "8bit")))
1205 (t (or (mail-position-on-field "Content-Type")
1206 (insert "text/plain; charset=US-ASCII"))
1207 (or (mail-position-on-field "Content-Transfer-Encoding")
1208 (insert "7bit")))))))
1209
1210(defun gnus-inews-do-fcc ()
1211 "Process FCC: fields in current article buffer.
1212Unless the first character of the field is `|', the article is saved
1213to the specified file using the function specified by the variable
1214gnus-author-copy-saver. The default function rmail-output saves in
1215Unix mailbox format.
1216If the first character is `|', the contents of the article is send to
1217a program specified by the rest of the value."
1218 (let ((fcc-list nil)
1219 (fcc-file nil)
1220 (case-fold-search t)) ;Should ignore case.
1221 (save-excursion
1222 (save-restriction
1223 (goto-char (point-min))
1224 (search-forward "\n\n")
1225 (narrow-to-region (point-min) (point))
1226 (goto-char (point-min))
1227 (while (re-search-forward "^FCC:[ \t]*" nil t)
1228 (setq fcc-list
1229 (cons (buffer-substring
1230 (point)
1231 (progn
1232 (end-of-line)
1233 (skip-chars-backward " \t")
1234 (point)))
1235 fcc-list))
1236 (delete-region (match-beginning 0)
1237 (progn (forward-line 1) (point))))
1238 ;; Process FCC operations.
1239 (widen)
1240 (while fcc-list
1241 (setq fcc-file (car fcc-list))
1242 (setq fcc-list (cdr fcc-list))
1243 (cond ((string-match "^[ \t]*|[ \t]*\\(.*\\)[ \t]*$" fcc-file)
1244 (let ((program (substring fcc-file
1245 (match-beginning 1) (match-end 1))))
1246 ;; Suggested by yuki@flab.fujitsu.junet.
1247 ;; Send article to named program.
1248 (call-process-region (point-min) (point-max) shell-file-name
1249 nil nil nil "-c" program)))
1250 (t
1251 ;; Suggested by hyoko@flab.fujitsu.junet.
1252 ;; Save article in Unix mail format by default.
1253 (gnus-make-directory (file-name-directory fcc-file))
1254 (if (and gnus-author-copy-saver
1255 (not (eq gnus-author-copy-saver 'rmail-output)))
1256 (funcall gnus-author-copy-saver fcc-file)
1257 (if (and (file-readable-p fcc-file)
1258 (mail-file-babyl-p fcc-file))
1259 (gnus-output-to-rmail fcc-file)
1260 (rmail-output fcc-file 1 t t))))))))))
1261
1262(defun gnus-inews-path ()
1263 "Return uucp path."
1264 (let ((login-name (gnus-inews-login-name)))
1265 (cond ((null gnus-use-generic-path)
1266 (concat (nth 1 gnus-select-method) "!" login-name))
1267 ((stringp gnus-use-generic-path)
1268 ;; Support GENERICPATH. Suggested by vixie@decwrl.dec.com.
1269 (concat gnus-use-generic-path "!" login-name))
1270 (t login-name))))
1271
1272(defun gnus-inews-user-name ()
1273 "Return user's network address as \"NAME@DOMAIN (FULL-NAME)\"."
1274 (let ((full-name (gnus-inews-full-name))
1275 (address (if (or gnus-user-login-name gnus-use-generic-from
1276 gnus-local-domain (getenv "DOMAINNAME"))
1277 (concat (gnus-inews-login-name) "@"
1278 (gnus-inews-domain-name gnus-use-generic-from))
1279 user-mail-address)))
1280 (or gnus-user-from-line
1281 (concat address
1282 ;; User's full name.
1283 (cond ((string-equal full-name "&") ;Unix hack.
1284 (concat " (" (user-login-name) ")"))
1285 ((string-match "[^ ]+@[^ ]+ +(.*)" address)
1286 "")
1287 (t
1288 (concat " (" full-name ")")))))))
1289
1290(defun gnus-inews-real-user-address ()
1291 "Return the \"real\" user address.
1292This function tries to ignore all user modifications, and
1293give as trustworthy answer as possible."
1294 (concat (user-login-name) "@" (gnus-inews-full-address)))
1295
1296(defun gnus-inews-login-name ()
1297 "Return login name."
1298 (or gnus-user-login-name (getenv "LOGNAME") (user-login-name)))
1299
1300(defun gnus-inews-full-name ()
1301 "Return full user name."
1302 (or gnus-user-full-name (getenv "NAME") (user-full-name)))
1303
1304(defun gnus-inews-domain-name (&optional genericfrom)
1305 "Return user's domain name.
1306If optional argument GENERICFROM is a string, use it as the domain
1307name; if it is non-nil, strip off local host name from the domain name.
1308If the function `system-name' returns full internet name and the
1309domain is undefined, the domain name is got from it."
1310 (if (or genericfrom gnus-local-domain (getenv "DOMAINNAME"))
1311 (let* ((system-name (system-name))
1312 (domain
1313 (or (if (stringp genericfrom) genericfrom)
1314 (getenv "DOMAINNAME")
1315 gnus-local-domain
1316 ;; Function `system-name' may return full internet name.
1317 ;; Suggested by Mike DeCorte <mrd@sun.soe.clarkson.edu>.
1318 (if (string-match "\\." system-name)
1319 (substring system-name (match-end 0)))
1320 (read-string "Domain name (no host): ")))
1321 (host (or (if (string-match "\\." system-name)
1322 (substring system-name 0 (match-beginning 0)))
1323 system-name)))
1324 (if (string-equal "." (substring domain 0 1))
1325 (setq domain (substring domain 1)))
1326 ;; Support GENERICFROM as same as standard Bnews system.
1327 ;; Suggested by ohm@kaba.junet and vixie@decwrl.dec.com.
1328 (cond ((null genericfrom)
1329 (concat host "." domain))
1330 ;;((stringp genericfrom) genericfrom)
1331 (t domain)))
1332 (if (string-match "\\." (system-name))
1333 (system-name)
1334 (substring user-mail-address
1335 (1+ (string-match "@" user-mail-address))))))
1336
1337(defun gnus-inews-full-address ()
1338 (let ((domain (gnus-inews-domain-name))
1339 (system (system-name))
1340 (case-fold-search t))
1341 (if (string-match "\\." system) system
1342 (if (string-match (concat "^" (regexp-quote system)) domain) domain
1343 (concat system "." domain)))))
1344
1345(defun gnus-inews-message-id ()
1346 "Generate unique Message-ID for user."
1347 ;; Message-ID should not contain a slash and should be terminated by
1348 ;; a number. I don't know the reason why it is so.
1349 (concat "<" (gnus-inews-unique-id) "@" (gnus-inews-full-address) ">"))
1350
1351(defvar gnus-unique-id-char nil)
1352
1353;; If you ever change this function, make sure the new version
1354;; cannot generate IDs that the old version could.
1355;; You might for example insert a "." somewhere (not next to another dot
1356;; or string boundary), or modify the newsreader name to "Ding".
1357(defun gnus-inews-unique-id ()
a7acbbe4 1358 ;; Don't use microseconds from (current-time), they may be unsupported.
41487370
LMI
1359 ;; Instead we use this randomly inited counter.
1360 (setq gnus-unique-id-char
1361 (% (1+ (or gnus-unique-id-char (logand (random t) (1- (lsh 1 20)))))
1362 ;; (current-time) returns 16-bit ints,
1363 ;; and 2^16*25 just fits into 4 digits i base 36.
1364 (* 25 25)))
1365 (let ((tm (if (fboundp 'current-time)
1366 (current-time) '(12191 46742 287898))))
1367 (concat
1368 (if (memq system-type '(ms-dos emx vax-vms))
1369 (let ((user (downcase (gnus-inews-login-name))))
1370 (while (string-match "[^a-z0-9_]" user)
1371 (aset user (match-beginning 0) ?_))
1372 user)
1373 (gnus-number-base36 (user-uid) -1))
1374 (gnus-number-base36 (+ (car tm) (lsh (% gnus-unique-id-char 25) 16)) 4)
1375 (gnus-number-base36 (+ (nth 1 tm) (lsh (/ gnus-unique-id-char 25) 16)) 4)
1376 ;; Append the newsreader name, because while the generated
1377 ;; ID is unique to this newsreader, other newsreaders might
1378 ;; otherwise generate the same ID via another algorithm.
1379 ".fsf")))
1380
1381
1382(defun gnus-inews-date ()
1383 "Current time string."
1384 (timezone-make-date-arpa-standard
1385 (current-time-string) (current-time-zone)))
1386
1387(defun gnus-inews-organization ()
1388 "Return user's organization.
1389The ORGANIZATION environment variable is used if defined.
1390If not, the variable `gnus-local-organization' is used instead.
1391If it is a function, the function will be called with the current
1392newsgroup name as the argument.
1393If this is a file name, the contents of this file will be used as the
1394organization."
1395 (let* ((organization
1396 (or (getenv "ORGANIZATION")
1397 (if gnus-local-organization
1398 (if (and (symbolp gnus-local-organization)
1399 (fboundp gnus-local-organization))
1400 (funcall gnus-local-organization gnus-newsgroup-name)
1401 gnus-local-organization))
1402 gnus-organization-file
1403 "~/.organization")))
1404 (and (stringp organization)
1405 (> (length organization) 0)
1406 (or (file-exists-p organization)
1407 (string-match " " organization)
1408 (not (string-match "^/usr/lib/" organization)))
1409 (save-excursion
1410 (gnus-set-work-buffer)
1411 (if (file-exists-p organization)
1412 (insert-file-contents organization)
1413 (insert organization))
1414 (goto-char (point-min))
1415 (while (re-search-forward " *\n *" nil t)
1416 (replace-match " " t t))
1417 (buffer-substring (point-min) (point-max))))))
1418
1419(defun gnus-inews-lines ()
1420 "Count the number of lines and return numeric string."
1421 (save-excursion
1422 (save-restriction
1423 (widen)
1424 (goto-char (point-min))
1425 (re-search-forward
1426 (concat "^" (regexp-quote mail-header-separator) "$"))
1427 (forward-line 1)
1428 (int-to-string (count-lines (point) (point-max))))))
1429
1430\f
1431;;;
1432;;; Gnus Mail Functions
1433;;;
1434
1435;;; Mail reply commands of Gnus summary mode
1436
1437(defun gnus-summary-reply (yank &optional yank-articles)
1438 "Reply mail to news author.
1439If prefix argument YANK is non-nil, original article is yanked automatically.
1440Customize the variable gnus-mail-reply-method to use another mailer."
1441 (interactive "P")
1442 ;; Bug fix by jbw@bigbird.bu.edu (Joe Wells)
1443 ;; Stripping headers should be specified with mail-yank-ignored-headers.
1444 (gnus-set-global-variables)
1445 (if yank-articles (gnus-summary-goto-subject (car yank-articles)))
1446 (gnus-summary-select-article)
1447 (let ((gnus-newsgroup-name gnus-newsgroup-name))
1448 (bury-buffer gnus-article-buffer)
1449 (funcall gnus-mail-reply-method (or yank-articles (not (not yank))))))
1450
1451(defun gnus-summary-reply-with-original (n)
1452 "Reply mail to news author with original article.
1453Customize the variable gnus-mail-reply-method to use another mailer."
1454 (interactive "P")
1455 (gnus-summary-reply t (gnus-summary-work-articles n)))
1456
1457(defun gnus-summary-mail-forward (post)
1458 "Forward the current message to another user.
1459Customize the variable gnus-mail-forward-method to use another mailer."
1460 (interactive "P")
1461 (gnus-set-global-variables)
1462 (gnus-summary-select-article)
1463 (gnus-copy-article-buffer)
1464 (let ((gnus-newsgroup-name gnus-newsgroup-name))
1465 (if post
1466 (gnus-forward-using-post gnus-article-copy)
1467 (funcall gnus-mail-forward-method gnus-article-copy))))
1468
1469(defun gnus-summary-post-forward ()
1470 "Forward the current article to a newsgroup."
1471 (interactive)
1472 (gnus-summary-mail-forward t))
1473
1474(defvar gnus-nastygram-message
1475 "The following article was inappropriately posted to %s.\n"
1476 "Format string to insert in nastygrams.
1477The current group name will be inserted at \"%s\".")
1478
1479(defun gnus-summary-mail-nastygram (n)
1480 "Send a nastygram to the author of the current article."
1481 (interactive "P")
1482 (if (or gnus-expert-user
1483 (gnus-y-or-n-p
1484 "Really send a nastygram to the author of the current article? "))
1485 (let ((group gnus-newsgroup-name))
1486 (gnus-summary-reply-with-original n)
1487 (set-buffer gnus-mail-buffer)
1488 (insert (format gnus-nastygram-message group))
1489 (gnus-mail-send-and-exit))))
1490
1491(defun gnus-summary-mail-other-window ()
1492 "Compose mail in other window.
1493Customize the variable `gnus-mail-other-window-method' to use another
1494mailer."
1495 (interactive)
1496 (gnus-set-global-variables)
1497 (let ((gnus-newsgroup-name gnus-newsgroup-name))
1498 (funcall gnus-mail-other-window-method)))
1499
1500(defun gnus-mail-reply-using-mail (&optional yank to-address)
1501 (save-excursion
1502 (set-buffer gnus-summary-buffer)
1503 (let ((group (gnus-group-real-name gnus-newsgroup-name))
1504 (cur (cons (current-buffer) (cdr gnus-article-current)))
1505 (winconf (current-window-configuration))
1506 from subject date reply-to message-of
1507 references message-id sender follow-to sendto elt)
1508 (set-buffer (get-buffer-create gnus-mail-buffer))
1509 (mail-mode)
1510 (make-local-variable 'gnus-article-reply)
1511 (setq gnus-article-reply cur)
1512 (make-local-variable 'gnus-prev-winconf)
1513 (setq gnus-prev-winconf winconf)
1514 (if (and (buffer-modified-p)
1515 (> (buffer-size) 0)
1516 (not (gnus-y-or-n-p
1517 "Unsent article being composed; erase it? ")))
1518 ()
1519 (erase-buffer)
1520 (save-excursion
1521 (gnus-copy-article-buffer)
1522 (save-restriction
1523 (set-buffer gnus-article-copy)
1524 (gnus-narrow-to-headers)
1525 (if (and (boundp 'gnus-reply-to-function)
1526 gnus-reply-to-function)
1527 (setq follow-to (funcall gnus-reply-to-function group)))
1528 (setq from (mail-fetch-field "from"))
1529 (setq date (or (mail-fetch-field "date")
1530 (mail-header-date gnus-current-headers)))
1531 (and from
1532 (let ((stop-pos
1533 (string-match " *at \\| *@ \\| *(\\| *<" from)))
1534 (setq message-of
1535 (concat (if stop-pos (substring from 0 stop-pos) from)
1536 "'s message of " date))))
1537 (setq sender (mail-fetch-field "sender"))
1538 (setq subject (or (mail-fetch-field "subject")
1539 "Re: none"))
1540 (or (string-match "^[Rr][Ee]:" subject)
1541 (setq subject (concat "Re: " subject)))
1542 (setq reply-to (mail-fetch-field "reply-to"))
1543 (setq references (mail-fetch-field "references"))
1544 (setq message-id (mail-fetch-field "message-id"))
1545 (widen))
1546 (setq news-reply-yank-from (or from "(nobody)")))
1547 (setq news-reply-yank-message-id
1548 (or message-id "(unknown Message-ID)"))
1549
1550 ;; Gather the "to" addresses out of the follow-to list and remove
1551 ;; them as we go.
1552 (if (and follow-to (listp follow-to))
1553 (while (setq elt (assoc "To" follow-to))
1554 (setq sendto (concat sendto (and sendto ", ") (cdr elt)))
1555 (setq follow-to (delq elt follow-to))))
1556
1557 (mail-setup (or to-address
1558 (if (and follow-to (not (stringp follow-to))) sendto
1559 (or follow-to reply-to from sender "")))
1560 subject message-of nil gnus-article-copy nil)
1561
1562 (auto-save-mode auto-save-default)
1563 (use-local-map (copy-keymap mail-mode-map))
1564 (local-set-key "\C-c\C-c" 'gnus-mail-send-and-exit)
1565
1566 (if (and follow-to (listp follow-to))
1567 (progn
1568 (goto-char (point-min))
1569 (re-search-forward "^To:" nil t)
1570 (beginning-of-line)
1571 (forward-line 1)
1572 (while follow-to
1573 (insert (car (car follow-to)) ": " (cdr (car follow-to)) "\n")
1574 (setq follow-to (cdr follow-to)))))
1575 (nnheader-insert-references references message-id)
1576 (goto-char (point-min))
1577 (re-search-forward
1578 (concat "^" (regexp-quote mail-header-separator) "$"))
1579 (forward-line 1)
1580 (if (not yank)
1581 (gnus-configure-windows 'reply 'force)
1582 (let ((last (point))
1583 end)
1584 (if (not (listp yank))
1585 (progn
1586 (save-excursion
1587 (mail-yank-original nil))
1588 (or mail-yank-hooks mail-citation-hook
1589 (run-hooks 'news-reply-header-hook)))
1590 (while yank
1591 (save-window-excursion
1592 (set-buffer gnus-summary-buffer)
1593 (gnus-summary-select-article nil nil nil (car yank))
1594 (gnus-summary-remove-process-mark (car yank)))
1595 (save-excursion
1596 (gnus-copy-article-buffer)
1597 (mail-yank-original nil)
1598 (setq end (point)))
1599 (or mail-yank-hooks mail-citation-hook
1600 (run-hooks 'news-reply-header-hook))
1601 (goto-char end)
1602 (setq yank (cdr yank))))
1603 (goto-char last))
1604 (gnus-configure-windows 'reply-yank 'force))
1605 (run-hooks 'gnus-mail-hook)))))
1606
1607(defun gnus-mail-yank-original ()
1608 (interactive)
1609 (save-excursion
1610 (mail-yank-original nil))
1611 (or mail-yank-hooks mail-citation-hook
1612 (run-hooks 'news-reply-header-hook)))
1613
1614(defun gnus-mail-send-and-exit ()
1615 (interactive)
1616 (let ((reply gnus-article-reply)
1617 (winconf gnus-prev-winconf))
1618 (mail-send-and-exit nil)
1619 (if (get-buffer gnus-group-buffer)
1620 (progn
1621 (if (gnus-buffer-exists-p (car-safe reply))
1622 (progn
1623 (set-buffer (car reply))
1624 (and (cdr reply)
1625 (gnus-summary-mark-article-as-replied
1626 (cdr reply)))))
1627 (and winconf (set-window-configuration winconf))))))
1628
1629(defun gnus-forward-make-subject (buffer)
1630 (save-excursion
1631 (set-buffer buffer)
1632 (concat "[" (if (memq 'mail (assoc (symbol-name
1633 (car (gnus-find-method-for-group
1634 gnus-newsgroup-name)))
1635 gnus-valid-select-methods))
1636 (gnus-fetch-field "From")
1637 gnus-newsgroup-name)
1638 "] " (or (gnus-fetch-field "Subject") ""))))
1639
1640(defun gnus-forward-insert-buffer (buffer)
1641 (let ((beg (goto-char (point-max))))
1642 (insert "------- Start of forwarded message -------\n")
1643 (insert-buffer buffer)
1644 (goto-char (point-max))
1645 (insert "------- End of forwarded message -------\n")
1646 ;; Suggested by Sudish Joseph <joseph@cis.ohio-state.edu>.
1647 (goto-char beg)
1648 (while (setq beg (next-single-property-change (point) 'invisible))
1649 (goto-char beg)
1650 (delete-region beg (or (next-single-property-change
1651 (point) 'invisible)
1652 (point-max))))))
1653
1654(defun gnus-mail-forward-using-mail (&optional buffer)
1655 "Forward the current message to another user using mail."
1656 ;; This is almost a carbon copy of rmail-forward in rmail.el.
1657 (let* ((forward-buffer (or buffer (current-buffer)))
1658 (winconf (current-window-configuration))
1659 (subject (gnus-forward-make-subject forward-buffer)))
1660 (set-buffer forward-buffer)
1661 (mail nil nil subject)
1662 (use-local-map (copy-keymap (current-local-map)))
1663 (local-set-key "\C-c\C-c" 'gnus-mail-send-and-exit)
1664 (make-local-variable 'gnus-prev-winconf)
1665 (setq gnus-prev-winconf winconf)
1666 (gnus-forward-insert-buffer forward-buffer)
1667 (goto-char (point-min))
1668 (re-search-forward "^To: " nil t)
1669 (gnus-configure-windows 'mail-forward 'force)
1670 ;; You have a chance to arrange the message.
1671 (run-hooks 'gnus-mail-forward-hook)
1672 (run-hooks 'gnus-mail-hook)))
1673
1674(defun gnus-forward-using-post (&optional buffer)
1675 (save-excursion
1676 (let* ((forward-buffer (or buffer (current-buffer)))
1677 (subject (gnus-forward-make-subject forward-buffer))
1678 (gnus-newsgroup-name nil))
1679 (gnus-post-news 'post nil nil nil nil subject)
1680 (save-excursion
1681 (gnus-forward-insert-buffer forward-buffer)
1682 ;; You have a chance to arrange the message.
1683 (run-hooks 'gnus-mail-forward-hook)))))
1684
1685(defun gnus-mail-other-window-using-mail ()
1686 "Compose mail other window using mail."
1687 (let ((winconf (current-window-configuration)))
1688 (mail-other-window nil nil nil nil nil (get-buffer gnus-article-buffer))
1689 (use-local-map (copy-keymap (current-local-map)))
1690 (local-set-key "\C-c\C-c" 'gnus-mail-send-and-exit)
1691 (make-local-variable 'gnus-prev-winconf)
1692 (setq gnus-prev-winconf winconf)
1693 (run-hooks 'gnus-mail-hook)
1694 (gnus-configure-windows 'summary-mail 'force)))
1695
1696(defun gnus-article-mail (yank)
1697 "Send a reply to the address near point.
1698If YANK is non-nil, include the original article."
1699 (interactive "P")
1700 (let ((address
1701 (buffer-substring
1702 (save-excursion (re-search-backward "[ \t\n]" nil t) (1+ (point)))
1703 (save-excursion (re-search-forward "[ \t\n]" nil t) (1- (point))))))
1704 (and address
1705 (progn
1706 (switch-to-buffer gnus-summary-buffer)
1707 (funcall gnus-mail-reply-method yank address)))))
1708
1709(defun gnus-bug ()
1710 "Send a bug report to the Gnus maintainers."
1711 (interactive)
1712 (let ((winconf (current-window-configuration)))
1713 (delete-other-windows)
1714 (switch-to-buffer "*Gnus Help Bug*")
1715 (erase-buffer)
1716 (insert gnus-bug-message)
1717 (goto-char (point-min))
1718 (pop-to-buffer "*Gnus Bug*")
1719 (erase-buffer)
1720 (mail-mode)
1721 (mail-setup gnus-maintainer nil nil nil nil nil)
1722 (auto-save-mode auto-save-default)
1723 (make-local-variable 'gnus-prev-winconf)
1724 (setq gnus-prev-winconf winconf)
1725 (use-local-map (copy-keymap mail-mode-map))
1726 (local-set-key "\C-c\C-c" 'gnus-bug-mail-send-and-exit)
1727 (goto-char (point-min))
1728 (re-search-forward (concat "^" (regexp-quote mail-header-separator) "$"))
1729 (forward-line 1)
1730 (insert (format "%s\n%s\n\n\n\n\n" (gnus-version) (emacs-version)))
1731 (gnus-debug)
1732 (goto-char (point-min))
1733 (search-forward "Subject: " nil t)
1734 (message "")))
1735
1736(defun gnus-bug-mail-send-and-exit ()
1737 "Send the bug message and exit."
1738 (interactive)
1739 (and (get-buffer "*Gnus Help Bug*")
1740 (kill-buffer "*Gnus Help Bug*"))
1741 (gnus-mail-send-and-exit))
1742
1743(defun gnus-debug ()
1744 "Attemps to go through the Gnus source file and report what variables have been changed.
1745The source file has to be in the Emacs load path."
1746 (interactive)
1747 (let ((files '("gnus.el" "gnus-msg.el" "gnus-score.el"))
1748 file dirs expr olist sym)
1749 (message "Please wait while we snoop your variables...")
1750 (sit-for 0)
1751 (save-excursion
1752 (set-buffer (get-buffer-create " *gnus bug info*"))
1753 (buffer-disable-undo (current-buffer))
1754 (while files
1755 (erase-buffer)
1756 (setq dirs load-path)
1757 (while dirs
1758 (if (or (not (car dirs))
1759 (not (stringp (car dirs)))
1760 (not (file-exists-p
1761 (setq file (concat (file-name-as-directory
1762 (car dirs)) (car files))))))
1763 (setq dirs (cdr dirs))
1764 (setq dirs nil)
1765 (insert-file-contents file)
1766 (goto-char (point-min))
1767 (or (re-search-forward "^;;* *Internal variables" nil t)
1768 (error "Malformed sources in file %s" file))
1769 (narrow-to-region (point-min) (point))
1770 (goto-char (point-min))
1771 (while (setq expr (condition-case ()
1772 (read (current-buffer)) (error nil)))
1773 (condition-case ()
1774 (and (eq (car expr) 'defvar)
1775 (stringp (nth 3 expr))
1776 (or (not (boundp (nth 1 expr)))
1777 (not (equal (eval (nth 2 expr))
1778 (symbol-value (nth 1 expr)))))
1779 (setq olist (cons (nth 1 expr) olist)))
1780 (error nil)))))
1781 (setq files (cdr files)))
1782 (kill-buffer (current-buffer)))
1783 (insert "------------------- Environment follows -------------------\n\n")
1784 (while olist
1785 (if (boundp (car olist))
1786 (insert "(setq " (symbol-name (car olist))
1787 (if (or (consp (setq sym (symbol-value (car olist))))
1788 (and (symbolp sym)
1789 (not (or (eq sym nil)
1790 (eq sym t)))))
1791 " '" " ")
1792 (prin1-to-string (symbol-value (car olist))) ")\n")
1793 (insert ";; (makeunbound '" (symbol-name (car olist)) ")\n"))
1794 (setq olist (cdr olist)))
1795 (insert "\n\n")
1796 ;; Remove any null chars - they seem to cause trouble for some
1797 ;; mailers. (Byte-compiled output from the stuff above.)
1798 (goto-char (point-min))
1799 (while (re-search-forward "[\000\200]" nil t)
1800 (replace-match "" t t))))
1801
1802(gnus-ems-redefine)
1803
1804(provide 'gnus-msg)
1805
1806;;; gnus-msg.el ends here