Sync up with gnus-6_9.
[elisp/gnus.git-] / lisp / message.el
1 ;;; message.el --- composing mail and news messages
2 ;; Copyright (C) 1996,97,98 Free Software Foundation, Inc.
3
4 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
5 ;;         MORIOKA Tomohiko <morioka@jaist.ac.jp>
6 ;;         Shuhei KOBAYASHI <shuhei-k@jaist.ac.jp>
7 ;;         Keiichi Suzuki <kei-suzu@mail.wbs.ne.jp>
8 ;; Keywords: mail, news, MIME
9
10 ;; This file is part of GNU Emacs.
11
12 ;; GNU Emacs is free software; you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation; either version 2, or (at your option)
15 ;; any later version.
16
17 ;; GNU Emacs is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 ;; GNU General Public License for more details.
21
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
24 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25 ;; Boston, MA 02111-1307, USA.
26
27 ;;; Commentary:
28
29 ;; This mode provides mail-sending facilities from within Emacs.  It
30 ;; consists mainly of large chunks of code from the sendmail.el,
31 ;; gnus-msg.el and rnewspost.el files.
32
33 ;;; Code:
34
35 (eval-when-compile
36   (require 'cl)
37   (require 'smtp)
38   )
39
40 (require 'mailheader)
41 (require 'nnheader)
42 (require 'timezone)
43 (require 'easymenu)
44 (require 'custom)
45 (if (string-match "XEmacs\\|Lucid" emacs-version)
46     (require 'mail-abbrevs)
47   (require 'mailabbrev))
48 (require 'mime-edit)
49
50 (defgroup message '((user-mail-address custom-variable)
51                     (user-full-name custom-variable))
52   "Mail and news message composing."
53   :link '(custom-manual "(message)Top")
54   :group 'mail
55   :group 'news)
56
57 (put 'user-mail-address 'custom-type 'string)
58 (put 'user-full-name 'custom-type 'string)
59
60 (defgroup message-various nil
61   "Various Message Variables"
62   :link '(custom-manual "(message)Various Message Variables")
63   :group 'message)
64
65 (defgroup message-buffers nil
66   "Message Buffers"
67   :link '(custom-manual "(message)Message Buffers")
68   :group 'message)
69
70 (defgroup message-sending nil
71   "Message Sending"
72   :link '(custom-manual "(message)Sending Variables")
73   :group 'message)
74
75 (defgroup message-interface nil
76   "Message Interface"
77   :link '(custom-manual "(message)Interface")
78   :group 'message)
79
80 (defgroup message-forwarding nil
81   "Message Forwarding"
82   :link '(custom-manual "(message)Forwarding")
83   :group 'message-interface)
84
85 (defgroup message-insertion nil
86   "Message Insertion"
87   :link '(custom-manual "(message)Insertion")
88   :group 'message)
89
90 (defgroup message-headers nil
91   "Message Headers"
92   :link '(custom-manual "(message)Message Headers")
93   :group 'message)
94
95 (defgroup message-news nil
96   "Composing News Messages"
97   :group 'message)
98
99 (defgroup message-mail nil
100   "Composing Mail Messages"
101   :group 'message)
102
103 (defgroup message-faces nil
104   "Faces used for message composing."
105   :group 'message
106   :group 'faces)
107
108 (defgroup message-frames nil
109   "Message frames"
110   :group 'message)
111
112 (defcustom message-directory "~/Mail/"
113   "*Directory from which all other mail file variables are derived."
114   :group 'message-various
115   :type 'directory)
116
117 (defcustom message-max-buffers 10
118   "*How many buffers to keep before starting to kill them off."
119   :group 'message-buffers
120   :type 'integer)
121
122 (defcustom message-send-rename-function nil
123   "Function called to rename the buffer after sending it."
124   :group 'message-buffers
125   :type 'function)
126
127 (defcustom message-fcc-handler-function 'message-output
128   "*A function called to save outgoing articles.
129 This function will be called with the name of the file to store the
130 article in.  The default function is `message-output' which saves in Unix
131 mailbox format."
132   :type '(radio (function-item message-output)
133                 (function :tag "Other"))
134   :group 'message-sending)
135
136 (defcustom message-encode-function 'message-maybe-encode
137   "*A function called to encode messages."
138   :group 'message-sending
139   :type 'function)
140
141 (defcustom message-8bit-encoding-list '(8bit binary)
142   "*8bit encoding type in Content-Transfer-Encoding field."
143   :group 'message-sending
144   :type '(repeat (symbol :tag "Type")))
145
146 (defcustom message-courtesy-message
147   "The following message is a courtesy copy of an article\nthat has been posted to %s as well.\n\n"
148   "*This is inserted at the start of a mailed copy of a posted message.
149 If the string contains the format spec \"%s\", the Newsgroups
150 the article has been posted to will be inserted there.
151 If this variable is nil, no such courtesy message will be added."
152   :group 'message-sending
153   :type 'string)
154
155 (defcustom message-ignored-bounced-headers "^\\(Received\\|Return-Path\\):"
156   "*Regexp that matches headers to be removed in resent bounced mail."
157   :group 'message-interface
158   :type 'regexp)
159
160 (defcustom message-bounce-setup-function 'message-bounce-setup-for-mime-edit
161   "Function to setup a re-sending bounced message."
162   :group 'message-sending
163   :type 'function)
164
165 ;;;###autoload
166 (defcustom message-from-style 'default
167   "*Specifies how \"From\" headers look.
168
169 If `nil', they contain just the return address like:
170         king@grassland.com
171 If `parens', they look like:
172         king@grassland.com (Elvis Parsley)
173 If `angles', they look like:
174         Elvis Parsley <king@grassland.com>
175
176 Otherwise, most addresses look like `angles', but they look like
177 `parens' if `angles' would need quoting and `parens' would not."
178   :type '(choice (const :tag "simple" nil)
179                  (const parens)
180                  (const angles)
181                  (const default))
182   :group 'message-headers)
183
184 (defcustom message-syntax-checks nil
185   ; Guess this one shouldn't be easy to customize...
186   "*Controls what syntax checks should not be performed on outgoing posts.
187 To disable checking of long signatures, for instance, add
188  `(signature . disabled)' to this list.
189
190 Don't touch this variable unless you really know what you're doing.
191
192 Checks include subject-cmsg multiple-headers sendsys message-id from
193 long-lines control-chars size new-text redirected-followup signature
194 approved sender empty empty-headers message-id from subject
195 shorten-followup-to existing-newsgroups buffer-file-name unchanged."
196   :group 'message-news)
197
198 (defcustom message-required-news-headers
199   '(From Newsgroups Subject Date Message-ID
200          (optional . Organization) Lines
201          (optional . User-Agent))
202   "*Headers to be generated or prompted for when posting an article.
203 RFC977 and RFC1036 require From, Date, Newsgroups, Subject,
204 Message-ID.  Organization, Lines, In-Reply-To, Expires, and
205 User-Agent are optional.  If don't you want message to insert some
206 header, remove it from this list."
207   :group 'message-news
208   :group 'message-headers
209   :type '(repeat sexp))
210
211 (defcustom message-required-mail-headers
212   '(From Subject Date (optional . In-Reply-To) Message-ID Lines
213          (optional . User-Agent))
214   "*Headers to be generated or prompted for when mailing a message.
215 RFC822 required that From, Date, To, Subject and Message-ID be
216 included.  Organization, Lines and User-Agent are optional."
217   :group 'message-mail
218   :group 'message-headers
219   :type '(repeat sexp))
220
221 (defcustom message-deletable-headers '(Message-ID Date Lines)
222   "Headers to be deleted if they already exist and were generated by message previously."
223   :group 'message-headers
224   :type 'sexp)
225
226 (defcustom message-ignored-news-headers
227   "^NNTP-Posting-Host:\\|^Xref:\\|^[BGF]cc:\\|^Resent-Fcc:"
228   "*Regexp of headers to be removed unconditionally before posting."
229   :group 'message-news
230   :group 'message-headers
231   :type 'regexp)
232
233 (defcustom message-ignored-mail-headers "^[GF]cc:\\|^Resent-Fcc:\\|^Xref:"
234   "*Regexp of headers to be removed unconditionally before mailing."
235   :group 'message-mail
236   :group 'message-headers
237   :type 'regexp)
238
239 (defcustom message-ignored-supersedes-headers "^Path:\\|^Date\\|^NNTP-Posting-Host:\\|^Xref:\\|^Lines:\\|^Received:\\|^X-From-Line:\\|^X-Trace:\\|^X-Complaints-To:\\|Return-Path:\\|^Supersedes:\\|^X-Trace:\\|^X-Complaints-To:"
240   "*Header lines matching this regexp will be deleted before posting.
241 It's best to delete old Path and Date headers before posting to avoid
242 any confusion."
243   :group 'message-interface
244   :type 'regexp)
245
246 (defcustom message-supersede-setup-function
247   'message-supersede-setup-for-mime-edit
248   "Function to setup a supersede message."
249   :group 'message-sending
250   :type 'function)
251
252 (defcustom message-subject-re-regexp "^[ \t]*\\([Rr][Ee]:[ \t]*\\)*[ \t]*"
253   "*Regexp matching \"Re: \" in the subject line."
254   :group 'message-various
255   :type 'regexp)
256
257 ;;;###autoload
258 (defcustom message-signature-separator "^-- *$"
259   "Regexp matching the signature separator."
260   :type 'regexp
261   :group 'message-various)
262
263 (defcustom message-elide-elipsis "\n[...]\n\n"
264   "*The string which is inserted for elided text."
265   :type 'string
266   :group 'message-various)
267
268 (defcustom message-interactive nil
269   "Non-nil means when sending a message wait for and display errors.
270 nil means let mailer mail back a message to report errors."
271   :group 'message-sending
272   :group 'message-mail
273   :type 'boolean)
274
275 (defcustom message-generate-new-buffers t
276   "*Non-nil means that a new message buffer will be created whenever `message-setup' is called.
277 If this is a function, call that function with three parameters:  The type,
278 the to address and the group name.  (Any of these may be nil.)  The function
279 should return the new buffer name."
280   :group 'message-buffers
281   :type '(choice (const :tag "off" nil)
282                  (const :tag "on" t)
283                  (function fun)))
284
285 (defcustom message-kill-buffer-on-exit nil
286   "*Non-nil means that the message buffer will be killed after sending a message."
287   :group 'message-buffers
288   :type 'boolean)
289
290 (defcustom message-kill-buffer-query-function 'yes-or-no-p
291   "*A function called to query the user whether to kill buffer anyway or not.
292 If it is t, the buffer will be killed peremptorily."
293   :type '(radio (function-item yes-or-no-p)
294                 (function-item y-or-n-p)
295                 (function-item nnheader-Y-or-n-p)
296                 (function :tag "Other" t))
297   :group 'message-buffers)
298
299 (defvar gnus-local-organization)
300 (defcustom message-user-organization
301   (or (and (boundp 'gnus-local-organization)
302            (stringp gnus-local-organization)
303            gnus-local-organization)
304       (getenv "ORGANIZATION")
305       t)
306   "*String to be used as an Organization header.
307 If t, use `message-user-organization-file'."
308   :group 'message-headers
309   :type '(choice string
310                  (const :tag "consult file" t)))
311
312 ;;;###autoload
313 (defcustom message-user-organization-file "/usr/lib/news/organization"
314   "*Local news organization file."
315   :type 'file
316   :group 'message-headers)
317
318 (defcustom message-forward-start-separator
319   (concat (mime-make-tag "message" "rfc822") "\n")
320   "*Delimiter inserted before forwarded messages."
321   :group 'message-forwarding
322   :type 'string)
323
324 (defcustom message-forward-end-separator
325   (concat (mime-make-tag "text" "plain") "\n")
326   "*Delimiter inserted after forwarded messages."
327   :group 'message-forwarding
328   :type 'string)
329
330 (defcustom message-signature-before-forwarded-message t
331   "*If non-nil, put the signature before any included forwarded message."
332   :group 'message-forwarding
333   :type 'boolean)
334
335 (defcustom message-included-forward-headers
336   "^From:\\|^Newsgroups:\\|^Subject:\\|^Date:\\|^Followup-To:\\|^Reply-To:\\|^Organization:\\|^Summary:\\|^Keywords:\\|^To:\\|^Cc:\\|^Posted-To:\\|^Mail-Copies-To:\\|^Apparently-To:\\|^Gnus-Warning:\\|^Resent-\\|^Message-ID:\\|^References:\\|^Content-\\|^MIME-Version:"
337   "*Regexp matching headers to be included in forwarded messages."
338   :group 'message-forwarding
339   :type 'regexp)
340
341 (defcustom message-make-forward-subject-function 
342   'message-forward-subject-author-subject
343  "*A list of functions that are called to generate a subject header for forwarded messages.
344 The subject generated by the previous function is passed into each
345 successive function.
346
347 The provided functions are:
348
349 * message-forward-subject-author-subject (Source of article (author or
350       newsgroup)), in brackets followed by the subject
351 * message-forward-subject-fwd (Subject of article with 'Fwd:' prepended
352       to it."
353  :group 'message-forwarding
354  :type '(radio (function-item message-forward-subject-author-subject)
355                (function-item message-forward-subject-fwd)))
356
357 (defcustom message-wash-forwarded-subjects nil
358   "*If non-nil, try to remove as much old cruft as possible from the subject of messages before generating the new subject of a forward."
359   :group 'message-forwarding
360   :type 'boolean)
361
362 (defcustom message-ignored-resent-headers "^Return-Receipt"
363   "*All headers that match this regexp will be deleted when resending a message."
364   :group 'message-interface
365   :type 'regexp)
366
367 (defcustom message-ignored-cited-headers "."
368   "*Delete these headers from the messages you yank."
369   :group 'message-insertion
370   :type 'regexp)
371
372 (defcustom message-cancel-message "I am canceling my own article."
373   "Message to be inserted in the cancel message."
374   :group 'message-interface
375   :type 'string)
376
377 ;; Useful to set in site-init.el
378 ;;;###autoload
379 (defcustom message-send-mail-function 'message-send-mail-with-sendmail
380   "Function to call to send the current buffer as mail.
381 The headers should be delimited by a line whose contents match the
382 variable `mail-header-separator'.
383
384 Legal values include `message-send-mail-with-sendmail' (the default),
385 `message-send-mail-with-mh', `message-send-mail-with-qmail' and
386 `message-send-mail-with-smtp'."
387   :type '(radio (function-item message-send-mail-with-sendmail)
388                 (function-item message-send-mail-with-mh)
389                 (function-item message-send-mail-with-qmail)
390                 (function-item message-send-mail-with-smtp)
391                 (function :tag "Other"))
392   :group 'message-sending
393   :group 'message-mail)
394
395 ;; 1997-09-29 by MORIOKA Tomohiko
396 (defcustom message-send-news-function 'message-send-news-with-gnus
397   "Function to call to send the current buffer as news.
398 The headers should be delimited by a line whose contents match the
399 variable `mail-header-separator'."
400   :group 'message-sending
401   :group 'message-news
402   :type 'function)
403
404 (defcustom message-reply-to-function nil
405   "Function that should return a list of headers.
406 This function should pick out addresses from the To, Cc, and From headers
407 and respond with new To and Cc headers."
408   :group 'message-interface
409   :type 'function)
410
411 (defcustom message-wide-reply-to-function nil
412   "Function that should return a list of headers.
413 This function should pick out addresses from the To, Cc, and From headers
414 and respond with new To and Cc headers."
415   :group 'message-interface
416   :type 'function)
417
418 (defcustom message-followup-to-function nil
419   "Function that should return a list of headers.
420 This function should pick out addresses from the To, Cc, and From headers
421 and respond with new To and Cc headers."
422   :group 'message-interface
423   :type 'function)
424
425 (defcustom message-use-followup-to 'ask
426   "*Specifies what to do with Followup-To header.
427 If nil, always ignore the header.  If it is t, use its value, but
428 query before using the \"poster\" value.  If it is the symbol `ask',
429 always query the user whether to use the value.  If it is the symbol
430 `use', always use the value."
431   :group 'message-interface
432   :type '(choice (const :tag "ignore" nil)
433                  (const use)
434                  (const ask)))
435
436 ;; stuff relating to broken sendmail in MMDF
437 (defcustom message-sendmail-f-is-evil nil
438   "*Non-nil means that \"-f username\" should not be added to the sendmail
439 command line, because it is even more evil than leaving it out."
440   :group 'message-sending
441   :type 'boolean)
442
443 ;; qmail-related stuff
444 (defcustom message-qmail-inject-program "/var/qmail/bin/qmail-inject"
445   "Location of the qmail-inject program."
446   :group 'message-sending
447   :type 'file)
448
449 (defcustom message-qmail-inject-args nil
450   "Arguments passed to qmail-inject programs.
451 This should be a list of strings, one string for each argument.
452
453 For e.g., if you wish to set the envelope sender address so that bounces
454 go to the right place or to deal with listserv's usage of that address, you
455 might set this variable to '(\"-f\" \"you@some.where\")."
456   :group 'message-sending
457   :type '(repeat string))
458
459 (defvar gnus-post-method)
460 (defvar gnus-select-method)
461 (defcustom message-post-method
462   (cond ((and (boundp 'gnus-post-method)
463               (listp gnus-post-method)
464               gnus-post-method)
465          gnus-post-method)
466         ((boundp 'gnus-select-method)
467          gnus-select-method)
468         (t '(nnspool "")))
469   "*Method used to post news.
470 Note that when posting from inside Gnus, for instance, this
471 variable isn't used."
472   :group 'message-news
473   :group 'message-sending
474   ;; This should be the `gnus-select-method' widget, but that might
475   ;; create a dependence to `gnus.el'.
476   :type 'sexp)
477
478 (defcustom message-generate-headers-first nil
479   "*If non-nil, generate all possible headers before composing."
480   :group 'message-headers
481   :type 'boolean)
482
483 (defcustom message-setup-hook '(message-mime-setup)
484   "Normal hook, run each time a new outgoing message is initialized.
485 The function `message-setup' runs this hook."
486   :group 'message-various
487   :type 'hook)
488
489 (defcustom message-signature-setup-hook nil
490   "Normal hook, run each time a new outgoing message is initialized.
491 It is run after the headers have been inserted and before
492 the signature is inserted."
493   :group 'message-various
494   :type 'hook)
495
496 (defcustom message-bounce-setup-hook nil
497   "Normal hook, run each time a a re-sending bounced message is initialized.
498 The function `message-bounce' runs this hook."
499   :group 'message-various
500   :type 'hook)
501
502 (defcustom message-supersede-setup-hook nil
503   "Normal hook, run each time a supersede message is initialized.
504 The function `message-supersede' runs this hook."
505   :group 'message-various
506   :type 'hook)
507
508 (defcustom message-mode-hook nil
509   "Hook run in message mode buffers."
510   :group 'message-various
511   :type 'hook)
512
513 (defcustom message-header-hook nil
514   "Hook run in a message mode before header encode. Buffer narrowed 
515 to the headers."
516    :group 'message-various
517    :type 'hook)
518
519 (defcustom message-header-encode-function
520   'eword-encode-header
521   "A function called to encode header."
522   :group 'message-various
523   :type 'function)
524
525 (defcustom message-header-encoded-hook nil
526   "Hook run in a message mode after header encoded. Buffer narrowed 
527 to the headers."
528   :group 'message-various
529   :type 'hook)
530
531 (defcustom message-header-setup-hook nil
532   "Hook called narrowed to the headers when setting up a message buffer."
533   :group 'message-various
534   :type 'hook)
535
536 ;;;###autoload
537 (defcustom message-citation-line-function 'message-insert-citation-line
538   "*Function called to insert the \"Whomever writes:\" line."
539   :type 'function
540   :group 'message-insertion)
541
542 ;;;###autoload
543 (defcustom message-yank-prefix "> "
544   "*Prefix inserted on the lines of yanked messages.
545 nil means use indentation."
546   :type 'string
547   :group 'message-insertion)
548
549 (defcustom message-indentation-spaces 3
550   "*Number of spaces to insert at the beginning of each cited line.
551 Used by `message-yank-original' via `message-yank-cite'."
552   :group 'message-insertion
553   :type 'integer)
554
555 ;;;###autoload
556 (defcustom message-cite-function 'message-cite-original
557   "*Function for citing an original message.
558 Predefined functions include `message-cite-original' and
559 `message-cite-original-without-signature'.
560 Note that `message-cite-original' uses `mail-citation-hook' if that is non-nil."
561   :type '(radio (function-item message-cite-original)
562                 (function-item sc-cite-original)
563                 (function :tag "Other"))
564   :group 'message-insertion)
565
566 ;;;###autoload
567 (defcustom message-indent-citation-function 'message-indent-citation
568   "*Function for modifying a citation just inserted in the mail buffer.
569 This can also be a list of functions.  Each function can find the
570 citation between (point) and (mark t).  And each function should leave
571 point and mark around the citation text as modified."
572   :type 'function
573   :group 'message-insertion)
574
575 (defvar message-abbrevs-loaded nil)
576
577 ;;;###autoload
578 (defcustom message-signature t
579   "*String to be inserted at the end of the message buffer.
580 If t, the `message-signature-file' file will be inserted instead.
581 If a function, the result from the function will be used instead.
582 If a form, the result from the form will be used instead."
583   :type 'sexp
584   :group 'message-insertion)
585
586 ;;;###autoload
587 (defcustom message-signature-file "~/.signature"
588   "*File containing the text inserted at end of message buffer."
589   :type 'file
590   :group 'message-insertion)
591
592 (defcustom message-distribution-function nil
593   "*Function called to return a Distribution header."
594   :group 'message-news
595   :group 'message-headers
596   :type 'function)
597
598 (defcustom message-expires 14
599   "Number of days before your article expires."
600   :group 'message-news
601   :group 'message-headers
602   :link '(custom-manual "(message)News Headers")
603   :type 'integer)
604
605 (defcustom message-user-path nil
606   "If nil, use the NNTP server name in the Path header.
607 If stringp, use this; if non-nil, use no host name (user name only)."
608   :group 'message-news
609   :group 'message-headers
610   :link '(custom-manual "(message)News Headers")
611   :type '(choice (const :tag "nntp" nil)
612                  (string :tag "name")
613                  (sexp :tag "none" :format "%t" t)))
614
615 (defvar message-reply-buffer nil)
616 (defvar message-reply-headers nil)
617 (defvar message-user-agent nil) ; XXX: This symbol is overloaded!  See below.
618 (defvar message-sent-message-via nil)
619 (defvar message-checksum nil)
620 (defvar message-send-actions nil
621   "A list of actions to be performed upon successful sending of a message.")
622 (defvar message-exit-actions nil
623   "A list of actions to be performed upon exiting after sending a message.")
624 (defvar message-kill-actions nil
625   "A list of actions to be performed before killing a message buffer.")
626 (defvar message-postpone-actions nil
627   "A list of actions to be performed after postponing a message.")
628 (defvar message-original-frame nil)
629 (defvar message-parameter-alist nil)
630 (defvar message-startup-parameter-alist nil)
631
632 (define-widget 'message-header-lines 'text
633   "All header lines must be LFD terminated."
634   :format "%t:%n%v"
635   :valid-regexp "^\\'"
636   :error "All header lines must be newline terminated")
637
638 (defcustom message-default-headers ""
639   "*A string containing header lines to be inserted in outgoing messages.
640 It is inserted before you edit the message, so you can edit or delete
641 these lines."
642   :group 'message-headers
643   :type 'message-header-lines)
644
645 (defcustom message-default-mail-headers ""
646   "*A string of header lines to be inserted in outgoing mails."
647   :group 'message-headers
648   :group 'message-mail
649   :type 'message-header-lines)
650
651 (defcustom message-default-news-headers ""
652   "*A string of header lines to be inserted in outgoing news
653 articles."
654   :group 'message-headers
655   :group 'message-news
656   :type 'message-header-lines)
657
658 ;; Note: could use /usr/ucb/mail instead of sendmail;
659 ;; options -t, and -v if not interactive.
660 (defcustom message-mailer-swallows-blank-line
661   (if (and (string-match "sparc-sun-sunos\\(\\'\\|[^5]\\)"
662                          system-configuration)
663            (file-readable-p "/etc/sendmail.cf")
664            (let ((buffer (get-buffer-create " *temp*")))
665              (unwind-protect
666                  (save-excursion
667                    (set-buffer buffer)
668                    (insert-file-contents "/etc/sendmail.cf")
669                    (goto-char (point-min))
670                    (let ((case-fold-search nil))
671                      (re-search-forward "^OR\\>" nil t)))
672                (kill-buffer buffer))))
673       ;; According to RFC822, "The field-name must be composed of printable
674       ;; ASCII characters (i. e., characters that have decimal values between
675       ;; 33 and 126, except colon)", i. e., any chars except ctl chars,
676       ;; space, or colon.
677       '(looking-at "[ \t]\\|[][!\"#$%&'()*+,-./0-9;<=>?@A-Z\\\\^_`a-z{|}~]+:"))
678   "*Set this non-nil if the system's mailer runs the header and body together.
679 \(This problem exists on Sunos 4 when sendmail is run in remote mode.)
680 The value should be an expression to test whether the problem will
681 actually occur."
682   :group 'message-sending
683   :type 'sexp)
684
685 ;;; XXX: This symbol is overloaded!  See below.
686 (defvar message-user-agent nil
687   "String of the form of PRODUCT/VERSION.  Used for User-Agent header field.")
688
689 ;; Ignore errors in case this is used in Emacs 19.
690 ;; Don't use ignore-errors because this is copied into loaddefs.el.
691 ;;;###autoload
692 (condition-case nil
693     (define-mail-user-agent 'message-user-agent
694       'message-mail 'message-send-and-exit
695       'message-kill-buffer 'message-send-hook)
696   (error nil))
697
698 (defvar message-mh-deletable-headers '(Message-ID Date Lines Sender)
699   "If non-nil, delete the deletable headers before feeding to mh.")
700
701 (defvar message-send-method-alist
702   '((news message-news-p message-send-via-news)
703     (mail message-mail-p message-send-via-mail))
704   "Alist of ways to send outgoing messages.
705 Each element has the form
706
707   \(TYPE PREDICATE FUNCTION)
708
709 where TYPE is a symbol that names the method; PREDICATE is a function
710 called without any parameters to determine whether the message is
711 a message of type TYPE; and FUNCTION is a function to be called if
712 PREDICATE returns non-nil.  FUNCTION is called with one parameter --
713 the prefix.")
714
715 (defvar message-mail-alias-type 'abbrev
716   "*What alias expansion type to use in Message buffers.
717 The default is `abbrev', which uses mailabbrev.  nil switches
718 mail aliases off.")
719
720 (defcustom message-auto-save-directory
721   (nnheader-concat message-directory "drafts/")
722   "*Directory where Message auto-saves buffers if Gnus isn't running.
723 If nil, Message won't auto-save."
724   :group 'message-buffers
725   :type 'directory)
726
727 ;;; Internal variables.
728 ;;; Well, not really internal.
729
730 (defvar message-mode-syntax-table
731   (let ((table (copy-syntax-table text-mode-syntax-table)))
732     (modify-syntax-entry ?% ". " table)
733     table)
734   "Syntax table used while in Message mode.")
735
736 (defvar message-mode-abbrev-table text-mode-abbrev-table
737   "Abbrev table used in Message mode buffers.
738 Defaults to `text-mode-abbrev-table'.")
739 (defgroup message-headers nil
740   "Message headers."
741   :link '(custom-manual "(message)Variables")
742   :group 'message)
743
744 (defface message-header-to-face
745   '((((class color)
746       (background dark))
747      (:foreground "green2" :bold t))
748     (((class color)
749       (background light))
750      (:foreground "MidnightBlue" :bold t))
751     (t
752      (:bold t :italic t)))
753   "Face used for displaying From headers."
754   :group 'message-faces)
755
756 (defface message-header-cc-face
757   '((((class color)
758       (background dark))
759      (:foreground "green4" :bold t))
760     (((class color)
761       (background light))
762      (:foreground "MidnightBlue"))
763     (t
764      (:bold t)))
765   "Face used for displaying Cc headers."
766   :group 'message-faces)
767
768 (defface message-header-subject-face
769   '((((class color)
770       (background dark))
771      (:foreground "green3"))
772     (((class color)
773       (background light))
774      (:foreground "navy blue" :bold t))
775     (t
776      (:bold t)))
777   "Face used for displaying subject headers."
778   :group 'message-faces)
779
780 (defface message-header-newsgroups-face
781   '((((class color)
782       (background dark))
783      (:foreground "yellow" :bold t :italic t))
784     (((class color)
785       (background light))
786      (:foreground "blue4" :bold t :italic t))
787     (t
788      (:bold t :italic t)))
789   "Face used for displaying newsgroups headers."
790   :group 'message-faces)
791
792 (defface message-header-other-face
793   '((((class color)
794       (background dark))
795      (:foreground "#b00000"))
796     (((class color)
797       (background light))
798      (:foreground "steel blue"))
799     (t
800      (:bold t :italic t)))
801   "Face used for displaying newsgroups headers."
802   :group 'message-faces)
803
804 (defface message-header-name-face
805   '((((class color)
806       (background dark))
807      (:foreground "DarkGreen"))
808     (((class color)
809       (background light))
810      (:foreground "cornflower blue"))
811     (t
812      (:bold t)))
813   "Face used for displaying header names."
814   :group 'message-faces)
815
816 (defface message-header-xheader-face
817   '((((class color)
818       (background dark))
819      (:foreground "blue"))
820     (((class color)
821       (background light))
822      (:foreground "blue"))
823     (t
824      (:bold t)))
825   "Face used for displaying X-Header headers."
826   :group 'message-faces)
827
828 (defface message-separator-face
829   '((((class color)
830       (background dark))
831      (:foreground "blue3"))
832     (((class color)
833       (background light))
834      (:foreground "brown"))
835     (t
836      (:bold t)))
837   "Face used for displaying the separator."
838   :group 'message-faces)
839
840 (defface message-cited-text-face
841   '((((class color)
842       (background dark))
843      (:foreground "red"))
844     (((class color)
845       (background light))
846      (:foreground "red"))
847     (t
848      (:bold t)))
849   "Face used for displaying cited text names."
850   :group 'message-faces)
851
852 (defvar message-font-lock-keywords
853   (let* ((cite-prefix "A-Za-z")
854          (cite-suffix (concat cite-prefix "0-9_.@-"))
855          (content "[ \t]*\\(.+\\(\n[ \t].*\\)*\\)"))
856     `((,(concat "^\\([Tt]o:\\)" content)
857        (1 'message-header-name-face)
858        (2 'message-header-to-face nil t))
859       (,(concat "^\\(^[GBF]?[Cc][Cc]:\\|^[Rr]eply-[Tt]o:\\)" content)
860        (1 'message-header-name-face)
861        (2 'message-header-cc-face nil t))
862       (,(concat "^\\([Ss]ubject:\\)" content)
863        (1 'message-header-name-face)
864        (2 'message-header-subject-face nil t))
865       (,(concat "^\\([Nn]ewsgroups:\\|Followup-[Tt]o:\\)" content)
866        (1 'message-header-name-face)
867        (2 'message-header-newsgroups-face nil t))
868       (,(concat "^\\([A-Z][^: \n\t]+:\\)" content)
869        (1 'message-header-name-face)
870        (2 'message-header-other-face nil t))
871       (,(concat "^\\(X-[A-Za-z0-9-]+\\|In-Reply-To\\):" content)
872        (1 'message-header-name-face)
873        (2 'message-header-name-face))
874       ,@(if (and mail-header-separator
875                  (not (equal mail-header-separator "")))
876             `((,(concat "^\\(" (regexp-quote mail-header-separator) "\\)$")
877                1 'message-separator-face))
878           nil)
879       (,(concat "^[ \t]*"
880                 "\\([" cite-prefix "]+[" cite-suffix "]*\\)?"
881                 "[:>|}].*")
882        (0 'message-cited-text-face))))
883   "Additional expressions to highlight in Message mode.")
884
885 ;; XEmacs does it like this.  For Emacs, we have to set the
886 ;; `font-lock-defaults' buffer-local variable.
887 (put 'message-mode 'font-lock-defaults '(message-font-lock-keywords t))
888
889 (defvar message-face-alist
890   '((bold . bold-region)
891     (underline . underline-region)
892     (default . (lambda (b e)
893                  (unbold-region b e)
894                  (ununderline-region b e))))
895   "Alist of mail and news faces for facemenu.
896 The cdr of ech entry is a function for applying the face to a region.")
897
898 (defcustom message-send-hook nil
899   "Hook run before sending messages."
900   :group 'message-various
901   :options '(ispell-message)
902   :type 'hook)
903
904 (defcustom message-send-mail-hook nil
905   "Hook run before sending mail messages."
906   :group 'message-various
907   :type 'hook)
908
909 (defcustom message-send-news-hook nil
910   "Hook run before sending news messages."
911   :group 'message-various
912   :type 'hook)
913
914 (defcustom message-sent-hook nil
915   "Hook run after sending messages."
916   :group 'message-various
917   :type 'hook)
918
919 (defcustom message-use-multi-frames nil
920   "Make new frame when sending messages."
921   :group 'message-frames
922   :type 'boolean)
923
924 (defcustom message-delete-frame-on-exit nil
925   "Delete frame after sending messages."
926   :group 'message-frames
927   :type '(choice (const :tag "off" nil)
928                  (const :tag "always" t)
929                  (const :tag "ask" ask)))
930
931 ;;; Internal variables.
932
933 (defvar message-buffer-list nil)
934 (defvar message-this-is-news nil)
935 (defvar message-this-is-mail nil)
936 (defvar message-draft-article nil)
937
938 ;; Byte-compiler warning
939 (defvar gnus-active-hashtb)
940 (defvar gnus-read-active-file)
941
942 ;;; Regexp matching the delimiter of messages in UNIX mail format
943 ;;; (UNIX From lines), minus the initial ^.  It should be a copy
944 ;;; of rmail.el's rmail-unix-mail-delimiter.
945 (defvar message-unix-mail-delimiter
946   (let ((time-zone-regexp
947          (concat "\\([A-Z]?[A-Z]?[A-Z][A-Z]\\( DST\\)?"
948                  "\\|[-+]?[0-9][0-9][0-9][0-9]"
949                  "\\|"
950                  "\\) *")))
951     (concat
952      "From "
953
954      ;; Many things can happen to an RFC 822 mailbox before it is put into
955      ;; a `From' line.  The leading phrase can be stripped, e.g.
956      ;; `Joe <@w.x:joe@y.z>' -> `<@w.x:joe@y.z>'.  The <> can be stripped, e.g.
957      ;; `<@x.y:joe@y.z>' -> `@x.y:joe@y.z'.  Everything starting with a CRLF
958      ;; can be removed, e.g.
959      ;;         From: joe@y.z (Joe      K
960      ;;                 User)
961      ;; can yield `From joe@y.z (Joe    K Fri Mar 22 08:11:15 1996', and
962      ;;         From: Joe User
963      ;;                 <joe@y.z>
964      ;; can yield `From Joe User Fri Mar 22 08:11:15 1996'.
965      ;; The mailbox can be removed or be replaced by white space, e.g.
966      ;;         From: "Joe User"{space}{tab}
967      ;;                 <joe@y.z>
968      ;; can yield `From {space}{tab} Fri Mar 22 08:11:15 1996',
969      ;; where {space} and {tab} represent the Ascii space and tab characters.
970      ;; We want to match the results of any of these manglings.
971      ;; The following regexp rejects names whose first characters are
972      ;; obviously bogus, but after that anything goes.
973      "\\([^\0-\b\n-\r\^?].*\\)? "
974
975      ;; The time the message was sent.
976      "\\([^\0-\r \^?]+\\) +"                            ; day of the week
977      "\\([^\0-\r \^?]+\\) +"                            ; month
978      "\\([0-3]?[0-9]\\) +"                              ; day of month
979      "\\([0-2][0-9]:[0-5][0-9]\\(:[0-6][0-9]\\)?\\) *"  ; time of day
980
981      ;; Perhaps a time zone, specified by an abbreviation, or by a
982      ;; numeric offset.
983      time-zone-regexp
984
985      ;; The year.
986      " \\([0-9][0-9]+\\) *"
987
988      ;; On some systems the time zone can appear after the year, too.
989      time-zone-regexp
990
991      ;; Old uucp cruft.
992      "\\(remote from .*\\)?"
993
994      "\n"))
995   "Regexp matching the delimiter of messages in UNIX mail format.")
996
997 (defvar message-unsent-separator
998   (concat "^ *---+ +Unsent message follows +---+ *$\\|"
999           "^ *---+ +Returned message +---+ *$\\|"
1000           "^Start of returned message$\\|"
1001           "^ *---+ +Original message +---+ *$\\|"
1002           "^ *--+ +begin message +--+ *$\\|"
1003           "^ *---+ +Original message follows +---+ *$\\|"
1004           "^|? *---+ +Message text follows: +---+ *|?$")
1005   "A regexp that matches the separator before the text of a failed message.")
1006
1007 (defvar message-header-format-alist
1008   `((Newsgroups)
1009     (To . message-fill-address)
1010     (Cc . message-fill-address)
1011     (Subject)
1012     (In-Reply-To)
1013     (Fcc)
1014     (Bcc)
1015     (Date)
1016     (Organization)
1017     (Distribution)
1018     (Lines)
1019     (Expires)
1020     (Message-ID)
1021     (References . message-fill-references)
1022     (User-Agent))
1023   "Alist used for formatting headers.")
1024
1025 (eval-and-compile
1026   (autoload 'message-setup-toolbar "messagexmas")
1027   (autoload 'mh-new-draft-name "mh-comp")
1028   (autoload 'mh-send-letter "mh-comp")
1029   (autoload 'gnus-point-at-eol "gnus-util")
1030   (autoload 'gnus-point-at-bol "gnus-util")
1031   (autoload 'gnus-output-to-mail "gnus-util")
1032   (autoload 'gnus-output-to-rmail "gnus-util")
1033   (autoload 'mail-abbrev-in-expansion-header-p "mailabbrev")
1034   (autoload 'nndraft-request-associate-buffer "nndraft")
1035   (autoload 'nndraft-request-expire-articles "nndraft")
1036   (autoload 'gnus-open-server "gnus-int")
1037   (autoload 'gnus-request-post "gnus-int")
1038   (autoload 'gnus-copy-article-buffer "gnus-msg")
1039   (autoload 'gnus-alive-p "gnus-util")
1040   (autoload 'rmail-output "rmail"))
1041
1042 \f
1043
1044 ;;;
1045 ;;; Utility functions.
1046 ;;;
1047 (defun message-eval-parameter (parameter)
1048   (condition-case ()
1049       (if (symbolp parameter)
1050           (if (functionp parameter)
1051               (funcall parameter)
1052             (eval parameter))
1053         parameter)
1054     (error nil)))
1055
1056 (defsubst message-get-parameter (key &optional alist)
1057   (unless alist
1058     (setq alist message-parameter-alist))
1059   (cdr (assq key alist)))
1060
1061 (defmacro message-get-parameter-with-eval (key &optional alist)
1062   `(message-eval-parameter (message-get-parameter ,key ,alist)))
1063
1064 (defmacro message-y-or-n-p (question show &rest text)
1065   "Ask QUESTION, displaying the rest of the arguments in a temp. buffer if SHOW"
1066   `(message-talkative-question 'y-or-n-p ,question ,show ,@text))
1067
1068 ;; Delete the current line (and the next N lines.);
1069 (defmacro message-delete-line (&optional n)
1070   `(delete-region (progn (beginning-of-line) (point))
1071                   (progn (forward-line ,(or n 1)) (point))))
1072
1073 (defun message-tokenize-header (header &optional separator)
1074   "Split HEADER into a list of header elements.
1075 \",\" is used as the separator."
1076   (if (not header)
1077       nil
1078     (let ((regexp (format "[%s]+" (or separator ",")))
1079           (beg 1)
1080           (first t)
1081           quoted elems paren)
1082       (save-excursion
1083         (message-set-work-buffer)
1084         (insert header)
1085         (goto-char (point-min))
1086         (while (not (eobp))
1087           (if first
1088               (setq first nil)
1089             (forward-char 1))
1090           (cond ((and (> (point) beg)
1091                       (or (eobp)
1092                           (and (looking-at regexp)
1093                                (not quoted)
1094                                (not paren))))
1095                  (push (buffer-substring beg (point)) elems)
1096                  (setq beg (match-end 0)))
1097                 ((= (following-char) ?\")
1098                  (setq quoted (not quoted)))
1099                 ((and (= (following-char) ?\()
1100                       (not quoted))
1101                  (setq paren t))
1102                 ((and (= (following-char) ?\))
1103                       (not quoted))
1104                  (setq paren nil))))
1105         (nreverse elems)))))
1106
1107 (defun message-mail-file-mbox-p (file)
1108   "Say whether FILE looks like a Unix mbox file."
1109   (when (and (file-exists-p file)
1110              (file-readable-p file)
1111              (file-regular-p file))
1112     (nnheader-temp-write nil
1113       (nnheader-insert-file-contents file)
1114       (goto-char (point-min))
1115       (looking-at message-unix-mail-delimiter))))
1116
1117 (defun message-fetch-field (header &optional not-all)
1118   "The same as `mail-fetch-field', only remove all newlines."
1119   (let* ((inhibit-point-motion-hooks t)
1120          (value (mail-fetch-field header nil (not not-all))))
1121     (when value
1122       (nnheader-replace-chars-in-string value ?\n ? ))))
1123
1124 (defun message-add-header (&rest headers)
1125   "Add the HEADERS to the message header, skipping those already present."
1126   (while headers
1127     (let (hclean)
1128       (unless (string-match "^\\([^:]+\\):[ \t]*[^ \t]" (car headers))
1129         (error "Invalid header `%s'" (car headers)))
1130       (setq hclean (match-string 1 (car headers)))
1131     (save-restriction
1132       (message-narrow-to-headers)
1133       (unless (re-search-forward (concat "^" (regexp-quote hclean) ":") nil t)
1134         (insert (car headers) ?\n))))
1135     (setq headers (cdr headers))))
1136
1137 (defun message-fetch-reply-field (header)
1138   "Fetch FIELD from the message we're replying to."
1139   (let ((buffer (message-eval-parameter message-reply-buffer)))
1140     (when (and buffer
1141                (buffer-name buffer))
1142       (save-excursion
1143         (set-buffer buffer)
1144       (message-fetch-field header)))))
1145
1146 (defun message-set-work-buffer ()
1147   (if (get-buffer " *message work*")
1148       (progn
1149         (set-buffer " *message work*")
1150         (erase-buffer))
1151     (set-buffer (get-buffer-create " *message work*"))
1152     (kill-all-local-variables)
1153     (buffer-disable-undo (current-buffer))))
1154
1155 (defun message-functionp (form)
1156   "Return non-nil if FORM is funcallable."
1157   (or (and (symbolp form) (fboundp form))
1158       (and (listp form) (eq (car form) 'lambda))
1159       (byte-code-function-p form)))
1160
1161 (defun message-strip-subject-re (subject)
1162   "Remove \"Re:\" from subject lines."
1163   (if (string-match message-subject-re-regexp subject)
1164       (substring subject (match-end 0))
1165     subject))
1166
1167 (defun message-remove-header (header &optional is-regexp first reverse)
1168   "Remove HEADER in the narrowed buffer.
1169 If REGEXP, HEADER is a regular expression.
1170 If FIRST, only remove the first instance of the header.
1171 Return the number of headers removed."
1172   (goto-char (point-min))
1173   (let ((regexp (if is-regexp header (concat "^" (regexp-quote header) ":")))
1174         (number 0)
1175         (case-fold-search t)
1176         last)
1177     (while (and (not (eobp))
1178                 (not last))
1179       (if (if reverse
1180               (not (looking-at regexp))
1181             (looking-at regexp))
1182           (progn
1183             (incf number)
1184             (when first
1185               (setq last t))
1186             (delete-region
1187              (point)
1188              ;; There might be a continuation header, so we have to search
1189              ;; until we find a new non-continuation line.
1190              (progn
1191                (forward-line 1)
1192                (if (re-search-forward "^[^ \t]" nil t)
1193                    (goto-char (match-beginning 0))
1194                  (point-max)))))
1195         (forward-line 1)
1196         (if (re-search-forward "^[^ \t]" nil t)
1197             (goto-char (match-beginning 0))
1198           (point-max))))
1199     number))
1200
1201 (defun message-narrow-to-headers ()
1202   "Narrow the buffer to the head of the message."
1203   (widen)
1204   (narrow-to-region
1205    (goto-char (point-min))
1206    (if (re-search-forward
1207         (concat "^" (regexp-quote mail-header-separator) "\n") nil t)
1208        (match-beginning 0)
1209      (point-max)))
1210   (goto-char (point-min)))
1211
1212 (defun message-narrow-to-head ()
1213   "Narrow the buffer to the head of the message."
1214   (widen)
1215   (narrow-to-region
1216    (goto-char (point-min))
1217    (if (search-forward "\n\n" nil 1)
1218        (1- (point))
1219      (point-max)))
1220   (goto-char (point-min)))
1221
1222 (defun message-news-p ()
1223   "Say whether the current buffer contains a news message."
1224   (and (not message-this-is-mail)
1225        (or message-this-is-news
1226            (save-excursion
1227              (save-restriction
1228                (message-narrow-to-headers)
1229                (and (message-fetch-field "newsgroups")
1230                     (not (message-fetch-field "posted-to"))))))))
1231
1232 (defun message-mail-p ()
1233   "Say whether the current buffer contains a mail message."
1234   (and (not message-this-is-news)
1235        (or message-this-is-mail
1236            (save-excursion
1237              (save-restriction
1238                (message-narrow-to-headers)
1239                (or (message-fetch-field "to")
1240                    (message-fetch-field "cc")
1241                    (message-fetch-field "bcc")))))))
1242
1243 (defun message-next-header ()
1244   "Go to the beginning of the next header."
1245   (beginning-of-line)
1246   (or (eobp) (forward-char 1))
1247   (not (if (re-search-forward "^[^ \t]" nil t)
1248            (beginning-of-line)
1249          (goto-char (point-max)))))
1250
1251 (defun message-sort-headers-1 ()
1252   "Sort the buffer as headers using `message-rank' text props."
1253   (goto-char (point-min))
1254   (sort-subr
1255    nil 'message-next-header
1256    (lambda ()
1257      (message-next-header)
1258      (unless (bobp)
1259        (forward-char -1)))
1260    (lambda ()
1261      (or (get-text-property (point) 'message-rank)
1262          10000))))
1263
1264 (defun message-sort-headers ()
1265   "Sort the headers of the current message according to `message-header-format-alist'."
1266   (interactive)
1267   (save-excursion
1268     (save-restriction
1269       (let ((max (1+ (length message-header-format-alist)))
1270             rank)
1271         (message-narrow-to-headers)
1272         (while (re-search-forward "^[^ \n]+:" nil t)
1273           (put-text-property
1274            (match-beginning 0) (1+ (match-beginning 0))
1275            'message-rank
1276            (if (setq rank (length (memq (assq (intern (buffer-substring
1277                                                        (match-beginning 0)
1278                                                        (1- (match-end 0))))
1279                                               message-header-format-alist)
1280                                         message-header-format-alist)))
1281                (- max rank)
1282              (1+ max)))))
1283       (message-sort-headers-1))))
1284 \f
1285
1286 ;;;
1287 ;;; Message mode
1288 ;;;
1289
1290 ;;; Set up keymap.
1291
1292 (defvar message-mode-map nil)
1293
1294 (unless message-mode-map
1295   (setq message-mode-map (copy-keymap text-mode-map))
1296   (define-key message-mode-map "\C-c?" 'describe-mode)
1297
1298   (define-key message-mode-map "\C-c\C-f\C-t" 'message-goto-to)
1299   (define-key message-mode-map "\C-c\C-f\C-b" 'message-goto-bcc)
1300   (define-key message-mode-map "\C-c\C-f\C-w" 'message-goto-fcc)
1301   (define-key message-mode-map "\C-c\C-f\C-c" 'message-goto-cc)
1302   (define-key message-mode-map "\C-c\C-f\C-s" 'message-goto-subject)
1303   (define-key message-mode-map "\C-c\C-f\C-r" 'message-goto-reply-to)
1304   (define-key message-mode-map "\C-c\C-f\C-n" 'message-goto-newsgroups)
1305   (define-key message-mode-map "\C-c\C-f\C-d" 'message-goto-distribution)
1306   (define-key message-mode-map "\C-c\C-f\C-f" 'message-goto-followup-to)
1307   (define-key message-mode-map "\C-c\C-f\C-k" 'message-goto-keywords)
1308   (define-key message-mode-map "\C-c\C-f\C-u" 'message-goto-summary)
1309   (define-key message-mode-map "\C-c\C-b" 'message-goto-body)
1310   (define-key message-mode-map "\C-c\C-i" 'message-goto-signature)
1311
1312   (define-key message-mode-map "\C-c\C-t" 'message-insert-to)
1313   (define-key message-mode-map "\C-c\C-n" 'message-insert-newsgroups)
1314
1315   (define-key message-mode-map "\C-c\C-y" 'message-yank-original)
1316   (define-key message-mode-map "\C-c\C-q" 'message-fill-yanked-message)
1317   (define-key message-mode-map "\C-c\C-w" 'message-insert-signature)
1318   (define-key message-mode-map "\C-c\C-r" 'message-caesar-buffer-body)
1319   (define-key message-mode-map "\C-c\C-o" 'message-sort-headers)
1320   (define-key message-mode-map "\C-c\M-r" 'message-rename-buffer)
1321
1322   (define-key message-mode-map "\C-c\C-c" 'message-send-and-exit)
1323   (define-key message-mode-map "\C-c\C-s" 'message-send)
1324   (define-key message-mode-map "\C-c\C-k" 'message-kill-buffer)
1325   (define-key message-mode-map "\C-c\C-d" 'message-dont-send)
1326
1327   (define-key message-mode-map "\C-c\C-e" 'message-elide-region)
1328   (define-key message-mode-map "\C-c\C-v" 'message-delete-not-region)
1329   (define-key message-mode-map "\C-c\C-z" 'message-kill-to-signature)
1330   (define-key message-mode-map "\M-\r" 'message-newline-and-reformat)
1331
1332   (define-key message-mode-map "\t" 'message-tab)
1333
1334   (define-key message-mode-map "\C-xk" 'message-mimic-kill-buffer))
1335
1336 (easy-menu-define
1337  message-mode-menu message-mode-map "Message Menu."
1338  '("Message"
1339    ["Sort Headers" message-sort-headers t]
1340    ["Yank Original" message-yank-original t]
1341    ["Fill Yanked Message" message-fill-yanked-message t]
1342    ["Insert Signature" message-insert-signature t]
1343    ["Caesar (rot13) Message" message-caesar-buffer-body t]
1344    ["Caesar (rot13) Region" message-caesar-region (mark t)]
1345    ["Elide Region" message-elide-region (mark t)]
1346    ["Delete Outside Region" message-delete-not-region (mark t)]
1347    ["Kill To Signature" message-kill-to-signature t]
1348    ["Newline and Reformat" message-newline-and-reformat t]
1349    ["Rename buffer" message-rename-buffer t]
1350    ["Spellcheck" ispell-message t]
1351    "----"
1352    ["Send Message" message-send-and-exit t]
1353    ["Abort Message" message-dont-send t]
1354    ["Kill Message" message-kill-buffer t]))
1355
1356 (easy-menu-define
1357  message-mode-field-menu message-mode-map ""
1358  '("Field"
1359    ["Fetch To" message-insert-to t]
1360    ["Fetch Newsgroups" message-insert-newsgroups t]
1361    "----"
1362    ["To" message-goto-to t]
1363    ["Subject" message-goto-subject t]
1364    ["Cc" message-goto-cc t]
1365    ["Reply-To" message-goto-reply-to t]
1366    ["Summary" message-goto-summary t]
1367    ["Keywords" message-goto-keywords t]
1368    ["Newsgroups" message-goto-newsgroups t]
1369    ["Followup-To" message-goto-followup-to t]
1370    ["Distribution" message-goto-distribution t]
1371    ["Body" message-goto-body t]
1372    ["Signature" message-goto-signature t]))
1373
1374 (defvar facemenu-add-face-function)
1375 (defvar facemenu-remove-face-function)
1376
1377 ;;;###autoload
1378 (defun message-mode ()
1379   "Major mode for editing mail and news to be sent.
1380 Like Text Mode but with these additional commands:
1381 C-c C-s  message-send (send the message)    C-c C-c  message-send-and-exit
1382 C-c C-f  move to a header field (and create it if there isn't):
1383          C-c C-f C-t  move to To        C-c C-f C-s  move to Subject
1384          C-c C-f C-c  move to Cc        C-c C-f C-b  move to Bcc
1385          C-c C-f C-w  move to Fcc       C-c C-f C-r  move to Reply-To
1386          C-c C-f C-u  move to Summary   C-c C-f C-n  move to Newsgroups
1387          C-c C-f C-k  move to Keywords  C-c C-f C-d  move to Distribution
1388          C-c C-f C-f  move to Followup-To
1389 C-c C-t  message-insert-to (add a To header to a news followup)
1390 C-c C-n  message-insert-newsgroups (add a Newsgroup header to a news reply)
1391 C-c C-b  message-goto-body (move to beginning of message text).
1392 C-c C-i  message-goto-signature (move to the beginning of the signature).
1393 C-c C-w  message-insert-signature (insert `message-signature-file' file).
1394 C-c C-y  message-yank-original (insert current message, if any).
1395 C-c C-q  message-fill-yanked-message (fill what was yanked).
1396 C-c C-e  message-elide-region (elide the text between point and mark).
1397 C-c C-z  message-kill-to-signature (kill the text up to the signature).
1398 C-c C-r  message-caesar-buffer-body (rot13 the message body)."
1399   (interactive)
1400   (kill-all-local-variables)
1401   (make-local-variable 'message-reply-buffer)
1402   (setq message-reply-buffer nil)
1403   (make-local-variable 'message-send-actions) 
1404   (make-local-variable 'message-exit-actions) 
1405   (make-local-variable 'message-kill-actions)
1406   (make-local-variable 'message-postpone-actions)
1407   (make-local-variable 'message-draft-article)
1408   (make-local-hook 'kill-buffer-hook)
1409   (set-syntax-table message-mode-syntax-table)
1410   (use-local-map message-mode-map)
1411   (setq local-abbrev-table message-mode-abbrev-table)
1412   (setq major-mode 'message-mode)
1413   (setq mode-name "Message")
1414   (setq buffer-offer-save t)
1415   (make-local-variable 'facemenu-add-face-function)
1416   (make-local-variable 'facemenu-remove-face-function)
1417   (setq facemenu-add-face-function
1418         (lambda (face end)
1419           (let ((face-fun (cdr (assq face message-face-alist))))
1420             (if face-fun
1421                 (funcall face-fun (point) end)
1422               (error "Face %s not configured for %s mode" face mode-name)))
1423           "")
1424         facemenu-remove-face-function t)
1425   (make-local-variable 'paragraph-separate)
1426   (make-local-variable 'paragraph-start)
1427   ;; `-- ' precedes the signature.  `-----' appears at the start of the
1428   ;; lines that delimit forwarded messages.
1429   ;; Lines containing just >= 3 dashes, perhaps after whitespace,
1430   ;; are also sometimes used and should be separators.
1431   (setq paragraph-start
1432         (concat (regexp-quote mail-header-separator)
1433                 "$\\|[ \t]*[a-z0-9A-Z]*>+[ \t]*$\\|[ \t]*$\\|"
1434                 "-- $\\|---+$\\|"
1435                 page-delimiter
1436                 ;;!!! Uhm... shurely this can't be right?
1437                 "[> " (regexp-quote message-yank-prefix) "]+$"))
1438   (setq paragraph-separate paragraph-start)
1439   (make-local-variable 'message-reply-headers)
1440   (setq message-reply-headers nil)
1441   (make-local-variable 'message-user-agent)
1442   (make-local-variable 'message-post-method)
1443   (make-local-variable 'message-sent-message-via)
1444   (setq message-sent-message-via nil)
1445   (make-local-variable 'message-checksum)
1446   (setq message-checksum nil)
1447   (make-local-variable 'message-parameter-alist)
1448   (setq message-parameter-alist
1449         (copy-sequence message-startup-parameter-alist))
1450   ;;(when (fboundp 'mail-hist-define-keys)
1451   ;;  (mail-hist-define-keys))
1452   (when (string-match "XEmacs\\|Lucid" emacs-version)
1453     (message-setup-toolbar))
1454   (easy-menu-add message-mode-menu message-mode-map)
1455   (easy-menu-add message-mode-field-menu message-mode-map)
1456   ;; Allow mail alias things.
1457   (when (eq message-mail-alias-type 'abbrev)
1458     (if (fboundp 'mail-abbrevs-setup)
1459         (mail-abbrevs-setup)
1460       (mail-aliases-setup)))
1461   (message-set-auto-save-file-name)
1462   (unless (string-match "XEmacs" emacs-version)
1463     (set (make-local-variable 'font-lock-defaults)
1464          '(message-font-lock-keywords t)))
1465   (make-local-variable 'adaptive-fill-regexp)
1466   (setq adaptive-fill-regexp
1467         (concat "[ \t]*[-a-z0-9A-Z]*>+[ \t]*\\|" adaptive-fill-regexp))
1468   (unless (boundp 'adaptive-fill-first-line-regexp)
1469     (setq adaptive-fill-first-line-regexp nil))
1470   (make-local-variable 'adaptive-fill-first-line-regexp)
1471   (setq adaptive-fill-first-line-regexp
1472         (concat "[ \t]*[-a-z0-9A-Z]*>+[ \t]*\\|"
1473                 adaptive-fill-first-line-regexp))
1474   (run-hooks 'text-mode-hook 'message-mode-hook))
1475
1476 \f
1477
1478 ;;;
1479 ;;; Message mode commands
1480 ;;;
1481
1482 ;;; Movement commands
1483
1484 (defun message-goto-to ()
1485   "Move point to the To header."
1486   (interactive)
1487   (message-position-on-field "To"))
1488
1489 (defun message-goto-subject ()
1490   "Move point to the Subject header."
1491   (interactive)
1492   (message-position-on-field "Subject"))
1493
1494 (defun message-goto-cc ()
1495   "Move point to the Cc header."
1496   (interactive)
1497   (message-position-on-field "Cc" "To"))
1498
1499 (defun message-goto-bcc ()
1500   "Move point to the Bcc  header."
1501   (interactive)
1502   (message-position-on-field "Bcc" "Cc" "To"))
1503
1504 (defun message-goto-fcc ()
1505   "Move point to the Fcc header."
1506   (interactive)
1507   (message-position-on-field "Fcc" "To" "Newsgroups"))
1508
1509 (defun message-goto-reply-to ()
1510   "Move point to the Reply-To header."
1511   (interactive)
1512   (message-position-on-field "Reply-To" "Subject"))
1513
1514 (defun message-goto-newsgroups ()
1515   "Move point to the Newsgroups header."
1516   (interactive)
1517   (message-position-on-field "Newsgroups"))
1518
1519 (defun message-goto-distribution ()
1520   "Move point to the Distribution header."
1521   (interactive)
1522   (message-position-on-field "Distribution"))
1523
1524 (defun message-goto-followup-to ()
1525   "Move point to the Followup-To header."
1526   (interactive)
1527   (message-position-on-field "Followup-To" "Newsgroups"))
1528
1529 (defun message-goto-keywords ()
1530   "Move point to the Keywords header."
1531   (interactive)
1532   (message-position-on-field "Keywords" "Subject"))
1533
1534 (defun message-goto-summary ()
1535   "Move point to the Summary header."
1536   (interactive)
1537   (message-position-on-field "Summary" "Subject"))
1538
1539 (defun message-goto-body ()
1540   "Move point to the beginning of the message body."
1541   (interactive)
1542   (if (looking-at "[ \t]*\n") (expand-abbrev))
1543   (goto-char (point-min))
1544   (search-forward (concat "\n" mail-header-separator "\n") nil t))
1545
1546 (defun message-goto-eoh ()
1547   "Move point to the end of the headers."
1548   (interactive)
1549   (message-goto-body)
1550   (forward-line -2))
1551
1552 (defun message-goto-signature ()
1553   "Move point to the beginning of the message signature.
1554 If there is no signature in the article, go to the end and
1555 return nil."
1556   (interactive)
1557   (goto-char (point-min))
1558   (if (re-search-forward message-signature-separator nil t)
1559       (forward-line 1)
1560     (goto-char (point-max))
1561     nil))
1562
1563 \f
1564
1565 (defun message-insert-to (&optional force)
1566   "Insert a To header that points to the author of the article being replied to.
1567 If the original author requested not to be sent mail, the function signals
1568 an error.
1569 With the prefix argument FORCE, insert the header anyway."
1570   (interactive "P")
1571   (let ((co (message-fetch-reply-field "mail-copies-to")))
1572     (when (and (null force)
1573                co
1574                (equal (downcase co) "never"))
1575       (error "The user has requested not to have copies sent via mail")))
1576   (when (and (message-position-on-field "To")
1577              (mail-fetch-field "to")
1578              (not (string-match "\\` *\\'" (mail-fetch-field "to"))))
1579     (insert ", "))
1580   (insert (or (message-fetch-reply-field "reply-to")
1581               (message-fetch-reply-field "from") "")))
1582
1583 (defun message-insert-newsgroups ()
1584   "Insert the Newsgroups header from the article being replied to."
1585   (interactive)
1586   (when (and (message-position-on-field "Newsgroups")
1587              (mail-fetch-field "newsgroups")
1588              (not (string-match "\\` *\\'" (mail-fetch-field "newsgroups"))))
1589     (insert ","))
1590   (insert (or (message-fetch-reply-field "newsgroups") "")))
1591
1592 \f
1593
1594 ;;; Various commands
1595
1596 (defun message-delete-not-region (beg end)
1597   "Delete everything in the body of the current message that is outside of the region."
1598   (interactive "r")
1599   (save-excursion
1600     (goto-char end)
1601     (delete-region (point) (if (not (message-goto-signature))
1602                                (point)
1603                              (forward-line -2)
1604                              (point)))
1605     (insert "\n")
1606     (goto-char beg)
1607     (delete-region beg (progn (message-goto-body)
1608                               (forward-line 2)
1609                               (point))))
1610   (when (message-goto-signature)
1611     (forward-line -2)))
1612
1613 (defun message-kill-to-signature ()
1614   "Deletes all text up to the signature."
1615   (interactive)
1616   (let ((point (point)))
1617     (message-goto-signature)
1618     (unless (eobp)
1619       (forward-line -2))
1620     (kill-region point (point))
1621     (unless (bolp)
1622       (insert "\n"))))
1623
1624 (defun message-newline-and-reformat ()
1625   "Insert four newlines, and then reformat if inside quoted text."
1626   (interactive)
1627   (let ((point (point))
1628         quoted)
1629     (save-excursion
1630       (beginning-of-line)
1631       (setq quoted (looking-at (regexp-quote message-yank-prefix))))
1632     (insert "\n\n\n\n")
1633     (when quoted
1634       (insert message-yank-prefix))
1635     (fill-paragraph nil)
1636     (goto-char point)
1637     (forward-line 2)))
1638
1639 (defun message-insert-signature (&optional force)
1640   "Insert a signature.  See documentation for the `message-signature' variable."
1641   (interactive (list 0))
1642   (let* ((signature
1643           (cond
1644            ((and (null message-signature)
1645                  (eq force 0))
1646             (save-excursion
1647               (goto-char (point-max))
1648               (not (re-search-backward
1649                     message-signature-separator nil t))))
1650            ((and (null message-signature)
1651                  force)
1652             t)
1653            ((message-functionp message-signature)
1654             (funcall message-signature))
1655            ((listp message-signature)
1656             (eval message-signature))
1657            (t message-signature)))
1658          (signature
1659           (cond ((stringp signature)
1660                  signature)
1661                 ((and (eq t signature)
1662                       message-signature-file
1663                       (file-exists-p message-signature-file))
1664                  signature))))
1665     (when signature
1666       (goto-char (point-max))
1667       ;; Insert the signature.
1668       (unless (bolp)
1669         (insert "\n"))
1670       (insert "\n-- \n")
1671       (if (eq signature t)
1672           (insert-file-contents message-signature-file)
1673         (insert signature))
1674       (goto-char (point-max))
1675       (or (bolp) (insert "\n")))))
1676
1677 (defun message-elide-region (b e)
1678   "Elide the text between point and mark.
1679 An ellipsis (from `message-elide-elipsis') will be inserted where the
1680 text was killed."
1681   (interactive "r")
1682   (kill-region b e)
1683   (unless (bolp)
1684     (insert "\n"))
1685   (insert message-elide-elipsis))
1686
1687 (defvar message-caesar-translation-table nil)
1688
1689 (defun message-caesar-region (b e &optional n)
1690   "Caesar rotation of region by N, default 13, for decrypting netnews."
1691   (interactive
1692    (list
1693     (min (point) (or (mark t) (point)))
1694     (max (point) (or (mark t) (point)))
1695     (when current-prefix-arg
1696       (prefix-numeric-value current-prefix-arg))))
1697
1698   (setq n (if (numberp n) (mod n 26) 13)) ;canonize N
1699   (unless (or (zerop n)                 ; no action needed for a rot of 0
1700               (= b e))                  ; no region to rotate
1701     ;; We build the table, if necessary.
1702     (when (or (not message-caesar-translation-table)
1703               (/= (aref message-caesar-translation-table ?a) (+ ?a n)))
1704         (setq message-caesar-translation-table
1705               (message-make-caesar-translation-table n)))
1706     ;; Then we translate the region.  Do it this way to retain
1707     ;; text properties.
1708     (while (< b e)
1709       (subst-char-in-region
1710        b (1+ b) (char-after b)
1711        (aref message-caesar-translation-table (char-after b)))
1712       (incf b))))
1713
1714 (defun message-make-caesar-translation-table (n)
1715   "Create a rot table with offset N."
1716   (let ((i -1)
1717         (table (make-string 256 0)))
1718     (while (< (incf i) 256)
1719       (aset table i i))
1720     (concat
1721      (substring table 0 ?A)
1722      (substring table (+ ?A n) (+ ?A n (- 26 n)))
1723      (substring table ?A (+ ?A n))
1724      (substring table (+ ?A 26) ?a)
1725      (substring table (+ ?a n) (+ ?a n (- 26 n)))
1726      (substring table ?a (+ ?a n))
1727      (substring table (+ ?a 26) 255))))
1728
1729 (defun message-caesar-buffer-body (&optional rotnum)
1730   "Caesar rotates all letters in the current buffer by 13 places.
1731 Used to encode/decode possiblyun offensive messages (commonly in net.jokes).
1732 With prefix arg, specifies the number of places to rotate each letter forward.
1733 Mail and USENET news headers are not rotated."
1734   (interactive (if current-prefix-arg
1735                    (list (prefix-numeric-value current-prefix-arg))
1736                  (list nil)))
1737   (save-excursion
1738     (save-restriction
1739       (when (message-goto-body)
1740         (narrow-to-region (point) (point-max)))
1741       (message-caesar-region (point-min) (point-max) rotnum))))
1742
1743 (defun message-pipe-buffer-body (program)
1744   "Pipe the message body in the current buffer through PROGRAM."
1745   (save-excursion
1746     (save-restriction
1747       (when (message-goto-body)
1748         (narrow-to-region (point) (point-max)))
1749       (let ((body (buffer-substring (point-min) (point-max))))
1750         (unless (equal 0 (call-process-region
1751                            (point-min) (point-max) program t t))
1752             (insert body)
1753             (message "%s failed." program))))))
1754
1755 (defun message-rename-buffer (&optional enter-string)
1756   "Rename the *message* buffer to \"*message* RECIPIENT\".
1757 If the function is run with a prefix, it will ask for a new buffer
1758 name, rather than giving an automatic name."
1759   (interactive "Pbuffer name: ")
1760   (save-excursion
1761     (save-restriction
1762       (goto-char (point-min))
1763       (narrow-to-region (point)
1764                         (search-forward mail-header-separator nil 'end))
1765       (let* ((mail-to (or
1766                        (if (message-news-p) (message-fetch-field "Newsgroups")
1767                          (message-fetch-field "To"))
1768                        ""))
1769              (mail-trimmed-to
1770               (if (string-match "," mail-to)
1771                   (concat (substring mail-to 0 (match-beginning 0)) ", ...")
1772                 mail-to))
1773              (name-default (concat "*message* " mail-trimmed-to))
1774              (name (if enter-string
1775                        (read-string "New buffer name: " name-default)
1776                      name-default)))
1777         (rename-buffer name t)))))
1778
1779 (defun message-fill-yanked-message (&optional justifyp)
1780   "Fill the paragraphs of a message yanked into this one.
1781 Numeric argument means justify as well."
1782   (interactive "P")
1783   (save-excursion
1784     (goto-char (point-min))
1785     (search-forward (concat "\n" mail-header-separator "\n") nil t)
1786     (let ((fill-prefix message-yank-prefix))
1787       (fill-individual-paragraphs (point) (point-max) justifyp t))))
1788
1789 (defun message-indent-citation ()
1790   "Modify text just inserted from a message to be cited.
1791 The inserted text should be the region.
1792 When this function returns, the region is again around the modified text.
1793
1794 Normally, indent each nonblank line `message-indentation-spaces' spaces.
1795 However, if `message-yank-prefix' is non-nil, insert that prefix on each line."
1796   (let ((start (point)))
1797     ;; Remove unwanted headers.
1798     (when message-ignored-cited-headers
1799       (let (all-removed)
1800         (save-restriction
1801           (narrow-to-region
1802            (goto-char start)
1803            (if (search-forward "\n\n" nil t)
1804                (1- (point))
1805              (point)))
1806           (message-remove-header message-ignored-cited-headers t)
1807           (when (= (point-min) (point-max))
1808             (setq all-removed t))
1809           (goto-char (point-max)))
1810         (if all-removed
1811             (goto-char start)
1812           (forward-line 1))))
1813     ;; Delete blank lines at the start of the buffer.
1814     (while (and (point-min)
1815                 (eolp)
1816                 (not (eobp)))
1817       (message-delete-line))
1818     ;; Delete blank lines at the end of the buffer.
1819     (goto-char (point-max))
1820     (unless (eolp)
1821       (insert "\n"))
1822     (while (and (zerop (forward-line -1))
1823                 (looking-at "$"))
1824       (message-delete-line))
1825     ;; Do the indentation.
1826     (if (null message-yank-prefix)
1827         (indent-rigidly start (mark t) message-indentation-spaces)
1828       (save-excursion
1829         (goto-char start)
1830         (while (< (point) (mark t))
1831           (insert message-yank-prefix)
1832           (forward-line 1))))
1833     (goto-char start)))
1834
1835 (defvar gnus-article-copy)
1836 (defun message-yank-original (&optional arg)
1837   "Insert the message being replied to, if any.
1838 Puts point before the text and mark after.
1839 Normally indents each nonblank line ARG spaces (default 3).  However,
1840 if `message-yank-prefix' is non-nil, insert that prefix on each line.
1841
1842 This function uses `message-cite-function' to do the actual citing.
1843
1844 Just \\[universal-argument] as argument means don't indent, insert no
1845 prefix, and don't delete any headers."
1846   (interactive "P")
1847   (let ((modified (buffer-modified-p))
1848         (buffer (message-eval-parameter message-reply-buffer)))
1849     (when (and buffer
1850                message-cite-function)
1851       (delete-windows-on buffer t)
1852       (insert-buffer buffer)
1853       (funcall message-cite-function)
1854       (message-exchange-point-and-mark)
1855       (unless (bolp)
1856         (insert ?\n))
1857       (unless modified
1858         (setq message-checksum (message-checksum))))))
1859
1860 (defun message-cite-original-without-signature ()
1861   "Cite function in the standard Message manner."
1862   (let ((start (point))
1863         (end (mark t))
1864         (functions
1865          (when message-indent-citation-function
1866            (if (listp message-indent-citation-function)
1867                message-indent-citation-function
1868              (list message-indent-citation-function)))))
1869     (goto-char end)
1870     (when (re-search-backward "^-- $" start t)
1871       ;; Also peel off any blank lines before the signature.
1872       (forward-line -1)
1873       (while (looking-at "^[ \t]*$")
1874         (forward-line -1))
1875       (forward-line 1)
1876       (delete-region (point) end))
1877     (goto-char start)
1878     (while functions
1879       (funcall (pop functions)))
1880     (when message-citation-line-function
1881       (unless (bolp)
1882         (insert "\n"))
1883       (funcall message-citation-line-function))))
1884
1885 (defvar mail-citation-hook) ;Compiler directive
1886 (defun message-cite-original ()
1887   "Cite function in the standard Message manner."
1888   (if (and (boundp 'mail-citation-hook)
1889            mail-citation-hook)
1890       (run-hooks 'mail-citation-hook)
1891     (let ((start (point))
1892           (functions
1893            (when message-indent-citation-function
1894              (if (listp message-indent-citation-function)
1895                  message-indent-citation-function
1896                (list message-indent-citation-function)))))
1897       (goto-char start)
1898       (while functions
1899         (funcall (pop functions)))
1900       (when message-citation-line-function
1901         (unless (bolp)
1902           (insert "\n"))
1903         (funcall message-citation-line-function)))))
1904
1905 (defun message-insert-citation-line ()
1906   "Function that inserts a simple citation line."
1907   (when message-reply-headers
1908     (insert (mail-header-from message-reply-headers) " writes:\n\n")))
1909
1910 (defun message-position-on-field (header &rest afters)
1911   (let ((case-fold-search t))
1912     (save-restriction
1913       (narrow-to-region
1914        (goto-char (point-min))
1915        (progn
1916          (re-search-forward
1917           (concat "^" (regexp-quote mail-header-separator) "$"))
1918          (match-beginning 0)))
1919       (goto-char (point-min))
1920       (if (re-search-forward (concat "^" (regexp-quote header) ":") nil t)
1921           (progn
1922             (re-search-forward "^[^ \t]" nil 'move)
1923             (beginning-of-line)
1924             (skip-chars-backward "\n")
1925             t)
1926         (while (and afters
1927                     (not (re-search-forward
1928                           (concat "^" (regexp-quote (car afters)) ":")
1929                           nil t)))
1930           (pop afters))
1931         (when afters
1932           (re-search-forward "^[^ \t]" nil 'move)
1933           (beginning-of-line))
1934         (insert header ": \n")
1935         (forward-char -1)
1936         nil))))
1937
1938 (defun message-remove-signature ()
1939   "Remove the signature from the text between point and mark.
1940 The text will also be indented the normal way."
1941   (save-excursion
1942     (let ((start (point))
1943           mark)
1944       (if (not (re-search-forward message-signature-separator (mark t) t))
1945           ;; No signature here, so we just indent the cited text.
1946           (message-indent-citation)
1947         ;; Find the last non-empty line.
1948         (forward-line -1)
1949         (while (looking-at "[ \t]*$")
1950           (forward-line -1))
1951         (forward-line 1)
1952         (setq mark (set-marker (make-marker) (point)))
1953         (goto-char start)
1954         (message-indent-citation)
1955         ;; Enable undoing the deletion.
1956         (undo-boundary)
1957         (delete-region mark (mark t))
1958         (set-marker mark nil)))))
1959
1960 \f
1961
1962 ;;;
1963 ;;; Sending messages
1964 ;;;
1965
1966 ;; Avoid byte-compile warning.
1967 (defvar message-encoding-buffer nil)
1968 (defvar message-edit-buffer nil)
1969 (defvar message-mime-mode nil)
1970
1971 (defun message-send-and-exit (&optional arg)
1972   "Send message like `message-send', then, if no errors, exit from mail buffer."
1973   (interactive "P")
1974   (let ((buf (current-buffer))
1975         (actions message-exit-actions)
1976         (frame (selected-frame))
1977         (org-frame message-original-frame))
1978     (when (and (message-send arg)
1979                (buffer-name buf))
1980       (if message-kill-buffer-on-exit
1981           (kill-buffer buf)
1982         (bury-buffer buf)
1983         (when (eq buf (current-buffer))
1984           (message-bury buf)))
1985       (message-do-actions actions)
1986       (message-delete-frame frame org-frame)
1987       t)))
1988
1989 (defun message-dont-send ()
1990   "Don't send the message you have been editing."
1991   (interactive)
1992   (set-buffer-modified-p t)
1993   (save-buffer)
1994   (let ((actions message-postpone-actions)
1995         (frame (selected-frame))
1996         (org-frame message-original-frame))
1997     (message-bury (current-buffer))
1998     (message-do-actions actions)
1999     (message-delete-frame frame org-frame)))
2000
2001 (defun message-kill-buffer ()
2002   "Kill the current buffer."
2003   (interactive)
2004   (when (or (not (buffer-modified-p))
2005             (eq t message-kill-buffer-query-function)
2006             (funcall message-kill-buffer-query-function
2007                      "The buffer modified; kill anyway? "))
2008     (let ((actions message-kill-actions)
2009           (frame (selected-frame))
2010           (org-frame message-original-frame))
2011       (setq buffer-file-name nil)
2012       (kill-buffer (current-buffer))
2013       (message-do-actions actions)
2014       (message-delete-frame frame org-frame)))
2015   (message ""))
2016
2017 (defun message-mimic-kill-buffer ()
2018   "Kill the current buffer with query."
2019   (interactive)
2020   (unless (eq 'message-mode major-mode)
2021     (error "%s must be invoked from a message buffer." this-command))
2022   (let ((command this-command)
2023         (bufname (read-buffer (format "Kill buffer: (default %s) "
2024                                       (buffer-name)))))
2025     (if (or (not bufname)
2026             (string-equal bufname "")
2027             (string-equal bufname (buffer-name)))
2028         (let ((message-delete-frame-on-exit nil))
2029           (message-kill-buffer))
2030       (message "%s must be invoked only for the current buffer." command))))
2031
2032 (defun message-delete-frame (frame org-frame)
2033   "Delete frame for editing message."
2034   (when (and (or (and (featurep 'xemacs)
2035                       (not (eq 'tty (device-type))))
2036                  window-system
2037                  (>= emacs-major-version 20))
2038              (or (and (eq message-delete-frame-on-exit t)
2039                       (select-frame frame)
2040                       (or (eq frame org-frame)
2041                           (prog1
2042                               (y-or-n-p "Delete this frame?")
2043                             (message ""))))
2044                  (and (eq message-delete-frame-on-exit 'ask)
2045                       (select-frame frame)
2046                       (prog1
2047                           (y-or-n-p "Delete this frame?")
2048                         (message "")))))
2049     (delete-frame frame)))
2050
2051 (defun message-bury (buffer)
2052   "Bury this mail buffer."
2053   (let ((newbuf (other-buffer buffer)))
2054     (bury-buffer buffer)
2055     (if (and (fboundp 'frame-parameters)
2056              (cdr (assq 'dedicated (frame-parameters)))
2057              (not (null (delq (selected-frame) (visible-frame-list)))))
2058         (delete-frame (selected-frame))
2059       (switch-to-buffer newbuf))))
2060
2061 (defun message-send (&optional arg)
2062   "Send the message in the current buffer.
2063 If `message-interactive' is non-nil, wait for success indication
2064 or error messages, and inform user.
2065 Otherwise any failure is reported in a message back to
2066 the user from the mailer."
2067   (interactive "P")
2068   ;; Disabled test.
2069   (when (or (buffer-modified-p)
2070             (message-check-element 'unchanged)
2071             (y-or-n-p "No changes in the buffer; really send? "))
2072     ;; Make it possible to undo the coming changes.
2073     (undo-boundary)
2074     (let ((inhibit-read-only t))
2075       (put-text-property (point-min) (point-max) 'read-only nil))
2076     (run-hooks 'message-send-hook)
2077     (message "Sending...")
2078     (let ((message-encoding-buffer
2079            (message-generate-new-buffer-clone-locals " message encoding"))
2080           (message-edit-buffer (current-buffer))
2081           (message-mime-mode mime-edit-mode-flag)
2082           (alist message-send-method-alist)
2083           (success t)
2084           elem sent)
2085       (save-excursion
2086         (set-buffer message-encoding-buffer)
2087         (erase-buffer)
2088         (insert-buffer message-edit-buffer)
2089         (funcall message-encode-function)
2090         (message-fix-before-sending)
2091         (while (and success
2092                     (setq elem (pop alist)))
2093           (when (and (or (not (funcall (cadr elem)))
2094                          (and (or (not (memq (car elem)
2095                                              message-sent-message-via))
2096                                   (y-or-n-p
2097                                    (format
2098                                     "Already sent message via %s; resend? "
2099                                     (car elem))))
2100                               (setq success (funcall (caddr elem) arg)))))
2101             (setq sent t))))
2102       (when (and success sent)
2103         (message-do-fcc)
2104         ;;(when (fboundp 'mail-hist-put-headers-into-history)
2105         ;; (mail-hist-put-headers-into-history))
2106         (run-hooks 'message-sent-hook)
2107         (message "Sending...done")
2108         ;; Mark the buffer as unmodified and delete auto-save.
2109         (set-buffer-modified-p nil)
2110         (delete-auto-save-file-if-necessary t)
2111         (message-disassociate-draft)
2112         ;; Delete other mail buffers and stuff.
2113         (message-do-send-housekeeping)
2114         (message-do-actions message-send-actions)
2115         ;; Return success.
2116         t))))
2117
2118 (defun message-send-via-mail (arg)
2119   "Send the current message via mail."
2120   (message-send-mail arg))
2121
2122 (defun message-send-via-news (arg)
2123   "Send the current message via news."
2124   (message-send-news arg))
2125
2126 (defun message-fix-before-sending ()
2127   "Do various things to make the message nice before sending it."
2128   ;; Make sure there's a newline at the end of the message.
2129   (goto-char (point-max))
2130   (unless (bolp)
2131     (insert "\n"))
2132   ;; Make all invisible text visible.
2133   ;;(when (text-property-any (point-min) (point-max) 'invisible t)
2134   ;;  (put-text-property (point-min) (point-max) 'invisible nil)
2135   ;;  (unless (yes-or-no-p "Invisible text found and made visible; continue posting?")
2136   ;;    (error "Invisible text found and made visible")))
2137   )
2138
2139 (defun message-add-action (action &rest types)
2140   "Add ACTION to be performed when doing an exit of type TYPES."
2141   (let (var)
2142     (while types
2143       (set (setq var (intern (format "message-%s-actions" (pop types))))
2144            (nconc (symbol-value var) (list action))))))
2145
2146 (defun message-do-actions (actions)
2147   "Perform all actions in ACTIONS."
2148   ;; Now perform actions on successful sending.
2149   (while actions
2150     (ignore-errors
2151       (cond
2152        ;; A simple function.
2153        ((message-functionp (car actions))
2154         (funcall (car actions)))
2155        ;; Something to be evaled.
2156        (t
2157         (eval (car actions)))))
2158     (pop actions)))
2159
2160 (defun message-send-mail (&optional arg)
2161   (require 'mail-utils)
2162   (let ((tembuf (message-generate-new-buffer-clone-locals " message temp"))
2163         (case-fold-search nil)
2164         (news (message-news-p)))
2165     (save-restriction
2166       (message-narrow-to-headers)
2167       ;; Insert some headers.
2168       (let ((message-deletable-headers
2169              (if news nil message-deletable-headers)))
2170         (message-generate-headers message-required-mail-headers))
2171       ;; Let the user do all of the above.
2172       (run-hooks 'message-header-hook)
2173       (when (functionp message-header-encode-function)
2174         (funcall message-header-encode-function))
2175       (run-hooks 'message-header-encoded-hook))
2176     (if (not (message-check-mail-syntax))
2177         (progn
2178           (message "")
2179           ;;(message "Posting not performed")
2180           nil)
2181       (unwind-protect
2182           (save-excursion
2183             (set-buffer tembuf)
2184             (erase-buffer)
2185             (insert-buffer message-encoding-buffer)
2186             ;; Remove some headers.
2187             (save-restriction
2188               (message-narrow-to-headers)
2189               (message-remove-header message-ignored-mail-headers t))
2190             (goto-char (point-max))
2191             ;; require one newline at the end.
2192             (or (= (preceding-char) ?\n)
2193                 (insert ?\n))
2194             (when (and news
2195                        (or (message-fetch-field "cc")
2196                            (message-fetch-field "to")))
2197               (message-insert-courtesy-copy))
2198             (mime-edit-maybe-split-and-send
2199              (function
2200               (lambda ()
2201                 (interactive)
2202                 (funcall message-send-mail-function)
2203                 )))
2204             (funcall message-send-mail-function))
2205         (kill-buffer tembuf))
2206       (set-buffer message-edit-buffer)
2207       (push 'mail message-sent-message-via))))
2208
2209 (defun message-send-mail-with-sendmail ()
2210   "Send off the prepared buffer with sendmail."
2211   (let ((errbuf (if message-interactive
2212                     (generate-new-buffer " sendmail errors")
2213                   0))
2214         resend-to-addresses delimline)
2215     (let ((case-fold-search t))
2216       (save-restriction
2217         (message-narrow-to-headers)
2218         (setq resend-to-addresses (message-fetch-field "resent-to")))
2219       ;; Change header-delimiter to be what sendmail expects.
2220       (goto-char (point-min))
2221       (re-search-forward
2222        (concat "^" (regexp-quote mail-header-separator) "\n"))
2223       (replace-match "\n")
2224       (backward-char 1)
2225       (setq delimline (point-marker))
2226       (run-hooks 'message-send-mail-hook)
2227       ;; Insert an extra newline if we need it to work around
2228       ;; Sun's bug that swallows newlines.
2229       (goto-char (1+ delimline))
2230       (when (eval message-mailer-swallows-blank-line)
2231         (newline))
2232       (when message-interactive
2233         (save-excursion
2234           (set-buffer errbuf)
2235           (erase-buffer))))
2236     (let ((default-directory "/"))
2237       (as-binary-process
2238        (apply 'call-process-region
2239               (append (list (point-min) (point-max)
2240                             (if (boundp 'sendmail-program)
2241                                 sendmail-program
2242                               "/usr/lib/sendmail")
2243                             nil errbuf nil "-oi")
2244                       ;; Always specify who from,
2245                       ;; since some systems have broken sendmails.
2246                       ;; But some systems are more broken with -f, so
2247                       ;; we'll let users override this.
2248                       (if (null message-sendmail-f-is-evil)
2249                           (list "-f" (user-login-name)))
2250                       ;; These mean "report errors by mail"
2251                       ;; and "deliver in background".
2252                       (if (null message-interactive) '("-oem" "-odb"))
2253                       ;; Get the addresses from the message
2254                       ;; unless this is a resend.
2255                       ;; We must not do that for a resend
2256                       ;; because we would find the original addresses.
2257                       ;; For a resend, include the specific addresses.
2258                       (if resend-to-addresses
2259                           (list resend-to-addresses)
2260                         '("-t"))))))
2261     (when message-interactive
2262       (save-excursion
2263         (set-buffer errbuf)
2264         (goto-char (point-min))
2265         (while (re-search-forward "\n\n* *" nil t)
2266           (replace-match "; "))
2267         (if (not (zerop (buffer-size)))
2268             (error "Sending...failed to %s"
2269                    (buffer-substring (point-min) (point-max)))))
2270       (when (bufferp errbuf)
2271         (kill-buffer errbuf)))))
2272
2273 (defun message-send-mail-with-qmail ()
2274   "Pass the prepared message buffer to qmail-inject.
2275 Refer to the documentation for the variable `message-send-mail-function'
2276 to find out how to use this."
2277   ;; replace the header delimiter with a blank line
2278   (goto-char (point-min))
2279   (re-search-forward
2280    (concat "^" (regexp-quote mail-header-separator) "\n"))
2281   (replace-match "\n")
2282   (run-hooks 'message-send-mail-hook)
2283   ;; send the message
2284   (case
2285       (as-binary-process
2286        (apply
2287         'call-process-region 1 (point-max) message-qmail-inject-program
2288         nil nil nil
2289         ;; qmail-inject's default behaviour is to look for addresses on the
2290         ;; command line; if there're none, it scans the headers.
2291         ;; yes, it does The Right Thing w.r.t. Resent-To and it's kin.
2292         ;;
2293         ;; in general, ALL of qmail-inject's defaults are perfect for simply
2294         ;; reading a formatted (i. e., at least a To: or Resent-To header)
2295         ;; message from stdin.
2296         ;;
2297         ;; qmail also has the advantage of not having been raped by
2298         ;; various vendors, so we don't have to allow for that, either --
2299         ;; compare this with message-send-mail-with-sendmail and weep
2300         ;; for sendmail's lost innocence.
2301         ;;
2302         ;; all this is way cool coz it lets us keep the arguments entirely
2303         ;; free for -inject-arguments -- a big win for the user and for us
2304         ;; since we don't have to play that double-guessing game and the user
2305         ;; gets full control (no gestapo'ish -f's, for instance).  --sj
2306         message-qmail-inject-args))
2307     ;; qmail-inject doesn't say anything on it's stdout/stderr,
2308     ;; we have to look at the retval instead
2309     (0 nil)
2310     (1   (error "qmail-inject reported permanent failure"))
2311     (111 (error "qmail-inject reported transient failure"))
2312     ;; should never happen
2313     (t   (error "qmail-inject reported unknown failure"))))
2314
2315 (defun message-send-mail-with-mh ()
2316   "Send the prepared message buffer with mh."
2317   (let ((mh-previous-window-config nil)
2318         (name (mh-new-draft-name)))
2319     (setq buffer-file-name name)
2320     ;; MH wants to generate these headers itself.
2321     (when message-mh-deletable-headers
2322       (let ((headers message-mh-deletable-headers))
2323         (while headers
2324           (goto-char (point-min))
2325           (and (re-search-forward
2326                 (concat "^" (symbol-name (car headers)) ": *") nil t)
2327                (message-delete-line))
2328           (pop headers))))
2329     (run-hooks 'message-send-mail-hook)
2330     ;; Pass it on to mh.
2331     (mh-send-letter)))
2332
2333 (defun message-send-mail-with-smtp ()
2334   "Send off the prepared buffer with SMTP."
2335   (require 'smtp) ; XXX
2336   (let ((case-fold-search t)
2337         recipients)
2338     (save-restriction
2339       (message-narrow-to-headers)
2340       (setq recipients
2341             ;; XXX: Should be replaced by better one.
2342             (smtp-deduce-address-list (current-buffer)
2343                                       (point-min) (point-max)))
2344       ;; Remove BCC lines.
2345       (message-remove-header "bcc"))
2346     ;; replace the header delimiter with a blank line.
2347     (goto-char (point-min))
2348     (re-search-forward
2349      (concat "^" (regexp-quote mail-header-separator) "\n"))
2350     (replace-match "\n")
2351     (backward-char 1)
2352     (run-hooks 'message-send-mail-hook)
2353     (if recipients
2354         (let ((result (smtp-via-smtp user-mail-address
2355                                      recipients
2356                                      (current-buffer))))
2357           (unless (eq result t)
2358             (error "Sending failed; " result)))
2359       (error "Sending failed; no recipients"))))
2360
2361 (defun message-send-news (&optional arg)
2362   (let ((tembuf (message-generate-new-buffer-clone-locals " *message temp*"))
2363         (case-fold-search nil)
2364         (method (if (message-functionp message-post-method)
2365                     (funcall message-post-method arg)
2366                   message-post-method))
2367         (message-syntax-checks
2368          (if arg
2369              (cons '(existing-newsgroups . disabled)
2370                    message-syntax-checks)
2371            message-syntax-checks))
2372         result)
2373     (save-restriction
2374       (message-narrow-to-headers)
2375       ;; Insert some headers.
2376       (message-generate-headers message-required-news-headers)
2377       ;; Let the user do all of the above.
2378       (run-hooks 'message-header-hook)
2379       (when (functionp message-header-encode-function)
2380         (funcall message-header-encode-function))
2381       (run-hooks 'message-header-encoded-hook))
2382     (message-cleanup-headers)
2383     (if (not (message-check-news-syntax))
2384         (progn
2385           (message "")
2386           ;;(message "Posting not performed")
2387           nil)
2388       (unwind-protect
2389           (save-excursion
2390             (set-buffer tembuf)
2391             (buffer-disable-undo (current-buffer))
2392             (erase-buffer)
2393             (insert-buffer message-encoding-buffer)
2394             ;; Remove some headers.
2395             (save-restriction
2396               (message-narrow-to-headers)
2397               ;; Remove some headers.
2398               (message-remove-header message-ignored-news-headers t))
2399             (goto-char (point-max))
2400             ;; require one newline at the end.
2401             (or (= (preceding-char) ?\n)
2402                 (insert ?\n))
2403             (mime-edit-maybe-split-and-send
2404              (function
2405               (lambda ()
2406                 (interactive)
2407                 (save-restriction
2408                   (std11-narrow-to-header mail-header-separator)
2409                   (goto-char (point-min))
2410                   (when (re-search-forward "^Message-Id:" nil t)
2411                     (delete-region (match-end 0)(std11-field-end))
2412                     (insert (concat " " (message-make-message-id)))
2413                     ))
2414                 (funcall message-send-news-function method)
2415                 )))
2416             (setq result (funcall message-send-news-function method)))
2417         (kill-buffer tembuf))
2418       (set-buffer message-edit-buffer)
2419       (if result
2420           (push 'news message-sent-message-via)
2421         (message "Couldn't send message via news: %s"
2422                  (nnheader-get-report (car method)))
2423         nil))))
2424
2425 ;; 1997-09-29 by MORIOKA Tomohiko
2426 (defun message-send-news-with-gnus (method)
2427   (let ((case-fold-search t))
2428     ;; Remove the delimiter.
2429     (goto-char (point-min))
2430     (re-search-forward
2431      (concat "^" (regexp-quote mail-header-separator) "\n"))
2432     (replace-match "\n")
2433     (backward-char 1)
2434     (run-hooks 'message-send-news-hook)
2435     ;;(require (car method))
2436     ;;(funcall (intern (format "%s-open-server" (car method)))
2437     ;;(cadr method) (cddr method))
2438     ;;(setq result
2439     ;;    (funcall (intern (format "%s-request-post" (car method)))
2440     ;;             (cadr method)))
2441     (gnus-open-server method)
2442     (gnus-request-post method)
2443     ))
2444
2445 ;;;
2446 ;;; Header generation & syntax checking.
2447 ;;;
2448
2449 (defmacro message-check (type &rest forms)
2450   "Eval FORMS if TYPE is to be checked."
2451   `(or (message-check-element ,type)
2452        (save-excursion
2453          ,@forms)))
2454
2455 (put 'message-check 'lisp-indent-function 1)
2456 (put 'message-check 'edebug-form-spec '(form body))
2457
2458 (defun message-check-element (type)
2459   "Returns non-nil if this type is not to be checked."
2460   (if (eq message-syntax-checks 'dont-check-for-anything-just-trust-me)
2461       t
2462     (let ((able (assq type message-syntax-checks)))
2463       (and (consp able)
2464            (eq (cdr able) 'disabled)))))
2465
2466 (defun message-check-news-syntax ()
2467   "Check the syntax of the message."
2468   (save-excursion
2469     (save-restriction
2470       (widen)
2471       (and
2472        ;; We narrow to the headers and check them first.
2473        (save-excursion
2474          (save-restriction
2475            (message-narrow-to-headers)
2476            (message-check-news-header-syntax)))
2477        ;; Check the body.
2478        (save-excursion
2479          (set-buffer message-edit-buffer)
2480          (message-check-news-body-syntax))))))
2481
2482 (defun message-check-news-header-syntax ()
2483   (and
2484    ;; Check the Subject header.
2485    (message-check 'subject
2486      (let* ((case-fold-search t)
2487             (subject (message-fetch-field "subject")))
2488        (or
2489         (and subject
2490              (not (string-match "\\`[ \t]*\\'" subject)))
2491         (ignore
2492          (message
2493           "The subject field is empty or missing.  Posting is denied.")))))
2494    ;; Check for commands in Subject.
2495    (message-check 'subject-cmsg
2496      (if (string-match "^cmsg " (message-fetch-field "subject"))
2497          (y-or-n-p
2498           "The control code \"cmsg\" is in the subject.  Really post? ")
2499        t))
2500    ;; Check for multiple identical headers.
2501    (message-check 'multiple-headers
2502      (let (found)
2503        (while (and (not found)
2504                    (re-search-forward "^[^ \t:]+: " nil t))
2505          (save-excursion
2506            (or (re-search-forward
2507                 (concat "^"
2508                         (regexp-quote
2509                          (setq found
2510                                (buffer-substring
2511                                 (match-beginning 0) (- (match-end 0) 2))))
2512                         ":")
2513                 nil t)
2514                (setq found nil))))
2515        (if found
2516            (y-or-n-p (format "Multiple %s headers.  Really post? " found))
2517          t)))
2518    ;; Check for Version and Sendsys.
2519    (message-check 'sendsys
2520      (if (re-search-forward "^Sendsys:\\|^Version:" nil t)
2521          (y-or-n-p
2522           (format "The article contains a %s command.  Really post? "
2523                   (buffer-substring (match-beginning 0)
2524                                     (1- (match-end 0)))))
2525        t))
2526    ;; See whether we can shorten Followup-To.
2527    (message-check 'shorten-followup-to
2528      (let ((newsgroups (message-fetch-field "newsgroups"))
2529            (followup-to (message-fetch-field "followup-to"))
2530            to)
2531        (when (and newsgroups
2532                   (string-match "," newsgroups)
2533                   (not followup-to)
2534                   (not
2535                    (zerop
2536                     (length
2537                      (setq to (completing-read
2538                                "Followups to: (default all groups) "
2539                                (mapcar (lambda (g) (list g))
2540                                        (cons "poster"
2541                                              (message-tokenize-header
2542                                               newsgroups)))))))))
2543          (goto-char (point-min))
2544          (insert "Followup-To: " to "\n"))
2545        t))
2546    ;; Check "Shoot me".
2547    (message-check 'shoot
2548      (if (re-search-forward
2549           "Message-ID.*.i-did-not-set--mail-host-address--so-shoot-me" nil t)
2550          (y-or-n-p "You appear to have a misconfigured system.  Really post? ")
2551        t))
2552    ;; Check for Approved.
2553    (message-check 'approved
2554      (if (re-search-forward "^Approved:" nil t)
2555          (y-or-n-p "The article contains an Approved header.  Really post? ")
2556        t))
2557    ;; Check the Message-ID header.
2558    (message-check 'message-id
2559      (let* ((case-fold-search t)
2560             (message-id (message-fetch-field "message-id" t)))
2561        (or (not message-id)
2562            ;; Is there an @ in the ID?
2563            (and (string-match "@" message-id)
2564                 ;; Is there a dot in the ID?
2565                 (string-match "@[^.]*\\." message-id)
2566                 ;; Does the ID end with a dot?
2567                 (not (string-match "\\.>" message-id)))
2568            (y-or-n-p
2569             (format "The Message-ID looks strange: \"%s\".  Really post? "
2570                     message-id)))))
2571    ;; Check the Newsgroups & Followup-To headers.
2572    (message-check 'existing-newsgroups
2573      (let* ((case-fold-search t)
2574             (newsgroups (message-fetch-field "newsgroups"))
2575             (followup-to (message-fetch-field "followup-to"))
2576             (groups (message-tokenize-header
2577                      (if followup-to
2578                          (concat newsgroups "," followup-to)
2579                        newsgroups)))
2580             (hashtb (and (boundp 'gnus-active-hashtb)
2581                          gnus-active-hashtb))
2582             errors)
2583        (if (or (not hashtb)
2584                (not (boundp 'gnus-read-active-file))
2585                (not gnus-read-active-file)
2586                (eq gnus-read-active-file 'some))
2587            t
2588          (while groups
2589            (when (and (not (boundp (intern (car groups) hashtb)))
2590                       (not (equal (car groups) "poster")))
2591              (push (car groups) errors))
2592            (pop groups))
2593          (if (not errors)
2594              t
2595            (y-or-n-p
2596             (format
2597              "Really post to %s unknown group%s: %s "
2598              (if (= (length errors) 1) "this" "these")
2599              (if (= (length errors) 1) "" "s")
2600              (mapconcat 'identity errors ", ")))))))
2601    ;; Check the Newsgroups & Followup-To headers for syntax errors.
2602    (message-check 'valid-newsgroups
2603      (let ((case-fold-search t)
2604            (headers '("Newsgroups" "Followup-To"))
2605            header error)
2606        (while (and headers (not error))
2607          (when (setq header (mail-fetch-field (car headers)))
2608            (if (or
2609                 (not
2610                  (string-match
2611                   "\\`\\([-+_&.a-zA-Z0-9]+\\)?\\(,[-+_&.a-zA-Z0-9]+\\)*\\'"
2612                   header))
2613                 (memq
2614                  nil (mapcar
2615                       (lambda (g)
2616                         (not (string-match "\\.\\'\\|\\.\\." g)))
2617                       (message-tokenize-header header ","))))
2618                (setq error t)))
2619          (unless error
2620            (pop headers)))
2621        (if (not error)
2622            t
2623          (y-or-n-p
2624           (format "The %s header looks odd: \"%s\".  Really post? "
2625                   (car headers) header)))))
2626    (message-check 'repeated-newsgroups
2627      (let ((case-fold-search t)
2628            (headers '("Newsgroups" "Followup-To"))
2629            header error groups group)
2630        (while (and headers
2631                    (not error))
2632          (when (setq header (mail-fetch-field (pop headers)))
2633            (setq groups (message-tokenize-header header ","))
2634            (while (setq group (pop groups))
2635              (when (member group groups)
2636                (setq error group
2637                      groups nil)))))
2638        (if (not error)
2639            t
2640          (y-or-n-p
2641           (format "Group %s is repeated in headers.  Really post? " error)))))
2642    ;; Check the From header.
2643    (message-check 'from
2644      (let* ((case-fold-search t)
2645             (from (message-fetch-field "from"))
2646             (ad (nth 1 (mail-extract-address-components from))))
2647        (cond
2648         ((not from)
2649          (message "There is no From line.  Posting is denied.")
2650          nil)
2651         ((or (not (string-match "@[^\\.]*\\." ad)) ;larsi@ifi
2652              (string-match "\\.\\." ad) ;larsi@ifi..uio
2653              (string-match "@\\." ad)   ;larsi@.ifi.uio
2654              (string-match "\\.$" ad)   ;larsi@ifi.uio.
2655              (not (string-match "^[^@]+@[^@]+$" ad)) ;larsi.ifi.uio
2656              (string-match "(.*).*(.*)" from)) ;(lars) (lars)
2657          (message
2658           "Denied posting -- the From looks strange: \"%s\"." from)
2659          nil)
2660         (t t))))))
2661
2662 (defun message-check-news-body-syntax ()
2663   (and
2664    ;; Check for long lines.
2665    (message-check 'long-lines
2666      (goto-char (point-min))
2667      (re-search-forward
2668       (concat "^" (regexp-quote mail-header-separator) "$"))
2669      (while (and
2670              (progn
2671                (end-of-line)
2672                (< (current-column) 80))
2673              (zerop (forward-line 1))))
2674      (or (bolp)
2675          (eobp)
2676          (y-or-n-p
2677           "You have lines longer than 79 characters.  Really post? ")))
2678    ;; Check whether the article is empty.
2679    (message-check 'empty
2680      (goto-char (point-min))
2681      (re-search-forward
2682       (concat "^" (regexp-quote mail-header-separator) "$"))
2683      (forward-line 1)
2684      (let ((b (point)))
2685        (goto-char (point-max))
2686        (re-search-backward message-signature-separator nil t)
2687        (beginning-of-line)
2688        (or (re-search-backward "[^ \n\t]" b t)
2689            (y-or-n-p "Empty article.  Really post? "))))
2690    ;; Check for control characters.
2691    (message-check 'control-chars
2692      (if (re-search-forward "[\000-\007\013\015-\037\200-\237]" nil t)
2693          (y-or-n-p
2694           "The article contains control characters.  Really post? ")
2695        t))
2696    ;; Check 8bit characters.
2697    (message-check '8bit
2698      (message-check-8bit))
2699    ;; Check excessive size.
2700    (message-check 'size
2701      (if (> (buffer-size) 60000)
2702          (y-or-n-p
2703           (format "The article is %d octets long.  Really post? "
2704                   (buffer-size)))
2705        t))
2706    ;; Check whether any new text has been added.
2707    (message-check 'new-text
2708      (or
2709       (not message-checksum)
2710       (not (eq (message-checksum) message-checksum))
2711       (y-or-n-p
2712        "It looks like no new text has been added.  Really post? ")))
2713    ;; Check the length of the signature.
2714    (message-check 'signature
2715      (goto-char (point-max))
2716      (if (or (not (re-search-backward message-signature-separator nil t))
2717              (search-forward message-forward-end-separator nil t))
2718          t
2719        (if (> (count-lines (point) (point-max)) 5)
2720            (y-or-n-p
2721             (format
2722              "Your .sig is %d lines; it should be max 4.  Really post? "
2723              (1- (count-lines (point) (point-max)))))
2724          t)))))
2725
2726 (defun message-check-mail-syntax ()
2727   "Check the syntax of the message."
2728   (save-excursion
2729     (save-restriction
2730       (widen)
2731       (and
2732        ;; We narrow to the headers and check them first.
2733        (save-excursion
2734          (save-restriction
2735            (message-narrow-to-headers)
2736            (message-check-mail-header-syntax)))
2737        ;; Check the body.
2738        (save-excursion
2739          (set-buffer message-edit-buffer)
2740          (message-check-mail-body-syntax))))))
2741
2742 (defun message-check-mail-header-syntax ()
2743   t)
2744
2745 (defun message-check-mail-body-syntax ()
2746   (and
2747    ;; Check 8bit characters.
2748    (message-check '8bit
2749      (message-check-8bit)
2750      )))
2751
2752 (defun message-check-8bit ()
2753   "Check the article contains 8bit characters."
2754   (save-excursion
2755     (set-buffer message-encoding-buffer)
2756     (message-narrow-to-headers)
2757     (let* ((case-fold-search t)
2758            (field-value (message-fetch-field "content-transfer-encoding")))
2759       (if (and field-value
2760                (member (downcase field-value) message-8bit-encoding-list))
2761           t
2762         (widen)
2763         (set-buffer (get-buffer-create " message syntax"))
2764         (erase-buffer)
2765         (goto-char (point-min))
2766         (set-buffer-multibyte nil)
2767         (insert-buffer message-encoding-buffer)
2768         (goto-char (point-min))
2769         (if (re-search-forward "[^\x00-\x7f]" nil t)
2770             (y-or-n-p
2771              "The article contains 8bit characters.  Really post? ")
2772           t)))))
2773
2774 (defun message-checksum ()
2775   "Return a \"checksum\" for the current buffer."
2776   (let ((sum 0))
2777     (save-excursion
2778       (goto-char (point-min))
2779       (re-search-forward
2780        (concat "^" (regexp-quote mail-header-separator) "$"))
2781       (while (not (eobp))
2782         (when (not (looking-at "[ \t\n]"))
2783           (setq sum (logxor (ash sum 1) (if (natnump sum) 0 1)
2784                             (following-char))))
2785         (forward-char 1)))
2786     sum))
2787
2788 (defun message-do-fcc ()
2789   "Process Fcc headers in the current buffer."
2790   (let ((case-fold-search t)
2791         (coding-system-for-write 'raw-text)
2792         list file)
2793     (save-excursion
2794       (set-buffer (get-buffer-create " *message temp*"))
2795       (buffer-disable-undo (current-buffer))
2796       (erase-buffer)
2797       (insert-buffer-substring message-encoding-buffer)
2798       (save-restriction
2799         (message-narrow-to-headers)
2800         (while (setq file (message-fetch-field "fcc"))
2801           (push file list)
2802           (message-remove-header "fcc" nil t)))
2803       (goto-char (point-min))
2804       (re-search-forward (concat "^" (regexp-quote mail-header-separator) "$"))
2805       (replace-match "" t t)
2806       ;; Process FCC operations.
2807       (while list
2808         (setq file (pop list))
2809         (if (string-match "^[ \t]*|[ \t]*\\(.*\\)[ \t]*$" file)
2810             ;; Pipe the article to the program in question.
2811             (call-process-region (point-min) (point-max) shell-file-name
2812                                  nil nil nil shell-command-switch
2813                                  (match-string 1 file))
2814           ;; Save the article.
2815           (setq file (expand-file-name file))
2816           (unless (file-exists-p (file-name-directory file))
2817             (make-directory (file-name-directory file) t))
2818           (if (and message-fcc-handler-function
2819                    (not (eq message-fcc-handler-function 'rmail-output)))
2820               (funcall message-fcc-handler-function file)
2821             (if (and (file-readable-p file) (mail-file-babyl-p file))
2822                 (rmail-output file 1 nil t)
2823               (let ((mail-use-rfc822 t))
2824                 (rmail-output file 1 t t))))))
2825
2826       (kill-buffer (current-buffer)))))
2827
2828 (defun message-output (filename)
2829   "Append this article to Unix/babyl mail file.."
2830   (if (and (file-readable-p filename)
2831            (mail-file-babyl-p filename))
2832       (gnus-output-to-rmail filename t)
2833     (gnus-output-to-mail filename t)))
2834
2835 (defun message-cleanup-headers ()
2836   "Do various automatic cleanups of the headers."
2837   ;; Remove empty lines in the header.
2838   (save-restriction
2839     (message-narrow-to-headers)
2840     ;; Remove blank lines.
2841     (while (re-search-forward "^[ \t]*\n" nil t)
2842       (replace-match "" t t))
2843
2844     ;; Correct Newsgroups and Followup-To headers:  Change sequence of
2845     ;; spaces to comma and eliminate spaces around commas.  Eliminate
2846     ;; embedded line breaks.
2847     (goto-char (point-min))
2848     (while (re-search-forward "^\\(Newsgroups\\|Followup-To\\): +" nil t)
2849       (save-restriction
2850         (narrow-to-region
2851          (point)
2852          (if (re-search-forward "^[^ \t]" nil t)
2853              (match-beginning 0)
2854            (forward-line 1)
2855            (point)))
2856         (goto-char (point-min))
2857         (while (re-search-forward "\n[ \t]+" nil t)
2858           (replace-match " " t t))      ;No line breaks (too confusing)
2859         (goto-char (point-min))
2860         (while (re-search-forward "[ \t\n]*,[ \t\n]*\\|[ \t]+" nil t)
2861           (replace-match "," t t))
2862         (goto-char (point-min))
2863         ;; Remove trailing commas.
2864         (when (re-search-forward ",+$" nil t)
2865           (replace-match "" t t))))))
2866
2867 (defun message-make-date ()
2868   "Make a valid data header."
2869   (let ((now (current-time)))
2870     (timezone-make-date-arpa-standard
2871      (current-time-string now) (current-time-zone now))))
2872
2873 (defun message-make-message-id ()
2874   "Make a unique Message-ID."
2875   (concat "<" (message-unique-id)
2876           (let ((psubject (save-excursion (message-fetch-field "subject")))
2877                 (psupersedes
2878                  (save-excursion (message-fetch-field "supersedes"))))
2879             (if (or
2880                  (and message-reply-headers
2881                       (mail-header-references message-reply-headers)
2882                       (mail-header-subject message-reply-headers)
2883                       psubject
2884                       (mail-header-subject message-reply-headers)
2885                       (not (string=
2886                             (message-strip-subject-re
2887                              (mail-header-subject message-reply-headers))
2888                             (message-strip-subject-re psubject))))
2889                  (and psupersedes
2890                       (string-match "_-_@" psupersedes)))
2891                 "_-_" ""))
2892           "@" (message-make-fqdn) ">"))
2893
2894 (defvar message-unique-id-char nil)
2895
2896 ;; If you ever change this function, make sure the new version
2897 ;; cannot generate IDs that the old version could.
2898 ;; You might for example insert a "." somewhere (not next to another dot
2899 ;; or string boundary), or modify the "fsf" string.
2900 (defun message-unique-id ()
2901   ;; Don't use microseconds from (current-time), they may be unsupported.
2902   ;; Instead we use this randomly inited counter.
2903   (setq message-unique-id-char
2904         (% (1+ (or message-unique-id-char (logand (random t) (1- (lsh 1 20)))))
2905            ;; (current-time) returns 16-bit ints,
2906            ;; and 2^16*25 just fits into 4 digits i base 36.
2907            (* 25 25)))
2908   (let ((tm (current-time)))
2909     (concat
2910      (if (memq system-type '(ms-dos emx vax-vms))
2911          (let ((user (downcase (user-login-name))))
2912            (while (string-match "[^a-z0-9_]" user)
2913              (aset user (match-beginning 0) ?_))
2914            user)
2915        (message-number-base36 (user-uid) -1))
2916      (message-number-base36 (+ (car   tm)
2917                                (lsh (% message-unique-id-char 25) 16)) 4)
2918      (message-number-base36 (+ (nth 1 tm)
2919                                (lsh (/ message-unique-id-char 25) 16)) 4)
2920      ;; Append the newsreader name, because while the generated
2921      ;; ID is unique to this newsreader, other newsreaders might
2922      ;; otherwise generate the same ID via another algorithm.
2923      ".fsf")))
2924
2925 (defun message-number-base36 (num len)
2926   (if (if (< len 0)
2927           (<= num 0)
2928         (= len 0))
2929       ""
2930     (concat (message-number-base36 (/ num 36) (1- len))
2931             (char-to-string (aref "zyxwvutsrqponmlkjihgfedcba9876543210"
2932                                   (% num 36))))))
2933
2934 (defun message-make-organization ()
2935   "Make an Organization header."
2936   (let* ((organization
2937           (when message-user-organization
2938                 (if (message-functionp message-user-organization)
2939                     (funcall message-user-organization)
2940                   message-user-organization))))
2941     (save-excursion
2942       (message-set-work-buffer)
2943       (cond ((stringp organization)
2944              (insert organization))
2945             ((and (eq t organization)
2946                   message-user-organization-file
2947                   (file-exists-p message-user-organization-file))
2948              (insert-file-contents message-user-organization-file)))
2949       (goto-char (point-min))
2950       (while (re-search-forward "[\t\n]+" nil t)
2951         (replace-match "" t t))
2952       (unless (zerop (buffer-size))
2953         (buffer-string)))))
2954
2955 (defun message-make-lines ()
2956   "Count the number of lines and return numeric string."
2957   (save-excursion
2958     (save-restriction
2959       (widen)
2960       (goto-char (point-min))
2961       (re-search-forward
2962        (concat "^" (regexp-quote mail-header-separator) "$"))
2963       (forward-line 1)
2964       (int-to-string (count-lines (point) (point-max))))))
2965
2966 (defun message-make-in-reply-to ()
2967   "Return the In-Reply-To header for this message."
2968   (when message-reply-headers
2969     (let ((from (mail-header-from message-reply-headers))
2970           (date (mail-header-date message-reply-headers))
2971           (msg-id (mail-header-message-id message-reply-headers)))
2972       (when msg-id
2973         (concat msg-id
2974                 (when from
2975                   (let ((stop-pos
2976                          (string-match "  *at \\|  *@ \\| *(\\| *<" from)))
2977                     (concat " ("
2978                             (if (and stop-pos
2979                                      (not (zerop stop-pos)))
2980                                 (substring from 0 stop-pos) from)
2981                             "'s message of \""
2982                             (if (or (not date) (string= date ""))
2983                                 "(unknown date)" date)
2984                             "\")"))))))))
2985
2986 (defun message-make-distribution ()
2987   "Make a Distribution header."
2988   (let ((orig-distribution (message-fetch-reply-field "distribution")))
2989     (cond ((message-functionp message-distribution-function)
2990            (funcall message-distribution-function))
2991           (t orig-distribution))))
2992
2993 (defun message-make-expires ()
2994   "Return an Expires header based on `message-expires'."
2995   (let ((current (current-time))
2996         (future (* 1.0 message-expires 60 60 24)))
2997     ;; Add the future to current.
2998     (setcar current (+ (car current) (round (/ future (expt 2 16)))))
2999     (setcar (cdr current) (+ (nth 1 current) (% (round future) (expt 2 16))))
3000     ;; Return the date in the future in UT.
3001     (timezone-make-date-arpa-standard
3002      (current-time-string current) (current-time-zone current) '(0 "UT"))))
3003
3004 (defun message-make-path ()
3005   "Return uucp path."
3006   (let ((login-name (user-login-name)))
3007     (cond ((null message-user-path)
3008            (concat (system-name) "!" login-name))
3009           ((stringp message-user-path)
3010            ;; Support GENERICPATH.  Suggested by vixie@decwrl.dec.com.
3011            (concat message-user-path "!" login-name))
3012           (t login-name))))
3013
3014 (defun message-make-from ()
3015   "Make a From header."
3016   (let* ((style message-from-style)
3017          (login (message-make-address))
3018          (fullname
3019           (or (and (boundp 'user-full-name)
3020                    user-full-name)
3021               (user-full-name))))
3022     (when (string= fullname "&")
3023       (setq fullname (user-login-name)))
3024     (save-excursion
3025       (message-set-work-buffer)
3026       (cond
3027        ((or (null style)
3028             (equal fullname ""))
3029         (insert login))
3030        ((or (eq style 'angles)
3031             (and (not (eq style 'parens))
3032                  ;; Use angles if no quoting is needed, or if parens would
3033                  ;; need quoting too.
3034                  (or (not (string-match "[^- !#-'*+/-9=?A-Z^-~]" fullname))
3035                      (let ((tmp (concat fullname nil)))
3036                        (while (string-match "([^()]*)" tmp)
3037                          (aset tmp (match-beginning 0) ?-)
3038                          (aset tmp (1- (match-end 0)) ?-))
3039                        (string-match "[\\()]" tmp)))))
3040         (insert fullname)
3041         (goto-char (point-min))
3042         ;; Look for a character that cannot appear unquoted
3043         ;; according to RFC 822.
3044         (when (re-search-forward "[^- !#-'*+/-9=?A-Z^-~]" nil 1)
3045           ;; Quote fullname, escaping specials.
3046           (goto-char (point-min))
3047           (insert "\"")
3048           (while (re-search-forward "[\"\\]" nil 1)
3049             (replace-match "\\\\\\&" t))
3050           (insert "\""))
3051         (insert " <" login ">"))
3052        (t                               ; 'parens or default
3053         (insert login " (")
3054         (let ((fullname-start (point)))
3055           (insert fullname)
3056           (goto-char fullname-start)
3057           ;; RFC 822 says \ and nonmatching parentheses
3058           ;; must be escaped in comments.
3059           ;; Escape every instance of ()\ ...
3060           (while (re-search-forward "[()\\]" nil 1)
3061             (replace-match "\\\\\\&" t))
3062           ;; ... then undo escaping of matching parentheses,
3063           ;; including matching nested parentheses.
3064           (goto-char fullname-start)
3065           (while (re-search-forward
3066                   "\\(\\=\\|[^\\]\\(\\\\\\\\\\)*\\)\\\\(\\(\\([^\\]\\|\\\\\\\\\\)*\\)\\\\)"
3067                   nil 1)
3068             (replace-match "\\1(\\3)" t)
3069             (goto-char fullname-start)))
3070         (insert ")")))
3071       (buffer-string))))
3072
3073 (defun message-make-sender ()
3074   "Return the \"real\" user address.
3075 This function tries to ignore all user modifications, and
3076 give as trustworthy answer as possible."
3077   (concat (user-login-name) "@" (system-name)))
3078
3079 (defun message-make-address ()
3080   "Make the address of the user."
3081   (or (message-user-mail-address)
3082       (concat (user-login-name) "@" (message-make-domain))))
3083
3084 (defun message-user-mail-address ()
3085   "Return the pertinent part of `user-mail-address'."
3086   (when user-mail-address
3087     (if (string-match " " user-mail-address)
3088         (nth 1 (mail-extract-address-components user-mail-address))
3089       user-mail-address)))
3090
3091 (defun message-make-fqdn ()
3092   "Return user's fully qualified domain name."
3093   (let ((system-name (system-name))
3094         (user-mail (message-user-mail-address)))
3095     (cond
3096      ((string-match "[^.]\\.[^.]" system-name)
3097       ;; `system-name' returned the right result.
3098       system-name)
3099      ;; Try `mail-host-address'.
3100      ((and (boundp 'mail-host-address)
3101            (stringp mail-host-address)
3102            (string-match "\\." mail-host-address))
3103       mail-host-address)
3104      ;; We try `user-mail-address' as a backup.
3105      ((and user-mail
3106            (string-match "\\." user-mail)
3107            (string-match "@\\(.*\\)\\'" user-mail))
3108       (match-string 1 user-mail))
3109      ;; Default to this bogus thing.
3110      (t
3111       (concat system-name ".i-did-not-set--mail-host-address--so-shoot-me")))))
3112
3113 (defun message-make-host-name ()
3114   "Return the name of the host."
3115   (let ((fqdn (message-make-fqdn)))
3116     (string-match "^[^.]+\\." fqdn)
3117     (substring fqdn 0 (1- (match-end 0)))))
3118
3119 (defun message-make-domain ()
3120   "Return the domain name."
3121   (or mail-host-address
3122       (message-make-fqdn)))
3123
3124 (defun message-make-user-agent ()
3125   "Return user-agent info."
3126   (if message-user-agent
3127       (save-excursion
3128         (goto-char (point-min))
3129         (let ((case-fold-search t)
3130               user-agent beg p end)
3131           (if (re-search-forward "^User-Agent:[ \t]*" nil t)
3132               (progn
3133                 (setq beg (match-beginning 0)
3134                       p (match-end 0)
3135                       end (std11-field-end)
3136                       user-agent (buffer-substring p end))
3137                 (delete-region beg (1+ end))
3138                 (concat message-user-agent " " user-agent)
3139                 )
3140             message-user-agent)))))
3141
3142 (defun message-generate-headers (headers)
3143   "Prepare article HEADERS.
3144 Headers already prepared in the buffer are not modified."
3145   (save-restriction
3146     (message-narrow-to-headers)
3147     (let* ((Date (message-make-date))
3148            (Message-ID (message-make-message-id))
3149            (Organization (message-make-organization))
3150            (From (message-make-from))
3151            (Path (message-make-path))
3152            (Subject nil)
3153            (Newsgroups nil)
3154            (In-Reply-To (message-make-in-reply-to))
3155            (To nil)
3156            (Distribution (message-make-distribution))
3157            (Lines (message-make-lines))
3158            (User-Agent (message-make-user-agent))
3159            (Expires (message-make-expires))
3160            (case-fold-search t)
3161            header value elem)
3162       ;; First we remove any old generated headers.
3163       (let ((headers message-deletable-headers))
3164         (unless (buffer-modified-p)
3165           (setq headers (delq 'Message-ID (copy-sequence headers))))
3166         (while headers
3167           (goto-char (point-min))
3168           (and (re-search-forward
3169                 (concat "^" (symbol-name (car headers)) ": *") nil t)
3170                (get-text-property (1+ (match-beginning 0)) 'message-deletable)
3171                (message-delete-line))
3172           (pop headers)))
3173       ;; Go through all the required headers and see if they are in the
3174       ;; articles already.  If they are not, or are empty, they are
3175       ;; inserted automatically - except for Subject, Newsgroups and
3176       ;; Distribution.
3177       (while headers
3178         (goto-char (point-min))
3179         (setq elem (pop headers))
3180         (if (consp elem)
3181             (if (eq (car elem) 'optional)
3182                 (setq header (cdr elem))
3183               (setq header (car elem)))
3184           (setq header elem))
3185         (when (or (not (re-search-forward
3186                         (concat "^"
3187                                 (regexp-quote
3188                                  (downcase
3189                                   (if (stringp header)
3190                                       header
3191                                     (symbol-name header))))
3192                                 ":")
3193                         nil t))
3194                   (progn
3195                     ;; The header was found.  We insert a space after the
3196                     ;; colon, if there is none.
3197                     (if (/= (following-char) ? ) (insert " ") (forward-char 1))
3198                     ;; Find out whether the header is empty...
3199                     (looking-at "[ \t]*$")))
3200           ;; So we find out what value we should insert.
3201           (setq value
3202                 (cond
3203                  ((and (consp elem) (eq (car elem) 'optional))
3204                   ;; This is an optional header.  If the cdr of this
3205                   ;; is something that is nil, then we do not insert
3206                   ;; this header.
3207                   (setq header (cdr elem))
3208                   (or (and (fboundp (cdr elem)) (funcall (cdr elem)))
3209                       (and (boundp (cdr elem)) (symbol-value (cdr elem)))))
3210                  ((consp elem)
3211                   ;; The element is a cons.  Either the cdr is a
3212                   ;; string to be inserted verbatim, or it is a
3213                   ;; function, and we insert the value returned from
3214                   ;; this function.
3215                   (or (and (stringp (cdr elem)) (cdr elem))
3216                       (and (fboundp (cdr elem)) (funcall (cdr elem)))))
3217                  ((and (boundp header) (symbol-value header))
3218                   ;; The element is a symbol.  We insert the value
3219                   ;; of this symbol, if any.
3220                   (symbol-value header))
3221                  (t
3222                   ;; We couldn't generate a value for this header,
3223                   ;; so we just ask the user.
3224                   (read-from-minibuffer
3225                    (format "Empty header for %s; enter value: " header)))))
3226           ;; Finally insert the header.
3227           (when (and value
3228                      (not (equal value "")))
3229             (save-excursion
3230               (if (bolp)
3231                   (progn
3232                     ;; This header didn't exist, so we insert it.
3233                     (goto-char (point-max))
3234                     (insert (if (stringp header) header (symbol-name header))
3235                             ": " value "\n")
3236                     (forward-line -1))
3237                 ;; The value of this header was empty, so we clear
3238                 ;; totally and insert the new value.
3239                 (delete-region (point) (gnus-point-at-eol))
3240                 (insert value))
3241               ;; Add the deletable property to the headers that require it.
3242               (and (memq header message-deletable-headers)
3243                    (progn (beginning-of-line) (looking-at "[^:]+: "))
3244                    (add-text-properties
3245                     (point) (match-end 0)
3246                     '(message-deletable t face italic) (current-buffer)))))))
3247       ;; Insert new Sender if the From is strange.
3248       (let ((from (message-fetch-field "from"))
3249             (sender (message-fetch-field "sender"))
3250             (secure-sender (message-make-sender)))
3251         (when (and from
3252                    (not (message-check-element 'sender))
3253                    (not (string=
3254                          (downcase
3255                           (cadr (mail-extract-address-components from)))
3256                          (downcase secure-sender)))
3257                    (or (null sender)
3258                        (not
3259                         (string=
3260                          (downcase
3261                           (cadr (mail-extract-address-components sender)))
3262                          (downcase secure-sender)))))
3263           (goto-char (point-min))
3264           ;; Rename any old Sender headers to Original-Sender.
3265           (when (re-search-forward "^\\(Original-\\)*Sender:" nil t)
3266             (beginning-of-line)
3267             (insert "Original-")
3268             (beginning-of-line))
3269           (when (or (message-news-p)
3270                     (string-match "@.+\\.." secure-sender))
3271             (insert "Sender: " secure-sender "\n")))))))
3272
3273 (defun message-insert-courtesy-copy ()
3274   "Insert a courtesy message in mail copies of combined messages."
3275   (let (newsgroups)
3276     (save-excursion
3277       (save-restriction
3278         (message-narrow-to-headers)
3279         (when (setq newsgroups (message-fetch-field "newsgroups"))
3280           (goto-char (point-max))
3281           (insert "Posted-To: " newsgroups "\n")))
3282       (forward-line 1)
3283       (when message-courtesy-message
3284         (cond
3285          ((string-match "%s" message-courtesy-message)
3286           (insert (format message-courtesy-message newsgroups)))
3287          (t
3288           (insert message-courtesy-message)))))))
3289
3290 ;;;
3291 ;;; Setting up a message buffer
3292 ;;;
3293
3294 (defun message-fill-address (header value)
3295   (save-restriction
3296     (narrow-to-region (point) (point))
3297     (insert (capitalize (symbol-name header))
3298             ": "
3299             (if (consp value) (car value) value)
3300             "\n")
3301     (narrow-to-region (point-min) (1- (point-max)))
3302     (let (quoted last)
3303       (goto-char (point-min))
3304       (while (not (eobp))
3305         (skip-chars-forward "^,\"" (point-max))
3306         (if (or (= (following-char) ?,)
3307                 (eobp))
3308             (when (not quoted)
3309               (if (and (> (current-column) 78)
3310                        last)
3311                   (progn
3312                     (save-excursion
3313                       (goto-char last)
3314                       (insert "\n\t"))
3315                     (setq last (1+ (point))))
3316                 (setq last (1+ (point)))))
3317           (setq quoted (not quoted)))
3318         (unless (eobp)
3319           (forward-char 1))))
3320     (goto-char (point-max))
3321     (widen)
3322     (forward-line 1)))
3323
3324 (defun message-fill-references (header value)
3325   (insert (capitalize (symbol-name header))
3326           ": "
3327           (std11-fill-msg-id-list-string
3328            (if (consp value) (car value) value))
3329           "\n"))
3330
3331 (defun message-fill-header (header value)
3332   (let ((begin (point))
3333         (fill-column 990)
3334         (fill-prefix "\t"))
3335     (insert (capitalize (symbol-name header))
3336             ": "
3337             (if (consp value) (car value) value)
3338             "\n")
3339     (save-restriction
3340       (narrow-to-region begin (point))
3341       (fill-region-as-paragraph begin (point))
3342       ;; Tapdance around looong Message-IDs.
3343       (forward-line -1)
3344       (when (looking-at "[ \t]*$")
3345         (message-delete-line))
3346       (goto-char begin)
3347       (re-search-forward ":" nil t)
3348       (when (looking-at "\n[ \t]+")
3349         (replace-match " " t t))
3350       (goto-char (point-max)))))
3351
3352 (defun message-shorten-references (header references)
3353   "Limit REFERENCES to be shorter than 988 characters."
3354   (let ((max 988)
3355         (cut 4)
3356         refs)
3357     (nnheader-temp-write nil
3358       (insert references)
3359       (goto-char (point-min))
3360       (while (re-search-forward "<[^>]+>" nil t)
3361         (push (match-string 0) refs))
3362       (setq refs (nreverse refs))
3363       (while (> (length (mapconcat 'identity refs " ")) max)
3364         (when (< (length refs) (1+ cut))
3365           (decf cut))
3366         (setcdr (nthcdr cut refs) (cddr (nthcdr cut refs)))))
3367     (insert (capitalize (symbol-name header)) ": "
3368             (mapconcat 'identity refs " ") "\n")))
3369
3370 (defun message-position-point ()
3371   "Move point to where the user probably wants to find it."
3372   (message-narrow-to-headers)
3373   (cond
3374    ((re-search-forward "^[^:]+:[ \t]*$" nil t)
3375     (search-backward ":" )
3376     (widen)
3377     (forward-char 1)
3378     (if (= (following-char) ? )
3379         (forward-char 1)
3380       (insert " ")))
3381    (t
3382     (goto-char (point-max))
3383     (widen)
3384     (forward-line 1)
3385     (unless (looking-at "$")
3386       (forward-line 2)))
3387    (sit-for 0)))
3388
3389 (defun message-buffer-name (type &optional to group)
3390   "Return a new (unique) buffer name based on TYPE and TO."
3391   (cond
3392    ;; Check whether `message-generate-new-buffers' is a function,
3393    ;; and if so, call it.
3394    ((message-functionp message-generate-new-buffers)
3395     (funcall message-generate-new-buffers type to group))
3396    ;; Generate a new buffer name The Message Way.
3397    (message-generate-new-buffers
3398     (generate-new-buffer-name
3399      (concat "*" type
3400              (if to
3401                  (concat " to "
3402                          (or (car (mail-extract-address-components to))
3403                              to) "")
3404                "")
3405              (if (and group (not (string= group ""))) (concat " on " group) "")
3406              "*")))
3407    ;; Use standard name.
3408    (t
3409     (format "*%s message*" type))))
3410
3411 (defun message-pop-to-buffer (name)
3412   "Pop to buffer NAME, and warn if it already exists and is modified."
3413   (let ((pop-up-frames pop-up-frames)
3414         (special-display-buffer-names special-display-buffer-names)
3415         (special-display-regexps special-display-regexps)
3416         (same-window-buffer-names same-window-buffer-names)
3417         (same-window-regexps same-window-regexps)
3418         (buffer (get-buffer name))
3419         (cur (current-buffer)))
3420     (if (or (and (featurep 'xemacs)
3421                  (not (eq 'tty (device-type))))
3422             window-system
3423             (>= emacs-major-version 20))
3424         (when message-use-multi-frames
3425           (setq pop-up-frames t
3426                 special-display-buffer-names nil
3427                 special-display-regexps nil
3428                 same-window-buffer-names nil
3429                 same-window-regexps nil))
3430       (setq pop-up-frames nil))
3431     (if (and buffer
3432              (buffer-name buffer))
3433         (progn
3434           (set-buffer (pop-to-buffer buffer))
3435           (when (and (buffer-modified-p)
3436                      (not (y-or-n-p
3437                            "Message already being composed; erase? ")))
3438             (error "Message being composed")))
3439       (set-buffer (pop-to-buffer name)))
3440     (erase-buffer)
3441     (message-mode)
3442     (when pop-up-frames
3443       (make-local-variable 'message-original-frame)
3444       (setq message-original-frame (selected-frame)))))
3445
3446 (defun message-do-send-housekeeping ()
3447   "Kill old message buffers."
3448   ;; We might have sent this buffer already.  Delete it from the
3449   ;; list of buffers.
3450   (setq message-buffer-list (delq (current-buffer) message-buffer-list))
3451   (while (and message-max-buffers
3452               message-buffer-list
3453               (>= (length message-buffer-list) message-max-buffers))
3454     ;; Kill the oldest buffer -- unless it has been changed.
3455     (let ((buffer (pop message-buffer-list)))
3456       (when (and (buffer-name buffer)
3457                  (not (buffer-modified-p buffer)))
3458         (kill-buffer buffer))))
3459   ;; Rename the buffer.
3460   (if message-send-rename-function
3461       (funcall message-send-rename-function)
3462     (when (string-match "\\`\\*" (buffer-name))
3463       (rename-buffer
3464        (concat "*sent " (substring (buffer-name) (match-end 0))) t)))
3465   ;; Push the current buffer onto the list.
3466   (when message-max-buffers
3467     (setq message-buffer-list
3468           (nconc message-buffer-list (list (current-buffer))))))
3469
3470 (defvar mc-modes-alist)
3471 (defun message-setup (headers &optional replybuffer actions)
3472   (when (and (boundp 'mc-modes-alist)
3473              (not (assq 'message-mode mc-modes-alist)))
3474     (push '(message-mode (encrypt . mc-encrypt-message)
3475                          (sign . mc-sign-message))
3476           mc-modes-alist))
3477   (when actions
3478     (setq message-send-actions actions))
3479   (setq message-reply-buffer
3480         (or (message-get-parameter 'reply-buffer)
3481             replybuffer))
3482   (goto-char (point-min))
3483   ;; Insert all the headers.
3484   (mail-header-format
3485    (let ((h headers)
3486          (alist message-header-format-alist))
3487      (while h
3488        (unless (assq (caar h) message-header-format-alist)
3489          (push (list (caar h)) alist))
3490        (pop h))
3491      alist)
3492    headers)
3493   (delete-region (point) (progn (forward-line -1) (point)))
3494   (when message-default-headers
3495     (insert message-default-headers)
3496     (or (bolp) (insert ?\n)))
3497   (put-text-property
3498    (point)
3499    (progn
3500      (insert mail-header-separator "\n")
3501      (1- (point)))
3502    'read-only nil)
3503   (forward-line -1)
3504   (when (message-news-p)
3505     (when message-default-news-headers
3506       (insert message-default-news-headers)
3507       (or (bolp) (insert ?\n)))
3508     (when message-generate-headers-first
3509       (message-generate-headers
3510        (delq 'Lines
3511              (delq 'Subject
3512                    (copy-sequence message-required-news-headers))))))
3513   (when (message-mail-p)
3514     (when message-default-mail-headers
3515       (insert message-default-mail-headers)
3516       (or (bolp) (insert ?\n)))
3517     (when message-generate-headers-first
3518       (message-generate-headers
3519        (delq 'Lines
3520              (delq 'Subject
3521                    (copy-sequence message-required-mail-headers))))))
3522   (run-hooks 'message-signature-setup-hook)
3523   (message-insert-signature)
3524   (save-restriction
3525     (message-narrow-to-headers)
3526     (run-hooks 'message-header-setup-hook))
3527   (set-buffer-modified-p nil)
3528   (setq buffer-undo-list nil)
3529   (run-hooks 'message-setup-hook)
3530   (message-position-point)
3531   (undo-boundary))
3532
3533 (defun message-set-auto-save-file-name ()
3534   "Associate the message buffer with a file in the drafts directory."
3535   (when message-auto-save-directory
3536     (if (gnus-alive-p)
3537         (setq message-draft-article
3538               (nndraft-request-associate-buffer "drafts"))
3539       (setq buffer-file-name (expand-file-name "*message*"
3540                                                message-auto-save-directory))
3541       (setq buffer-auto-save-file-name (make-auto-save-file-name)))
3542     (clear-visited-file-modtime)))
3543
3544 (defun message-disassociate-draft ()
3545   "Disassociate the message buffer from the drafts directory."
3546   (when message-draft-article
3547     (nndraft-request-expire-articles
3548      (list message-draft-article) "drafts" nil t)))
3549
3550 \f
3551
3552 ;;;
3553 ;;; Commands for interfacing with message
3554 ;;;
3555
3556 ;;;###autoload
3557 (defun message-mail (&optional to subject
3558                                other-headers continue switch-function
3559                                yank-action send-actions)
3560   "Start editing a mail message to be sent.
3561 OTHER-HEADERS is an alist of header/value pairs."
3562   (interactive)
3563   (let ((message-this-is-mail t))
3564     (message-pop-to-buffer (message-buffer-name "mail" to))
3565     (message-setup
3566      (nconc
3567       `((To . ,(or to "")) (Subject . ,(or subject "")))
3568       (when other-headers other-headers)))))
3569
3570 ;;;###autoload
3571 (defun message-news (&optional newsgroups subject)
3572   "Start editing a news article to be sent."
3573   (interactive)
3574   (let ((message-this-is-news t))
3575     (message-pop-to-buffer (message-buffer-name "news" nil newsgroups))
3576     (message-setup `((Newsgroups . ,(or newsgroups ""))
3577                      (Subject . ,(or subject ""))))))
3578
3579 ;;;###autoload
3580 (defun message-reply (&optional to-address wide)
3581   "Start editing a reply to the article in the current buffer."
3582   (interactive)
3583   (let ((cur (current-buffer))
3584         from subject date reply-to to cc
3585         references message-id follow-to
3586         (inhibit-point-motion-hooks t)
3587         mct never-mct gnus-warning)
3588     (save-restriction
3589       (message-narrow-to-head)
3590       ;; Allow customizations to have their say.
3591       (if (not wide)
3592           ;; This is a regular reply.
3593           (if (message-functionp message-reply-to-function)
3594               (setq follow-to (funcall message-reply-to-function)))
3595         ;; This is a followup.
3596         (if (message-functionp message-wide-reply-to-function)
3597             (save-excursion
3598               (setq follow-to
3599                     (funcall message-wide-reply-to-function)))))
3600       ;; Find all relevant headers we need.
3601       (setq from (message-fetch-field "from")
3602             date (message-fetch-field "date")
3603             subject (or (message-fetch-field "subject") "none")
3604             to (message-fetch-field "to")
3605             cc (message-fetch-field "cc")
3606             mct (message-fetch-field "mail-copies-to")
3607             reply-to (message-fetch-field "reply-to")
3608             references (message-fetch-field "references")
3609             message-id (message-fetch-field "message-id" t))
3610       ;; Remove any (buggy) Re:'s that are present and make a
3611       ;; proper one.
3612       (when (string-match message-subject-re-regexp subject)
3613         (setq subject (substring subject (match-end 0))))
3614       (setq subject (concat "Re: " subject))
3615
3616       (when (and (setq gnus-warning (message-fetch-field "gnus-warning"))
3617                  (string-match "<[^>]+>" gnus-warning))
3618         (setq message-id (match-string 0 gnus-warning)))
3619
3620       ;; Handle special values of Mail-Copies-To.
3621       (when mct
3622         (cond ((equal (downcase mct) "never")
3623                (setq never-mct t)
3624                (setq mct nil))
3625               ((equal (downcase mct) "always")
3626                (setq mct (or reply-to from)))))
3627
3628       (unless follow-to
3629         (if (or (not wide)
3630                 to-address)
3631             (progn
3632               (setq follow-to (list (cons 'To (or to-address reply-to from))))
3633               (when (and wide mct)
3634                 (push (cons 'Cc mct) follow-to)))
3635           (let (ccalist)
3636             (save-excursion
3637               (message-set-work-buffer)
3638               (unless never-mct
3639                 (insert (or reply-to from "")))
3640               (insert (if to (concat (if (bolp) "" ", ") to "") ""))
3641               (insert (if mct (concat (if (bolp) "" ", ") mct) ""))
3642               (insert (if cc (concat (if (bolp) "" ", ") cc) ""))
3643               (goto-char (point-min))
3644               (while (re-search-forward "[ \t]+" nil t)
3645                 (replace-match " " t t))
3646               ;; Remove addresses that match `rmail-dont-reply-to-names'.
3647               (insert (prog1 (rmail-dont-reply-to (buffer-string))
3648                         (erase-buffer)))
3649               (goto-char (point-min))
3650               ;; Perhaps Mail-Copies-To: never removed the only address?
3651               (when (eobp)
3652                 (insert (or reply-to from "")))
3653               (setq ccalist
3654                     (mapcar
3655                      (lambda (addr)
3656                        (cons (mail-strip-quoted-names addr) addr))
3657                      (message-tokenize-header (buffer-string))))
3658               (let ((s ccalist))
3659                 (while s
3660                   (setq ccalist (delq (assoc (car (pop s)) s) ccalist)))))
3661             (setq follow-to (list (cons 'To (cdr (pop ccalist)))))
3662             (when ccalist
3663               (let ((ccs (cons 'Cc (mapconcat
3664                                     (lambda (addr) (cdr addr)) ccalist ", "))))
3665                 (when (string-match "^ +" (cdr ccs))
3666                   (setcdr ccs (substring (cdr ccs) (match-end 0))))
3667                 (push ccs follow-to))))))
3668       (widen))
3669
3670     (message-pop-to-buffer (message-buffer-name
3671                             (if wide "wide reply" "reply") from
3672                             (if wide to-address nil)))
3673
3674     (setq message-reply-headers
3675           (make-full-mail-header-from-decoded-header
3676            0 subject from date message-id references 0 0 ""))
3677
3678     (message-setup
3679      `((Subject . ,subject)
3680        ,@follow-to
3681        ,@(if (or references message-id)
3682              `((References . ,(concat (or references "") (and references " ")
3683                                       (or message-id ""))))
3684            nil))
3685      cur)))
3686
3687 ;;;###autoload
3688 (defun message-wide-reply (&optional to-address)
3689   "Make a \"wide\" reply to the message in the current buffer."
3690   (interactive)
3691   (message-reply to-address t))
3692
3693 ;;;###autoload
3694 (defun message-followup (&optional to-newsgroups)
3695   "Follow up to the message in the current buffer.
3696 If TO-NEWSGROUPS, use that as the new Newsgroups line."
3697   (interactive)
3698   (let ((cur (current-buffer))
3699         from subject date reply-to mct
3700         references message-id follow-to
3701         (inhibit-point-motion-hooks t)
3702         (message-this-is-news t)
3703         followup-to distribution newsgroups gnus-warning posted-to)
3704     (save-restriction
3705       (narrow-to-region
3706        (goto-char (point-min))
3707        (if (search-forward "\n\n" nil t)
3708            (1- (point))
3709          (point-max)))
3710       (when (message-functionp message-followup-to-function)
3711         (setq follow-to
3712               (funcall message-followup-to-function)))
3713       (setq from (message-fetch-field "from")
3714             date (message-fetch-field "date")
3715             subject (or (message-fetch-field "subject") "none")
3716             references (message-fetch-field "references")
3717             message-id (message-fetch-field "message-id" t)
3718             followup-to (message-fetch-field "followup-to")
3719             newsgroups (message-fetch-field "newsgroups")
3720             posted-to (message-fetch-field "posted-to")
3721             reply-to (message-fetch-field "reply-to")
3722             distribution (message-fetch-field "distribution")
3723             mct (message-fetch-field "mail-copies-to"))
3724       (when (and (setq gnus-warning (message-fetch-field "gnus-warning"))
3725                  (string-match "<[^>]+>" gnus-warning))
3726         (setq message-id (match-string 0 gnus-warning)))
3727       ;; Remove bogus distribution.
3728       (when (and (stringp distribution)
3729                  (let ((case-fold-search t))
3730                    (string-match "world" distribution)))
3731         (setq distribution nil))
3732       ;; Remove any (buggy) Re:'s that are present and make a
3733       ;; proper one.
3734       (when (string-match message-subject-re-regexp subject)
3735         (setq subject (substring subject (match-end 0))))
3736       (setq subject (concat "Re: " subject))
3737       (widen))
3738
3739     (message-pop-to-buffer (message-buffer-name "followup" from newsgroups))
3740
3741     (message-setup
3742      `((Subject . ,subject)
3743        ,@(cond
3744           (to-newsgroups
3745            (list (cons 'Newsgroups to-newsgroups)))
3746           (follow-to follow-to)
3747           ((and followup-to message-use-followup-to)
3748            (list
3749             (cond
3750              ((equal (downcase followup-to) "poster")
3751               (if (or (eq message-use-followup-to 'use)
3752                       (message-y-or-n-p "Obey Followup-To: poster? " t "\
3753 You should normally obey the Followup-To: header.
3754
3755 `Followup-To: poster' sends your response via e-mail instead of news.
3756
3757 A typical situation where `Followup-To: poster' is used is when the poster
3758 does not read the newsgroup, so he wouldn't see any replies sent to it."))
3759                   (progn
3760                     (setq message-this-is-news nil)
3761                     (cons 'To (or reply-to from "")))
3762                 (cons 'Newsgroups newsgroups)))
3763              (t
3764               (if (or (equal followup-to newsgroups)
3765                       (not (eq message-use-followup-to 'ask))
3766                       (message-y-or-n-p
3767                        (concat "Obey Followup-To: " followup-to "? ") t "\
3768 You should normally obey the Followup-To: header.
3769
3770         `Followup-To: " followup-to "'
3771 directs your response to " (if (string-match "," followup-to)
3772                                "the specified newsgroups"
3773                              "that newsgroup only") ".
3774
3775 If a message is posted to several newsgroups, Followup-To is often
3776 used to direct the following discussion to one newsgroup only,
3777 because discussions that are spread over several newsgroup tend to
3778 be fragmented and very difficult to follow.
3779
3780 Also, some source/announcement newsgroups are not indented for discussion;
3781 responses here are directed to other newsgroups."))
3782                   (cons 'Newsgroups followup-to)
3783                 (cons 'Newsgroups newsgroups))))))
3784           (posted-to
3785            `((Newsgroups . ,posted-to)))
3786           (t
3787            `((Newsgroups . ,newsgroups))))
3788        ,@(and distribution (list (cons 'Distribution distribution)))
3789        ,@(if (or references message-id)
3790              `((References . ,(concat (or references "") (and references " ")
3791                                       (or message-id "")))))
3792        ,@(when (and mct
3793                     (not (equal (downcase mct) "never")))
3794            (list (cons 'Cc (if (equal (downcase mct) "always")
3795                                (or reply-to from "")
3796                              mct)))))
3797
3798      cur)
3799
3800     (setq message-reply-headers
3801           (make-full-mail-header-from-decoded-header
3802            0 subject from date message-id references 0 0 ""))))
3803
3804
3805 ;;;###autoload
3806 (defun message-cancel-news ()
3807   "Cancel an article you posted."
3808   (interactive)
3809   (unless (message-news-p)
3810     (error "This is not a news article; canceling is impossible"))
3811   (when (yes-or-no-p "Do you really want to cancel this article? ")
3812     (let (from newsgroups message-id distribution buf sender)
3813       (save-excursion
3814         ;; Get header info. from original article.
3815         (save-restriction
3816           (message-narrow-to-head)
3817           (setq from (message-fetch-field "from")
3818                 sender (message-fetch-field "sender")
3819                 newsgroups (message-fetch-field "newsgroups")
3820                 message-id (message-fetch-field "message-id" t)
3821                 distribution (message-fetch-field "distribution")))
3822         ;; Make sure that this article was written by the user.
3823         (unless (or (and sender
3824                          (string-equal
3825                           (downcase sender)
3826                           (downcase (message-make-sender))))
3827                     (string-equal
3828                      (downcase (cadr (mail-extract-address-components from)))
3829                      (downcase (cadr (mail-extract-address-components
3830                                       (message-make-from))))))
3831           (error "This article is not yours"))
3832         ;; Make control message.
3833         (setq buf (set-buffer (get-buffer-create " *message cancel*")))
3834         (buffer-disable-undo (current-buffer))
3835         (erase-buffer)
3836         (insert "Newsgroups: " newsgroups "\n"
3837                 "From: " (message-make-from) "\n"
3838                 "Subject: cmsg cancel " message-id "\n"
3839                 "Control: cancel " message-id "\n"
3840                 (if distribution
3841                     (concat "Distribution: " distribution "\n")
3842                   "")
3843                 mail-header-separator "\n"
3844                 message-cancel-message)
3845         (message "Canceling your article...")
3846         (if (let ((message-syntax-checks
3847                    'dont-check-for-anything-just-trust-me)
3848                   (message-encoding-buffer (current-buffer))
3849                   (message-edit-buffer (current-buffer)))
3850               (message-send-news))
3851             (message "Canceling your article...done"))
3852         (kill-buffer buf)))))
3853
3854 (defun message-supersede-setup-for-mime-edit ()
3855   (set (make-local-variable 'message-setup-hook) nil)
3856   (mime-edit-again))
3857
3858 ;;;###autoload
3859 (defun message-supersede ()
3860   "Start composing a message to supersede the current message.
3861 This is done simply by taking the old article and adding a Supersedes
3862 header line with the old Message-ID."
3863   (interactive)
3864   (let ((cur (current-buffer))
3865         (sender (message-fetch-field "sender"))
3866         (from (message-fetch-field "from")))
3867     ;; Check whether the user owns the article that is to be superseded.
3868     (unless (or (and sender
3869                      (string-equal
3870                       (downcase sender)
3871                       (downcase (message-make-sender))))
3872                 (string-equal
3873                  (downcase (cadr (mail-extract-address-components from)))
3874                  (downcase (cadr (mail-extract-address-components
3875                                   (message-make-from))))))
3876       (error "This article is not yours"))
3877     ;; Get a normal message buffer.
3878     (message-pop-to-buffer (message-buffer-name "supersede"))
3879     (insert-buffer-substring cur)
3880     (message-narrow-to-head)
3881     ;; Remove unwanted headers.
3882     (when message-ignored-supersedes-headers
3883       (message-remove-header message-ignored-supersedes-headers t))
3884     (goto-char (point-min))
3885     (if (not (re-search-forward "^Message-ID: " nil t))
3886         (error "No Message-ID in this article")
3887       (replace-match "Supersedes: " t t))
3888     (goto-char (point-max))
3889     (insert mail-header-separator)
3890     (widen)
3891     (when message-supersede-setup-function
3892       (funcall message-supersede-setup-function))
3893     (run-hooks 'message-supersede-setup-hook)
3894     (goto-char (point-min))
3895     (search-forward (concat "\n" mail-header-separator "\n") nil t)))
3896
3897 ;;;###autoload
3898 (defun message-recover ()
3899   "Reread contents of current buffer from its last auto-save file."
3900   (interactive)
3901   (let ((file-name (make-auto-save-file-name)))
3902     (cond ((save-window-excursion
3903              (if (not (eq system-type 'vax-vms))
3904                  (with-output-to-temp-buffer "*Directory*"
3905                    (buffer-disable-undo standard-output)
3906                    (let ((default-directory "/"))
3907                      (call-process
3908                       "ls" nil standard-output nil "-l" file-name))))
3909              (yes-or-no-p (format "Recover auto save file %s? " file-name)))
3910            (let ((buffer-read-only nil))
3911              (erase-buffer)
3912              (insert-file-contents file-name nil)))
3913           (t (error "message-recover cancelled")))))
3914
3915 ;;; Washing Subject:
3916
3917 (defun message-wash-subject (subject)
3918   "Remove junk like \"Re:\", \"(fwd)\", etc. that was added to the subject by previous forwarders, replyers, etc."
3919   (nnheader-temp-write nil
3920     (insert-string subject)
3921     (goto-char (point-min))
3922     ;; strip Re/Fwd stuff off the beginning
3923     (while (re-search-forward
3924             "\\([Rr][Ee]:\\|[Ff][Ww][Dd]\\(\\[[0-9]*\\]\\)?:\\|[Ff][Ww]:\\)" nil t)
3925       (replace-match ""))
3926
3927     ;; and gnus-style forwards [foo@bar.com] subject
3928     (goto-char (point-min))
3929     (while (re-search-forward "\\[[^ \t]*\\(@\\|\\.\\)[^ \t]*\\]" nil t)
3930       (replace-match ""))
3931
3932     ;; and off the end
3933     (goto-char (point-max))
3934     (while (re-search-backward "([Ff][Ww][Dd])" nil t)
3935       (replace-match ""))
3936
3937     ;; and finally, any whitespace that was left-over
3938     (goto-char (point-min))
3939     (while (re-search-forward "^[ \t]+" nil t)
3940       (replace-match ""))
3941     (goto-char (point-max))
3942     (while (re-search-backward "[ \t]+$" nil t)
3943       (replace-match ""))
3944
3945     (buffer-string)))
3946     
3947 ;;; Forwarding messages.
3948
3949 (defun message-forward-subject-author-subject (subject)
3950   "Generate a subject for a forwarded message.
3951 The form is: [Source] Subject, where if the original message was mail,
3952 Source is the sender, and if the original message was news, Source is
3953 the list of newsgroups is was posted to."
3954   (concat "["
3955           (or (message-fetch-field
3956                (if (message-news-p) "newsgroups" "from"))
3957               "(nowhere)")
3958           "] " subject))
3959
3960 (defun message-forward-subject-fwd (subject)
3961   "Generate a subject for a forwarded message.
3962 The form is: Fwd: Subject, where Subject is the original subject of
3963 the message."
3964   (concat "Fwd: " subject))
3965
3966 (defun message-make-forward-subject ()
3967   "Return a Subject header suitable for the message in the current buffer."
3968   (save-excursion
3969     (save-restriction
3970       (current-buffer)
3971       (message-narrow-to-head)
3972       (let ((funcs message-make-forward-subject-function)
3973             (subject (if message-wash-forwarded-subjects
3974                          (message-wash-subject
3975                           (or (nnheader-decode-subject
3976                                (message-fetch-field "Subject"))
3977                               ""))
3978                        (or (nnheader-decode-subject
3979                             (message-fetch-field "Subject"))
3980                            ""))))
3981         ;; Make sure funcs is a list.
3982         (and funcs
3983              (not (listp funcs))
3984              (setq funcs (list funcs)))
3985         ;; Apply funcs in order, passing subject generated by previous
3986         ;; func to the next one.
3987         (while funcs
3988           (when (message-functionp (car funcs))
3989             (setq subject (funcall (car funcs) subject)))
3990           (setq funcs (cdr funcs)))
3991         subject))))
3992
3993 ;;;###autoload
3994 (defun message-forward (&optional news)
3995   "Forward the current message via mail.
3996 Optional NEWS will use news to forward instead of mail."
3997   (interactive "P")
3998   (let ((cur (current-buffer))
3999         (subject (message-make-forward-subject))
4000         art-beg)
4001     (if news (message-news nil subject) (message-mail nil subject))
4002     ;; Put point where we want it before inserting the forwarded
4003     ;; message.
4004     (if message-signature-before-forwarded-message
4005         (goto-char (point-max))
4006       (message-goto-body))
4007     ;; Make sure we're at the start of the line.
4008     (unless (eolp)
4009       (insert "\n"))
4010     ;; Narrow to the area we are to insert.
4011     (narrow-to-region (point) (point))
4012     ;; Insert the separators and the forwarded buffer.
4013     (insert message-forward-start-separator)
4014     (setq art-beg (point))
4015     (insert-buffer-substring cur)
4016     (goto-char (point-max))
4017     (insert message-forward-end-separator)
4018     (set-text-properties (point-min) (point-max) nil)
4019     ;; Remove all unwanted headers.
4020     (goto-char art-beg)
4021     (narrow-to-region (point) (if (search-forward "\n\n" nil t)
4022                                   (1- (point))
4023                                 (point)))
4024     (goto-char (point-min))
4025     (message-remove-header message-included-forward-headers t nil t)
4026     (widen)
4027     (message-position-point)))
4028
4029 ;;;###autoload
4030 (defun message-resend (address)
4031   "Resend the current article to ADDRESS."
4032   (interactive "sResend message to: ")
4033   (message "Resending message to %s..." address)
4034   (save-excursion
4035     (let ((cur (current-buffer))
4036           beg)
4037       ;; We first set up a normal mail buffer.
4038       (set-buffer (get-buffer-create " *message resend*"))
4039       (buffer-disable-undo (current-buffer))
4040       (erase-buffer)
4041       ;; avoid to turn-on-mime-edit
4042       (let (message-setup-hook)
4043         (message-setup `((To . ,address)))
4044         )
4045       ;; Insert our usual headers.
4046       (message-generate-headers '(From Date To))
4047       (message-narrow-to-headers)
4048       ;; Rename them all to "Resent-*".
4049       (while (re-search-forward "^[A-Za-z]" nil t)
4050         (forward-char -1)
4051         (insert "Resent-"))
4052       (widen)
4053       (forward-line)
4054       (delete-region (point) (point-max))
4055       (setq beg (point))
4056       ;; Insert the message to be resent.
4057       (insert-buffer-substring cur)
4058       (goto-char (point-min))
4059       (search-forward "\n\n")
4060       (forward-char -1)
4061       (save-restriction
4062         (narrow-to-region beg (point))
4063         (message-remove-header message-ignored-resent-headers t)
4064         (goto-char (point-max)))
4065       (insert mail-header-separator)
4066       ;; Rename all old ("Also-")Resent headers.
4067       (while (re-search-backward "^\\(Also-\\)*Resent-" beg t)
4068         (beginning-of-line)
4069         (insert "Also-"))
4070       ;; Quote any "From " lines at the beginning.
4071       (goto-char beg)
4072       (when (looking-at "From ")
4073         (replace-match "X-From-Line: "))
4074       ;; Send it.
4075       (let ((message-encoding-buffer (current-buffer))
4076             (message-edit-buffer (current-buffer)))
4077         (message-send-mail))
4078       (kill-buffer (current-buffer)))
4079     (message "Resending message to %s...done" address)))
4080
4081 (defun message-bounce-setup-for-mime-edit ()
4082   (set (make-local-variable 'message-setup-hook) nil)
4083   (mime-edit-again))
4084
4085 ;;;###autoload
4086 (defun message-bounce ()
4087   "Re-mail the current message.
4088 This only makes sense if the current message is a bounce message than
4089 contains some mail you have written which has been bounced back to
4090 you."
4091   (interactive)
4092   (let ((cur (current-buffer))
4093         boundary)
4094     (message-pop-to-buffer (message-buffer-name "bounce"))
4095     (insert-buffer-substring cur)
4096     (undo-boundary)
4097     (message-narrow-to-head)
4098     (if (and (message-fetch-field "MIME-Version")
4099              (setq boundary (message-fetch-field "Content-Type")))
4100         (if (string-match "boundary=\"\\([^\"]+\\)\"" boundary)
4101             (setq boundary (concat (match-string 1 boundary) " *\n"
4102                                    "Content-Type: message/rfc822"))
4103           (setq boundary nil)))
4104     (widen)
4105     (goto-char (point-min))
4106     (search-forward "\n\n" nil t)
4107     (or (and boundary
4108              (re-search-forward boundary nil t)
4109              (forward-line 2))
4110         (and (re-search-forward message-unsent-separator nil t)
4111              (forward-line 1))
4112         (re-search-forward "^Return-Path:.*\n" nil t))
4113     ;; We remove everything before the bounced mail.
4114     (delete-region
4115      (point-min)
4116      (if (re-search-forward "^[^ \n\t]+:" nil t)
4117          (match-beginning 0)
4118        (point)))
4119     (save-restriction
4120       (message-narrow-to-head)
4121       (message-remove-header message-ignored-bounced-headers t)
4122       (goto-char (point-max))
4123       (insert mail-header-separator))
4124     (when message-bounce-setup-function
4125       (funcall message-bounce-setup-function))
4126     (run-hooks 'message-bounce-setup-hook)
4127     (message-position-point)))
4128
4129 ;;;
4130 ;;; Interactive entry points for new message buffers.
4131 ;;;
4132
4133 ;;;###autoload
4134 (defun message-mail-other-window (&optional to subject)
4135   "Like `message-mail' command, but display mail buffer in another window."
4136   (interactive)
4137   (let ((pop-up-windows t)
4138         (special-display-buffer-names nil)
4139         (special-display-regexps nil)
4140         (same-window-buffer-names nil)
4141         (same-window-regexps nil))
4142     (message-pop-to-buffer (message-buffer-name "mail" to)))
4143   (let ((message-this-is-mail t))
4144     (message-setup `((To . ,(or to "")) (Subject . ,(or subject ""))))))
4145
4146 ;;;###autoload
4147 (defun message-mail-other-frame (&optional to subject)
4148   "Like `message-mail' command, but display mail buffer in another frame."
4149   (interactive)
4150   (let ((pop-up-frames t)
4151         (special-display-buffer-names nil)
4152         (special-display-regexps nil)
4153         (same-window-buffer-names nil)
4154         (same-window-regexps nil))
4155     (message-pop-to-buffer (message-buffer-name "mail" to)))
4156   (let ((message-this-is-mail t))
4157     (message-setup `((To . ,(or to "")) (Subject . ,(or subject ""))))))
4158
4159 ;;;###autoload
4160 (defun message-news-other-window (&optional newsgroups subject)
4161   "Start editing a news article to be sent."
4162   (interactive)
4163   (let ((pop-up-windows t)
4164         (special-display-buffer-names nil)
4165         (special-display-regexps nil)
4166         (same-window-buffer-names nil)
4167         (same-window-regexps nil))
4168     (message-pop-to-buffer (message-buffer-name "news" nil newsgroups)))
4169   (let ((message-this-is-news t))
4170     (message-setup `((Newsgroups . ,(or newsgroups ""))
4171                      (Subject . ,(or subject ""))))))
4172
4173 ;;;###autoload
4174 (defun message-news-other-frame (&optional newsgroups subject)
4175   "Start editing a news article to be sent."
4176   (interactive)
4177   (let ((pop-up-frames t)
4178         (special-display-buffer-names nil)
4179         (special-display-regexps nil)
4180         (same-window-buffer-names nil)
4181         (same-window-regexps nil))
4182     (message-pop-to-buffer (message-buffer-name "news" nil newsgroups)))
4183   (let ((message-this-is-news t))
4184     (message-setup `((Newsgroups . ,(or newsgroups ""))
4185                      (Subject . ,(or subject ""))))))
4186
4187 ;;; underline.el
4188
4189 ;; This code should be moved to underline.el (from which it is stolen).
4190
4191 ;;;###autoload
4192 (defun bold-region (start end)
4193   "Bold all nonblank characters in the region.
4194 Works by overstriking characters.
4195 Called from program, takes two arguments START and END
4196 which specify the range to operate on."
4197   (interactive "r")
4198   (save-excursion
4199     (let ((end1 (make-marker)))
4200       (move-marker end1 (max start end))
4201       (goto-char (min start end))
4202       (while (< (point) end1)
4203         (or (looking-at "[_\^@- ]")
4204             (insert (following-char) "\b"))
4205         (forward-char 1)))))
4206
4207 ;;;###autoload
4208 (defun unbold-region (start end)
4209   "Remove all boldness (overstruck characters) in the region.
4210 Called from program, takes two arguments START and END
4211 which specify the range to operate on."
4212   (interactive "r")
4213   (save-excursion
4214     (let ((end1 (make-marker)))
4215       (move-marker end1 (max start end))
4216       (goto-char (min start end))
4217       (while (re-search-forward "\b" end1 t)
4218         (if (eq (following-char) (char-after (- (point) 2)))
4219             (delete-char -2))))))
4220
4221 (defalias 'message-exchange-point-and-mark 'exchange-point-and-mark)
4222
4223 ;; Support for toolbar
4224 (when (string-match "XEmacs\\|Lucid" emacs-version)
4225   (require 'messagexmas))
4226
4227 ;;; Group name completion.
4228
4229 (defvar message-newgroups-header-regexp
4230   "^\\(Newsgroups\\|Followup-To\\|Posted-To\\|Gcc\\):"
4231   "Regexp that match headers that lists groups.")
4232
4233 (defun message-tab ()
4234   "Expand group names in Newsgroups and Followup-To headers.
4235 Do a `tab-to-tab-stop' if not in those headers."
4236   (interactive)
4237   (if (let ((mail-abbrev-mode-regexp message-newgroups-header-regexp))
4238         (mail-abbrev-in-expansion-header-p))
4239       (message-expand-group)
4240     (tab-to-tab-stop)))
4241
4242 (defvar gnus-active-hashtb)
4243 (defun message-expand-group ()
4244   "Expand the group name under point."
4245   (let* ((b (save-excursion
4246               (save-restriction
4247                 (narrow-to-region
4248                  (save-excursion
4249                    (beginning-of-line)
4250                    (skip-chars-forward "^:")
4251                    (1+ (point)))
4252                  (point))
4253                 (skip-chars-backward "^, \t\n") (point))))
4254          (completion-ignore-case t)
4255          (string (buffer-substring b (progn (skip-chars-forward "^,\t\n ")
4256                                             (point))))
4257          (hashtb (and (boundp 'gnus-active-hashtb) gnus-active-hashtb))
4258          (completions (all-completions string hashtb))
4259          comp)
4260     (delete-region b (point))
4261     (cond
4262      ((= (length completions) 1)
4263       (if (string= (car completions) string)
4264           (progn
4265             (insert string)
4266             (message "Only matching group"))
4267         (insert (car completions))))
4268      ((and (setq comp (try-completion string hashtb))
4269            (not (string= comp string)))
4270       (insert comp))
4271      (t
4272       (insert string)
4273       (if (not comp)
4274           (message "No matching groups")
4275         (save-selected-window
4276           (pop-to-buffer "*Completions*")
4277           (buffer-disable-undo (current-buffer))
4278           (let ((buffer-read-only nil))
4279             (erase-buffer)
4280             (let ((standard-output (current-buffer)))
4281               (display-completion-list (sort completions 'string<)))
4282             (goto-char (point-min))
4283             (delete-region (point) (progn (forward-line 3) (point))))))))))
4284
4285 ;;; Help stuff.
4286
4287 (defun message-talkative-question (ask question show &rest text)
4288   "Call FUNCTION with argument QUESTION; optionally display TEXT... args.
4289 If SHOW is non-nil, the arguments TEXT... are displayed in a temp buffer.
4290 The following arguments may contain lists of values."
4291   (if (and show
4292            (setq text (message-flatten-list text)))
4293       (save-window-excursion
4294         (save-excursion
4295           (with-output-to-temp-buffer " *MESSAGE information message*"
4296             (set-buffer " *MESSAGE information message*")
4297             (mapcar 'princ text)
4298             (goto-char (point-min))))
4299         (funcall ask question))
4300     (funcall ask question)))
4301
4302 (defun message-flatten-list (list)
4303   "Return a new, flat list that contains all elements of LIST.
4304
4305 \(message-flatten-list '(1 (2 3 (4 5 (6))) 7))
4306 => (1 2 3 4 5 6 7)"
4307   (cond ((consp list)
4308          (apply 'append (mapcar 'message-flatten-list list)))
4309         (list
4310          (list list))))
4311
4312 (defun message-generate-new-buffer-clone-locals (name &optional varstr)
4313   "Create and return a buffer with a name based on NAME using generate-new-buffer.
4314 Then clone the local variables and values from the old buffer to the
4315 new one, cloning only the locals having a substring matching the
4316 regexp varstr."
4317   (let ((oldbuf (current-buffer)))
4318     (save-excursion
4319       (set-buffer (generate-new-buffer name))
4320       (message-clone-locals oldbuf)
4321       (current-buffer))))
4322
4323 (defun message-clone-locals (buffer)
4324   "Clone the local variables from BUFFER to the current buffer."
4325   (let ((locals (save-excursion
4326                   (set-buffer buffer)
4327                   (buffer-local-variables)))
4328         (regexp "^\\(gnus\\|nn\\|message\\|user-\\(mail-address\\|full-name\\)\\)"))
4329     (mapcar
4330      (lambda (local)
4331        (when (and (consp local)
4332                   (car local)
4333                   (string-match regexp (symbol-name (car local))))
4334          (ignore-errors
4335            (set (make-local-variable (car local))
4336                 (cdr local)))))
4337      locals)))
4338
4339 ;;; @ for MIME Edit mode
4340 ;;;
4341
4342 (defun message-maybe-encode ()
4343   (when message-mime-mode
4344     (run-hooks 'mime-edit-translate-hook)
4345     (if (catch 'mime-edit-error
4346           (save-excursion
4347             (mime-edit-translate-body)
4348             ))
4349         (error "Translation error!")
4350       )
4351     (end-of-invisible)
4352     (run-hooks 'mime-edit-exit-hook)
4353     ))
4354
4355 (defun message-mime-insert-article (&optional message)
4356   (interactive)
4357   (let ((message-cite-function 'mime-edit-inserted-message-filter)
4358         (message-reply-buffer
4359          (message-get-parameter-with-eval 'original-buffer))
4360         (start (point)))
4361     (message-yank-original nil)
4362     (save-excursion
4363       (narrow-to-region (goto-char start)
4364                         (if (search-forward "\n\n" nil t)
4365                             (1- (point))
4366                           (point-max)))
4367       (goto-char (point-min))
4368       (message-remove-header message-included-forward-headers t nil t)
4369       (widen))))
4370
4371 (set-alist 'mime-edit-message-inserter-alist
4372            'message-mode (function message-mime-insert-article))
4373
4374 (defun message-mime-encode (start end &optional orig-buf)
4375   (save-restriction
4376     (narrow-to-region start end)
4377     (when (with-current-buffer orig-buf
4378             mime-edit-mode-flag)
4379       (run-hooks 'mime-edit-translate-hook)
4380       (mime-edit-translate-buffer)
4381       )
4382     (goto-char start)
4383     (and (search-forward (concat "\n" mail-header-separator "\n") nil t)
4384          (replace-match "\n\n"))
4385     ))
4386
4387 (set-alist 'format-alist
4388            'mime-message
4389            '("MIME message."
4390              "1\\(^\\)"
4391              nil
4392              message-mime-encode
4393              t nil))
4394
4395 (defun message-mime-setup ()
4396   (turn-on-mime-edit)
4397   (add-to-list 'buffer-file-format 'mime-message))
4398
4399 ;;; Miscellaneous functions
4400
4401 ;; stolen (and renamed) from nnheader.el
4402 (defun message-replace-chars-in-string (string from to)
4403   "Replace characters in STRING from FROM to TO."
4404   (let ((string (substring string 0))   ;Copy string.
4405         (len (length string))
4406         (idx 0))
4407     ;; Replace all occurrences of FROM with TO.
4408     (while (< idx len)
4409       (when (= (aref string idx) from)
4410         (aset string idx to))
4411       (setq idx (1+ idx)))
4412     string))
4413
4414 (run-hooks 'message-load-hook)
4415
4416 (provide 'message)
4417
4418 ;;; message.el ends here