2001-03-16 16:00:00 ShengHuo ZHU <zsh@cs.rochester.edu>
[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
44 (defgroup message '((user-mail-address custom-variable)
45                     (user-full-name custom-variable))
46   "Mail and news message composing."
47   :link '(custom-manual "(message)Top")
48   :group 'mail
49   :group 'news)
50
51 (put 'user-mail-address 'custom-type 'string)
52 (put 'user-full-name 'custom-type 'string)
53
54 (defgroup message-various nil
55   "Various Message Variables"
56   :link '(custom-manual "(message)Various Message Variables")
57   :group 'message)
58
59 (defgroup message-buffers nil
60   "Message Buffers"
61   :link '(custom-manual "(message)Message Buffers")
62   :group 'message)
63
64 (defgroup message-sending nil
65   "Message Sending"
66   :link '(custom-manual "(message)Sending Variables")
67   :group 'message)
68
69 (defgroup message-interface nil
70   "Message Interface"
71   :link '(custom-manual "(message)Interface")
72   :group 'message)
73
74 (defgroup message-forwarding nil
75   "Message Forwarding"
76   :link '(custom-manual "(message)Forwarding")
77   :group 'message-interface)
78
79 (defgroup message-insertion nil
80   "Message Insertion"
81   :link '(custom-manual "(message)Insertion")
82   :group 'message)
83
84 (defgroup message-headers nil
85   "Message Headers"
86   :link '(custom-manual "(message)Message Headers")
87   :group 'message)
88
89 (defgroup message-news nil
90   "Composing News Messages"
91   :group 'message)
92
93 (defgroup message-mail nil
94   "Composing Mail Messages"
95   :group 'message)
96
97 (defgroup message-faces nil
98   "Faces used for message composing."
99   :group 'message
100   :group 'faces)
101
102 (defcustom message-directory "~/Mail/"
103   "*Directory from which all other mail file variables are derived."
104   :group 'message-various
105   :type 'directory)
106
107 (defcustom message-max-buffers 10
108   "*How many buffers to keep before starting to kill them off."
109   :group 'message-buffers
110   :type 'integer)
111
112 (defcustom message-send-rename-function nil
113   "Function called to rename the buffer after sending it."
114   :group 'message-buffers
115   :type '(choice function (const nil)))
116
117 (defcustom message-fcc-handler-function 'message-output
118   "*A function called to save outgoing articles.
119 This function will be called with the name of the file to store the
120 article in.  The default function is `message-output' which saves in Unix
121 mailbox format."
122   :type '(radio (function-item message-output)
123                 (function :tag "Other"))
124   :group 'message-sending)
125
126 (defcustom message-courtesy-message
127   "The following message is a courtesy copy of an article\nthat has been posted to %s as well.\n\n"
128   "*This is inserted at the start of a mailed copy of a posted message.
129 If the string contains the format spec \"%s\", the Newsgroups
130 the article has been posted to will be inserted there.
131 If this variable is nil, no such courtesy message will be added."
132   :group 'message-sending
133   :type 'string)
134
135 (defcustom message-ignored-bounced-headers "^\\(Received\\|Return-Path\\):"
136   "*Regexp that matches headers to be removed in resent bounced mail."
137   :group 'message-interface
138   :type 'regexp)
139
140 ;;;###autoload
141 (defcustom message-from-style 'default
142   "*Specifies how \"From\" headers look.
143
144 If nil, they contain just the return address like:
145         king@grassland.com
146 If `parens', they look like:
147         king@grassland.com (Elvis Parsley)
148 If `angles', they look like:
149         Elvis Parsley <king@grassland.com>
150
151 Otherwise, most addresses look like `angles', but they look like
152 `parens' if `angles' would need quoting and `parens' would not."
153   :type '(choice (const :tag "simple" nil)
154                  (const parens)
155                  (const angles)
156                  (const default))
157   :group 'message-headers)
158
159 (defcustom message-syntax-checks nil
160   ;; Guess this one shouldn't be easy to customize...
161   "*Controls what syntax checks should not be performed on outgoing posts.
162 To disable checking of long signatures, for instance, add
163  `(signature . disabled)' to this list.
164
165 Don't touch this variable unless you really know what you're doing.
166
167 Checks include `subject-cmsg', `multiple-headers', `sendsys',
168 `message-id', `from', `long-lines', `control-chars', `size',
169 `new-text', `quoting-style', `redirected-followup', `signature',
170 `approved', `sender', `empty', `empty-headers', `message-id', `from',
171 `subject', `shorten-followup-to', `existing-newsgroups',
172 `buffer-file-name', `unchanged', `newsgroups'."
173   :group 'message-news
174   :type '(repeat sexp))                 ; Fixme: improve this
175
176 (defcustom message-required-news-headers
177   '(From Newsgroups Subject Date Message-ID
178          (optional . Organization) Lines
179          (optional . User-Agent))
180   "*Headers to be generated or prompted for when posting an article.
181 RFC977 and RFC1036 require From, Date, Newsgroups, Subject,
182 Message-ID.  Organization, Lines, In-Reply-To, Expires, and
183 User-Agent are optional.  If don't you want message to insert some
184 header, remove it from this list."
185   :group 'message-news
186   :group 'message-headers
187   :type '(repeat sexp))
188
189 (defcustom message-required-mail-headers
190   '(From Subject Date (optional . In-Reply-To) Message-ID Lines
191          (optional . User-Agent))
192   "*Headers to be generated or prompted for when mailing a message.
193 RFC822 required that From, Date, To, Subject and Message-ID be
194 included.  Organization, Lines and User-Agent are optional."
195   :group 'message-mail
196   :group 'message-headers
197   :type '(repeat sexp))
198
199 (defcustom message-deletable-headers '(Message-ID Date Lines)
200   "Headers to be deleted if they already exist and were generated by message previously."
201   :group 'message-headers
202   :type 'sexp)
203
204 (defcustom message-ignored-news-headers
205   "^NNTP-Posting-Host:\\|^Xref:\\|^[BGF]cc:\\|^Resent-Fcc:\\|^X-Draft-From:"
206   "*Regexp of headers to be removed unconditionally before posting."
207   :group 'message-news
208   :group 'message-headers
209   :type 'regexp)
210
211 (defcustom message-ignored-mail-headers
212   "^[GF]cc:\\|^Resent-Fcc:\\|^Xref:\\|^X-Draft-From:"
213   "*Regexp of headers to be removed unconditionally before mailing."
214   :group 'message-mail
215   :group 'message-headers
216   :type 'regexp)
217
218 (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:"
219   "*Header lines matching this regexp will be deleted before posting.
220 It's best to delete old Path and Date headers before posting to avoid
221 any confusion."
222   :group 'message-interface
223   :type 'regexp)
224
225 (defcustom message-subject-re-regexp "^[ \t]*\\([Rr][Ee]:[ \t]*\\)*[ \t]*"
226   "*Regexp matching \"Re: \" in the subject line."
227   :group 'message-various
228   :type 'regexp)
229
230 ;;;###autoload
231 (defcustom message-signature-separator "^-- *$"
232   "Regexp matching the signature separator."
233   :type 'regexp
234   :group 'message-various)
235
236 (defcustom message-elide-ellipsis "\n[...]\n\n"
237   "*The string which is inserted for elided text."
238   :type 'string
239   :group 'message-various)
240
241 (defcustom message-interactive nil
242   "Non-nil means when sending a message wait for and display errors.
243 nil means let mailer mail back a message to report errors."
244   :group 'message-sending
245   :group 'message-mail
246   :type 'boolean)
247
248 (defcustom message-generate-new-buffers 'unique
249   "*Non-nil means create a new message buffer whenever `message-setup' is called.
250 If this is a function, call that function with three parameters:  The type,
251 the to address and the group name.  (Any of these may be nil.)  The function
252 should return the new buffer name."
253   :group 'message-buffers
254   :type '(choice (const :tag "off" nil)
255                  (const :tag "unique" unique)
256                  (const :tag "unsent" unsent)
257                  (function fun)))
258
259 (defcustom message-kill-buffer-on-exit nil
260   "*Non-nil means that the message buffer will be killed after sending a message."
261   :group 'message-buffers
262   :type 'boolean)
263
264 (eval-when-compile
265   (defvar gnus-local-organization))
266 (defcustom message-user-organization
267   (or (and (boundp 'gnus-local-organization)
268            (stringp gnus-local-organization)
269            gnus-local-organization)
270       (getenv "ORGANIZATION")
271       t)
272   "*String to be used as an Organization header.
273 If t, use `message-user-organization-file'."
274   :group 'message-headers
275   :type '(choice string
276                  (const :tag "consult file" t)))
277
278 ;;;###autoload
279 (defcustom message-user-organization-file "/usr/lib/news/organization"
280   "*Local news organization file."
281   :type 'file
282   :group 'message-headers)
283
284 (defcustom message-make-forward-subject-function
285   'message-forward-subject-author-subject
286   "*List of functions called to generate subject headers for forwarded messages.
287 The subject generated by the previous function is passed into each
288 successive function.
289
290 The provided functions are:
291
292 * `message-forward-subject-author-subject' (Source of article (author or
293       newsgroup)), in brackets followed by the subject
294 * `message-forward-subject-fwd' (Subject of article with 'Fwd:' prepended
295       to it."
296   :group 'message-forwarding
297   :type '(radio (function-item message-forward-subject-author-subject)
298                 (function-item message-forward-subject-fwd)
299                 (repeat :tag "List of functions" function)))
300
301 (defcustom message-forward-as-mime t
302   "*If non-nil, forward messages as an inline/rfc822 MIME section.  Otherwise, directly inline the old message in the forwarded message."
303   :version "21.1"
304   :group 'message-forwarding
305   :type 'boolean)
306
307 (defcustom message-forward-show-mml t
308   "*If non-nil, forward messages are shown as mml.  Otherwise, forward messages are unchanged."
309   :version "21.1"
310   :group 'message-forwarding
311   :type 'boolean)
312
313 (defcustom message-forward-before-signature t
314   "*If non-nil, put forwarded message before signature, else after."
315   :group 'message-forwarding
316   :type 'boolean)
317
318 (defcustom message-wash-forwarded-subjects nil
319   "*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."
320   :group 'message-forwarding
321   :type 'boolean)
322
323 (defcustom message-ignored-resent-headers "^Return-receipt\\|^X-Gnus\\|^Gnus-Warning:"
324   "*All headers that match this regexp will be deleted when resending a message."
325   :group 'message-interface
326   :type 'regexp)
327
328 (defcustom message-forward-ignored-headers "^Content-Transfer-Encoding:\\|^X-Gnus"
329   "*All headers that match this regexp will be deleted when forwarding a message."
330   :version "21.1"
331   :group 'message-forwarding
332   :type '(choice (const :tag "None" nil)
333                  regexp))
334
335 (defcustom message-ignored-cited-headers "."
336   "*Delete these headers from the messages you yank."
337   :group 'message-insertion
338   :type 'regexp)
339
340 (defcustom message-cite-prefix-regexp
341   ;; ?-, ?_ or ?. MUST NOT be in syntax entry w.
342   "\\([ \t]*\\(\\w\\|[-_.]\\)+>+\\|[ \t]*[]>»|:}+]\\)+"
343   "*Regexp matching the longest possible citation prefix on a line."
344   :group 'message-insertion
345   :type 'regexp)
346
347 (defcustom message-cancel-message "I am canceling my own article.\n"
348   "Message to be inserted in the cancel message."
349   :group 'message-interface
350   :type 'string)
351
352 ;; Useful to set in site-init.el
353 ;;;###autoload
354 (defcustom message-send-mail-function 'message-send-mail-with-sendmail
355   "Function to call to send the current buffer as mail.
356 The headers should be delimited by a line whose contents match the
357 variable `mail-header-separator'.
358
359 Valid values include `message-send-mail-with-sendmail' (the default),
360 `message-send-mail-with-mh', `message-send-mail-with-qmail',
361 `smtpmail-send-it' and `feedmail-send-it'.
362
363 See also `send-mail-function'."
364   :type '(radio (function-item message-send-mail-with-sendmail)
365                 (function-item message-send-mail-with-mh)
366                 (function-item message-send-mail-with-qmail)
367                 (function-item smtpmail-send-it)
368                 (function-item feedmail-send-it)
369                 (function :tag "Other"))
370   :group 'message-sending
371   :group 'message-mail)
372
373 (defcustom message-send-news-function 'message-send-news
374   "Function to call to send the current buffer as news.
375 The headers should be delimited by a line whose contents match the
376 variable `mail-header-separator'."
377   :group 'message-sending
378   :group 'message-news
379   :type 'function)
380
381 (defcustom message-reply-to-function nil
382   "If non-nil, function that should return a list of headers.
383 This function should pick out addresses from the To, Cc, and From headers
384 and respond with new To and Cc headers."
385   :group 'message-interface
386   :type '(choice function (const nil)))
387
388 (defcustom message-wide-reply-to-function nil
389   "If non-nil, function that should return a list of headers.
390 This function should pick out addresses from the To, Cc, and From headers
391 and respond with new To and Cc headers."
392   :group 'message-interface
393   :type '(choice function (const nil)))
394
395 (defcustom message-followup-to-function nil
396   "If non-nil, function that should return a list of headers.
397 This function should pick out addresses from the To, Cc, and From headers
398 and respond with new To and Cc headers."
399   :group 'message-interface
400   :type '(choice function (const nil)))
401
402 (defcustom message-use-followup-to 'ask
403   "*Specifies what to do with Followup-To header.
404 If nil, always ignore the header.  If it is t, use its value, but
405 query before using the \"poster\" value.  If it is the symbol `ask',
406 always query the user whether to use the value.  If it is the symbol
407 `use', always use the value."
408   :group 'message-interface
409   :type '(choice (const :tag "ignore" nil)
410                  (const use)
411                  (const ask)))
412
413 (defcustom message-sendmail-f-is-evil nil
414   "*Non-nil means don't add \"-f username\" to the sendmail command line.
415 Doing so would be even more evil than leaving it out."
416   :group 'message-sending
417   :type 'boolean)
418
419 ;; qmail-related stuff
420 (defcustom message-qmail-inject-program "/var/qmail/bin/qmail-inject"
421   "Location of the qmail-inject program."
422   :group 'message-sending
423   :type 'file)
424
425 (defcustom message-qmail-inject-args nil
426   "Arguments passed to qmail-inject programs.
427 This should be a list of strings, one string for each argument.
428
429 For e.g., if you wish to set the envelope sender address so that bounces
430 go to the right place or to deal with listserv's usage of that address, you
431 might set this variable to '(\"-f\" \"you@some.where\")."
432   :group 'message-sending
433   :type '(repeat string))
434
435 (defvar message-cater-to-broken-inn t
436   "Non-nil means Gnus should not fold the `References' header.
437 Folding `References' makes ancient versions of INN create incorrect
438 NOV lines.")
439
440 (eval-when-compile
441   (defvar gnus-post-method)
442   (defvar gnus-select-method))
443 (defcustom message-post-method
444   (cond ((and (boundp 'gnus-post-method)
445               (listp gnus-post-method)
446               gnus-post-method)
447          gnus-post-method)
448         ((boundp 'gnus-select-method)
449          gnus-select-method)
450         (t '(nnspool "")))
451   "*Method used to post news.
452 Note that when posting from inside Gnus, for instance, this
453 variable isn't used."
454   :group 'message-news
455   :group 'message-sending
456   ;; This should be the `gnus-select-method' widget, but that might
457   ;; create a dependence to `gnus.el'.
458   :type 'sexp)
459
460 (defcustom message-generate-headers-first nil
461   "*If non-nil, generate all required headers before composing.
462 The variables `message-required-news-headers' and
463 `message-required-mail-headers' specify which headers to generate.
464
465 Note that the variable `message-deletable-headers' specifies headers which
466 are to be deleted and then re-generated before sending, so this variable
467 will not have a visible effect for those headers."
468   :group 'message-headers
469   :type 'boolean)
470
471 (defcustom message-setup-hook nil
472   "Normal hook, run each time a new outgoing message is initialized.
473 The function `message-setup' runs this hook."
474   :group 'message-various
475   :type 'hook)
476
477 (defcustom message-cancel-hook nil
478   "Hook run when cancelling articles."
479   :group 'message-various
480   :type 'hook)
481
482 (defcustom message-signature-setup-hook nil
483   "Normal hook, run each time a new outgoing message is initialized.
484 It is run after the headers have been inserted and before
485 the signature is inserted."
486   :group 'message-various
487   :type 'hook)
488
489 (defcustom message-mode-hook nil
490   "Hook run in message mode buffers."
491   :group 'message-various
492   :type 'hook)
493
494 (defcustom message-header-hook nil
495   "Hook run in a message mode buffer narrowed to the headers."
496   :group 'message-various
497   :type 'hook)
498
499 (defcustom message-header-setup-hook nil
500   "Hook called narrowed to the headers when setting up a message buffer."
501   :group 'message-various
502   :type 'hook)
503
504 (defcustom message-minibuffer-local-map
505   (let ((map (make-sparse-keymap 'message-minibuffer-local-map)))
506     (set-keymap-parent map minibuffer-local-map)
507     map)
508   "Keymap for `message-read-from-minibuffer'.")
509
510 ;;;###autoload
511 (defcustom message-citation-line-function 'message-insert-citation-line
512   "*Function called to insert the \"Whomever writes:\" line."
513   :type 'function
514   :group 'message-insertion)
515
516 ;;;###autoload
517 (defcustom message-yank-prefix "> "
518   "*Prefix inserted on the lines of yanked messages.
519 Fix `message-cite-prefix-regexp' if it is set to an abnormal value."
520   :type 'string
521   :group 'message-insertion)
522
523 (defcustom message-indentation-spaces 3
524   "*Number of spaces to insert at the beginning of each cited line.
525 Used by `message-yank-original' via `message-yank-cite'."
526   :group 'message-insertion
527   :type 'integer)
528
529 ;;;###autoload
530 (defcustom message-cite-function 'message-cite-original
531   "*Function for citing an original message.
532 Predefined functions include `message-cite-original' and
533 `message-cite-original-without-signature'.
534 Note that `message-cite-original' uses `mail-citation-hook' if that is non-nil."
535   :type '(radio (function-item message-cite-original)
536                 (function-item message-cite-original-without-signature)
537                 (function-item sc-cite-original)
538                 (function :tag "Other"))
539   :group 'message-insertion)
540
541 ;;;###autoload
542 (defcustom message-indent-citation-function 'message-indent-citation
543   "*Function for modifying a citation just inserted in the mail buffer.
544 This can also be a list of functions.  Each function can find the
545 citation between (point) and (mark t).  And each function should leave
546 point and mark around the citation text as modified."
547   :type 'function
548   :group 'message-insertion)
549
550 (defvar message-abbrevs-loaded nil)
551
552 ;;;###autoload
553 (defcustom message-signature t
554   "*String to be inserted at the end of the message buffer.
555 If t, the `message-signature-file' file will be inserted instead.
556 If a function, the result from the function will be used instead.
557 If a form, the result from the form will be used instead."
558   :type 'sexp
559   :group 'message-insertion)
560
561 ;;;###autoload
562 (defcustom message-signature-file "~/.signature"
563   "*Name of file containing the text inserted at end of message buffer.
564 Ignored if the named file doesn't exist.
565 If nil, don't insert a signature."
566   :type '(choice file (const :tags "None" nil))
567   :group 'message-insertion)
568
569 (defcustom message-distribution-function nil
570   "*Function called to return a Distribution header."
571   :group 'message-news
572   :group 'message-headers
573   :type '(choice function (const nil)))
574
575 (defcustom message-expires 14
576   "Number of days before your article expires."
577   :group 'message-news
578   :group 'message-headers
579   :link '(custom-manual "(message)News Headers")
580   :type 'integer)
581
582 (defcustom message-user-path nil
583   "If nil, use the NNTP server name in the Path header.
584 If stringp, use this; if non-nil, use no host name (user name only)."
585   :group 'message-news
586   :group 'message-headers
587   :link '(custom-manual "(message)News Headers")
588   :type '(choice (const :tag "nntp" nil)
589                  (string :tag "name")
590                  (sexp :tag "none" :format "%t" t)))
591
592 (defvar message-reply-buffer nil)
593 (defvar message-reply-headers nil)
594 (defvar message-newsreader nil)
595 (defvar message-mailer nil)
596 (defvar message-sent-message-via nil)
597 (defvar message-checksum nil)
598 (defvar message-send-actions nil
599   "A list of actions to be performed upon successful sending of a message.")
600 (defvar message-exit-actions nil
601   "A list of actions to be performed upon exiting after sending a message.")
602 (defvar message-kill-actions nil
603   "A list of actions to be performed before killing a message buffer.")
604 (defvar message-postpone-actions nil
605   "A list of actions to be performed after postponing a message.")
606
607 (define-widget 'message-header-lines 'text
608   "All header lines must be LFD terminated."
609   :format "%{%t%}:%n%v"
610   :valid-regexp "^\\'"
611   :error "All header lines must be newline terminated")
612
613 (defcustom message-default-headers ""
614   "*A string containing header lines to be inserted in outgoing messages.
615 It is inserted before you edit the message, so you can edit or delete
616 these lines."
617   :group 'message-headers
618   :type 'message-header-lines)
619
620 (defcustom message-default-mail-headers ""
621   "*A string of header lines to be inserted in outgoing mails."
622   :group 'message-headers
623   :group 'message-mail
624   :type 'message-header-lines)
625
626 (defcustom message-default-news-headers ""
627   "*A string of header lines to be inserted in outgoing news articles."
628   :group 'message-headers
629   :group 'message-news
630   :type 'message-header-lines)
631
632 ;; Note: could use /usr/ucb/mail instead of sendmail;
633 ;; options -t, and -v if not interactive.
634 (defcustom message-mailer-swallows-blank-line
635   (if (and (string-match "sparc-sun-sunos\\(\\'\\|[^5]\\)"
636                          system-configuration)
637            (file-readable-p "/etc/sendmail.cf")
638            (let ((buffer (get-buffer-create " *temp*")))
639              (unwind-protect
640                  (save-excursion
641                    (set-buffer buffer)
642                    (insert-file-contents "/etc/sendmail.cf")
643                    (goto-char (point-min))
644                    (let ((case-fold-search nil))
645                      (re-search-forward "^OR\\>" nil t)))
646                (kill-buffer buffer))))
647       ;; According to RFC822, "The field-name must be composed of printable
648       ;; ASCII characters (i. e., characters that have decimal values between
649       ;; 33 and 126, except colon)", i. e., any chars except ctl chars,
650       ;; space, or colon.
651       '(looking-at "[ \t]\\|[][!\"#$%&'()*+,-./0-9;<=>?@A-Z\\\\^_`a-z{|}~]+:"))
652   "*Set this non-nil if the system's mailer runs the header and body together.
653 \(This problem exists on Sunos 4 when sendmail is run in remote mode.)
654 The value should be an expression to test whether the problem will
655 actually occur."
656   :group 'message-sending
657   :type 'sexp)
658
659 ;;;###autoload
660 (define-mail-user-agent 'message-user-agent
661   'message-mail 'message-send-and-exit
662   'message-kill-buffer 'message-send-hook)
663
664 (defvar message-mh-deletable-headers '(Message-ID Date Lines Sender)
665   "If non-nil, delete the deletable headers before feeding to mh.")
666
667 (defvar message-send-method-alist
668   '((news message-news-p message-send-via-news)
669     (mail message-mail-p message-send-via-mail))
670   "Alist of ways to send outgoing messages.
671 Each element has the form
672
673   \(TYPE PREDICATE FUNCTION)
674
675 where TYPE is a symbol that names the method; PREDICATE is a function
676 called without any parameters to determine whether the message is
677 a message of type TYPE; and FUNCTION is a function to be called if
678 PREDICATE returns non-nil.  FUNCTION is called with one parameter --
679 the prefix.")
680
681 (defcustom message-mail-alias-type 'abbrev
682   "*What alias expansion type to use in Message buffers.
683 The default is `abbrev', which uses mailabbrev.  nil switches
684 mail aliases off."
685   :group 'message
686   :link '(custom-manual "(message)Mail Aliases")
687   :type '(choice (const :tag "Use Mailabbrev" abbrev)
688                  (const :tag "No expansion" nil)))
689
690 (defcustom message-auto-save-directory
691   (file-name-as-directory (nnheader-concat message-directory "drafts"))
692   "*Directory where Message auto-saves buffers if Gnus isn't running.
693 If nil, Message won't auto-save."
694   :group 'message-buffers
695   :type '(choice directory (const :tag "Don't auto-save" nil)))
696
697 (defcustom message-buffer-naming-style 'unique
698   "*The way new message buffers are named.
699 Valid valued are `unique' and `unsent'."
700   :version "21.1"
701   :group 'message-buffers
702   :type '(choice (const :tag "unique" unique)
703                  (const :tag "unsent" unsent)))
704
705 (defcustom message-default-charset
706   (and (not (mm-multibyte-p)) 'iso-8859-1)
707   "Default charset used in non-MULE Emacsen.
708 If nil, you might be asked to input the charset."
709   :version "21.1"
710   :group 'message
711   :type 'symbol)
712
713 (defcustom message-dont-reply-to-names
714   (and (boundp 'rmail-dont-reply-to-names) rmail-dont-reply-to-names)
715   "*A regexp specifying names to prune when doing wide replies.
716 A value of nil means exclude your own name only."
717   :version "21.1"
718   :group 'message
719   :type '(choice (const :tag "Yourself" nil)
720                  regexp))
721
722 (defvar message-shoot-gnksa-feet nil
723   "*A list of GNKSA feet you are allowed to shoot.
724 Gnus gives you all the opportunity you could possibly want for
725 shooting yourself in the foot.  Also, Gnus allows you to shoot the
726 feet of Good Net-Keeping Seal of Approval. The following are foot
727 candidates:
728 `empty-article'     Allow you to post an empty article;
729 `quoted-text-only'  Allow you to post quoted text only;
730 `multiple-copies'   Allow you to post multiple copies.")
731 ;; `cancel-messages'   Allow you to cancel or supersede others' messages.
732
733 (defsubst message-gnksa-enable-p (feature)
734   (or (not (listp message-shoot-gnksa-feet))
735       (memq feature message-shoot-gnksa-feet)))
736
737 ;;; Internal variables.
738 ;;; Well, not really internal.
739
740 (defvar message-mode-syntax-table
741   (let ((table (copy-syntax-table text-mode-syntax-table)))
742     (modify-syntax-entry ?% ". " table)
743     (modify-syntax-entry ?> ". " table)
744     (modify-syntax-entry ?< ". " table)
745     table)
746   "Syntax table used while in Message mode.")
747
748 (defvar message-mode-abbrev-table text-mode-abbrev-table
749   "Abbrev table used in Message mode buffers.
750 Defaults to `text-mode-abbrev-table'.")
751
752 (defface message-header-to-face
753   '((((class color)
754       (background dark))
755      (:foreground "green2" :bold t))
756     (((class color)
757       (background light))
758      (:foreground "MidnightBlue" :bold t))
759     (t
760      (:bold t :italic t)))
761   "Face used for displaying From headers."
762   :group 'message-faces)
763
764 (defface message-header-cc-face
765   '((((class color)
766       (background dark))
767      (:foreground "green4" :bold t))
768     (((class color)
769       (background light))
770      (:foreground "MidnightBlue"))
771     (t
772      (:bold t)))
773   "Face used for displaying Cc headers."
774   :group 'message-faces)
775
776 (defface message-header-subject-face
777   '((((class color)
778       (background dark))
779      (:foreground "green3"))
780     (((class color)
781       (background light))
782      (:foreground "navy blue" :bold t))
783     (t
784      (:bold t)))
785   "Face used for displaying subject headers."
786   :group 'message-faces)
787
788 (defface message-header-newsgroups-face
789   '((((class color)
790       (background dark))
791      (:foreground "yellow" :bold t :italic t))
792     (((class color)
793       (background light))
794      (:foreground "blue4" :bold t :italic t))
795     (t
796      (:bold t :italic t)))
797   "Face used for displaying newsgroups headers."
798   :group 'message-faces)
799
800 (defface message-header-other-face
801   '((((class color)
802       (background dark))
803      (:foreground "#b00000"))
804     (((class color)
805       (background light))
806      (:foreground "steel blue"))
807     (t
808      (:bold t :italic t)))
809   "Face used for displaying newsgroups headers."
810   :group 'message-faces)
811
812 (defface message-header-name-face
813   '((((class color)
814       (background dark))
815      (:foreground "DarkGreen"))
816     (((class color)
817       (background light))
818      (:foreground "cornflower blue"))
819     (t
820      (:bold t)))
821   "Face used for displaying header names."
822   :group 'message-faces)
823
824 (defface message-header-xheader-face
825   '((((class color)
826       (background dark))
827      (:foreground "blue"))
828     (((class color)
829       (background light))
830      (:foreground "blue"))
831     (t
832      (:bold t)))
833   "Face used for displaying X-Header headers."
834   :group 'message-faces)
835
836 (defface message-separator-face
837   '((((class color)
838       (background dark))
839      (:foreground "blue3"))
840     (((class color)
841       (background light))
842      (:foreground "brown"))
843     (t
844      (:bold t)))
845   "Face used for displaying the separator."
846   :group 'message-faces)
847
848 (defface message-cited-text-face
849   '((((class color)
850       (background dark))
851      (:foreground "red"))
852     (((class color)
853       (background light))
854      (:foreground "red"))
855     (t
856      (:bold t)))
857   "Face used for displaying cited text names."
858   :group 'message-faces)
859
860 (defface message-mml-face
861   '((((class color)
862       (background dark))
863      (:foreground "ForestGreen"))
864     (((class color)
865       (background light))
866      (:foreground "ForestGreen"))
867     (t
868      (:bold t)))
869   "Face used for displaying MML."
870   :group 'message-faces)
871
872 (defvar message-font-lock-keywords
873   (let ((content "[ \t]*\\(.+\\(\n[ \t].*\\)*\\)\n?"))
874     `((,(concat "^\\([Tt]o:\\)" content)
875        (1 'message-header-name-face)
876        (2 'message-header-to-face nil t))
877       (,(concat "^\\(^[GBF]?[Cc][Cc]:\\|^[Rr]eply-[Tt]o:\\)" content)
878        (1 'message-header-name-face)
879        (2 'message-header-cc-face nil t))
880       (,(concat "^\\([Ss]ubject:\\)" content)
881        (1 'message-header-name-face)
882        (2 'message-header-subject-face nil t))
883       (,(concat "^\\([Nn]ewsgroups:\\|Followup-[Tt]o:\\)" content)
884        (1 'message-header-name-face)
885        (2 'message-header-newsgroups-face nil t))
886       (,(concat "^\\([A-Z][^: \n\t]+:\\)" content)
887        (1 'message-header-name-face)
888        (2 'message-header-other-face nil t))
889       (,(concat "^\\(X-[A-Za-z0-9-]+\\|In-Reply-To\\):" content)
890        (1 'message-header-name-face)
891        (2 'message-header-name-face))
892       ,@(if (and mail-header-separator
893                  (not (equal mail-header-separator "")))
894             `((,(concat "^\\(" (regexp-quote mail-header-separator) "\\)$")
895                1 'message-separator-face))
896           nil)
897       (,(concat "^\\(" message-cite-prefix-regexp "\\).*")
898        (0 'message-cited-text-face))
899       ("<#/?\\(multipart\\|part\\|external\\|mml\\).*>"
900        (0 'message-mml-face))))
901   "Additional expressions to highlight in Message mode.")
902
903 ;; XEmacs does it like this.  For Emacs, we have to set the
904 ;; `font-lock-defaults' buffer-local variable.
905 (put 'message-mode 'font-lock-defaults '(message-font-lock-keywords t))
906
907 (defvar message-face-alist
908   '((bold . bold-region)
909     (underline . underline-region)
910     (default . (lambda (b e)
911                  (unbold-region b e)
912                  (ununderline-region b e))))
913   "Alist of mail and news faces for facemenu.
914 The cdr of ech entry is a function for applying the face to a region.")
915
916 (defcustom message-send-hook nil
917   "Hook run before sending messages."
918   :group 'message-various
919   :options '(ispell-message)
920   :type 'hook)
921
922 (defcustom message-send-mail-hook nil
923   "Hook run before sending mail messages."
924   :group 'message-various
925   :type 'hook)
926
927 (defcustom message-send-news-hook nil
928   "Hook run before sending news messages."
929   :group 'message-various
930   :type 'hook)
931
932 (defcustom message-sent-hook nil
933   "Hook run after sending messages."
934   :group 'message-various
935   :type 'hook)
936
937 (defvar message-send-coding-system 'binary
938   "Coding system to encode outgoing mail.")
939
940 (defvar message-draft-coding-system
941   mm-auto-save-coding-system
942   "Coding system to compose mail.")
943
944 (defcustom message-send-mail-partially-limit 1000000
945   "The limitation of messages sent as message/partial.
946 The lower bound of message size in characters, beyond which the message
947 should be sent in several parts.  If it is nil, the size is unlimited."
948   :version "21.1"
949   :group 'message-buffers
950   :type '(choice (const :tag "unlimited" nil)
951                  (integer 1000000)))
952
953 (defcustom message-alternative-emails nil
954   "A regexp to match the alternative email addresses.
955 The first matched address (not primary one) is used in the From field."
956   :group 'message-headers
957   :type '(choice (const :tag "Always use primary" nil)
958                  regexp))
959
960 (defcustom message-mail-user-agent nil
961   "Like `mail-user-agent'.
962 Except if it is nil, use Gnus native MUA; if it is t, use
963 `mail-user-agent'."
964   :type '(radio (const :tag "Gnus native"
965                        :format "%t\n"
966                        nil)
967                 (const :tag "`mail-user-agent'"
968                        :format "%t\n"
969                        t)
970                 (function-item :tag "Default Emacs mail"
971                                :format "%t\n"
972                                sendmail-user-agent)
973                 (function-item :tag "Emacs interface to MH"
974                                :format "%t\n"
975                                mh-e-user-agent)
976                 (function :tag "Other"))
977   :version "21.1"
978   :group 'message)
979
980 ;;; Internal variables.
981
982 (defvar message-sending-message "Sending...")
983 (defvar message-buffer-list nil)
984 (defvar message-this-is-news nil)
985 (defvar message-this-is-mail nil)
986 (defvar message-draft-article nil)
987 (defvar message-mime-part nil)
988 (defvar message-posting-charset nil)
989
990 ;; Byte-compiler warning
991 (eval-when-compile
992   (defvar gnus-active-hashtb)
993   (defvar gnus-read-active-file))
994
995 ;;; Regexp matching the delimiter of messages in UNIX mail format
996 ;;; (UNIX From lines), minus the initial ^.  It should be a copy
997 ;;; of rmail.el's rmail-unix-mail-delimiter.
998 (defvar message-unix-mail-delimiter
999   (let ((time-zone-regexp
1000          (concat "\\([A-Z]?[A-Z]?[A-Z][A-Z]\\( DST\\)?"
1001                  "\\|[-+]?[0-9][0-9][0-9][0-9]"
1002                  "\\|"
1003                  "\\) *")))
1004     (concat
1005      "From "
1006
1007      ;; Many things can happen to an RFC 822 mailbox before it is put into
1008      ;; a `From' line.  The leading phrase can be stripped, e.g.
1009      ;; `Joe <@w.x:joe@y.z>' -> `<@w.x:joe@y.z>'.  The <> can be stripped, e.g.
1010      ;; `<@x.y:joe@y.z>' -> `@x.y:joe@y.z'.  Everything starting with a CRLF
1011      ;; can be removed, e.g.
1012      ;;         From: joe@y.z (Joe      K
1013      ;;                 User)
1014      ;; can yield `From joe@y.z (Joe    K Fri Mar 22 08:11:15 1996', and
1015      ;;         From: Joe User
1016      ;;                 <joe@y.z>
1017      ;; can yield `From Joe User Fri Mar 22 08:11:15 1996'.
1018      ;; The mailbox can be removed or be replaced by white space, e.g.
1019      ;;         From: "Joe User"{space}{tab}
1020      ;;                 <joe@y.z>
1021      ;; can yield `From {space}{tab} Fri Mar 22 08:11:15 1996',
1022      ;; where {space} and {tab} represent the Ascii space and tab characters.
1023      ;; We want to match the results of any of these manglings.
1024      ;; The following regexp rejects names whose first characters are
1025      ;; obviously bogus, but after that anything goes.
1026      "\\([^\0-\b\n-\r\^?].*\\)? "
1027
1028      ;; The time the message was sent.
1029      "\\([^\0-\r \^?]+\\) +"            ; day of the week
1030      "\\([^\0-\r \^?]+\\) +"            ; month
1031      "\\([0-3]?[0-9]\\) +"              ; day of month
1032      "\\([0-2][0-9]:[0-5][0-9]\\(:[0-6][0-9]\\)?\\) *" ; time of day
1033
1034      ;; Perhaps a time zone, specified by an abbreviation, or by a
1035      ;; numeric offset.
1036      time-zone-regexp
1037
1038      ;; The year.
1039      " \\([0-9][0-9]+\\) *"
1040
1041      ;; On some systems the time zone can appear after the year, too.
1042      time-zone-regexp
1043
1044      ;; Old uucp cruft.
1045      "\\(remote from .*\\)?"
1046
1047      "\n"))
1048   "Regexp matching the delimiter of messages in UNIX mail format.")
1049
1050 (defvar message-unsent-separator
1051   (concat "^ *---+ +Unsent message follows +---+ *$\\|"
1052           "^ *---+ +Returned message +---+ *$\\|"
1053           "^Start of returned message$\\|"
1054           "^ *---+ +Original message +---+ *$\\|"
1055           "^ *--+ +begin message +--+ *$\\|"
1056           "^ *---+ +Original message follows +---+ *$\\|"
1057           "^ *---+ +Undelivered message follows +---+ *$\\|"
1058           "^|? *---+ +Message text follows: +---+ *|?$")
1059   "A regexp that matches the separator before the text of a failed message.")
1060
1061 (defvar message-header-format-alist
1062   `((Newsgroups)
1063     (To . message-fill-address)
1064     (Cc . message-fill-address)
1065     (Subject)
1066     (In-Reply-To)
1067     (Fcc)
1068     (Bcc)
1069     (Date)
1070     (Organization)
1071     (Distribution)
1072     (Lines)
1073     (Expires)
1074     (Message-ID)
1075     (References . message-shorten-references)
1076     (User-Agent))
1077   "Alist used for formatting headers.")
1078
1079 (defvar message-options nil
1080   "Some saved answers when sending message.")
1081
1082 (eval-and-compile
1083   (autoload 'message-setup-toolbar "messagexmas")
1084   (autoload 'mh-new-draft-name "mh-comp")
1085   (autoload 'mh-send-letter "mh-comp")
1086   (autoload 'gnus-point-at-eol "gnus-util")
1087   (autoload 'gnus-point-at-bol "gnus-util")
1088   (autoload 'gnus-output-to-rmail "gnus-util")
1089   (autoload 'gnus-output-to-mail "gnus-util")
1090   (autoload 'mail-abbrev-in-expansion-header-p "mailabbrev")
1091   (autoload 'nndraft-request-associate-buffer "nndraft")
1092   (autoload 'nndraft-request-expire-articles "nndraft")
1093   (autoload 'gnus-open-server "gnus-int")
1094   (autoload 'gnus-request-post "gnus-int")
1095   (autoload 'gnus-alive-p "gnus-util")
1096   (autoload 'gnus-group-name-charset "gnus-group")
1097   (autoload 'rmail-output "rmail"))
1098
1099 \f
1100
1101 ;;;
1102 ;;; Utility functions.
1103 ;;;
1104
1105 (defmacro message-y-or-n-p (question show &rest text)
1106   "Ask QUESTION, displaying remaining args in a temporary buffer if SHOW."
1107   `(message-talkative-question 'y-or-n-p ,question ,show ,@text))
1108
1109 (defmacro message-delete-line (&optional n)
1110   "Delete the current line (and the next N lines)."
1111   `(delete-region (progn (beginning-of-line) (point))
1112                   (progn (forward-line ,(or n 1)) (point))))
1113
1114 (defun message-unquote-tokens (elems)
1115   "Remove double quotes (\") from strings in list ELEMS."
1116   (mapcar (lambda (item)
1117             (while (string-match "^\\(.*\\)\"\\(.*\\)$" item)
1118               (setq item (concat (match-string 1 item)
1119                                  (match-string 2 item))))
1120             item)
1121           elems))
1122
1123 (defun message-tokenize-header (header &optional separator)
1124   "Split HEADER into a list of header elements.
1125 SEPARATOR is a string of characters to be used as separators.  \",\"
1126 is used by default."
1127   (if (not header)
1128       nil
1129     (let ((regexp (format "[%s]+" (or separator ",")))
1130           (beg 1)
1131           (first t)
1132           quoted elems paren)
1133       (save-excursion
1134         (message-set-work-buffer)
1135         (insert header)
1136         (goto-char (point-min))
1137         (while (not (eobp))
1138           (if first
1139               (setq first nil)
1140             (forward-char 1))
1141           (cond ((and (> (point) beg)
1142                       (or (eobp)
1143                           (and (looking-at regexp)
1144                                (not quoted)
1145                                (not paren))))
1146                  (push (buffer-substring beg (point)) elems)
1147                  (setq beg (match-end 0)))
1148                 ((eq (char-after) ?\")
1149                  (setq quoted (not quoted)))
1150                 ((and (eq (char-after) ?\()
1151                       (not quoted))
1152                  (setq paren t))
1153                 ((and (eq (char-after) ?\))
1154                       (not quoted))
1155                  (setq paren nil))))
1156         (nreverse elems)))))
1157
1158 (defun message-mail-file-mbox-p (file)
1159   "Say whether FILE looks like a Unix mbox file."
1160   (when (and (file-exists-p file)
1161              (file-readable-p file)
1162              (file-regular-p file))
1163     (with-temp-buffer
1164       (nnheader-insert-file-contents file)
1165       (goto-char (point-min))
1166       (looking-at message-unix-mail-delimiter))))
1167
1168 (defun message-fetch-field (header &optional not-all)
1169   "The same as `mail-fetch-field', only remove all newlines."
1170   (let* ((inhibit-point-motion-hooks t)
1171          (case-fold-search t)
1172          (value (mail-fetch-field header nil (not not-all))))
1173     (when value
1174       (while (string-match "\n[\t ]+" value)
1175         (setq value (replace-match " " t t value)))
1176       (set-text-properties 0 (length value) nil value)
1177       value)))
1178
1179 (defun message-narrow-to-field ()
1180   "Narrow the buffer to the header on the current line."
1181   (beginning-of-line)
1182   (narrow-to-region
1183    (point)
1184    (progn
1185      (forward-line 1)
1186      (if (re-search-forward "^[^ \n\t]" nil t)
1187          (progn
1188            (beginning-of-line)
1189            (point))
1190        (point-max))))
1191   (goto-char (point-min)))
1192
1193 (defun message-add-header (&rest headers)
1194   "Add the HEADERS to the message header, skipping those already present."
1195   (while headers
1196     (let (hclean)
1197       (unless (string-match "^\\([^:]+\\):[ \t]*[^ \t]" (car headers))
1198         (error "Invalid header `%s'" (car headers)))
1199       (setq hclean (match-string 1 (car headers)))
1200       (save-restriction
1201         (message-narrow-to-headers)
1202         (unless (re-search-forward (concat "^" (regexp-quote hclean) ":") nil t)
1203           (goto-char (point-max))
1204           (if (string-match "\n$" (car headers))
1205               (insert (car headers))
1206             (insert (car headers) ?\n)))))
1207     (setq headers (cdr headers))))
1208
1209
1210 (defun message-fetch-reply-field (header)
1211   "Fetch field HEADER from the message we're replying to."
1212   (when (and message-reply-buffer
1213              (buffer-name message-reply-buffer))
1214     (save-excursion
1215       (set-buffer message-reply-buffer)
1216       (message-fetch-field header))))
1217
1218 (defun message-set-work-buffer ()
1219   (if (get-buffer " *message work*")
1220       (progn
1221         (set-buffer " *message work*")
1222         (erase-buffer))
1223     (set-buffer (get-buffer-create " *message work*"))
1224     (kill-all-local-variables)
1225     (mm-enable-multibyte)))
1226
1227 (defun message-functionp (form)
1228   "Return non-nil if FORM is funcallable."
1229   (or (and (symbolp form) (fboundp form))
1230       (and (listp form) (eq (car form) 'lambda))
1231       (byte-code-function-p form)))
1232
1233 (defun message-strip-list-identifiers (subject)
1234   "Remove list identifiers in `gnus-list-identifiers' from string SUBJECT."
1235   (require 'gnus-sum)                   ; for gnus-list-identifiers
1236   (let ((regexp (if (stringp gnus-list-identifiers)
1237                     gnus-list-identifiers
1238                   (mapconcat 'identity gnus-list-identifiers " *\\|"))))
1239     (if (string-match (concat "\\(\\(\\(Re: +\\)?\\(" regexp
1240                                 " *\\)\\)+\\(Re: +\\)?\\)") subject)
1241         (concat (substring subject 0 (match-beginning 1))
1242                 (or (match-string 3 subject)
1243                     (match-string 5 subject))
1244                 (substring subject
1245                            (match-end 1)))
1246       subject)))
1247
1248 (defun message-strip-subject-re (subject)
1249   "Remove \"Re:\" from subject lines in string SUBJECT."
1250   (if (string-match message-subject-re-regexp subject)
1251       (substring subject (match-end 0))
1252     subject))
1253
1254 (defun message-remove-header (header &optional is-regexp first reverse)
1255   "Remove HEADER in the narrowed buffer.
1256 If IS-REGEXP, HEADER is a regular expression.
1257 If FIRST, only remove the first instance of the header.
1258 Return the number of headers removed."
1259   (goto-char (point-min))
1260   (let ((regexp (if is-regexp header (concat "^" (regexp-quote header) ":")))
1261         (number 0)
1262         (case-fold-search t)
1263         last)
1264     (while (and (not (eobp))
1265                 (not last))
1266       (if (if reverse
1267               (not (looking-at regexp))
1268             (looking-at regexp))
1269           (progn
1270             (incf number)
1271             (when first
1272               (setq last t))
1273             (delete-region
1274              (point)
1275              ;; There might be a continuation header, so we have to search
1276              ;; until we find a new non-continuation line.
1277              (progn
1278                (forward-line 1)
1279                (if (re-search-forward "^[^ \t]" nil t)
1280                    (goto-char (match-beginning 0))
1281                  (point-max)))))
1282         (forward-line 1)
1283         (if (re-search-forward "^[^ \t]" nil t)
1284             (goto-char (match-beginning 0))
1285           (goto-char (point-max)))))
1286     number))
1287
1288 (defun message-remove-first-header (header)
1289   "Remove the first instance of HEADER if there is more than one."
1290   (let ((count 0)
1291         (regexp (concat "^" (regexp-quote header) ":")))
1292     (save-excursion
1293       (goto-char (point-min))
1294       (while (re-search-forward regexp nil t)
1295         (incf count)))
1296     (while (> count 1)
1297       (message-remove-header header nil t)
1298       (decf count))))
1299
1300 (defun message-narrow-to-headers ()
1301   "Narrow the buffer to the head of the message."
1302   (widen)
1303   (narrow-to-region
1304    (goto-char (point-min))
1305    (if (re-search-forward
1306         (concat "^" (regexp-quote mail-header-separator) "\n") nil t)
1307        (match-beginning 0)
1308      (point-max)))
1309   (goto-char (point-min)))
1310
1311 (defun message-narrow-to-head-1 ()
1312   "Like `message-narrow-to-head'.  Don't widen."
1313   (narrow-to-region
1314    (goto-char (point-min))
1315    (if (search-forward "\n\n" nil 1)
1316        (1- (point))
1317      (point-max)))
1318   (goto-char (point-min)))
1319
1320 (defun message-narrow-to-head ()
1321   "Narrow the buffer to the head of the message.
1322 Point is left at the beginning of the narrowed-to region."
1323   (widen)
1324   (message-narrow-to-head-1))
1325
1326 (defun message-narrow-to-headers-or-head ()
1327   "Narrow the buffer to the head of the message."
1328   (widen)
1329   (narrow-to-region
1330    (goto-char (point-min))
1331    (cond
1332     ((re-search-forward
1333       (concat "^" (regexp-quote mail-header-separator) "\n") nil t)
1334      (match-beginning 0))
1335     ((search-forward "\n\n" nil t)
1336      (1- (point)))
1337     (t
1338      (point-max))))
1339   (goto-char (point-min)))
1340
1341 (defun message-news-p ()
1342   "Say whether the current buffer contains a news message."
1343   (and (not message-this-is-mail)
1344        (or message-this-is-news
1345            (save-excursion
1346              (save-restriction
1347                (message-narrow-to-headers)
1348                (and (message-fetch-field "newsgroups")
1349                     (not (message-fetch-field "posted-to"))))))))
1350
1351 (defun message-mail-p ()
1352   "Say whether the current buffer contains a mail message."
1353   (and (not message-this-is-news)
1354        (or message-this-is-mail
1355            (save-excursion
1356              (save-restriction
1357                (message-narrow-to-headers)
1358                (or (message-fetch-field "to")
1359                    (message-fetch-field "cc")
1360                    (message-fetch-field "bcc")))))))
1361
1362 (defun message-next-header ()
1363   "Go to the beginning of the next header."
1364   (beginning-of-line)
1365   (or (eobp) (forward-char 1))
1366   (not (if (re-search-forward "^[^ \t]" nil t)
1367            (beginning-of-line)
1368          (goto-char (point-max)))))
1369
1370 (defun message-sort-headers-1 ()
1371   "Sort the buffer as headers using `message-rank' text props."
1372   (goto-char (point-min))
1373   (require 'sort)
1374   (sort-subr
1375    nil 'message-next-header
1376    (lambda ()
1377      (message-next-header)
1378      (unless (bobp)
1379        (forward-char -1)))
1380    (lambda ()
1381      (or (get-text-property (point) 'message-rank)
1382          10000))))
1383
1384 (defun message-sort-headers ()
1385   "Sort the headers of the current message according to `message-header-format-alist'."
1386   (interactive)
1387   (save-excursion
1388     (save-restriction
1389       (let ((max (1+ (length message-header-format-alist)))
1390             rank)
1391         (message-narrow-to-headers)
1392         (while (re-search-forward "^[^ \n]+:" nil t)
1393           (put-text-property
1394            (match-beginning 0) (1+ (match-beginning 0))
1395            'message-rank
1396            (if (setq rank (length (memq (assq (intern (buffer-substring
1397                                                        (match-beginning 0)
1398                                                        (1- (match-end 0))))
1399                                               message-header-format-alist)
1400                                         message-header-format-alist)))
1401                (- max rank)
1402              (1+ max)))))
1403       (message-sort-headers-1))))
1404
1405 \f
1406
1407 ;;;
1408 ;;; Message mode
1409 ;;;
1410
1411 ;;; Set up keymap.
1412
1413 (defvar message-mode-map nil)
1414
1415 (unless message-mode-map
1416   (setq message-mode-map (make-keymap))
1417   (set-keymap-parent message-mode-map text-mode-map)
1418   (define-key message-mode-map "\C-c?" 'describe-mode)
1419
1420   (define-key message-mode-map "\C-c\C-f\C-t" 'message-goto-to)
1421   (define-key message-mode-map "\C-c\C-f\C-b" 'message-goto-bcc)
1422   (define-key message-mode-map "\C-c\C-f\C-w" 'message-goto-fcc)
1423   (define-key message-mode-map "\C-c\C-f\C-c" 'message-goto-cc)
1424   (define-key message-mode-map "\C-c\C-f\C-s" 'message-goto-subject)
1425   (define-key message-mode-map "\C-c\C-f\C-r" 'message-goto-reply-to)
1426   (define-key message-mode-map "\C-c\C-f\C-n" 'message-goto-newsgroups)
1427   (define-key message-mode-map "\C-c\C-f\C-d" 'message-goto-distribution)
1428   (define-key message-mode-map "\C-c\C-f\C-f" 'message-goto-followup-to)
1429   (define-key message-mode-map "\C-c\C-f\C-k" 'message-goto-keywords)
1430   (define-key message-mode-map "\C-c\C-f\C-u" 'message-goto-summary)
1431   (define-key message-mode-map "\C-c\C-b" 'message-goto-body)
1432   (define-key message-mode-map "\C-c\C-i" 'message-goto-signature)
1433
1434   (define-key message-mode-map "\C-c\C-t" 'message-insert-to)
1435   (define-key message-mode-map "\C-c\C-n" 'message-insert-newsgroups)
1436
1437   (define-key message-mode-map "\C-c\C-y" 'message-yank-original)
1438   (define-key message-mode-map "\C-c\M-\C-y" 'message-yank-buffer)
1439   (define-key message-mode-map "\C-c\C-q" 'message-fill-yanked-message)
1440   (define-key message-mode-map "\C-c\C-w" 'message-insert-signature)
1441   (define-key message-mode-map "\C-c\M-h" 'message-insert-headers)
1442   (define-key message-mode-map "\C-c\C-r" 'message-caesar-buffer-body)
1443   (define-key message-mode-map "\C-c\C-o" 'message-sort-headers)
1444   (define-key message-mode-map "\C-c\M-r" 'message-rename-buffer)
1445
1446   (define-key message-mode-map "\C-c\C-c" 'message-send-and-exit)
1447   (define-key message-mode-map "\C-c\C-s" 'message-send)
1448   (define-key message-mode-map "\C-c\C-k" 'message-kill-buffer)
1449   (define-key message-mode-map "\C-c\C-d" 'message-dont-send)
1450
1451   (define-key message-mode-map "\C-c\C-e" 'message-elide-region)
1452   (define-key message-mode-map "\C-c\C-v" 'message-delete-not-region)
1453   (define-key message-mode-map "\C-c\C-z" 'message-kill-to-signature)
1454   (define-key message-mode-map "\M-\r" 'message-newline-and-reformat)
1455   (define-key message-mode-map "\M-q" 'message-fill-paragraph)
1456
1457   (define-key message-mode-map "\C-c\C-a" 'mml-attach-file)
1458
1459   (define-key message-mode-map "\t" 'message-tab)
1460   (define-key message-mode-map "\M-;" 'comment-region))
1461
1462 (easy-menu-define
1463  message-mode-menu message-mode-map "Message Menu."
1464  `("Message"
1465    ["Sort Headers" message-sort-headers t]
1466    ["Yank Original" message-yank-original t]
1467    ["Fill Yanked Message" message-fill-yanked-message t]
1468    ["Insert Signature" message-insert-signature t]
1469    ["Caesar (rot13) Message" message-caesar-buffer-body t]
1470    ["Caesar (rot13) Region" message-caesar-region (mark t)]
1471    ["Elide Region" message-elide-region (mark t)]
1472    ["Delete Outside Region" message-delete-not-region (mark t)]
1473    ["Kill To Signature" message-kill-to-signature t]
1474    ["Newline and Reformat" message-newline-and-reformat t]
1475    ["Rename buffer" message-rename-buffer t]
1476    ["Spellcheck" ispell-message
1477     ,@(if (featurep 'xemacs) '(t)
1478         '(:help "Spellcheck this message"))]
1479    ["Attach file as MIME" mml-attach-file
1480     ,@(if (featurep 'xemacs) '(t)
1481         '(:help "Attach a file at point"))]
1482    "----"
1483    ["Send Message" message-send-and-exit
1484     ,@(if (featurep 'xemacs) '(t)
1485         '(:help "Send this message"))]
1486    ["Abort Message" message-dont-send
1487     ,@(if (featurep 'xemacs) '(t)
1488         '(:help "File this draft message and exit"))]
1489    ["Kill Message" message-kill-buffer
1490     ,@(if (featurep 'xemacs) '(t)
1491         '(:help "Delete this message without sending"))]))
1492
1493 (easy-menu-define
1494  message-mode-field-menu message-mode-map ""
1495  '("Field"
1496    ["Fetch To" message-insert-to t]
1497    ["Fetch Newsgroups" message-insert-newsgroups t]
1498    "----"
1499    ["To" message-goto-to t]
1500    ["Subject" message-goto-subject t]
1501    ["Cc" message-goto-cc t]
1502    ["Reply-To" message-goto-reply-to t]
1503    ["Summary" message-goto-summary t]
1504    ["Keywords" message-goto-keywords t]
1505    ["Newsgroups" message-goto-newsgroups t]
1506    ["Followup-To" message-goto-followup-to t]
1507    ["Distribution" message-goto-distribution t]
1508    ["Body" message-goto-body t]
1509    ["Signature" message-goto-signature t]))
1510
1511 (defvar message-tool-bar-map nil)
1512
1513 (eval-when-compile
1514   (defvar facemenu-add-face-function)
1515   (defvar facemenu-remove-face-function))
1516
1517 ;;;###autoload
1518 (defun message-mode ()
1519   "Major mode for editing mail and news to be sent.
1520 Like Text Mode but with these additional commands:\\<message-mode-map>
1521 C-c C-s  `message-send' (send the message)  C-c C-c  `message-send-and-exit'
1522 C-c C-d  Postpone sending the message       C-c C-k  Kill the message
1523 C-c C-f  move to a header field (and create it if there isn't):
1524          C-c C-f C-t  move to To        C-c C-f C-s  move to Subject
1525          C-c C-f C-c  move to Cc        C-c C-f C-b  move to Bcc
1526          C-c C-f C-w  move to Fcc       C-c C-f C-r  move to Reply-To
1527          C-c C-f C-u  move to Summary   C-c C-f C-n  move to Newsgroups
1528          C-c C-f C-k  move to Keywords  C-c C-f C-d  move to Distribution
1529          C-c C-f C-f  move to Followup-To
1530 C-c C-t  `message-insert-to' (add a To header to a news followup)
1531 C-c C-n  `message-insert-newsgroups' (add a Newsgroup header to a news reply)
1532 C-c C-b  `message-goto-body' (move to beginning of message text).
1533 C-c C-i  `message-goto-signature' (move to the beginning of the signature).
1534 C-c C-w  `message-insert-signature' (insert `message-signature-file' file).
1535 C-c C-y  `message-yank-original' (insert current message, if any).
1536 C-c C-q  `message-fill-yanked-message' (fill what was yanked).
1537 C-c C-e  `message-elide-region' (elide the text between point and mark).
1538 C-c C-v  `message-delete-not-region' (remove the text outside the region).
1539 C-c C-z  `message-kill-to-signature' (kill the text up to the signature).
1540 C-c C-r  `message-caesar-buffer-body' (rot13 the message body).
1541 C-c C-a  `mml-attach-file' (attach a file as MIME).
1542 M-RET    `message-newline-and-reformat' (break the line and reformat)."
1543   (interactive)
1544   (if (local-variable-p 'mml-buffer-list (current-buffer))
1545       (mml-destroy-buffers))
1546   (kill-all-local-variables)
1547   (set (make-local-variable 'message-reply-buffer) nil)
1548   (make-local-variable 'message-send-actions)
1549   (make-local-variable 'message-exit-actions)
1550   (make-local-variable 'message-kill-actions)
1551   (make-local-variable 'message-postpone-actions)
1552   (make-local-variable 'message-draft-article)
1553   (make-local-hook 'kill-buffer-hook)
1554   (set-syntax-table message-mode-syntax-table)
1555   (use-local-map message-mode-map)
1556   (setq local-abbrev-table message-mode-abbrev-table)
1557   (setq major-mode 'message-mode)
1558   (setq mode-name "Message")
1559   (setq buffer-offer-save t)
1560   (make-local-variable 'facemenu-add-face-function)
1561   (make-local-variable 'facemenu-remove-face-function)
1562   (setq facemenu-add-face-function
1563         (lambda (face end)
1564           (let ((face-fun (cdr (assq face message-face-alist))))
1565             (if face-fun
1566                 (funcall face-fun (point) end)
1567               (error "Face %s not configured for %s mode" face mode-name)))
1568           "")
1569         facemenu-remove-face-function t)
1570   (make-local-variable 'message-reply-headers)
1571   (setq message-reply-headers nil)
1572   (make-local-variable 'message-newsreader)
1573   (make-local-variable 'message-mailer)
1574   (make-local-variable 'message-post-method)
1575   (set (make-local-variable 'message-sent-message-via) nil)
1576   (set (make-local-variable 'message-checksum) nil)
1577   (set (make-local-variable 'message-mime-part) 0)
1578   (message-setup-fill-variables)
1579   ;; Allow using comment commands to add/remove quoting.
1580   (set (make-local-variable 'comment-start) message-yank-prefix)
1581   (if (featurep 'xemacs)
1582       (message-setup-toolbar)
1583     (set (make-local-variable 'font-lock-defaults)
1584          '(message-font-lock-keywords t))
1585     (if (boundp 'tool-bar-map)
1586         (set (make-local-variable 'tool-bar-map) (message-tool-bar-map))))
1587   (easy-menu-add message-mode-menu message-mode-map)
1588   (easy-menu-add message-mode-field-menu message-mode-map)
1589   ;; Allow mail alias things.
1590   (when (eq message-mail-alias-type 'abbrev)
1591     (if (fboundp 'mail-abbrevs-setup)
1592         (mail-abbrevs-setup)
1593       (mail-aliases-setup)))
1594   (message-set-auto-save-file-name)
1595   (mm-enable-multibyte)
1596   (make-local-variable 'indent-tabs-mode) ;Turn off tabs for indentation.
1597   (setq indent-tabs-mode nil)
1598   (mml-mode)
1599   (run-hooks 'text-mode-hook 'message-mode-hook))
1600
1601 (defun message-setup-fill-variables ()
1602   "Setup message fill variables."
1603   (make-local-variable 'paragraph-separate)
1604   (make-local-variable 'paragraph-start)
1605   (make-local-variable 'adaptive-fill-regexp)
1606   (unless (boundp 'adaptive-fill-first-line-regexp)
1607     (setq adaptive-fill-first-line-regexp nil))
1608   (make-local-variable 'adaptive-fill-first-line-regexp)
1609   (make-local-variable 'auto-fill-inhibit-regexp)
1610   (let ((quote-prefix-regexp
1611          ;; User should change message-cite-prefix-regexp if
1612          ;; message-yank-prefix is set to an abnormal value.
1613          (concat "\\(" message-cite-prefix-regexp "\\)[ \t]*")))
1614     (setq paragraph-start
1615           (concat
1616            (regexp-quote mail-header-separator) "$\\|"
1617            "[ \t]*$\\|"                 ; blank lines
1618            "-- $\\|"                    ; signature delimiter
1619            "---+$\\|"                   ; delimiters for forwarded messages
1620            page-delimiter "$\\|"        ; spoiler warnings
1621            ".*wrote:$\\|"               ; attribution lines
1622            quote-prefix-regexp "$"))    ; empty lines in quoted text
1623     (setq paragraph-separate paragraph-start)
1624     (setq adaptive-fill-regexp
1625           (concat quote-prefix-regexp "\\|" adaptive-fill-regexp))
1626     (setq adaptive-fill-first-line-regexp
1627           (concat quote-prefix-regexp "\\|"
1628                   adaptive-fill-first-line-regexp))
1629     (setq auto-fill-inhibit-regexp "^[A-Z][^: \n\t]+:")))
1630
1631 \f
1632
1633 ;;;
1634 ;;; Message mode commands
1635 ;;;
1636
1637 ;;; Movement commands
1638
1639 (defun message-goto-to ()
1640   "Move point to the To header."
1641   (interactive)
1642   (message-position-on-field "To"))
1643
1644 (defun message-goto-subject ()
1645   "Move point to the Subject header."
1646   (interactive)
1647   (message-position-on-field "Subject"))
1648
1649 (defun message-goto-cc ()
1650   "Move point to the Cc header."
1651   (interactive)
1652   (message-position-on-field "Cc" "To"))
1653
1654 (defun message-goto-bcc ()
1655   "Move point to the Bcc  header."
1656   (interactive)
1657   (message-position-on-field "Bcc" "Cc" "To"))
1658
1659 (defun message-goto-fcc ()
1660   "Move point to the Fcc header."
1661   (interactive)
1662   (message-position-on-field "Fcc" "To" "Newsgroups"))
1663
1664 (defun message-goto-reply-to ()
1665   "Move point to the Reply-To header."
1666   (interactive)
1667   (message-position-on-field "Reply-To" "Subject"))
1668
1669 (defun message-goto-newsgroups ()
1670   "Move point to the Newsgroups header."
1671   (interactive)
1672   (message-position-on-field "Newsgroups"))
1673
1674 (defun message-goto-distribution ()
1675   "Move point to the Distribution header."
1676   (interactive)
1677   (message-position-on-field "Distribution"))
1678
1679 (defun message-goto-followup-to ()
1680   "Move point to the Followup-To header."
1681   (interactive)
1682   (message-position-on-field "Followup-To" "Newsgroups"))
1683
1684 (defun message-goto-keywords ()
1685   "Move point to the Keywords header."
1686   (interactive)
1687   (message-position-on-field "Keywords" "Subject"))
1688
1689 (defun message-goto-summary ()
1690   "Move point to the Summary header."
1691   (interactive)
1692   (message-position-on-field "Summary" "Subject"))
1693
1694 (defun message-goto-body (&optional interactivep)
1695   "Move point to the beginning of the message body."
1696   (interactive (list t))
1697   (when (and interactivep
1698              (looking-at "[ \t]*\n"))
1699     (expand-abbrev))
1700   (goto-char (point-min))
1701   (or (search-forward (concat "\n" mail-header-separator "\n") nil t)
1702       (search-forward "\n\n" nil t)))
1703
1704 (defun message-goto-eoh ()
1705   "Move point to the end of the headers."
1706   (interactive)
1707   (message-goto-body)
1708   (forward-line -1))
1709
1710 (defun message-goto-signature ()
1711   "Move point to the beginning of the message signature.
1712 If there is no signature in the article, go to the end and
1713 return nil."
1714   (interactive)
1715   (goto-char (point-min))
1716   (if (re-search-forward message-signature-separator nil t)
1717       (forward-line 1)
1718     (goto-char (point-max))
1719     nil))
1720
1721 \f
1722
1723 (defun message-insert-to (&optional force)
1724   "Insert a To header that points to the author of the article being replied to.
1725 If the original author requested not to be sent mail, the function signals
1726 an error.
1727 With the prefix argument FORCE, insert the header anyway."
1728   (interactive "P")
1729   (let ((co (message-fetch-reply-field "mail-copies-to")))
1730     (when (and (null force)
1731                co
1732                (or (equal (downcase co) "never")
1733                    (equal (downcase co) "nobody")))
1734       (error "The user has requested not to have copies sent via mail")))
1735   (when (and (message-position-on-field "To")
1736              (mail-fetch-field "to")
1737              (not (string-match "\\` *\\'" (mail-fetch-field "to"))))
1738     (insert ", "))
1739   (insert (or (message-fetch-reply-field "mail-reply-to")
1740               (message-fetch-reply-field "reply-to")
1741               (message-fetch-reply-field "from") "")))
1742
1743 (defun message-widen-reply ()
1744   "Widen the reply to include maximum recipients."
1745   (interactive)
1746   (let ((follow-to
1747          (and message-reply-buffer
1748               (buffer-name message-reply-buffer)
1749               (save-excursion
1750                 (set-buffer message-reply-buffer)
1751                 (message-get-reply-headers t)))))
1752     (save-excursion
1753       (save-restriction
1754         (message-narrow-to-headers)
1755         (dolist (elem follow-to)
1756           (message-remove-header (symbol-name (car elem)))
1757           (goto-char (point-min))
1758           (insert (symbol-name (car elem)) ": "
1759                   (cdr elem) "\n"))))))
1760
1761 (defun message-insert-newsgroups ()
1762   "Insert the Newsgroups header from the article being replied to."
1763   (interactive)
1764   (when (and (message-position-on-field "Newsgroups")
1765              (mail-fetch-field "newsgroups")
1766              (not (string-match "\\` *\\'" (mail-fetch-field "newsgroups"))))
1767     (insert ","))
1768   (insert (or (message-fetch-reply-field "newsgroups") "")))
1769
1770 \f
1771
1772 ;;; Various commands
1773
1774 (defun message-delete-not-region (beg end)
1775   "Delete everything in the body of the current message outside of the region."
1776   (interactive "r")
1777   (save-excursion
1778     (goto-char end)
1779     (delete-region (point) (if (not (message-goto-signature))
1780                                (point)
1781                              (forward-line -2)
1782                              (point)))
1783     (insert "\n")
1784     (goto-char beg)
1785     (delete-region beg (progn (message-goto-body)
1786                               (forward-line 2)
1787                               (point))))
1788   (when (message-goto-signature)
1789     (forward-line -2)))
1790
1791 (defun message-kill-to-signature ()
1792   "Deletes all text up to the signature."
1793   (interactive)
1794   (let ((point (point)))
1795     (message-goto-signature)
1796     (unless (eobp)
1797       (forward-line -2))
1798     (kill-region point (point))
1799     (unless (bolp)
1800       (insert "\n"))))
1801
1802 (defun message-newline-and-reformat (&optional not-break)
1803   "Insert four newlines, and then reformat if inside quoted text."
1804   (interactive)
1805   (let (quoted point beg end leading-space bolp)
1806     (setq point (point))
1807     (beginning-of-line)
1808     (setq beg (point))
1809     (setq bolp (= beg point))
1810     ;; Find first line of the paragraph.
1811     (if not-break
1812         (while (and (not (eobp))
1813                     (not (looking-at message-cite-prefix-regexp))
1814                 (looking-at paragraph-start))
1815           (forward-line 1)))
1816     ;; Find the prefix
1817     (when (looking-at message-cite-prefix-regexp)
1818       (setq quoted (match-string 0))
1819       (goto-char (match-end 0))
1820       (looking-at "[ \t]*")
1821       (setq leading-space (match-string 0)))
1822     (if (and quoted
1823              (not not-break)
1824              (not bolp)
1825              (< (- point beg) (length quoted)))
1826         ;; break inside the cite prefix.
1827         (setq quoted nil
1828               end nil))
1829     (if quoted
1830         (progn
1831           (forward-line 1)
1832           (while (and (not (eobp))
1833                       (not (looking-at paragraph-separate))
1834                       (looking-at message-cite-prefix-regexp)
1835                       (equal quoted (match-string 0)))
1836             (goto-char (match-end 0))
1837             (looking-at "[ \t]*")
1838             (if (> (length leading-space) (length (match-string 0)))
1839                 (setq leading-space (match-string 0)))
1840             (forward-line 1))
1841           (setq end (point))
1842           (goto-char beg)
1843           (while (and (if (bobp) nil (forward-line -1) t)
1844                       (not (looking-at paragraph-start))
1845                       (looking-at message-cite-prefix-regexp)
1846                       (equal quoted (match-string 0)))
1847             (setq beg (point))
1848             (goto-char (match-end 0))
1849             (looking-at "[ \t]*")
1850             (if (> (length leading-space) (length (match-string 0)))
1851                 (setq leading-space (match-string 0)))))
1852       (while (and (not (eobp))
1853                   (not (looking-at paragraph-separate))
1854                   (not (looking-at message-cite-prefix-regexp)))
1855         (forward-line 1))
1856       (setq end (point))
1857       (goto-char beg)
1858       (while (and (if (bobp) nil (forward-line -1) t)
1859                   (not (looking-at paragraph-start))
1860                   (not (looking-at message-cite-prefix-regexp)))
1861         (setq beg (point))))
1862     (goto-char point)
1863     (save-restriction
1864       (narrow-to-region beg end)
1865       (if not-break
1866           (setq point nil)
1867         (if bolp
1868             (insert "\n")
1869           (insert "\n\n"))
1870         (setq point (point))
1871         (insert "\n\n")
1872         (delete-region (point) (re-search-forward "[ \t]*"))
1873         (when (and quoted (not bolp))
1874           (insert quoted leading-space)))
1875       (if quoted
1876           (let* ((adaptive-fill-regexp
1877                  (regexp-quote (concat quoted leading-space)))
1878                  (adaptive-fill-first-line-regexp
1879                   adaptive-fill-regexp ))
1880             (fill-paragraph nil))
1881         (fill-paragraph nil))
1882       (if point (goto-char point)))))
1883
1884 (defun message-fill-paragraph ()
1885   "Like `fill-paragraph'."
1886   (interactive)
1887   (message-newline-and-reformat t))
1888
1889 (defun message-insert-signature (&optional force)
1890   "Insert a signature.  See documentation for variable `message-signature'."
1891   (interactive (list 0))
1892   (let* ((signature
1893           (cond
1894            ((and (null message-signature)
1895                  (eq force 0))
1896             (save-excursion
1897               (goto-char (point-max))
1898               (not (re-search-backward message-signature-separator nil t))))
1899            ((and (null message-signature)
1900                  force)
1901             t)
1902            ((message-functionp message-signature)
1903             (funcall message-signature))
1904            ((listp message-signature)
1905             (eval message-signature))
1906            (t message-signature)))
1907          (signature
1908           (cond ((stringp signature)
1909                  signature)
1910                 ((and (eq t signature)
1911                       message-signature-file
1912                       (file-exists-p message-signature-file))
1913                  signature))))
1914     (when signature
1915       (goto-char (point-max))
1916       ;; Insert the signature.
1917       (unless (bolp)
1918         (insert "\n"))
1919       (insert "\n-- \n")
1920       (if (eq signature t)
1921           (insert-file-contents message-signature-file)
1922         (insert signature))
1923       (goto-char (point-max))
1924       (or (bolp) (insert "\n")))))
1925
1926 (defun message-elide-region (b e)
1927   "Elide the text in the region.
1928 An ellipsis (from `message-elide-ellipsis') will be inserted where the
1929 text was killed."
1930   (interactive "r")
1931   (kill-region b e)
1932   (insert message-elide-ellipsis))
1933
1934 (defvar message-caesar-translation-table nil)
1935
1936 (defun message-caesar-region (b e &optional n)
1937   "Caesar rotate region B to E by N, default 13, for decrypting netnews."
1938   (interactive
1939    (list
1940     (min (point) (or (mark t) (point)))
1941     (max (point) (or (mark t) (point)))
1942     (when current-prefix-arg
1943       (prefix-numeric-value current-prefix-arg))))
1944
1945   (setq n (if (numberp n) (mod n 26) 13)) ;canonize N
1946   (unless (or (zerop n)                 ; no action needed for a rot of 0
1947               (= b e))                  ; no region to rotate
1948     ;; We build the table, if necessary.
1949     (when (or (not message-caesar-translation-table)
1950               (/= (aref message-caesar-translation-table ?a) (+ ?a n)))
1951       (setq message-caesar-translation-table
1952             (message-make-caesar-translation-table n)))
1953     (translate-region b e message-caesar-translation-table)))
1954
1955 (defun message-make-caesar-translation-table (n)
1956   "Create a rot table with offset N."
1957   (let ((i -1)
1958         (table (make-string 256 0)))
1959     (while (< (incf i) 256)
1960       (aset table i i))
1961     (concat
1962      (substring table 0 ?A)
1963      (substring table (+ ?A n) (+ ?A n (- 26 n)))
1964      (substring table ?A (+ ?A n))
1965      (substring table (+ ?A 26) ?a)
1966      (substring table (+ ?a n) (+ ?a n (- 26 n)))
1967      (substring table ?a (+ ?a n))
1968      (substring table (+ ?a 26) 255))))
1969
1970 (defun message-caesar-buffer-body (&optional rotnum)
1971   "Caesar rotate all letters in the current buffer by 13 places.
1972 Used to encode/decode possibly offensive messages (commonly in rec.humor).
1973 With prefix arg, specifies the number of places to rotate each letter forward.
1974 Mail and USENET news headers are not rotated."
1975   (interactive (if current-prefix-arg
1976                    (list (prefix-numeric-value current-prefix-arg))
1977                  (list nil)))
1978   (save-excursion
1979     (save-restriction
1980       (when (message-goto-body)
1981         (narrow-to-region (point) (point-max)))
1982       (message-caesar-region (point-min) (point-max) rotnum))))
1983
1984 (defun message-pipe-buffer-body (program)
1985   "Pipe the message body in the current buffer through PROGRAM."
1986   (save-excursion
1987     (save-restriction
1988       (when (message-goto-body)
1989         (narrow-to-region (point) (point-max)))
1990       (shell-command-on-region
1991        (point-min) (point-max) program nil t))))
1992
1993 (defun message-rename-buffer (&optional enter-string)
1994   "Rename the *message* buffer to \"*message* RECIPIENT\".
1995 If the function is run with a prefix, it will ask for a new buffer
1996 name, rather than giving an automatic name."
1997   (interactive "Pbuffer name: ")
1998   (save-excursion
1999     (save-restriction
2000       (goto-char (point-min))
2001       (narrow-to-region (point)
2002                         (search-forward mail-header-separator nil 'end))
2003       (let* ((mail-to (or
2004                        (if (message-news-p) (message-fetch-field "Newsgroups")
2005                          (message-fetch-field "To"))
2006                        ""))
2007              (mail-trimmed-to
2008               (if (string-match "," mail-to)
2009                   (concat (substring mail-to 0 (match-beginning 0)) ", ...")
2010                 mail-to))
2011              (name-default (concat "*message* " mail-trimmed-to))
2012              (name (if enter-string
2013                        (read-string "New buffer name: " name-default)
2014                      name-default)))
2015         (rename-buffer name t)))))
2016
2017 (defun message-fill-yanked-message (&optional justifyp)
2018   "Fill the paragraphs of a message yanked into this one.
2019 Numeric argument means justify as well."
2020   (interactive "P")
2021   (save-excursion
2022     (goto-char (point-min))
2023     (search-forward (concat "\n" mail-header-separator "\n") nil t)
2024     (let ((fill-prefix message-yank-prefix))
2025       (fill-individual-paragraphs (point) (point-max) justifyp))))
2026
2027 (defun message-indent-citation ()
2028   "Modify text just inserted from a message to be cited.
2029 The inserted text should be the region.
2030 When this function returns, the region is again around the modified text.
2031
2032 Normally, indent each nonblank line `message-indentation-spaces' spaces.
2033 However, if `message-yank-prefix' is non-nil, insert that prefix on each line."
2034   (let ((start (point)))
2035     ;; Remove unwanted headers.
2036     (when message-ignored-cited-headers
2037       (let (all-removed)
2038         (save-restriction
2039           (narrow-to-region
2040            (goto-char start)
2041            (if (search-forward "\n\n" nil t)
2042                (1- (point))
2043              (point)))
2044           (message-remove-header message-ignored-cited-headers t)
2045           (when (= (point-min) (point-max))
2046             (setq all-removed t))
2047           (goto-char (point-max)))
2048         (if all-removed
2049             (goto-char start)
2050           (forward-line 1))))
2051     ;; Delete blank lines at the start of the buffer.
2052     (while (and (point-min)
2053                 (eolp)
2054                 (not (eobp)))
2055       (message-delete-line))
2056     ;; Delete blank lines at the end of the buffer.
2057     (goto-char (point-max))
2058     (unless (eolp)
2059       (insert "\n"))
2060     (while (and (zerop (forward-line -1))
2061                 (looking-at "$"))
2062       (message-delete-line))
2063     ;; Do the indentation.
2064     (if (null message-yank-prefix)
2065         (indent-rigidly start (mark t) message-indentation-spaces)
2066       (save-excursion
2067         (goto-char start)
2068         (while (< (point) (mark t))
2069           (insert message-yank-prefix)
2070           (forward-line 1))))
2071     (goto-char start)))
2072
2073 (defun message-yank-original (&optional arg)
2074   "Insert the message being replied to, if any.
2075 Puts point before the text and mark after.
2076 Normally indents each nonblank line ARG spaces (default 3).  However,
2077 if `message-yank-prefix' is non-nil, insert that prefix on each line.
2078
2079 This function uses `message-cite-function' to do the actual citing.
2080
2081 Just \\[universal-argument] as argument means don't indent, insert no
2082 prefix, and don't delete any headers."
2083   (interactive "P")
2084   (let ((modified (buffer-modified-p)))
2085     (when (and message-reply-buffer
2086                message-cite-function)
2087       (delete-windows-on message-reply-buffer t)
2088       (insert-buffer message-reply-buffer)
2089       (unless arg
2090         (funcall message-cite-function))
2091       (message-exchange-point-and-mark)
2092       (unless (bolp)
2093         (insert ?\n))
2094       (unless modified
2095         (setq message-checksum (message-checksum))))))
2096
2097 (defun message-yank-buffer (buffer)
2098   "Insert BUFFER into the current buffer and quote it."
2099   (interactive "bYank buffer: ")
2100   (let ((message-reply-buffer buffer))
2101     (save-window-excursion
2102       (message-yank-original))))
2103
2104 (defun message-buffers ()
2105   "Return a list of active message buffers."
2106   (let (buffers)
2107     (save-excursion
2108       (dolist (buffer (buffer-list t))
2109         (set-buffer buffer)
2110         (when (and (eq major-mode 'message-mode)
2111                    (null message-sent-message-via))
2112           (push (buffer-name buffer) buffers))))
2113     (nreverse buffers)))
2114
2115 (defun message-cite-original-without-signature ()
2116   "Cite function in the standard Message manner."
2117   (let ((start (point))
2118         (end (mark t))
2119         (functions
2120          (when message-indent-citation-function
2121            (if (listp message-indent-citation-function)
2122                message-indent-citation-function
2123              (list message-indent-citation-function)))))
2124     (mml-quote-region start end)
2125     ;; Allow undoing.
2126     (undo-boundary)
2127     (goto-char end)
2128     (when (re-search-backward message-signature-separator start t)
2129       ;; Also peel off any blank lines before the signature.
2130       (forward-line -1)
2131       (while (looking-at "^[ \t]*$")
2132         (forward-line -1))
2133       (forward-line 1)
2134       (delete-region (point) end)
2135       (unless (search-backward "\n\n" start t)
2136         ;; Insert a blank line if it is peeled off.
2137         (insert "\n")))
2138     (goto-char start)
2139     (while functions
2140       (funcall (pop functions)))
2141     (when message-citation-line-function
2142       (unless (bolp)
2143         (insert "\n"))
2144       (funcall message-citation-line-function))))
2145
2146 (eval-when-compile (defvar mail-citation-hook))         ;Compiler directive
2147 (defun message-cite-original ()
2148   "Cite function in the standard Message manner."
2149   (if (and (boundp 'mail-citation-hook)
2150            mail-citation-hook)
2151       (run-hooks 'mail-citation-hook)
2152     (let ((start (point))
2153           (end (mark t))
2154           (functions
2155            (when message-indent-citation-function
2156              (if (listp message-indent-citation-function)
2157                  message-indent-citation-function
2158                (list message-indent-citation-function)))))
2159       (mml-quote-region start end)
2160       (goto-char start)
2161       (while functions
2162         (funcall (pop functions)))
2163       (when message-citation-line-function
2164         (unless (bolp)
2165           (insert "\n"))
2166         (funcall message-citation-line-function)))))
2167
2168 (defun message-insert-citation-line ()
2169   "Insert a simple citation line."
2170   (when message-reply-headers
2171     (insert (mail-header-from message-reply-headers) " writes:\n\n")))
2172
2173 (defun message-position-on-field (header &rest afters)
2174   (let ((case-fold-search t))
2175     (save-restriction
2176       (narrow-to-region
2177        (goto-char (point-min))
2178        (progn
2179          (re-search-forward
2180           (concat "^" (regexp-quote mail-header-separator) "$"))
2181          (match-beginning 0)))
2182       (goto-char (point-min))
2183       (if (re-search-forward (concat "^" (regexp-quote header) ":") nil t)
2184           (progn
2185             (re-search-forward "^[^ \t]" nil 'move)
2186             (beginning-of-line)
2187             (skip-chars-backward "\n")
2188             t)
2189         (while (and afters
2190                     (not (re-search-forward
2191                           (concat "^" (regexp-quote (car afters)) ":")
2192                           nil t)))
2193           (pop afters))
2194         (when afters
2195           (re-search-forward "^[^ \t]" nil 'move)
2196           (beginning-of-line))
2197         (insert header ": \n")
2198         (forward-char -1)
2199         nil))))
2200
2201 (defun message-remove-signature ()
2202   "Remove the signature from the text between point and mark.
2203 The text will also be indented the normal way."
2204   (save-excursion
2205     (let ((start (point))
2206           mark)
2207       (if (not (re-search-forward message-signature-separator (mark t) t))
2208           ;; No signature here, so we just indent the cited text.
2209           (message-indent-citation)
2210         ;; Find the last non-empty line.
2211         (forward-line -1)
2212         (while (looking-at "[ \t]*$")
2213           (forward-line -1))
2214         (forward-line 1)
2215         (setq mark (set-marker (make-marker) (point)))
2216         (goto-char start)
2217         (message-indent-citation)
2218         ;; Enable undoing the deletion.
2219         (undo-boundary)
2220         (delete-region mark (mark t))
2221         (set-marker mark nil)))))
2222
2223 \f
2224
2225 ;;;
2226 ;;; Sending messages
2227 ;;;
2228
2229 (defun message-send-and-exit (&optional arg)
2230   "Send message like `message-send', then, if no errors, exit from mail buffer."
2231   (interactive "P")
2232   (let ((buf (current-buffer))
2233         (actions message-exit-actions))
2234     (when (and (message-send arg)
2235                (buffer-name buf))
2236       (if message-kill-buffer-on-exit
2237           (kill-buffer buf)
2238         (bury-buffer buf)
2239         (when (eq buf (current-buffer))
2240           (message-bury buf)))
2241       (message-do-actions actions)
2242       t)))
2243
2244 (defun message-dont-send ()
2245   "Don't send the message you have been editing."
2246   (interactive)
2247   (set-buffer-modified-p t)
2248   (save-buffer)
2249   (let ((actions message-postpone-actions))
2250     (message-bury (current-buffer))
2251     (message-do-actions actions)))
2252
2253 (defun message-kill-buffer ()
2254   "Kill the current buffer."
2255   (interactive)
2256   (when (or (not (buffer-modified-p))
2257             (yes-or-no-p "Message modified; kill anyway? "))
2258     (let ((actions message-kill-actions))
2259       (setq buffer-file-name nil)
2260       (kill-buffer (current-buffer))
2261       (message-do-actions actions))))
2262
2263 (defun message-bury (buffer)
2264   "Bury this mail BUFFER."
2265   (let ((newbuf (other-buffer buffer)))
2266     (bury-buffer buffer)
2267     (if (and (fboundp 'frame-parameters)
2268              (cdr (assq 'dedicated (frame-parameters)))
2269              (not (null (delq (selected-frame) (visible-frame-list)))))
2270         (delete-frame (selected-frame))
2271       (switch-to-buffer newbuf))))
2272
2273 (defun message-send (&optional arg)
2274   "Send the message in the current buffer.
2275 If `message-interactive' is non-nil, wait for success indication or
2276 error messages, and inform user.
2277 Otherwise any failure is reported in a message back to the user from
2278 the mailer.
2279 The usage of ARG is defined by the instance that called Message.
2280 It should typically alter the sending method in some way or other."
2281   (interactive "P")
2282   ;; Make it possible to undo the coming changes.
2283   (undo-boundary)
2284   (let ((inhibit-read-only t))
2285     (put-text-property (point-min) (point-max) 'read-only nil))
2286   (message-fix-before-sending)
2287   (run-hooks 'message-send-hook)
2288   (message message-sending-message)
2289   (let ((alist message-send-method-alist)
2290         (success t)
2291         elem sent
2292         (message-options message-options))
2293     (message-options-set-recipient)
2294     (while (and success
2295                 (setq elem (pop alist)))
2296       (when (funcall (cadr elem))
2297         (when (and (or (not (memq (car elem)
2298                                   message-sent-message-via))
2299                        (if (or (message-gnksa-enable-p 'multiple-copies)
2300                                (not (eq (car elem) 'news)))
2301                            (y-or-n-p
2302                             (format
2303                              "Already sent message via %s; resend? "
2304                              (car elem)))
2305                          (error "Denied posting -- multiple copies.")))
2306                    (setq success (funcall (caddr elem) arg)))
2307           (setq sent t))))
2308     (unless (or sent (not success))
2309       (error "No methods specified to send by"))
2310     (when (and success sent)
2311       (message-do-fcc)
2312       (save-excursion
2313         (run-hooks 'message-sent-hook))
2314       (message "Sending...done")
2315       ;; Mark the buffer as unmodified and delete auto-save.
2316       (set-buffer-modified-p nil)
2317       (delete-auto-save-file-if-necessary t)
2318       (message-disassociate-draft)
2319       ;; Delete other mail buffers and stuff.
2320       (message-do-send-housekeeping)
2321       (message-do-actions message-send-actions)
2322       ;; Return success.
2323       t)))
2324
2325 (defun message-send-via-mail (arg)
2326   "Send the current message via mail."
2327   (message-send-mail arg))
2328
2329 (defun message-send-via-news (arg)
2330   "Send the current message via news."
2331   (funcall message-send-news-function arg))
2332
2333 (defmacro message-check (type &rest forms)
2334   "Eval FORMS if TYPE is to be checked."
2335   `(or (message-check-element ,type)
2336        (save-excursion
2337          ,@forms)))
2338
2339 (put 'message-check 'lisp-indent-function 1)
2340 (put 'message-check 'edebug-form-spec '(form body))
2341
2342 (defun message-fix-before-sending ()
2343   "Do various things to make the message nice before sending it."
2344   ;; Make sure there's a newline at the end of the message.
2345   (goto-char (point-max))
2346   (unless (bolp)
2347     (insert "\n"))
2348   ;; Delete all invisible text.
2349   (message-check 'invisible-text
2350     (when (text-property-any (point-min) (point-max) 'invisible t)
2351       (put-text-property (point-min) (point-max) 'invisible nil)
2352       (unless (yes-or-no-p
2353                "Invisible text found and made visible; continue posting? ")
2354         (error "Invisible text found and made visible")))))
2355
2356 (defun message-add-action (action &rest types)
2357   "Add ACTION to be performed when doing an exit of type TYPES."
2358   (let (var)
2359     (while types
2360       (set (setq var (intern (format "message-%s-actions" (pop types))))
2361            (nconc (symbol-value var) (list action))))))
2362
2363 (defun message-do-actions (actions)
2364   "Perform all actions in ACTIONS."
2365   ;; Now perform actions on successful sending.
2366   (while actions
2367     (ignore-errors
2368       (cond
2369        ;; A simple function.
2370        ((message-functionp (car actions))
2371         (funcall (car actions)))
2372        ;; Something to be evaled.
2373        (t
2374         (eval (car actions)))))
2375     (pop actions)))
2376
2377 (defun message-send-mail-partially ()
2378   "Sendmail as message/partial."
2379   ;; replace the header delimiter with a blank line
2380   (goto-char (point-min))
2381   (re-search-forward
2382    (concat "^" (regexp-quote mail-header-separator) "\n"))
2383   (replace-match "\n")
2384   (run-hooks 'message-send-mail-hook)
2385   (let ((p (goto-char (point-min)))
2386         (tembuf (message-generate-new-buffer-clone-locals " message temp"))
2387         (curbuf (current-buffer))
2388         (id (message-make-message-id)) (n 1)
2389         plist total  header required-mail-headers)
2390     (while (not (eobp))
2391       (if (< (point-max) (+ p message-send-mail-partially-limit))
2392           (goto-char (point-max))
2393         (goto-char (+ p message-send-mail-partially-limit))
2394         (beginning-of-line)
2395         (if (<= (point) p) (forward-line 1))) ;; In case of bad message.
2396       (push p plist)
2397       (setq p (point)))
2398     (setq total (length plist))
2399     (push (point-max) plist)
2400     (setq plist (nreverse plist))
2401     (unwind-protect
2402         (save-excursion
2403           (setq p (pop plist))
2404           (while plist
2405             (set-buffer curbuf)
2406             (copy-to-buffer tembuf p (car plist))
2407             (set-buffer tembuf)
2408             (goto-char (point-min))
2409             (if header
2410                 (progn
2411                   (goto-char (point-min))
2412                   (narrow-to-region (point) (point))
2413                   (insert header))
2414               (message-goto-eoh)
2415               (setq header (buffer-substring (point-min) (point)))
2416               (goto-char (point-min))
2417               (narrow-to-region (point) (point))
2418               (insert header)
2419               (message-remove-header "Mime-Version")
2420               (message-remove-header "Content-Type")
2421               (message-remove-header "Content-Transfer-Encoding")
2422               (message-remove-header "Message-ID")
2423               (message-remove-header "Lines")
2424               (goto-char (point-max))
2425               (insert "Mime-Version: 1.0\n")
2426               (setq header (buffer-substring (point-min) (point-max))))
2427             (goto-char (point-max))
2428             (insert (format "Content-Type: message/partial; id=\"%s\"; number=%d; total=%d\n"
2429                             id n total))
2430             (let ((mail-header-separator ""))
2431               (when (memq 'Message-ID message-required-mail-headers)
2432                 (insert "Message-ID: " (message-make-message-id) "\n"))
2433               (when (memq 'Lines message-required-mail-headers)
2434                 (let ((mail-header-separator ""))
2435                   (insert "Lines: " (message-make-lines) "\n")))
2436               (message-goto-subject)
2437               (end-of-line)
2438               (insert (format " (%d/%d)" n total))
2439               (goto-char (point-max))
2440               (insert "\n")
2441               (widen)
2442               (mm-with-unibyte-current-buffer
2443                 (funcall message-send-mail-function)))
2444             (setq n (+ n 1))
2445             (setq p (pop plist))
2446             (erase-buffer)))
2447       (kill-buffer tembuf))))
2448
2449 (defun message-send-mail (&optional arg)
2450   (require 'mail-utils)
2451   (let* ((tembuf (message-generate-new-buffer-clone-locals " message temp"))
2452          (case-fold-search nil)
2453          (news (message-news-p))
2454          (mailbuf (current-buffer))
2455          (message-this-is-mail t)
2456          (message-posting-charset
2457           (if (fboundp 'gnus-setup-posting-charset)
2458               (gnus-setup-posting-charset nil)
2459             message-posting-charset)))
2460     (save-restriction
2461       (message-narrow-to-headers)
2462       ;; Insert some headers.
2463       (let ((message-deletable-headers
2464              (if news nil message-deletable-headers)))
2465         (message-generate-headers message-required-mail-headers))
2466       ;; Let the user do all of the above.
2467       (run-hooks 'message-header-hook))
2468     (unwind-protect
2469         (save-excursion
2470           (set-buffer tembuf)
2471           (erase-buffer)
2472           ;; Avoid copying text props.
2473           (insert (with-current-buffer mailbuf
2474                     (buffer-substring-no-properties (point-min) (point-max))))
2475           ;; Remove some headers.
2476           (message-encode-message-body)
2477           (save-restriction
2478             (message-narrow-to-headers)
2479             ;; We (re)generate the Lines header.
2480             (when (memq 'Lines message-required-mail-headers)
2481               (message-generate-headers '(Lines)))
2482             ;; Remove some headers.
2483             (message-remove-header message-ignored-mail-headers t)
2484             (let ((mail-parse-charset message-default-charset))
2485               (mail-encode-encoded-word-buffer)))
2486           (goto-char (point-max))
2487           ;; require one newline at the end.
2488           (or (= (preceding-char) ?\n)
2489               (insert ?\n))
2490           (when
2491               (save-restriction
2492                 (message-narrow-to-headers)
2493                 (and news
2494                      (or (message-fetch-field "cc")
2495                          (message-fetch-field "to"))
2496                      (let ((content-type (message-fetch-field "content-type")))
2497                        (or
2498                         (not content-type)
2499                         (string= "text/plain"
2500                                  (car
2501                                   (mail-header-parse-content-type
2502                                    content-type)))))))
2503             (message-insert-courtesy-copy))
2504           (if (or (not message-send-mail-partially-limit)
2505                   (< (point-max) message-send-mail-partially-limit)
2506                   (not (y-or-n-p "The message size is too large, should it be sent partially? ")))
2507               (mm-with-unibyte-current-buffer
2508                 (funcall message-send-mail-function))
2509             (message-send-mail-partially)))
2510       (kill-buffer tembuf))
2511     (set-buffer mailbuf)
2512     (push 'mail message-sent-message-via)))
2513
2514 (defun message-send-mail-with-sendmail ()
2515   "Send off the prepared buffer with sendmail."
2516   (let ((errbuf (if message-interactive
2517                     (message-generate-new-buffer-clone-locals
2518                      " sendmail errors")
2519                   0))
2520         resend-to-addresses delimline)
2521     (let ((case-fold-search t))
2522       (save-restriction
2523         (message-narrow-to-headers)
2524         (setq resend-to-addresses (message-fetch-field "resent-to")))
2525       ;; Change header-delimiter to be what sendmail expects.
2526       (goto-char (point-min))
2527       (re-search-forward
2528        (concat "^" (regexp-quote mail-header-separator) "\n"))
2529       (replace-match "\n")
2530       (backward-char 1)
2531       (setq delimline (point-marker))
2532       (run-hooks 'message-send-mail-hook)
2533       ;; Insert an extra newline if we need it to work around
2534       ;; Sun's bug that swallows newlines.
2535       (goto-char (1+ delimline))
2536       (when (eval message-mailer-swallows-blank-line)
2537         (newline))
2538       (when message-interactive
2539         (save-excursion
2540           (set-buffer errbuf)
2541           (erase-buffer))))
2542     (let ((default-directory "/")
2543           (coding-system-for-write message-send-coding-system))
2544       (apply 'call-process-region
2545              (append (list (point-min) (point-max)
2546                            (if (boundp 'sendmail-program)
2547                                sendmail-program
2548                              "/usr/lib/sendmail")
2549                            nil errbuf nil "-oi")
2550                      ;; Always specify who from,
2551                      ;; since some systems have broken sendmails.
2552                      ;; But some systems are more broken with -f, so
2553                      ;; we'll let users override this.
2554                      (if (null message-sendmail-f-is-evil)
2555                          (list "-f" (message-make-address)))
2556                      ;; These mean "report errors by mail"
2557                      ;; and "deliver in background".
2558                      (if (null message-interactive) '("-oem" "-odb"))
2559                      ;; Get the addresses from the message
2560                      ;; unless this is a resend.
2561                      ;; We must not do that for a resend
2562                      ;; because we would find the original addresses.
2563                      ;; For a resend, include the specific addresses.
2564                      (if resend-to-addresses
2565                          (list resend-to-addresses)
2566                        '("-t")))))
2567     (when message-interactive
2568       (save-excursion
2569         (set-buffer errbuf)
2570         (goto-char (point-min))
2571         (while (re-search-forward "\n\n* *" nil t)
2572           (replace-match "; "))
2573         (if (not (zerop (buffer-size)))
2574             (error "Sending...failed to %s"
2575                    (buffer-substring (point-min) (point-max)))))
2576       (when (bufferp errbuf)
2577         (kill-buffer errbuf)))))
2578
2579 (defun message-send-mail-with-qmail ()
2580   "Pass the prepared message buffer to qmail-inject.
2581 Refer to the documentation for the variable `message-send-mail-function'
2582 to find out how to use this."
2583   ;; replace the header delimiter with a blank line
2584   (goto-char (point-min))
2585   (re-search-forward
2586    (concat "^" (regexp-quote mail-header-separator) "\n"))
2587   (replace-match "\n")
2588   (run-hooks 'message-send-mail-hook)
2589   ;; send the message
2590   (case
2591       (let ((coding-system-for-write message-send-coding-system))
2592         (apply
2593          'call-process-region 1 (point-max) message-qmail-inject-program
2594          nil nil nil
2595          ;; qmail-inject's default behaviour is to look for addresses on the
2596          ;; command line; if there're none, it scans the headers.
2597          ;; yes, it does The Right Thing w.r.t. Resent-To and it's kin.
2598          ;;
2599          ;; in general, ALL of qmail-inject's defaults are perfect for simply
2600          ;; reading a formatted (i. e., at least a To: or Resent-To header)
2601          ;; message from stdin.
2602          ;;
2603          ;; qmail also has the advantage of not having been raped by
2604          ;; various vendors, so we don't have to allow for that, either --
2605          ;; compare this with message-send-mail-with-sendmail and weep
2606          ;; for sendmail's lost innocence.
2607          ;;
2608          ;; all this is way cool coz it lets us keep the arguments entirely
2609          ;; free for -inject-arguments -- a big win for the user and for us
2610          ;; since we don't have to play that double-guessing game and the user
2611          ;; gets full control (no gestapo'ish -f's, for instance).  --sj
2612          message-qmail-inject-args))
2613     ;; qmail-inject doesn't say anything on it's stdout/stderr,
2614     ;; we have to look at the retval instead
2615     (0 nil)
2616     (1   (error "qmail-inject reported permanent failure"))
2617     (111 (error "qmail-inject reported transient failure"))
2618     ;; should never happen
2619     (t   (error "qmail-inject reported unknown failure"))))
2620
2621 (defun message-send-mail-with-mh ()
2622   "Send the prepared message buffer with mh."
2623   (let ((mh-previous-window-config nil)
2624         (name (mh-new-draft-name)))
2625     (setq buffer-file-name name)
2626     ;; MH wants to generate these headers itself.
2627     (when message-mh-deletable-headers
2628       (let ((headers message-mh-deletable-headers))
2629         (while headers
2630           (goto-char (point-min))
2631           (and (re-search-forward
2632                 (concat "^" (symbol-name (car headers)) ": *") nil t)
2633                (message-delete-line))
2634           (pop headers))))
2635     (run-hooks 'message-send-mail-hook)
2636     ;; Pass it on to mh.
2637     (mh-send-letter)))
2638
2639 (defun message-send-news (&optional arg)
2640   (let* ((tembuf (message-generate-new-buffer-clone-locals " *message temp*"))
2641          (case-fold-search nil)
2642          (method (if (message-functionp message-post-method)
2643                      (funcall message-post-method arg)
2644                    message-post-method))
2645          (group-name-charset (gnus-group-name-charset method ""))
2646          (rfc2047-header-encoding-alist
2647           (if group-name-charset
2648               (cons (cons "Newsgroups" group-name-charset)
2649                     rfc2047-header-encoding-alist)
2650             rfc2047-header-encoding-alist))
2651          (messbuf (current-buffer))
2652          (message-syntax-checks
2653           (if arg
2654               (cons '(existing-newsgroups . disabled)
2655                     message-syntax-checks)
2656             message-syntax-checks))
2657          (message-this-is-news t)
2658          (message-posting-charset (gnus-setup-posting-charset
2659                                    (save-restriction
2660                                      (message-narrow-to-headers-or-head)
2661                                      (message-fetch-field "Newsgroups"))))
2662          result)
2663     (if (not (message-check-news-body-syntax))
2664         nil
2665       (save-restriction
2666         (message-narrow-to-headers)
2667         ;; Insert some headers.
2668         (message-generate-headers message-required-news-headers)
2669         ;; Let the user do all of the above.
2670         (run-hooks 'message-header-hook))
2671       (if group-name-charset
2672           (setq message-syntax-checks
2673               (cons '(valid-newsgroups . disabled)
2674                     message-syntax-checks)))
2675       (message-cleanup-headers)
2676       (if (not (message-check-news-syntax))
2677           nil
2678         (unwind-protect
2679             (save-excursion
2680               (set-buffer tembuf)
2681               (buffer-disable-undo)
2682               (erase-buffer)
2683               ;; Avoid copying text props.
2684               (insert (with-current-buffer messbuf
2685                         (buffer-substring-no-properties
2686                          (point-min) (point-max))))
2687               (message-encode-message-body)
2688               ;; Remove some headers.
2689               (save-restriction
2690                 (message-narrow-to-headers)
2691                 ;; We (re)generate the Lines header.
2692                 (when (memq 'Lines message-required-mail-headers)
2693                   (message-generate-headers '(Lines)))
2694                 ;; Remove some headers.
2695                 (message-remove-header message-ignored-news-headers t)
2696                 (let ((mail-parse-charset message-default-charset))
2697                   (mail-encode-encoded-word-buffer)))
2698               (goto-char (point-max))
2699               ;; require one newline at the end.
2700               (or (= (preceding-char) ?\n)
2701                   (insert ?\n))
2702               (let ((case-fold-search t))
2703                 ;; Remove the delimiter.
2704                 (goto-char (point-min))
2705                 (re-search-forward
2706                  (concat "^" (regexp-quote mail-header-separator) "\n"))
2707                 (replace-match "\n")
2708                 (backward-char 1))
2709               (run-hooks 'message-send-news-hook)
2710               (gnus-open-server method)
2711               (setq result (let ((mail-header-separator ""))
2712                              (gnus-request-post method))))
2713           (kill-buffer tembuf))
2714         (set-buffer messbuf)
2715         (if result
2716             (push 'news message-sent-message-via)
2717           (message "Couldn't send message via news: %s"
2718                    (nnheader-get-report (car method)))
2719           nil)))))
2720
2721 ;;;
2722 ;;; Header generation & syntax checking.
2723 ;;;
2724
2725 (defun message-check-element (type)
2726   "Return non-nil if this TYPE is not to be checked."
2727   (if (eq message-syntax-checks 'dont-check-for-anything-just-trust-me)
2728       t
2729     (let ((able (assq type message-syntax-checks)))
2730       (and (consp able)
2731            (eq (cdr able) 'disabled)))))
2732
2733 (defun message-check-news-syntax ()
2734   "Check the syntax of the message."
2735   (save-excursion
2736     (save-restriction
2737       (widen)
2738       ;; We narrow to the headers and check them first.
2739       (save-excursion
2740         (save-restriction
2741           (message-narrow-to-headers)
2742           (message-check-news-header-syntax))))))
2743
2744 (defun message-check-news-header-syntax ()
2745   (and
2746    ;; Check Newsgroups header.
2747    (message-check 'newsgroups
2748      (let ((group (message-fetch-field "newsgroups")))
2749        (or
2750         (and group
2751              (not (string-match "\\`[ \t]*\\'" group)))
2752         (ignore
2753          (message
2754           "The newsgroups field is empty or missing.  Posting is denied.")))))
2755    ;; Check the Subject header.
2756    (message-check 'subject
2757      (let* ((case-fold-search t)
2758             (subject (message-fetch-field "subject")))
2759        (or
2760         (and subject
2761              (not (string-match "\\`[ \t]*\\'" subject)))
2762         (ignore
2763          (message
2764           "The subject field is empty or missing.  Posting is denied.")))))
2765    ;; Check for commands in Subject.
2766    (message-check 'subject-cmsg
2767      (if (string-match "^cmsg " (message-fetch-field "subject"))
2768          (y-or-n-p
2769           "The control code \"cmsg\" is in the subject.  Really post? ")
2770        t))
2771    ;; Check for multiple identical headers.
2772    (message-check 'multiple-headers
2773      (let (found)
2774        (while (and (not found)
2775                    (re-search-forward "^[^ \t:]+: " nil t))
2776          (save-excursion
2777            (or (re-search-forward
2778                 (concat "^"
2779                         (regexp-quote
2780                          (setq found
2781                                (buffer-substring
2782                                 (match-beginning 0) (- (match-end 0) 2))))
2783                         ":")
2784                 nil t)
2785                (setq found nil))))
2786        (if found
2787            (y-or-n-p (format "Multiple %s headers.  Really post? " found))
2788          t)))
2789    ;; Check for Version and Sendsys.
2790    (message-check 'sendsys
2791      (if (re-search-forward "^Sendsys:\\|^Version:" nil t)
2792          (y-or-n-p
2793           (format "The article contains a %s command.  Really post? "
2794                   (buffer-substring (match-beginning 0)
2795                                     (1- (match-end 0)))))
2796        t))
2797    ;; See whether we can shorten Followup-To.
2798    (message-check 'shorten-followup-to
2799      (let ((newsgroups (message-fetch-field "newsgroups"))
2800            (followup-to (message-fetch-field "followup-to"))
2801            to)
2802        (when (and newsgroups
2803                   (string-match "," newsgroups)
2804                   (not followup-to)
2805                   (not
2806                    (zerop
2807                     (length
2808                      (setq to (completing-read
2809                                "Followups to: (default all groups) "
2810                                (mapcar (lambda (g) (list g))
2811                                        (cons "poster"
2812                                              (message-tokenize-header
2813                                               newsgroups)))))))))
2814          (goto-char (point-min))
2815          (insert "Followup-To: " to "\n"))
2816        t))
2817    ;; Check "Shoot me".
2818    (message-check 'shoot
2819      (if (re-search-forward
2820           "Message-ID.*.i-did-not-set--mail-host-address--so-shoot-me" nil t)
2821          (y-or-n-p "You appear to have a misconfigured system.  Really post? ")
2822        t))
2823    ;; Check for Approved.
2824    (message-check 'approved
2825      (if (re-search-forward "^Approved:" nil t)
2826          (y-or-n-p "The article contains an Approved header.  Really post? ")
2827        t))
2828    ;; Check the Message-ID header.
2829    (message-check 'message-id
2830      (let* ((case-fold-search t)
2831             (message-id (message-fetch-field "message-id" t)))
2832        (or (not message-id)
2833            ;; Is there an @ in the ID?
2834            (and (string-match "@" message-id)
2835                 ;; Is there a dot in the ID?
2836                 (string-match "@[^.]*\\." message-id)
2837                 ;; Does the ID end with a dot?
2838                 (not (string-match "\\.>" message-id)))
2839            (y-or-n-p
2840             (format "The Message-ID looks strange: \"%s\".  Really post? "
2841                     message-id)))))
2842    ;; Check the Newsgroups & Followup-To headers.
2843    (message-check 'existing-newsgroups
2844      (let* ((case-fold-search t)
2845             (newsgroups (message-fetch-field "newsgroups"))
2846             (followup-to (message-fetch-field "followup-to"))
2847             (groups (message-tokenize-header
2848                      (if followup-to
2849                          (concat newsgroups "," followup-to)
2850                        newsgroups)))
2851             (hashtb (and (boundp 'gnus-active-hashtb)
2852                          gnus-active-hashtb))
2853             errors)
2854        (if (or (not hashtb)
2855                (not (boundp 'gnus-read-active-file))
2856                (not gnus-read-active-file)
2857                (eq gnus-read-active-file 'some))
2858            t
2859          (while groups
2860            (when (and (not (boundp (intern (car groups) hashtb)))
2861                       (not (equal (car groups) "poster")))
2862              (push (car groups) errors))
2863            (pop groups))
2864          (if (not errors)
2865              t
2866            (y-or-n-p
2867             (format
2868              "Really post to %s unknown group%s: %s? "
2869              (if (= (length errors) 1) "this" "these")
2870              (if (= (length errors) 1) "" "s")
2871              (mapconcat 'identity errors ", ")))))))
2872    ;; Check the Newsgroups & Followup-To headers for syntax errors.
2873    (message-check 'valid-newsgroups
2874      (let ((case-fold-search t)
2875            (headers '("Newsgroups" "Followup-To"))
2876            header error)
2877        (while (and headers (not error))
2878          (when (setq header (mail-fetch-field (car headers)))
2879            (if (or
2880                 (not
2881                  (string-match
2882                   "\\`\\([-+_&.a-zA-Z0-9]+\\)?\\(,[-+_&.a-zA-Z0-9]+\\)*\\'"
2883                   header))
2884                 (memq
2885                  nil (mapcar
2886                       (lambda (g)
2887                         (not (string-match "\\.\\'\\|\\.\\." g)))
2888                       (message-tokenize-header header ","))))
2889                (setq error t)))
2890          (unless error
2891            (pop headers)))
2892        (if (not error)
2893            t
2894          (y-or-n-p
2895           (format "The %s header looks odd: \"%s\".  Really post? "
2896                   (car headers) header)))))
2897    (message-check 'repeated-newsgroups
2898      (let ((case-fold-search t)
2899            (headers '("Newsgroups" "Followup-To"))
2900            header error groups group)
2901        (while (and headers
2902                    (not error))
2903          (when (setq header (mail-fetch-field (pop headers)))
2904            (setq groups (message-tokenize-header header ","))
2905            (while (setq group (pop groups))
2906              (when (member group groups)
2907                (setq error group
2908                      groups nil)))))
2909        (if (not error)
2910            t
2911          (y-or-n-p
2912           (format "Group %s is repeated in headers.  Really post? " error)))))
2913    ;; Check the From header.
2914    (message-check 'from
2915      (let* ((case-fold-search t)
2916             (from (message-fetch-field "from"))
2917             ad)
2918        (cond
2919         ((not from)
2920          (message "There is no From line.  Posting is denied.")
2921          nil)
2922         ((or (not (string-match
2923                    "@[^\\.]*\\."
2924                    (setq ad (nth 1 (mail-extract-address-components
2925                                     from))))) ;larsi@ifi
2926              (string-match "\\.\\." ad) ;larsi@ifi..uio
2927              (string-match "@\\." ad)   ;larsi@.ifi.uio
2928              (string-match "\\.$" ad)   ;larsi@ifi.uio.
2929              (not (string-match "^[^@]+@[^@]+$" ad)) ;larsi.ifi.uio
2930              (string-match "(.*).*(.*)" from)) ;(lars) (lars)
2931          (message
2932           "Denied posting -- the From looks strange: \"%s\"." from)
2933          nil)
2934         (t t))))))
2935
2936 (defun message-check-news-body-syntax ()
2937   (and
2938    ;; Check for long lines.
2939    (message-check 'long-lines
2940      (goto-char (point-min))
2941      (re-search-forward
2942       (concat "^" (regexp-quote mail-header-separator) "$"))
2943      (forward-line 1)
2944      (while (and
2945              (or (looking-at 
2946                   "<#\\(/\\)?\\(multipart\\|part\\|external\\|mml\\)")
2947                  (let ((p (point)))
2948                    (end-of-line)
2949                    (< (- (point) p) 80)))
2950              (zerop (forward-line 1))))
2951      (or (bolp)
2952          (eobp)
2953          (y-or-n-p
2954           "You have lines longer than 79 characters.  Really post? ")))
2955    ;; Check whether the article is empty.
2956    (message-check 'empty
2957      (goto-char (point-min))
2958      (re-search-forward
2959       (concat "^" (regexp-quote mail-header-separator) "$"))
2960      (forward-line 1)
2961      (let ((b (point)))
2962        (goto-char (point-max))
2963        (re-search-backward message-signature-separator nil t)
2964        (beginning-of-line)
2965        (or (re-search-backward "[^ \n\t]" b t)
2966            (if (message-gnksa-enable-p 'empty-article)
2967                (y-or-n-p "Empty article.  Really post? ")
2968              (message "Denied posting -- Empty article.")
2969              nil))))
2970    ;; Check for control characters.
2971    (message-check 'control-chars
2972      (if (re-search-forward "[\000-\007\013\015-\032\034-\037\200-\237]" nil t)
2973          (y-or-n-p
2974           "The article contains control characters.  Really post? ")
2975        t))
2976    ;; Check excessive size.
2977    (message-check 'size
2978      (if (> (buffer-size) 60000)
2979          (y-or-n-p
2980           (format "The article is %d octets long.  Really post? "
2981                   (buffer-size)))
2982        t))
2983    ;; Check whether any new text has been added.
2984    (message-check 'new-text
2985      (or
2986       (not message-checksum)
2987       (not (eq (message-checksum) message-checksum))
2988       (if (message-gnksa-enable-p 'quoted-text-only)
2989           (y-or-n-p
2990            "It looks like no new text has been added.  Really post? ")
2991         (message "Denied posting -- no new text has been added.")
2992         nil)))
2993    ;; Check the length of the signature.
2994    (message-check 'signature
2995      (goto-char (point-max))
2996      (if (> (count-lines (point) (point-max)) 5)
2997          (y-or-n-p
2998           (format
2999            "Your .sig is %d lines; it should be max 4.  Really post? "
3000            (1- (count-lines (point) (point-max)))))
3001        t))
3002    ;; Ensure that text follows last quoted portion.
3003    (message-check 'quoting-style
3004      (goto-char (point-max))
3005      (let ((no-problem t))
3006        (when (search-backward-regexp "^>[^\n]*\n" nil t)
3007          (setq no-problem (search-forward-regexp "^[ \t]*[^>\n]" nil t)))
3008        (if no-problem
3009            t
3010          (if (message-gnksa-enable-p 'quoted-text-only)
3011              (y-or-n-p "Your text should follow quoted text.  Really post? ")
3012            ;; Ensure that
3013            (goto-char (point-min))
3014            (re-search-forward
3015             (concat "^" (regexp-quote mail-header-separator) "$"))
3016            (if (search-forward-regexp "^[ \t]*[^>\n]" nil t)
3017                (y-or-n-p "Your text should follow quoted text.  Really post? ")
3018              (message "Denied posting -- only quoted text.")
3019              nil)))))))
3020
3021 (defun message-checksum ()
3022   "Return a \"checksum\" for the current buffer."
3023   (let ((sum 0))
3024     (save-excursion
3025       (goto-char (point-min))
3026       (re-search-forward
3027        (concat "^" (regexp-quote mail-header-separator) "$"))
3028       (while (not (eobp))
3029         (when (not (looking-at "[ \t\n]"))
3030           (setq sum (logxor (ash sum 1) (if (natnump sum) 0 1)
3031                             (char-after))))
3032         (forward-char 1)))
3033     sum))
3034
3035 (defun message-do-fcc ()
3036   "Process Fcc headers in the current buffer."
3037   (let ((case-fold-search t)
3038         (buf (current-buffer))
3039         list file)
3040     (save-excursion
3041       (set-buffer (get-buffer-create " *message temp*"))
3042       (erase-buffer)
3043       (insert-buffer-substring buf)
3044       (save-restriction
3045         (message-narrow-to-headers)
3046         (while (setq file (message-fetch-field "fcc"))
3047           (push file list)
3048           (message-remove-header "fcc" nil t)))
3049       (message-encode-message-body)
3050       (save-restriction
3051         (message-narrow-to-headers)
3052         (let ((mail-parse-charset message-default-charset)
3053               (rfc2047-header-encoding-alist
3054                (cons '("Newsgroups" . default)
3055                      rfc2047-header-encoding-alist)))
3056           (mail-encode-encoded-word-buffer)))
3057       (goto-char (point-min))
3058       (when (re-search-forward
3059              (concat "^" (regexp-quote mail-header-separator) "$")
3060              nil t)
3061         (replace-match "" t t ))
3062       ;; Process FCC operations.
3063       (while list
3064         (setq file (pop list))
3065         (if (string-match "^[ \t]*|[ \t]*\\(.*\\)[ \t]*$" file)
3066             ;; Pipe the article to the program in question.
3067             (call-process-region (point-min) (point-max) shell-file-name
3068                                  nil nil nil shell-command-switch
3069                                  (match-string 1 file))
3070           ;; Save the article.
3071           (setq file (expand-file-name file))
3072           (unless (file-exists-p (file-name-directory file))
3073             (make-directory (file-name-directory file) t))
3074           (if (and message-fcc-handler-function
3075                    (not (eq message-fcc-handler-function 'rmail-output)))
3076               (funcall message-fcc-handler-function file)
3077             (if (and (file-readable-p file) (mail-file-babyl-p file))
3078                 (rmail-output file 1 nil t)
3079               (let ((mail-use-rfc822 t))
3080                 (rmail-output file 1 t t))))))
3081       (kill-buffer (current-buffer)))))
3082
3083 (defun message-output (filename)
3084   "Append this article to Unix/babyl mail file FILENAME."
3085   (if (and (file-readable-p filename)
3086            (mail-file-babyl-p filename))
3087       (gnus-output-to-rmail filename t)
3088     (gnus-output-to-mail filename t)))
3089
3090 (defun message-cleanup-headers ()
3091   "Do various automatic cleanups of the headers."
3092   ;; Remove empty lines in the header.
3093   (save-restriction
3094     (message-narrow-to-headers)
3095     ;; Remove blank lines.
3096     (while (re-search-forward "^[ \t]*\n" nil t)
3097       (replace-match "" t t))
3098
3099     ;; Correct Newsgroups and Followup-To headers:  Change sequence of
3100     ;; spaces to comma and eliminate spaces around commas.  Eliminate
3101     ;; embedded line breaks.
3102     (goto-char (point-min))
3103     (while (re-search-forward "^\\(Newsgroups\\|Followup-To\\): +" nil t)
3104       (save-restriction
3105         (narrow-to-region
3106          (point)
3107          (if (re-search-forward "^[^ \t]" nil t)
3108              (match-beginning 0)
3109            (forward-line 1)
3110            (point)))
3111         (goto-char (point-min))
3112         (while (re-search-forward "\n[ \t]+" nil t)
3113           (replace-match " " t t))      ;No line breaks (too confusing)
3114         (goto-char (point-min))
3115         (while (re-search-forward "[ \t\n]*,[ \t\n]*\\|[ \t]+" nil t)
3116           (replace-match "," t t))
3117         (goto-char (point-min))
3118         ;; Remove trailing commas.
3119         (when (re-search-forward ",+$" nil t)
3120           (replace-match "" t t))))))
3121
3122 (defun message-make-date (&optional now)
3123   "Make a valid data header.
3124 If NOW, use that time instead."
3125   (let* ((now (or now (current-time)))
3126          (zone (nth 8 (decode-time now)))
3127          (sign "+"))
3128     (when (< zone 0)
3129       (setq sign "-")
3130       (setq zone (- zone)))
3131     (concat
3132      (format-time-string "%d" now)
3133      ;; The month name of the %b spec is locale-specific.  Pfff.
3134      (format " %s "
3135              (capitalize (car (rassoc (nth 4 (decode-time now))
3136                                       parse-time-months))))
3137      (format-time-string "%Y %H:%M:%S " now)
3138      ;; We do all of this because XEmacs doesn't have the %z spec.
3139      (format "%s%02d%02d" sign (/ zone 3600) (/ (% zone 3600) 60)))))
3140
3141 (defun message-make-message-id ()
3142   "Make a unique Message-ID."
3143   (concat "<" (message-unique-id)
3144           (let ((psubject (save-excursion (message-fetch-field "subject")))
3145                 (psupersedes
3146                  (save-excursion (message-fetch-field "supersedes"))))