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