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