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