2001-03-17 10: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 "rmailout"))
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   (set (make-local-variable 'fill-paragraph-function) 
1604        'message-fill-paragraph)
1605   (make-local-variable 'paragraph-separate)
1606   (make-local-variable 'paragraph-start)
1607   (make-local-variable 'adaptive-fill-regexp)
1608   (unless (boundp 'adaptive-fill-first-line-regexp)
1609     (setq adaptive-fill-first-line-regexp nil))
1610   (make-local-variable 'adaptive-fill-first-line-regexp)
1611   (make-local-variable 'auto-fill-inhibit-regexp)
1612   (let ((quote-prefix-regexp
1613          ;; User should change message-cite-prefix-regexp if
1614          ;; message-yank-prefix is set to an abnormal value.
1615          (concat "\\(" message-cite-prefix-regexp "\\)[ \t]*")))
1616     (setq paragraph-start
1617           (concat
1618            (regexp-quote mail-header-separator) "$\\|"
1619            "[ \t]*$\\|"                 ; blank lines
1620            "-- $\\|"                    ; signature delimiter
1621            "---+$\\|"                   ; delimiters for forwarded messages
1622            page-delimiter "$\\|"        ; spoiler warnings
1623            ".*wrote:$\\|"               ; attribution lines
1624            quote-prefix-regexp "$"))    ; empty lines in quoted text
1625     (setq paragraph-separate paragraph-start)
1626     (setq adaptive-fill-regexp
1627           (concat quote-prefix-regexp "\\|" adaptive-fill-regexp))
1628     (setq adaptive-fill-first-line-regexp
1629           (concat quote-prefix-regexp "\\|"
1630                   adaptive-fill-first-line-regexp))
1631     (setq auto-fill-inhibit-regexp "^[A-Z][^: \n\t]+:")))
1632
1633 \f
1634
1635 ;;;
1636 ;;; Message mode commands
1637 ;;;
1638
1639 ;;; Movement commands
1640
1641 (defun message-goto-to ()
1642   "Move point to the To header."
1643   (interactive)
1644   (message-position-on-field "To"))
1645
1646 (defun message-goto-subject ()
1647   "Move point to the Subject header."
1648   (interactive)
1649   (message-position-on-field "Subject"))
1650
1651 (defun message-goto-cc ()
1652   "Move point to the Cc header."
1653   (interactive)
1654   (message-position-on-field "Cc" "To"))
1655
1656 (defun message-goto-bcc ()
1657   "Move point to the Bcc  header."
1658   (interactive)
1659   (message-position-on-field "Bcc" "Cc" "To"))
1660
1661 (defun message-goto-fcc ()
1662   "Move point to the Fcc header."
1663   (interactive)
1664   (message-position-on-field "Fcc" "To" "Newsgroups"))
1665
1666 (defun message-goto-reply-to ()
1667   "Move point to the Reply-To header."
1668   (interactive)
1669   (message-position-on-field "Reply-To" "Subject"))
1670
1671 (defun message-goto-newsgroups ()
1672   "Move point to the Newsgroups header."
1673   (interactive)
1674   (message-position-on-field "Newsgroups"))
1675
1676 (defun message-goto-distribution ()
1677   "Move point to the Distribution header."
1678   (interactive)
1679   (message-position-on-field "Distribution"))
1680
1681 (defun message-goto-followup-to ()
1682   "Move point to the Followup-To header."
1683   (interactive)
1684   (message-position-on-field "Followup-To" "Newsgroups"))
1685
1686 (defun message-goto-keywords ()
1687   "Move point to the Keywords header."
1688   (interactive)
1689   (message-position-on-field "Keywords" "Subject"))
1690
1691 (defun message-goto-summary ()
1692   "Move point to the Summary header."
1693   (interactive)
1694   (message-position-on-field "Summary" "Subject"))
1695
1696 (defun message-goto-body (&optional interactivep)
1697   "Move point to the beginning of the message body."
1698   (interactive (list t))
1699   (when (and interactivep
1700              (looking-at "[ \t]*\n"))
1701     (expand-abbrev))
1702   (goto-char (point-min))
1703   (or (search-forward (concat "\n" mail-header-separator "\n") nil t)
1704       (search-forward "\n\n" nil t)))
1705
1706 (defun message-goto-eoh ()
1707   "Move point to the end of the headers."
1708   (interactive)
1709   (message-goto-body)
1710   (forward-line -1))
1711
1712 (defun message-goto-signature ()
1713   "Move point to the beginning of the message signature.
1714 If there is no signature in the article, go to the end and
1715 return nil."
1716   (interactive)
1717   (goto-char (point-min))
1718   (if (re-search-forward message-signature-separator nil t)
1719       (forward-line 1)
1720     (goto-char (point-max))
1721     nil))
1722
1723 \f
1724
1725 (defun message-insert-to (&optional force)
1726   "Insert a To header that points to the author of the article being replied to.
1727 If the original author requested not to be sent mail, the function signals
1728 an error.
1729 With the prefix argument FORCE, insert the header anyway."
1730   (interactive "P")
1731   (let ((co (message-fetch-reply-field "mail-copies-to")))
1732     (when (and (null force)
1733                co
1734                (or (equal (downcase co) "never")
1735                    (equal (downcase co) "nobody")))
1736       (error "The user has requested not to have copies sent via mail")))
1737   (when (and (message-position-on-field "To")
1738              (mail-fetch-field "to")
1739              (not (string-match "\\` *\\'" (mail-fetch-field "to"))))
1740     (insert ", "))
1741   (insert (or (message-fetch-reply-field "mail-reply-to")
1742               (message-fetch-reply-field "reply-to")
1743               (message-fetch-reply-field "from") "")))
1744
1745 (defun message-widen-reply ()
1746   "Widen the reply to include maximum recipients."
1747   (interactive)
1748   (let ((follow-to
1749          (and message-reply-buffer
1750               (buffer-name message-reply-buffer)
1751               (save-excursion
1752                 (set-buffer message-reply-buffer)
1753                 (message-get-reply-headers t)))))
1754     (save-excursion
1755       (save-restriction
1756         (message-narrow-to-headers)
1757         (dolist (elem follow-to)
1758           (message-remove-header (symbol-name (car elem)))
1759           (goto-char (point-min))
1760           (insert (symbol-name (car elem)) ": "
1761                   (cdr elem) "\n"))))))
1762
1763 (defun message-insert-newsgroups ()
1764   "Insert the Newsgroups header from the article being replied to."
1765   (interactive)
1766   (when (and (message-position-on-field "Newsgroups")
1767              (mail-fetch-field "newsgroups")
1768              (not (string-match "\\` *\\'" (mail-fetch-field "newsgroups"))))
1769     (insert ","))
1770   (insert (or (message-fetch-reply-field "newsgroups") "")))
1771
1772 \f
1773
1774 ;;; Various commands
1775
1776 (defun message-delete-not-region (beg end)
1777   "Delete everything in the body of the current message outside of the region."
1778   (interactive "r")
1779   (save-excursion
1780     (goto-char end)
1781     (delete-region (point) (if (not (message-goto-signature))
1782                                (point)
1783                              (forward-line -2)
1784                              (point)))
1785     (insert "\n")
1786     (goto-char beg)
1787     (delete-region beg (progn (message-goto-body)
1788                               (forward-line 2)
1789                               (point))))
1790   (when (message-goto-signature)
1791     (forward-line -2)))
1792
1793 (defun message-kill-to-signature ()
1794   "Deletes all text up to the signature."
1795   (interactive)
1796   (let ((point (point)))
1797     (message-goto-signature)
1798     (unless (eobp)
1799       (forward-line -2))
1800     (kill-region point (point))
1801     (unless (bolp)
1802       (insert "\n"))))
1803
1804 (defun message-newline-and-reformat (&optional arg not-break)
1805   "Insert four newlines, and then reformat if inside quoted text.
1806 Prefix arg means justify as well."
1807   (interactive (list (if current-prefix-arg 'full)))
1808   (let (quoted point beg end leading-space bolp)
1809     (setq point (point))
1810     (beginning-of-line)
1811     (setq beg (point))
1812     (setq bolp (= beg point))
1813     ;; Find first line of the paragraph.
1814     (if not-break
1815         (while (and (not (eobp))
1816                     (not (looking-at message-cite-prefix-regexp))
1817                 (looking-at paragraph-start))
1818           (forward-line 1)))
1819     ;; Find the prefix
1820     (when (looking-at message-cite-prefix-regexp)
1821       (setq quoted (match-string 0))
1822       (goto-char (match-end 0))
1823       (looking-at "[ \t]*")
1824       (setq leading-space (match-string 0)))
1825     (if (and quoted
1826              (not not-break)
1827              (not bolp)
1828              (< (- point beg) (length quoted)))
1829         ;; break inside the cite prefix.
1830         (setq quoted nil
1831               end nil))
1832     (if quoted
1833         (progn
1834           (forward-line 1)
1835           (while (and (not (eobp))
1836                       (not (looking-at paragraph-separate))
1837                       (looking-at message-cite-prefix-regexp)
1838                       (equal quoted (match-string 0)))
1839             (goto-char (match-end 0))
1840             (looking-at "[ \t]*")
1841             (if (> (length leading-space) (length (match-string 0)))
1842                 (setq leading-space (match-string 0)))
1843             (forward-line 1))
1844           (setq end (point))
1845           (goto-char beg)
1846           (while (and (if (bobp) nil (forward-line -1) t)
1847                       (not (looking-at paragraph-start))
1848                       (looking-at message-cite-prefix-regexp)
1849                       (equal quoted (match-string 0)))
1850             (setq beg (point))
1851             (goto-char (match-end 0))
1852             (looking-at "[ \t]*")
1853             (if (> (length leading-space) (length (match-string 0)))
1854                 (setq leading-space (match-string 0)))))
1855       (while (and (not (eobp))
1856                   (not (looking-at paragraph-separate))
1857                   (not (looking-at message-cite-prefix-regexp)))
1858         (forward-line 1))
1859       (setq end (point))
1860       (goto-char beg)
1861       (while (and (if (bobp) nil (forward-line -1) t)
1862                   (not (looking-at paragraph-start))
1863                   (not (looking-at message-cite-prefix-regexp)))
1864         (setq beg (point))))
1865     (goto-char point)
1866     (save-restriction
1867       (narrow-to-region beg end)
1868       (if not-break
1869           (setq point nil)
1870         (if bolp
1871             (insert "\n")
1872           (insert "\n\n"))
1873         (setq point (point))
1874         (insert "\n\n")
1875         (delete-region (point) (re-search-forward "[ \t]*"))
1876         (when (and quoted (not bolp))
1877           (insert quoted leading-space)))
1878       (if quoted
1879           (let* ((adaptive-fill-regexp
1880                  (regexp-quote (concat quoted leading-space)))
1881                  (adaptive-fill-first-line-regexp
1882                   adaptive-fill-regexp ))
1883             (fill-paragraph arg))
1884         (fill-paragraph arg))
1885       (if point (goto-char point)))))
1886
1887 (defun message-fill-paragraph (&optional arg)
1888   "Like `fill-paragraph'."
1889   (interactive (list (if current-prefix-arg 'full)))
1890   (message-newline-and-reformat arg t)
1891   t)
1892
1893 (defun message-insert-signature (&optional force)
1894   "Insert a signature.  See documentation for variable `message-signature'."
1895   (interactive (list 0))
1896   (let* ((signature
1897           (cond
1898            ((and (null message-signature)
1899                  (eq force 0))
1900             (save-excursion
1901               (goto-char (point-max))
1902               (not (re-search-backward message-signature-separator nil t))))
1903            ((and (null message-signature)
1904                  force)
1905             t)
1906            ((message-functionp message-signature)
1907             (funcall message-signature))
1908            ((listp message-signature)
1909             (eval message-signature))
1910            (t message-signature)))
1911          (signature
1912           (cond ((stringp signature)
1913                  signature)
1914                 ((and (eq t signature)
1915                       message-signature-file
1916                       (file-exists-p message-signature-file))
1917                  signature))))
1918     (when signature
1919       (goto-char (point-max))
1920       ;; Insert the signature.
1921       (unless (bolp)
1922         (insert "\n"))
1923       (insert "\n-- \n")
1924       (if (eq signature t)
1925           (insert-file-contents message-signature-file)
1926         (insert signature))
1927       (goto-char (point-max))
1928       (or (bolp) (insert "\n")))))
1929
1930 (defun message-elide-region (b e)
1931   "Elide the text in the region.
1932 An ellipsis (from `message-elide-ellipsis') will be inserted where the
1933 text was killed."
1934   (interactive "r")
1935   (kill-region b e)
1936   (insert message-elide-ellipsis))
1937
1938 (defvar message-caesar-translation-table nil)
1939
1940 (defun message-caesar-region (b e &optional n)
1941   "Caesar rotate region B to E by N, default 13, for decrypting netnews."
1942   (interactive
1943    (list
1944     (min (point) (or (mark t) (point)))
1945     (max (point) (or (mark t) (point)))
1946     (when current-prefix-arg
1947       (prefix-numeric-value current-prefix-arg))))
1948
1949   (setq n (if (numberp n) (mod n 26) 13)) ;canonize N
1950   (unless (or (zerop n)                 ; no action needed for a rot of 0
1951               (= b e))                  ; no region to rotate
1952     ;; We build the table, if necessary.
1953     (when (or (not message-caesar-translation-table)
1954               (/= (aref message-caesar-translation-table ?a) (+ ?a n)))
1955       (setq message-caesar-translation-table
1956             (message-make-caesar-translation-table n)))
1957     (translate-region b e message-caesar-translation-table)))
1958
1959 (defun message-make-caesar-translation-table (n)
1960   "Create a rot table with offset N."
1961   (let ((i -1)
1962         (table (make-string 256 0)))
1963     (while (< (incf i) 256)
1964       (aset table i i))
1965     (concat
1966      (substring table 0 ?A)
1967      (substring table (+ ?A n) (+ ?A n (- 26 n)))
1968      (substring table ?A (+ ?A n))
1969      (substring table (+ ?A 26) ?a)
1970      (substring table (+ ?a n) (+ ?a n (- 26 n)))
1971      (substring table ?a (+ ?a n))
1972      (substring table (+ ?a 26) 255))))
1973
1974 (defun message-caesar-buffer-body (&optional rotnum)
1975   "Caesar rotate all letters in the current buffer by 13 places.
1976 Used to encode/decode possibly offensive messages (commonly in rec.humor).
1977 With prefix arg, specifies the number of places to rotate each letter forward.
1978 Mail and USENET news headers are not rotated."
1979   (interactive (if current-prefix-arg
1980                    (list (prefix-numeric-value current-prefix-arg))
1981                  (list nil)))
1982   (save-excursion
1983     (save-restriction
1984       (when (message-goto-body)
1985         (narrow-to-region (point) (point-max)))
1986       (message-caesar-region (point-min) (point-max) rotnum))))
1987
1988 (defun message-pipe-buffer-body (program)
1989   "Pipe the message body in the current buffer through PROGRAM."
1990   (save-excursion
1991     (save-restriction
1992       (when (message-goto-body)
1993         (narrow-to-region (point) (point-max)))
1994       (shell-command-on-region
1995        (point-min) (point-max) program nil t))))
1996
1997 (defun message-rename-buffer (&optional enter-string)
1998   "Rename the *message* buffer to \"*message* RECIPIENT\".
1999 If the function is run with a prefix, it will ask for a new buffer
2000 name, rather than giving an automatic name."
2001   (interactive "Pbuffer name: ")
2002   (save-excursion
2003     (save-restriction
2004       (goto-char (point-min))
2005       (narrow-to-region (point)
2006                         (search-forward mail-header-separator nil 'end))
2007       (let* ((mail-to (or
2008                        (if (message-news-p) (message-fetch-field "Newsgroups")
2009                          (message-fetch-field "To"))
2010                        ""))
2011              (mail-trimmed-to
2012               (if (string-match "," mail-to)
2013                   (concat (substring mail-to 0 (match-beginning 0)) ", ...")
2014                 mail-to))
2015              (name-default (concat "*message* " mail-trimmed-to))
2016              (name (if enter-string
2017                        (read-string "New buffer name: " name-default)
2018                      name-default)))
2019         (rename-buffer name t)))))
2020
2021 (defun message-fill-yanked-message (&optional justifyp)
2022   "Fill the paragraphs of a message yanked into this one.
2023 Numeric argument means justify as well."
2024   (interactive "P")
2025   (save-excursion
2026     (goto-char (point-min))
2027     (search-forward (concat "\n" mail-header-separator "\n") nil t)
2028     (let ((fill-prefix message-yank-prefix))
2029       (fill-individual-paragraphs (point) (point-max) justifyp))))
2030
2031 (defun message-indent-citation ()
2032   "Modify text just inserted from a message to be cited.
2033 The inserted text should be the region.
2034 When this function returns, the region is again around the modified text.
2035
2036 Normally, indent each nonblank line `message-indentation-spaces' spaces.
2037 However, if `message-yank-prefix' is non-nil, insert that prefix on each line."
2038   (let ((start (point)))
2039     ;; Remove unwanted headers.
2040     (when message-ignored-cited-headers
2041       (let (all-removed)
2042         (save-restriction
2043           (narrow-to-region
2044            (goto-char start)
2045            (if (search-forward "\n\n" nil t)
2046                (1- (point))
2047              (point)))
2048           (message-remove-header message-ignored-cited-headers t)
2049           (when (= (point-min) (point-max))
2050             (setq all-removed t))
2051           (goto-char (point-max)))
2052         (if all-removed
2053             (goto-char start)
2054           (forward-line 1))))
2055     ;; Delete blank lines at the start of the buffer.
2056     (while (and (point-min)
2057                 (eolp)
2058                 (not (eobp)))
2059       (message-delete-line))
2060     ;; Delete blank lines at the end of the buffer.
2061     (goto-char (point-max))
2062     (unless (eolp)
2063       (insert "\n"))
2064     (while (and (zerop (forward-line -1))
2065                 (looking-at "$"))
2066       (message-delete-line))
2067     ;; Do the indentation.
2068     (if (null message-yank-prefix)
2069         (indent-rigidly start (mark t) message-indentation-spaces)
2070       (save-excursion
2071         (goto-char start)
2072         (while (< (point) (mark t))
2073           (insert message-yank-prefix)
2074           (forward-line 1))))
2075     (goto-char start)))
2076
2077 (defun message-yank-original (&optional arg)
2078   "Insert the message being replied to, if any.
2079 Puts point before the text and mark after.
2080 Normally indents each nonblank line ARG spaces (default 3).  However,
2081 if `message-yank-prefix' is non-nil, insert that prefix on each line.
2082
2083 This function uses `message-cite-function' to do the actual citing.
2084
2085 Just \\[universal-argument] as argument means don't indent, insert no
2086 prefix, and don't delete any headers."
2087   (interactive "P")
2088   (let ((modified (buffer-modified-p)))
2089     (when (and message-reply-buffer
2090                message-cite-function)
2091       (delete-windows-on message-reply-buffer t)
2092       (insert-buffer message-reply-buffer)
2093       (unless arg
2094         (funcall message-cite-function))
2095       (message-exchange-point-and-mark)
2096       (unless (bolp)
2097         (insert ?\n))
2098       (unless modified
2099         (setq message-checksum (message-checksum))))))
2100
2101 (defun message-yank-buffer (buffer)
2102   "Insert BUFFER into the current buffer and quote it."
2103   (interactive "bYank buffer: ")
2104   (let ((message-reply-buffer buffer))
2105     (save-window-excursion
2106       (message-yank-original))))
2107
2108 (defun message-buffers ()
2109   "Return a list of active message buffers."
2110   (let (buffers)
2111     (save-excursion
2112       (dolist (buffer (buffer-list t))
2113         (set-buffer buffer)
2114         (when (and (eq major-mode 'message-mode)
2115                    (null message-sent-message-via))
2116           (push (buffer-name buffer) buffers))))
2117     (nreverse buffers)))
2118
2119 (defun message-cite-original-without-signature ()
2120   "Cite function in the standard Message manner."
2121   (let ((start (point))
2122         (end (mark t))
2123         (functions
2124          (when message-indent-citation-function
2125            (if (listp message-indent-citation-function)
2126                message-indent-citation-function
2127              (list message-indent-citation-function)))))
2128     (mml-quote-region start end)
2129     ;; Allow undoing.
2130     (undo-boundary)
2131     (goto-char end)
2132     (when (re-search-backward message-signature-separator start t)
2133       ;; Also peel off any blank lines before the signature.
2134       (forward-line -1)
2135       (while (looking-at "^[ \t]*$")
2136         (forward-line -1))
2137       (forward-line 1)
2138       (delete-region (point) end)
2139       (unless (search-backward "\n\n" start t)
2140         ;; Insert a blank line if it is peeled off.
2141         (insert "\n")))
2142     (goto-char start)
2143     (while functions
2144       (funcall (pop functions)))
2145     (when message-citation-line-function
2146       (unless (bolp)
2147         (insert "\n"))
2148       (funcall message-citation-line-function))))
2149
2150 (eval-when-compile (defvar mail-citation-hook))         ;Compiler directive
2151 (defun message-cite-original ()
2152   "Cite function in the standard Message manner."
2153   (if (and (boundp 'mail-citation-hook)
2154            mail-citation-hook)
2155       (run-hooks 'mail-citation-hook)
2156     (let ((start (point))
2157           (end (mark t))
2158           (functions
2159            (when message-indent-citation-function
2160              (if (listp message-indent-citation-function)
2161                  message-indent-citation-function
2162                (list message-indent-citation-function)))))
2163       (mml-quote-region start end)
2164       (goto-char start)
2165       (while functions
2166         (funcall (pop functions)))
2167       (when message-citation-line-function
2168         (unless (bolp)
2169           (insert "\n"))
2170         (funcall message-citation-line-function)))))
2171
2172 (defun message-insert-citation-line ()
2173   "Insert a simple citation line."
2174   (when message-reply-headers
2175     (insert (mail-header-from message-reply-headers) " writes:\n\n")))
2176
2177 (defun message-position-on-field (header &rest afters)
2178   (let ((case-fold-search t))
2179     (save-restriction
2180       (narrow-to-region
2181        (goto-char (point-min))
2182        (progn
2183          (re-search-forward
2184           (concat "^" (regexp-quote mail-header-separator) "$"))
2185          (match-beginning 0)))
2186       (goto-char (point-min))
2187       (if (re-search-forward (concat "^" (regexp-quote header) ":") nil t)
2188           (progn
2189             (re-search-forward "^[^ \t]" nil 'move)
2190             (beginning-of-line)
2191             (skip-chars-backward "\n")
2192             t)
2193         (while (and afters
2194                     (not (re-search-forward
2195                           (concat "^" (regexp-quote (car afters)) ":")
2196                           nil t)))
2197           (pop afters))
2198         (when afters
2199           (re-search-forward "^[^ \t]" nil 'move)
2200           (beginning-of-line))
2201         (insert header ": \n")
2202         (forward-char -1)
2203         nil))))
2204
2205 (defun message-remove-signature ()
2206   "Remove the signature from the text between point and mark.
2207 The text will also be indented the normal way."
2208   (save-excursion
2209     (let ((start (point))
2210           mark)
2211       (if (not (re-search-forward message-signature-separator (mark t) t))
2212           ;; No signature here, so we just indent the cited text.
2213           (message-indent-citation)
2214         ;; Find the last non-empty line.
2215         (forward-line -1)
2216         (while (looking-at "[ \t]*$")
2217           (forward-line -1))
2218         (forward-line 1)
2219         (setq mark (set-marker (make-marker) (point)))
2220         (goto-char start)
2221         (message-indent-citation)
2222         ;; Enable undoing the deletion.
2223         (undo-boundary)
2224         (delete-region mark (mark t))
2225         (set-marker mark nil)))))
2226
2227 \f
2228
2229 ;;;
2230 ;;; Sending messages
2231 ;;;
2232
2233 (defun message-send-and-exit (&optional arg)
2234   "Send message like `message-send', then, if no errors, exit from mail buffer."
2235   (interactive "P")
2236   (let ((buf (current-buffer))
2237         (actions message-exit-actions))
2238     (when (and (message-send arg)
2239                (buffer-name buf))
2240       (if message-kill-buffer-on-exit
2241           (kill-buffer buf)
2242         (bury-buffer buf)
2243         (when (eq buf (current-buffer))
2244           (message-bury buf)))
2245       (message-do-actions actions)
2246       t)))
2247
2248 (defun message-dont-send ()
2249   "Don't send the message you have been editing."
2250   (interactive)
2251   (set-buffer-modified-p t)
2252   (save-buffer)
2253   (let ((actions message-postpone-actions))
2254     (message-bury (current-buffer))
2255     (message-do-actions actions)))
2256
2257 (defun message-kill-buffer ()
2258   "Kill the current buffer."
2259   (interactive)
2260   (when (or (not (buffer-modified-p))
2261             (yes-or-no-p "Message modified; kill anyway? "))
2262     (let ((actions message-kill-actions))
2263       (setq buffer-file-name nil)
2264       (kill-buffer (current-buffer))
2265       (message-do-actions actions))))
2266
2267 (defun message-bury (buffer)
2268   "Bury this mail BUFFER."
2269   (let ((newbuf (other-buffer buffer)))
2270     (bury-buffer buffer)
2271     (if (and (fboundp 'frame-parameters)
2272              (cdr (assq 'dedicated (frame-parameters)))
2273              (not (null (delq (selected-frame) (visible-frame-list)))))
2274         (delete-frame (selected-frame))
2275       (switch-to-buffer newbuf))))
2276
2277 (defun message-send (&optional arg)
2278   "Send the message in the current buffer.
2279 If `message-interactive' is non-nil, wait for success indication or
2280 error messages, and inform user.
2281 Otherwise any failure is reported in a message back to the user from
2282 the mailer.
2283 The usage of ARG is defined by the instance that called Message.
2284 It should typically alter the sending method in some way or other."
2285   (interactive "P")
2286   ;; Make it possible to undo the coming changes.
2287   (undo-boundary)
2288   (let ((inhibit-read-only t))
2289     (put-text-property (point-min) (point-max) 'read-only nil))
2290   (message-fix-before-sending)
2291   (run-hooks 'message-send-hook)
2292   (message message-sending-message)
2293   (let ((alist message-send-method-alist)
2294         (success t)
2295         elem sent
2296         (message-options message-options))
2297     (message-options-set-recipient)
2298     (while (and success
2299                 (setq elem (pop alist)))
2300       (when (funcall (cadr elem))
2301         (when (and (or (not (memq (car elem)
2302                                   message-sent-message-via))
2303                        (if (or (message-gnksa-enable-p 'multiple-copies)
2304                                (not (eq (car elem) 'news)))
2305                            (y-or-n-p
2306                             (format
2307                              "Already sent message via %s; resend? "
2308                              (car elem)))
2309                          (error "Denied posting -- multiple copies.")))
2310                    (setq success (funcall (caddr elem) arg)))
2311           (setq sent t))))
2312     (unless (or sent (not success))
2313       (error "No methods specified to send by"))
2314     (when (and success sent)
2315       (message-do-fcc)
2316       (save-excursion
2317         (run-hooks 'message-sent-hook))
2318       (message "Sending...done")
2319       ;; Mark the buffer as unmodified and delete auto-save.
2320       (set-buffer-modified-p nil)
2321       (delete-auto-save-file-if-necessary t)
2322       (message-disassociate-draft)
2323       ;; Delete other mail buffers and stuff.
2324       (message-do-send-housekeeping)
2325       (message-do-actions message-send-actions)
2326       ;; Return success.
2327       t)))
2328
2329 (defun message-send-via-mail (arg)
2330   "Send the current message via mail."
2331   (message-send-mail arg))
2332
2333 (defun message-send-via-news (arg)
2334   "Send the current message via news."
2335   (funcall message-send-news-function arg))
2336
2337 (defmacro message-check (type &rest forms)
2338   "Eval FORMS if TYPE is to be checked."
2339   `(or (message-check-element ,type)
2340        (save-excursion
2341          ,@forms)))
2342
2343 (put 'message-check 'lisp-indent-function 1)
2344 (put 'message-check 'edebug-form-spec '(form body))
2345
2346 (defun message-fix-before-sending ()
2347   "Do various things to make the message nice before sending it."
2348   ;; Make sure there's a newline at the end of the message.
2349   (goto-char (point-max))
2350   (unless (bolp)
2351     (insert "\n"))
2352   ;; Delete all invisible text.
2353   (message-check 'invisible-text
2354     (when (text-property-any (point-min) (point-max) 'invisible t)
2355       (put-text-property (point-min) (point-max) 'invisible nil)
2356       (unless (yes-or-no-p
2357                "Invisible text found and made visible; continue posting? ")
2358         (error "Invisible text found and made visible")))))
2359
2360 (defun message-add-action (action &rest types)
2361   "Add ACTION to be performed when doing an exit of type TYPES."
2362   (let (var)
2363     (while types
2364       (set (setq var (intern (format "message-%s-actions" (pop types))))
2365            (nconc (symbol-value var) (list action))))))
2366
2367 (defun message-do-actions (actions)
2368   "Perform all actions in ACTIONS."
2369   ;; Now perform actions on successful sending.
2370   (while actions
2371     (ignore-errors
2372       (cond
2373        ;; A simple function.
2374        ((message-functionp (car actions))
2375         (funcall (car actions)))
2376        ;; Something to be evaled.
2377        (t
2378         (eval (car actions)))))
2379     (pop actions)))
2380
2381 (defun message-send-mail-partially ()
2382   "Sendmail as message/partial."
2383   ;; replace the header delimiter with a blank line
2384   (goto-char (point-min))
2385   (re-search-forward
2386    (concat "^" (regexp-quote mail-header-separator) "\n"))
2387   (replace-match "\n")
2388   (run-hooks 'message-send-mail-hook)
2389   (let ((p (goto-char (point-min)))
2390         (tembuf (message-generate-new-buffer-clone-locals " message temp"))
2391         (curbuf (current-buffer))
2392         (id (message-make-message-id)) (n 1)
2393         plist total  header required-mail-headers)
2394     (while (not (eobp))
2395       (if (< (point-max) (+ p message-send-mail-partially-limit))
2396           (goto-char (point-max))
2397         (goto-char (+ p message-send-mail-partially-limit))
2398         (beginning-of-line)
2399         (if (<= (point) p) (forward-line 1))) ;; In case of bad message.
2400       (push p plist)
2401       (setq p (point)))
2402     (setq total (length plist))
2403     (push (point-max) plist)
2404     (setq plist (nreverse plist))
2405     (unwind-protect
2406         (save-excursion
2407           (setq p (pop plist))
2408           (while plist
2409             (set-buffer curbuf)
2410             (copy-to-buffer tembuf p (car plist))
2411             (set-buffer tembuf)
2412             (goto-char (point-min))
2413             (if header
2414                 (progn
2415                   (goto-char (point-min))
2416                   (narrow-to-region (point) (point))
2417                   (insert header))
2418               (message-goto-eoh)
2419               (setq header (buffer-substring (point-min) (point)))
2420               (goto-char (point-min))
2421               (narrow-to-region (point) (point))
2422               (insert header)
2423               (message-remove-header "Mime-Version")
2424               (message-remove-header "Content-Type")
2425               (message-remove-header "Content-Transfer-Encoding")
2426               (message-remove-header "Message-ID")
2427               (message-remove-header "Lines")
2428               (goto-char (point-max))
2429               (insert "Mime-Version: 1.0\n")
2430               (setq header (buffer-substring (point-min) (point-max))))
2431             (goto-char (point-max))
2432             (insert (format "Content-Type: message/partial; id=\"%s\"; number=%d; total=%d\n"
2433                             id n total))
2434             (let ((mail-header-separator ""))
2435               (when (memq 'Message-ID message-required-mail-headers)
2436                 (insert "Message-ID: " (message-make-message-id) "\n"))
2437               (when (memq 'Lines message-required-mail-headers)
2438                 (let ((mail-header-separator ""))
2439                   (insert "Lines: " (message-make-lines) "\n")))
2440               (message-goto-subject)
2441               (end-of-line)
2442               (insert (format " (%d/%d)" n total))
2443               (goto-char (point-max))
2444               (insert "\n")
2445               (widen)
2446               (mm-with-unibyte-current-buffer
2447                 (funcall message-send-mail-function)))
2448             (setq n (+ n 1))
2449             (setq p (pop plist))
2450             (erase-buffer)))
2451       (kill-buffer tembuf))))
2452
2453 (defun message-send-mail (&optional arg)
2454   (require 'mail-utils)
2455   (let* ((tembuf (message-generate-new-buffer-clone-locals " message temp"))
2456          (case-fold-search nil)
2457          (news (message-news-p))
2458          (mailbuf (current-buffer))
2459          (message-this-is-mail t)
2460          (message-posting-charset
2461           (if (fboundp 'gnus-setup-posting-charset)
2462               (gnus-setup-posting-charset nil)
2463             message-posting-charset)))
2464     (save-restriction
2465       (message-narrow-to-headers)
2466       ;; Insert some headers.
2467       (let ((message-deletable-headers
2468              (if news nil message-deletable-headers)))
2469         (message-generate-headers message-required-mail-headers))
2470       ;; Let the user do all of the above.
2471       (run-hooks 'message-header-hook))
2472     (unwind-protect
2473         (save-excursion
2474           (set-buffer tembuf)
2475           (erase-buffer)
2476           ;; Avoid copying text props.
2477           (insert (with-current-buffer mailbuf
2478                     (buffer-substring-no-properties (point-min) (point-max))))
2479           ;; Remove some headers.
2480           (message-encode-message-body)
2481           (save-restriction
2482             (message-narrow-to-headers)
2483             ;; We (re)generate the Lines header.
2484             (when (memq 'Lines message-required-mail-headers)
2485               (message-generate-headers '(Lines)))
2486             ;; Remove some headers.
2487             (message-remove-header message-ignored-mail-headers t)
2488             (let ((mail-parse-charset message-default-charset))
2489               (mail-encode-encoded-word-buffer)))
2490           (goto-char (point-max))
2491           ;; require one newline at the end.
2492           (or (= (preceding-char) ?\n)
2493               (insert ?\n))
2494           (when
2495               (save-restriction
2496                 (message-narrow-to-headers)
2497                 (and news
2498                      (or (message-fetch-field "cc")
2499                          (message-fetch-field "to"))
2500                      (let ((content-type (message-fetch-field "content-type")))
2501                        (or
2502                         (not content-type)
2503                         (string= "text/plain"
2504                                  (car
2505                                   (mail-header-parse-content-type
2506                                    content-type)))))))
2507             (message-insert-courtesy-copy))
2508           (if (or (not message-send-mail-partially-limit)
2509                   (< (point-max) message-send-mail-partially-limit)
2510                   (not (y-or-n-p "The message size is too large, should it be sent partially? ")))
2511               (mm-with-unibyte-current-buffer
2512                 (funcall message-send-mail-function))
2513             (message-send-mail-partially)))
2514       (kill-buffer tembuf))
2515     (set-buffer mailbuf)
2516     (push 'mail message-sent-message-via)))
2517
2518 (defun message-send-mail-with-sendmail ()
2519   "Send off the prepared buffer with sendmail."
2520   (let ((errbuf (if message-interactive
2521                     (message-generate-new-buffer-clone-locals
2522                      " sendmail errors")
2523                   0))
2524         resend-to-addresses delimline)
2525     (let ((case-fold-search t))
2526       (save-restriction
2527         (message-narrow-to-headers)
2528         (setq resend-to-addresses (message-fetch-field "resent-to")))
2529       ;; Change header-delimiter to be what sendmail expects.
2530       (goto-char (point-min))
2531       (re-search-forward
2532        (concat "^" (regexp-quote mail-header-separator) "\n"))
2533       (replace-match "\n")
2534       (backward-char 1)
2535       (setq delimline (point-marker))
2536       (run-hooks 'message-send-mail-hook)
2537       ;; Insert an extra newline if we need it to work around
2538       ;; Sun's bug that swallows newlines.
2539       (goto-char (1+ delimline))
2540       (when (eval message-mailer-swallows-blank-line)
2541         (newline))
2542       (when message-interactive
2543         (save-excursion
2544           (set-buffer errbuf)
2545           (erase-buffer))))
2546     (let ((default-directory "/")
2547           (coding-system-for-write message-send-coding-system))
2548       (apply 'call-process-region
2549              (append (list (point-min) (point-max)
2550                            (if (boundp 'sendmail-program)
2551                                sendmail-program
2552                              "/usr/lib/sendmail")
2553                            nil errbuf nil "-oi")
2554                      ;; Always specify who from,
2555                      ;; since some systems have broken sendmails.
2556                      ;; But some systems are more broken with -f, so
2557                      ;; we'll let users override this.
2558                      (if (null message-sendmail-f-is-evil)
2559                          (list "-f" (message-make-address)))
2560                      ;; These mean "report errors by mail"
2561                      ;; and "deliver in background".
2562                      (if (null message-interactive) '("-oem" "-odb"))
2563                      ;; Get the addresses from the message
2564                      ;; unless this is a resend.
2565                      ;; We must not do that for a resend
2566                      ;; because we would find the original addresses.
2567                      ;; For a resend, include the specific addresses.
2568                      (if resend-to-addresses
2569                          (list resend-to-addresses)
2570                        '("-t")))))
2571     (when message-interactive
2572       (save-excursion
2573         (set-buffer errbuf)
2574         (goto-char (point-min))
2575         (while (re-search-forward "\n\n* *" nil t)
2576           (replace-match "; "))
2577         (if (not (zerop (buffer-size)))
2578             (error "Sending...failed to %s"
2579                    (buffer-substring (point-min) (point-max)))))
2580       (when (bufferp errbuf)
2581         (kill-buffer errbuf)))))
2582
2583 (defun message-send-mail-with-qmail ()
2584   "Pass the prepared message buffer to qmail-inject.
2585 Refer to the documentation for the variable `message-send-mail-function'
2586 to find out how to use this."
2587   ;; replace the header delimiter with a blank line
2588   (goto-char (point-min))
2589   (re-search-forward
2590    (concat "^" (regexp-quote mail-header-separator) "\n"))
2591   (replace-match "\n")
2592   (run-hooks 'message-send-mail-hook)
2593   ;; send the message
2594   (case
2595       (let ((coding-system-for-write message-send-coding-system))
2596         (apply
2597          'call-process-region 1 (point-max) message-qmail-inject-program
2598          nil nil nil
2599          ;; qmail-inject's default behaviour is to look for addresses on the
2600          ;; command line; if there're none, it scans the headers.
2601          ;; yes, it does The Right Thing w.r.t. Resent-To and it's kin.
2602          ;;
2603          ;; in general, ALL of qmail-inject's defaults are perfect for simply
2604          ;; reading a formatted (i. e., at least a To: or Resent-To header)
2605          ;; message from stdin.
2606          ;;
2607          ;; qmail also has the advantage of not having been raped by
2608          ;; various vendors, so we don't have to allow for that, either --
2609          ;; compare this with message-send-mail-with-sendmail and weep
2610          ;; for sendmail's lost innocence.
2611          ;;
2612          ;; all this is way cool coz it lets us keep the arguments entirely
2613          ;; free for -inject-arguments -- a big win for the user and for us
2614          ;; since we don't have to play that double-guessing game and the user
2615          ;; gets full control (no gestapo'ish -f's, for instance).  --sj
2616          message-qmail-inject-args))
2617     ;; qmail-inject doesn't say anything on it's stdout/stderr,
2618     ;; we have to look at the retval instead
2619     (0 nil)
2620     (1   (error "qmail-inject reported permanent failure"))
2621     (111 (error "qmail-inject reported transient failure"))
2622     ;; should never happen
2623     (t   (error "qmail-inject reported unknown failure"))))
2624
2625 (defun message-send-mail-with-mh ()
2626   "Send the prepared message buffer with mh."
2627   (let ((mh-previous-window-config nil)
2628         (name (mh-new-draft-name)))
2629     (setq buffer-file-name name)
2630     ;; MH wants to generate these headers itself.
2631     (when message-mh-deletable-headers
2632       (let ((headers message-mh-deletable-headers))
2633         (while headers
2634           (goto-char (point-min))
2635           (and (re-search-forward
2636                 (concat "^" (symbol-name (car headers)) ": *") nil t)
2637                (message-delete-line))
2638           (pop headers))))
2639     (run-hooks 'message-send-mail-hook)
2640     ;; Pass it on to mh.
2641     (mh-send-letter)))
2642
2643 (defun message-send-news (&optional arg)
2644   (let* ((tembuf (message-generate-new-buffer-clone-locals " *message temp*"))
2645          (case-fold-search nil)
2646          (method (if (message-functionp message-post-method)
2647                      (funcall message-post-method arg)
2648                    message-post-method))
2649          (group-name-charset (gnus-group-name-charset method ""))
2650          (rfc2047-header-encoding-alist
2651           (if group-name-charset
2652               (cons (cons "Newsgroups" group-name-charset)
2653                     rfc2047-header-encoding-alist)
2654             rfc2047-header-encoding-alist))
2655          (messbuf (current-buffer))
2656          (message-syntax-checks
2657           (if arg
2658               (cons '(existing-newsgroups . disabled)
2659                     message-syntax-checks)
2660             message-syntax-checks))
2661          (message-this-is-news t)
2662          (message-posting-charset (gnus-setup-posting-charset
2663                                    (save-restriction
2664                                      (message-narrow-to-headers-or-head)
2665                                      (message-fetch-field "Newsgroups"))))
2666          result)
2667     (if (not (message-check-news-body-syntax))
2668         nil
2669       (save-restriction
2670         (message-narrow-to-headers)
2671         ;; Insert some headers.
2672         (message-generate-headers message-required-news-headers)
2673         ;; Let the user do all of the above.
2674         (run-hooks 'message-header-hook))
2675       (if group-name-charset
2676           (setq message-syntax-checks
2677               (cons '(valid-newsgroups . disabled)
2678                     message-syntax-checks)))
2679       (message-cleanup-headers)
2680       (if (not (message-check-news-syntax))
2681           nil
2682         (unwind-protect
2683             (save-excursion
2684               (set-buffer tembuf)
2685               (buffer-disable-undo)
2686               (erase-buffer)
2687               ;; Avoid copying text props.
2688               (insert (with-current-buffer messbuf
2689                         (buffer-substring-no-properties
2690                          (point-min) (point-max))))
2691               (message-encode-message-body)
2692               ;; Remove some headers.
2693               (save-restriction
2694                 (message-narrow-to-headers)
2695                 ;; We (re)generate the Lines header.
2696                 (when (memq 'Lines message-required-mail-headers)
2697                   (message-generate-headers '(Lines)))
2698                 ;; Remove some headers.
2699                 (message-remove-header message-ignored-news-headers t)
2700                 (let ((mail-parse-charset message-default-charset))
2701                   (mail-encode-encoded-word-buffer)))
2702               (goto-char (point-max))
2703               ;; require one newline at the end.
2704               (or (= (preceding-char) ?\n)
2705                   (insert ?\n))
2706               (let ((case-fold-search t))
2707                 ;; Remove the delimiter.
2708                 (goto-char (point-min))
2709                 (re-search-forward
2710                  (concat "^" (regexp-quote mail-header-separator) "\n"))
2711                 (replace-match "\n")
2712                 (backward-char 1))
2713               (run-hooks 'message-send-news-hook)
2714               (gnus-open-server method)
2715               (setq result (let ((mail-header-separator ""))
2716                              (gnus-request-post method))))
2717           (kill-buffer tembuf))
2718         (set-buffer messbuf)
2719         (if result
2720             (push 'news message-sent-message-via)
2721           (message "Couldn't send message via news: %s"
2722                    (nnheader-get-report (car method)))
2723           nil)))))
2724
2725 ;;;
2726 ;;; Header generation & syntax checking.
2727 ;;;
2728
2729 (defun message-check-element (type)
2730   "Return non-nil if this TYPE is not to be checked."
2731   (if (eq message-syntax-checks 'dont-check-for-anything-just-trust-me)
2732       t
2733     (let ((able (assq type message-syntax-checks)))
2734       (and (consp able)
2735            (eq (cdr able) 'disabled)))))
2736
2737 (defun message-check-news-syntax ()
2738   "Check the syntax of the message."
2739   (save-excursion
2740     (save-restriction
2741       (widen)
2742       ;; We narrow to the headers and check them first.
2743       (save-excursion
2744         (save-restriction
2745           (message-narrow-to-headers)
2746           (message-check-news-header-syntax))))))
2747
2748 (defun message-check-news-header-syntax ()
2749   (and
2750    ;; Check Newsgroups header.
2751    (message-check 'newsgroups
2752      (let ((group (message-fetch-field "newsgroups")))
2753        (or
2754         (and group
2755              (not (string-match "\\`[ \t]*\\'" group)))
2756         (ignore
2757          (message
2758           "The newsgroups field is empty or missing.  Posting is denied.")))))
2759    ;; Check the Subject header.
2760    (message-check 'subject
2761      (let* ((case-fold-search t)
2762             (subject (message-fetch-field "subject")))
2763        (or
2764         (and subject
2765              (not (string-match "\\`[ \t]*\\'" subject)))
2766         (ignore
2767          (message
2768           "The subject field is empty or missing.  Posting is denied.")))))
2769    ;; Check for commands in Subject.
2770    (message-check 'subject-cmsg
2771      (if (string-match "^cmsg " (message-fetch-field "subject"))
2772          (y-or-n-p
2773           "The control code \"cmsg\" is in the subject.  Really post? ")
2774        t))
2775    ;; Check for multiple identical headers.
2776    (message-check 'multiple-headers
2777      (let (found)
2778        (while (and (not found)
2779                    (re-search-forward "^[^ \t:]+: " nil t))
2780          (save-excursion
2781            (or (re-search-forward
2782                 (concat "^"
2783                         (regexp-quote
2784                          (setq found
2785                                (buffer-substring
2786                                 (match-beginning 0) (- (match-end 0) 2))))
2787                         ":")
2788                 nil t)
2789                (setq found nil))))
2790        (if found
2791            (y-or-n-p (format "Multiple %s headers.  Really post? " found))
2792          t)))
2793    ;; Check for Version and Sendsys.
2794    (message-check 'sendsys
2795      (if (re-search-forward "^Sendsys:\\|^Version:" nil t)
2796          (y-or-n-p
2797           (format "The article contains a %s command.  Really post? "
2798                   (buffer-substring (match-beginning 0)
2799                                     (1- (match-end 0)))))
2800        t))
2801    ;; See whether we can shorten Followup-To.
2802    (message-check 'shorten-followup-to
2803      (let ((newsgroups (message-fetch-field "newsgroups"))
2804            (followup-to (message-fetch-field "followup-to"))
2805            to)
2806        (when (and newsgroups
2807                   (string-match "," newsgroups)
2808                   (not followup-to)
2809                   (not
2810                    (zerop
2811                     (length
2812                      (setq to (completing-read
2813                                "Followups to: (default all groups) "
2814                                (mapcar (lambda (g) (list g))
2815                                        (cons "poster"
2816                                              (message-tokenize-header
2817                                               newsgroups)))))))))
2818          (goto-char (point-min))
2819          (insert "Followup-To: " to "\n"))
2820        t))
2821    ;; Check "Shoot me".
2822    (message-check 'shoot
2823      (if (re-search-forward
2824           "Message-ID.*.i-did-not-set--mail-host-address--so-shoot-me" nil t)
2825          (y-or-n-p "You appear to have a misconfigured system.  Really post? ")
2826        t))
2827    ;; Check for Approved.
2828    (message-check 'approved
2829      (if (re-search-forward "^Approved:" nil t)
2830          (y-or-n-p "The article contains an Approved header.  Really post? ")
2831        t))
2832    ;; Check the Message-ID header.
2833    (message-check 'message-id
2834      (let* ((case-fold-search t)
2835             (message-id (message-fetch-field "message-id" t)))
2836        (or (not message-id)
2837            ;; Is there an @ in the ID?
2838            (and (string-match "@" message-id)
2839                 ;; Is there a dot in the ID?
2840                 (string-match "@[^.]*\\." message-id)
2841                 ;; Does the ID end with a dot?
2842                 (not (string-match "\\.>" message-id)))
2843            (y-or-n-p
2844             (format "The Message-ID looks strange: \"%s\".  Really post? "
2845                     message-id)))))
2846    ;; Check the Newsgroups & Followup-To headers.
2847    (message-check 'existing-newsgroups
2848      (let* ((case-fold-search t)
2849             (newsgroups (message-fetch-field "newsgroups"))
2850             (followup-to (message-fetch-field "followup-to"))
2851             (groups (message-tokenize-header
2852                      (if followup-to
2853                          (concat newsgroups "," followup-to)
2854                        newsgroups)))
2855             (hashtb (and (boundp 'gnus-active-hashtb)
2856                          gnus-active-hashtb))
2857             errors)
2858        (if (or (not hashtb)
2859                (not (boundp 'gnus-read-active-file))
2860                (not gnus-read-active-file)
2861                (eq gnus-read-active-file 'some))
2862            t
2863          (while groups
2864            (when (and (not (boundp (intern (car groups) hashtb)))
2865                       (not (equal (car groups) "poster")))
2866              (push (car groups) errors))
2867            (pop groups))
2868          (if (not errors)
2869              t
2870            (y-or-n-p
2871             (format
2872              "Really post to %s unknown group%s: %s? "
2873              (if (= (length errors) 1) "this" "these")
2874              (if (= (length errors) 1) "" "s")
2875              (mapconcat 'identity errors ", ")))))))
2876    ;; Check the Newsgroups & Followup-To headers for syntax errors.
2877    (message-check 'valid-newsgroups
2878      (let ((case-fold-search t)
2879            (headers '("Newsgroups" "Followup-To"))
2880            header error)
2881        (while (and headers (not error))
2882          (when (setq header (mail-fetch-field (car headers)))
2883            (if (or
2884                 (not
2885                  (string-match
2886                   "\\`\\([-+_&.a-zA-Z0-9]+\\)?\\(,[-+_&.a-zA-Z0-9]+\\)*\\'"
2887                   header))
2888                 (memq
2889                  nil (mapcar
2890                       (lambda (g)
2891                         (not (string-match "\\.\\'\\|\\.\\." g)))
2892                       (message-tokenize-header header ","))))
2893                (setq error t)))
2894          (unless error
2895            (pop headers)))
2896        (if (not error)
2897            t
2898          (y-or-n-p
2899           (format "The %s header looks odd: \"%s\".  Really post? "
2900                   (car headers) header)))))
2901    (message-check 'repeated-newsgroups
2902      (let ((case-fold-search t)
2903            (headers '("Newsgroups" "Followup-To"))
2904            header error groups group)
2905        (while (and headers
2906                    (not error))
2907          (when (setq header (mail-fetch-field (pop headers)))
2908            (setq groups (message-tokenize-header header ","))
2909            (while (setq group (pop groups))
2910              (when (member group groups)
2911                (setq error group
2912                      groups nil)))))
2913        (if (not error)
2914            t
2915          (y-or-n-p
2916           (format "Group %s is repeated in headers.  Really post? " error)))))
2917    ;; Check the From header.
2918    (message-check 'from
2919      (let* ((case-fold-search t)
2920             (from (message-fetch-field "from"))
2921             ad)
2922        (cond
2923         ((not from)
2924          (message "There is no From line.  Posting is denied.")
2925          nil)
2926         ((or (not (string-match
2927                    "@[^\\.]*\\."
2928                    (setq ad (nth 1 (mail-extract-address-components
2929                                     from))))) ;larsi@ifi
2930              (string-match "\\.\\." ad) ;larsi@ifi..uio
2931              (string-match "@\\." ad)   ;larsi@.ifi.uio
2932              (string-match "\\.$" ad)   ;larsi@ifi.uio.
2933              (not (string-match "^[^@]+@[^@]+$" ad)) ;larsi.ifi.uio
2934              (string-match "(.*).*(.*)" from)) ;(lars) (lars)
2935          (message
2936           "Denied posting -- the From looks strange: \"%s\"." from)
2937          nil)
2938         (t t))))))
2939
2940 (defun message-check-news-body-syntax ()
2941   (and
2942    ;; Check for long lines.
2943    (message-check 'long-lines
2944      (goto-char (point-min))
2945      (re-search-forward
2946       (concat "^" (regexp-quote mail-header-separator) "$"))
2947      (forward-line 1)
2948      (while (and
2949              (or (looking-at 
2950                   "<#\\(/\\)?\\(multipart\\|part\\|external\\|mml\\)")
2951                  (let ((p (point)))
2952                    (end-of-line)
2953                    (< (- (point) p) 80)))
2954              (zerop (forward-line 1))))
2955      (or (bolp)
2956          (eobp)
2957          (y-or-n-p
2958           "You have lines longer than 79 characters.  Really post? ")))
2959    ;; Check whether the article is empty.
2960    (message-check 'empty
2961      (goto-char (point-min))
2962      (re-search-forward
2963       (concat "^" (regexp-quote mail-header-separator) "$"))
2964      (forward-line 1)
2965      (let ((b (point)))
2966        (goto-char (point-max))
2967        (re-search-backward message-signature-separator nil t)
2968        (beginning-of-line)
2969        (or (re-search-backward "[^ \n\t]" b t)
2970            (if (message-gnksa-enable-p 'empty-article)
2971                (y-or-n-p "Empty article.  Really post? ")
2972              (message "Denied posting -- Empty article.")
2973              nil))))
2974    ;; Check for control characters.
2975    (message-check 'control-chars
2976      (if (re-search-forward "[\000-\007\013\015-\032\034-\037\200-\237]" nil t)
2977          (y-or-n-p
2978           "The article contains control characters.  Really post? ")
2979        t))
2980    ;; Check excessive size.
2981    (message-check 'size
2982      (if (> (buffer-size) 60000)
2983          (y-or-n-p
2984           (format "The article is %d octets long.  Really post? "
2985                   (buffer-size)))
2986        t))
2987    ;; Check whether any new text has been added.
2988    (message-check 'new-text
2989      (or
2990       (not message-checksum)
2991       (not (eq (message-checksum) message-checksum))
2992       (if (message-gnksa-enable-p 'quoted-text-only)
2993           (y-or-n-p
2994            "It looks like no new text has been added.  Really post? ")
2995         (message "Denied posting -- no new text has been added.")
2996         nil)))
2997    ;; Check the length of the signature.
2998    (message-check 'signature
2999      (goto-char (point-max))
3000      (if (> (count-lines (point) (point-max)) 5)
3001          (y-or-n-p
3002           (format
3003            "Your .sig is %d lines; it should be max 4.  Really post? "
3004            (1- (count-lines (point) (point-max)))))
3005        t))
3006    ;; Ensure that text follows last quoted portion.
3007    (message-check 'quoting-style
3008      (goto-char (point-max))
3009      (let ((no-problem t))
3010        (when (search-backward-regexp "^>[^\n]*\n" nil t)
3011          (setq no-problem (search-forward-regexp "^[ \t]*[^>\n]" nil t)))
3012        (if no-problem
3013            t
3014          (if (message-gnksa-enable-p 'quoted-text-only)
3015              (y-or-n-p "Your text should follow quoted text.  Really post? ")
3016            ;; Ensure that
3017            (goto-char (point-min))
3018            (re-search-forward
3019             (concat "^" (regexp-quote mail-header-separator) "$"))
3020            (if (search-forward-regexp "^[ \t]*[^>\n]" nil t)
3021                (y-or-n-p "Your text should follow quoted text.  Really post? ")
3022              (message "Denied posting -- only quoted text.")
3023              nil)))))))
3024
3025 (defun message-checksum ()
3026   "Return a \"checksum\" for the current buffer."
3027   (let ((sum 0))
3028     (save-excursion
3029       (goto-char (point-min))
3030       (re-search-forward
3031        (concat "^" (regexp-quote mail-header-separator) "$"))
3032       (while (not (eobp))
3033         (when (not (looking-at "[ \t\n]"))
3034           (setq sum (logxor (ash sum 1) (if (natnump sum) 0 1)
3035                             (char-after))))
3036         (forward-char 1)))
3037     sum))
3038
3039 (defun message-do-fcc ()
3040   "Process Fcc headers in the current buffer."
3041   (let ((case-fold-search t)
3042         (buf (current-buffer))
3043         list file)
3044     (save-excursion
3045       (set-buffer (get-buffer-create " *message temp*"))
3046       (erase-buffer)
3047       (insert-buffer-substring buf)
3048       (save-restriction
3049         (message-narrow-to-headers)
3050         (while (setq file (message-fetch-field "fcc"))
3051           (push file list)
3052           (message-remove-header "fcc" nil t)))
3053       (message-encode-message-body)
3054       (save-restriction
3055         (message-narrow-to-headers)
3056         (let ((mail-parse-charset message-default-charset)
3057               (rfc2047-header-encoding-alist
3058                (cons '("Newsgroups" . default)
3059                      rfc2047-header-encoding-alist)))
3060           (mail-encode-encoded-word-buffer)))
3061       (goto-char (point-min))
3062       (when (re-search-forward
3063              (concat "^" (regexp-quote mail-header-separator) "$")
3064              nil t)
3065         (replace-match "" t t ))
3066       ;; Process FCC operations.
3067       (while list
3068         (setq file (pop list))
3069         (if (string-match "^[ \t]*|[ \t]*\\(.*\\)[ \t]*$" file)
3070             ;; Pipe the article to the program in question.
3071             (call-process-region (point-min) (point-max) shell-file-name
3072                                  nil nil nil shell-command-switch
3073                                  (match-string 1 file))
3074           ;; Save the article.
3075           (setq file (expand-file-name file))
3076           (unless (file-exists-p (file-name-directory file))
3077             (make-directory (file-name-directory file) t))
3078           (if (and message-fcc-handler-function
3079                    (not (eq message-fcc-handler-function 'rmail-output)))
3080               (funcall message-fcc-handler-function file)
3081             (if (and (file-readable-p file) (mail-file-babyl-p file))
3082                 (rmail-output file 1 nil t)
3083               (let ((mail-use-rfc822 t))
3084                 (rmail-output file 1 t t))))))
3085       (kill-buffer (current-buffer)))))
3086
3087 (defun message-output (filename)
3088   "Append this article to Unix/babyl mail file FILENAME."
3089   (if (and (file-readable-p filename)
3090            (mail-file-babyl-p filename))
3091       (gnus-output-to-rmail filename t)
3092     (gnus-output-to-mail filename t)))
3093
3094 (defun message-cleanup-headers ()
3095   "Do various automatic cleanups of the headers."
3096   ;; Remove empty lines in the header.
3097   (save-restriction
3098     (message-narrow-to-headers)
3099     ;; Remove blank lines.
3100     (while (re-search-forward "^[ \t]*\n" nil t)
3101       (replace-match "" t t))
3102
3103     ;; Correct Newsgroups and Followup-To headers:  Change sequence of
3104     ;; spaces to comma and eliminate spaces around commas.  Eliminate
3105     ;; embedded line breaks.
3106     (goto-char (point-min))
3107     (while (re-search-forward "^\\(Newsgroups\\|Followup-To\\): +" nil t)
3108       (save-restriction
3109         (narrow-to-region
3110          (point)
3111          (if (re-search-forward "^[^ \t]" nil t)
3112              (match-beginning 0)
3113            (forward-line 1)
3114            (point)))
3115         (goto-char (point-min))
3116         (while (re-search-forward "\n[ \t]+" nil t)
3117           (replace-match " " t t))      ;No line breaks (too confusing)
3118         (goto-char (point-min))
3119         (while (re-search-forward "[ \t\n]*,[ \t\n]*\\|[ \t]+" nil t)
3120           (replace-match "," t t))
3121         (goto-char (point-min))
3122         ;; Remove trailing commas.
3123         (when (re-search-forward ",+$" nil t)
3124           (replace-match "" t t))))))
3125
3126 (defun message-make-date (&optional now)
3127   "Make a valid data header.
3128 If NOW, use that time instead."
3129   (let* ((now (or now (current-time)))
3130          (zone (nth 8 (decode-time now)))
3131          (sign "+"))
3132     (when (< zone 0)
3133       (setq sign "-")
3134       (setq zone (- zone)))
3135     (concat
3136      (format-time-string "%d" now)
3137      ;; The month name of the %b spec is locale-specific.  Pfff.
3138      (format " %s "
3139              (capitalize (car (rassoc (nth 4 (decode-time now))
3140                                       parse-time-months))))
3141      (format-time-string "%Y %H:%M:%S " now)
3142      ;; We do all of this because XEmacs doesn't have the %z spec.
3143      (format "%s%02d%02d" sign (/ zone 3600) (/ (% zone 3600) 60)))))
3144
3145 (defun message-make-message-id ()
3146   "Make a unique Message-ID."
3147   (concat "<" (message-unique-id)
3148           (let ((psubject (save-excursion (message-fetch-field "subject")))
3149                 (psupersedes
3150                  (save-excursion (message-fetch-field "supersedes"))))
3151             (if (or
3152                  (and message-reply-headers
3153                       (mail-header-references message-reply-headers)
3154                       (mail-header-subject message-reply-headers)
3155                       psubject
3156                       (not (string=
3157                             (message-strip-subject-re
3158                              (mail-header-subject message-reply-headers))
3159                             (message-strip-subject-re psubject))))
3160                  (and psupersedes
3161                       (string-match "_-_@" psupersedes)))
3162                 "_-_" ""))
3163           "@" (message-make-fqdn) ">"))
3164
3165 (defvar message-unique-id-char nil)
3166
3167 ;; If you ever change this function, make sure the new version
3168 ;; cannot generate IDs that the old version could.
3169 ;; You might for example insert a "." somewhere (not next to another dot
3170 ;; or string boundary), or modify the "fsf" string.
3171 (defun message-unique-id ()
3172   ;; Don't use microseconds from (current-time), they may be unsupported.
3173   ;; Instead we use this randomly inited counter.
3174   (setq message-unique-id-char
3175         (% (1+ (or message-unique-id-char (logand (random t) (1- (lsh 1 20)))))
3176            ;; (current-time) returns 16-bit ints,
3177            ;; and 2^16*25 just fits into 4 digits i base 36.
3178            (* 25 25)))
3179   (let ((tm (current-time)))
3180     (concat
3181      (if (memq system-type '(ms-dos emx vax-vms))
3182          (let ((user (downcase (user-login-name))))
3183            (while (string-match "[^a-z0-9_]" user)
3184              (aset user (match-beginning 0) ?_))
3185            user)
3186        (message-number-base36 (user-uid) -1))
3187      (message-number-base36 (+ (car   tm)
3188                                (lsh (% message-unique-id-char 25) 16)) 4)
3189      (message-number-base36 (+ (nth 1 tm)
3190                                (lsh (/ message-unique-id-char 25) 16)) 4)
3191      ;; Append the newsreader name, because while the generated
3192      ;; ID is unique to this newsreader, other newsreaders might
3193      ;; otherwise generate the same ID via another algorithm.
3194      ".fsf")))
3195
3196 (defun message-number-base36 (num len)
3197   (if (if (< len 0)
3198           (<= num 0)
3199         (= len 0))
3200       ""
3201     (concat (message-number-base36 (/ num 36) (1- len))
3202             (char-to-string (aref "zyxwvutsrqponmlkjihgfedcba9876543210"
3203                                   (% num 36))))))
3204
3205 (defun message-make-organization ()
3206   "Make an Organization header."
3207   (let* ((organization
3208           (when message-user-organization
3209             (if (message-functionp message-user-organization)
3210                 (funcall message-user-organization)
3211               message-user-organization))))
3212     (save-excursion
3213       (message-set-work-buffer)
3214       (cond ((stringp organization)
3215              (insert organization))
3216             ((and (eq t organization)
3217                   message-user-organization-file
3218                   (file-exists-p message-user-organization-file))
3219              (insert-file-contents message-user-organization-file)))
3220       (goto-char (point-min))
3221       (while (re-search-forward "[\t\n]+" nil t)
3222         (replace-match "" t t))
3223       (unless (zerop (buffer-size))
3224         (buffer-string)))))
3225
3226 (defun message-make-lines ()
3227   "Count the number of lines and return numeric string."
3228   (save-excursion
3229     (save-restriction
3230       (widen)
3231       (message-goto-body)
3232       (int-to-string (count-lines (point) (point-max))))))
3233
3234 (defun message-make-in-reply-to ()
3235   "Return the In-Reply-To header for this message."
3236   (when message-reply-headers
3237     (let ((from (mail-header-from message-reply-headers))
3238           (date (mail-header-date message-reply-headers))
3239           (msg-id (mail-header-message-id message-reply-headers)))
3240       (when from
3241         (let ((stop-pos
3242                (string-match "  *at \\|  *@ \\| *(\\| *<" from)))
3243           (concat msg-id (if msg-id " (")
3244                   (if (and stop-pos
3245                            (not (zerop stop-pos)))
3246                       (substring from 0 stop-pos) from)
3247                   "'s message of \""
3248                   (if (or (not date) (string= date ""))
3249                       "(unknown date)" date)
3250                   "\"" (if msg-id ")")))))))
3251
3252 (defun message-make-distribution ()
3253   "Make a Distribution header."
3254   (let ((orig-distribution (message-fetch-reply-field "distribution")))
3255     (cond ((message-functionp message-distribution-function)
3256            (funcall message-distribution-function))
3257           (t orig-distribution))))
3258
3259 (defun message-make-expires ()
3260   "Return an Expires header based on `message-expires'."
3261   (let ((current (current-time))
3262         (future (* 1.0 message-expires 60 60 24)))
3263     ;; Add the future to current.
3264     (setcar current (+ (car current) (round (/ future (expt 2 16)))))
3265     (setcar (cdr current) (+ (nth 1 current) (% (round future) (expt 2 16))))
3266     (message-make-date current)))
3267
3268 (defun message-make-path ()
3269   "Return uucp path."
3270   (let ((login-name (user-login-name)))
3271     (cond ((null message-user-path)
3272            (concat (system-name) "!" login-name))
3273           ((stringp message-user-path)
3274            ;; Support GENERICPATH.  Suggested by vixie@decwrl.dec.com.
3275            (concat message-user-path "!" login-name))
3276           (t login-name))))
3277
3278 (defun message-make-from ()
3279   "Make a From header."
3280   (let* ((style message-from-style)
3281          (login (message-make-address))
3282          (fullname
3283           (or (and (boundp 'user-full-name)
3284                    user-full-name)
3285               (user-full-name))))
3286     (when (string= fullname "&")
3287       (setq fullname (user-login-name)))
3288     (save-excursion
3289       (message-set-work-buffer)
3290       (cond
3291        ((or (null style)
3292             (equal fullname ""))
3293         (insert login))
3294        ((or (eq style 'angles)
3295             (and (not (eq style 'parens))
3296                  ;; Use angles if no quoting is needed, or if parens would
3297                  ;; need quoting too.
3298                  (or (not (string-match "[^- !#-'*+/-9=?A-Z^-~]" fullname))
3299                      (let ((tmp (concat fullname nil)))
3300                        (while (string-match "([^()]*)" tmp)
3301                          (aset tmp (match-beginning 0) ?-)
3302                          (aset tmp (1- (match-end 0)) ?-))
3303                        (string-match "[\\()]" tmp)))))
3304         (insert fullname)
3305         (goto-char (point-min))
3306         ;; Look for a character that cannot appear unquoted
3307         ;; according to RFC 822.
3308         (when (re-search-forward "[^- !#-'*+/-9=?A-Z^-~]" nil 1)
3309           ;; Quote fullname, escaping specials.
3310           (goto-char (point-min))
3311           (insert "\"")
3312           (while (re-search-forward "[\"\\]" nil 1)
3313             (replace-match "\\\\\\&" t))
3314           (insert "\""))
3315         (insert " <" login ">"))
3316        (t                               ; 'parens or default
3317         (insert login " (")
3318         (let ((fullname-start (point)))
3319           (insert fullname)
3320           (goto-char fullname-start)
3321           ;; RFC 822 says \ and nonmatching parentheses
3322           ;; must be escaped in comments.
3323           ;; Escape every instance of ()\ ...
3324           (while (re-search-forward "[()\\]" nil 1)
3325             (replace-match "\\\\\\&" t))
3326           ;; ... then undo escaping of matching parentheses,
3327           ;; including matching nested parentheses.
3328           (goto-char fullname-start)
3329           (while (re-search-forward
3330                   "\\(\\=\\|[^\\]\\(\\\\\\\\\\)*\\)\\\\(\\(\\([^\\]\\|\\\\\\\\\\)*\\)\\\\)"
3331                   nil 1)
3332             (replace-match "\\1(\\3)" t)
3333             (goto-char fullname-start)))
3334         (insert ")")))
3335       (buffer-string))))
3336
3337 (defun message-make-sender ()
3338   "Return the \"real\" user address.
3339 This function tries to ignore all user modifications, and
3340 give as trustworthy answer as possible."
3341   (concat (user-login-name) "@" (system-name)))
3342
3343 (defun message-make-address ()
3344   "Make the address of the user."
3345   (or (message-user-mail-address)
3346       (concat (user-login-name) "@" (message-make-domain))))
3347
3348 (defun message-user-mail-address ()
3349   "Return the pertinent part of `user-mail-address'."
3350   (when user-mail-address
3351     (if (string-match " " user-mail-address)
3352         (nth 1 (mail-extract-address-components user-mail-address))
3353       user-mail-address)))
3354
3355 (defun message-make-fqdn ()
3356   "Return user's fully qualified domain name."
3357   (let ((system-name (system-name))
3358         (user-mail (message-user-mail-address)))
3359     (cond
3360      ((string-match "[^.]\\.[^.]" system-name)
3361       ;; `system-name' returned the right result.
3362       system-name)
3363      ;; Try `mail-host-address'.
3364      ((and (boundp 'mail-host-address)
3365            (stringp mail-host-address)
3366            (string-match "\\." mail-host-address))
3367       mail-host-address)
3368      ;; We try `user-mail-address' as a backup.
3369      ((and user-mail
3370            (string-match "\\." user-mail)
3371            (string-match "@\\(.*\\)\\'" user-mail))
3372       (match-string 1 user-mail))
3373      ;; Default to this bogus thing.
3374      (t
3375       (concat system-name ".i-did-not-set--mail-host-address--so-shoot-me")))))
3376
3377 (defun message-make-host-name ()
3378   "Return the name of the host."
3379   (let ((fqdn (message-make-fqdn)))
3380     (string-match "^[^.]+\\." fqdn)
3381     (substring fqdn 0 (1- (match-end 0)))))
3382
3383 (defun message-make-domain ()
3384   "Return the domain name."
3385   (or mail-host-address
3386       (message-make-fqdn)))
3387
3388 (defun message-generate-headers (headers)
3389   "Prepare article HEADERS.
3390 Headers already prepared in the buffer are not modified."
3391   (save-restriction
3392     (message-narrow-to-headers)
3393     (let* ((Date (message-make-date))
3394            (Message-ID (message-make-message-id))
3395            (Organization (message-make-organization))
3396            (From (message-make-from))
3397            (Path (message-make-path))
3398            (Subject nil)
3399            (Newsgroups nil)
3400            (In-Reply-To (message-make-in-reply-to))
3401            (To nil)
3402            (Distribution (message-make-distribution))
3403            (Lines (message-make-lines))
3404            (User-Agent message-newsreader)
3405            (Expires (message-make-expires))
3406            (case-fold-search t)
3407            header value elem)
3408       ;; First we remove any old generated headers.
3409       (let ((headers message-deletable-headers))
3410         (unless (buffer-modified-p)
3411           (setq headers (delq 'Message-ID (copy-sequence headers))))
3412         (while headers
3413           (goto-char (point-min))
3414           (and (re-search-forward
3415                 (concat "^" (symbol-name (car headers)) ": *") nil t)
3416                (get-text-property (1+ (match-beginning 0)) 'message-deletable)
3417                (message-delete-line))
3418           (pop headers)))
3419       ;; Go through all the required headers and see if they are in the
3420       ;; articles already.  If they are not, or are empty, they are
3421       ;; inserted automatically - except for Subject, Newsgroups and
3422       ;; Distribution.
3423       (while headers
3424         (goto-char (point-min))
3425         (setq elem (pop headers))
3426         (if (consp elem)
3427             (if (eq (car elem) 'optional)
3428                 (setq header (cdr elem))
3429               (setq header (car elem)))
3430           (setq header elem))
3431         (when (or (not (re-search-forward
3432                         (concat "^"
3433                                 (regexp-quote
3434                                  (downcase
3435                                   (if (stringp header)
3436                                       header
3437                                     (symbol-name header))))
3438                                 ":")
3439                         nil t))
3440                   (progn
3441                     ;; The header was found.  We insert a space after the
3442                     ;; colon, if there is none.
3443                     (if (/= (char-after) ? ) (insert " ") (forward-char 1))
3444                     ;; Find out whether the header is empty...
3445                     (looking-at "[ \t]*\n[^ \t]")))
3446           ;; So we find out what value we should insert.
3447           (setq value
3448                 (cond
3449                  ((and (consp elem) (eq (car elem) 'optional))
3450                   ;; This is an optional header.  If the cdr of this
3451                   ;; is something that is nil, then we do not insert
3452                   ;; this header.
3453                   (setq header (cdr elem))
3454                   (or (and (fboundp (cdr elem)) (funcall (cdr elem)))
3455                       (and (boundp (cdr elem)) (symbol-value (cdr elem)))))
3456                  ((consp elem)
3457                   ;; The element is a cons.  Either the cdr is a
3458                   ;; string to be inserted verbatim, or it is a
3459                   ;; function, and we insert the value returned from
3460                   ;; this function.
3461                   (or (and (stringp (cdr elem)) (cdr elem))
3462                       (and (fboundp (cdr elem)) (funcall (cdr elem)))))
3463                  ((and (boundp header) (symbol-value header))
3464                   ;; The element is a symbol.  We insert the value
3465                   ;; of this symbol, if any.
3466                   (symbol-value header))
3467                  ((not (message-check-element header))
3468                   ;; We couldn't generate a value for this header,
3469                   ;; so we just ask the user.
3470                   (read-from-minibuffer
3471                    (format "Empty header for %s; enter value: " header)))))
3472           ;; Finally insert the header.
3473           (when (and value
3474                      (not (equal value "")))
3475             (save-excursion
3476               (if (bolp)
3477                   (progn
3478                     ;; This header didn't exist, so we insert it.
3479                     (goto-char (point-max))
3480                     (insert (if (stringp header) header (symbol-name header))
3481                             ": " value "\n")
3482                     (forward-line -1))
3483                 ;; The value of this header was empty, so we clear
3484                 ;; totally and insert the new value.
3485                 (delete-region (point) (gnus-point-at-eol))
3486                 (insert value))
3487               ;; Add the deletable property to the headers that require it.
3488               (and (memq header message-deletable-headers)
3489                    (progn (beginning-of-line) (looking-at "[^:]+: "))
3490                    (add-text-properties
3491                     (point) (match-end 0)
3492                     '(message-deletable t face italic) (current-buffer)))))))
3493       ;; Insert new Sender if the From is strange.
3494       (let ((from (message-fetch-field "from"))
3495             (sender (message-fetch-field "sender"))
3496             (secure-sender (message-make-sender)))
3497         (when (and from
3498                    (not (message-check-element 'sender))
3499                    (not (string=
3500                          (downcase
3501                           (cadr (mail-extract-address-components from)))
3502                          (downcase secure-sender)))
3503                    (or (null sender)
3504                        (not
3505                         (string=
3506                          (downcase
3507                           (cadr (mail-extract-address-components sender)))
3508                          (downcase secure-sender)))))
3509           (goto-char (point-min))
3510           ;; Rename any old Sender headers to Original-Sender.
3511           (when (re-search-forward "^\\(Original-\\)*Sender:" nil t)
3512             (beginning-of-line)
3513             (insert "Original-")
3514             (beginning-of-line))
3515           (when (or (message-news-p)
3516                     (string-match "@.+\\.." secure-sender))
3517             (insert "Sender: " secure-sender "\n")))))))
3518
3519 (defun message-insert-courtesy-copy ()
3520   "Insert a courtesy message in mail copies of combined messages."
3521   (let (newsgroups)
3522     (save-excursion
3523       (save-restriction
3524         (message-narrow-to-headers)
3525         (when (setq newsgroups (message-fetch-field "newsgroups"))
3526           (goto-char (point-max))
3527           (insert "Posted-To: " newsgroups "\n")))
3528       (forward-line 1)
3529       (when message-courtesy-message
3530         (cond
3531          ((string-match "%s" message-courtesy-message)
3532           (insert (format message-courtesy-message newsgroups)))
3533          (t
3534           (insert message-courtesy-message)))))))
3535
3536 ;;;
3537 ;;; Setting up a message buffer
3538 ;;;
3539
3540 (defun message-fill-address (header value)
3541   (save-restriction
3542     (narrow-to-region (point) (point))
3543     (insert (capitalize (symbol-name header))
3544             ": "
3545             (if (consp value) (car value) value)
3546             "\n")
3547     (narrow-to-region (point-min) (1- (point-max)))
3548     (let (quoted last)
3549       (goto-char (point-min))
3550       (while (not (eobp))
3551         (skip-chars-forward "^,\"" (point-max))
3552         (if (or (eq (char-after) ?,)
3553                 (eobp))
3554             (when (not quoted)
3555               (if (and (> (current-column) 78)
3556                        last)
3557                   (progn
3558                     (save-excursion
3559                       (goto-char last)
3560                       (insert "\n\t"))
3561                     (setq last (1+ (point))))
3562                 (setq last (1+ (point)))))
3563           (setq quoted (not quoted)))
3564         (unless (eobp)
3565           (forward-char 1))))
3566     (goto-char (point-max))
3567     (widen)
3568     (forward-line 1)))
3569
3570 (defun message-fill-header (header value)
3571   (let ((begin (point))
3572         (fill-column 78)
3573         (fill-prefix "\t"))
3574     (insert (capitalize (symbol-name header))
3575             ": "
3576             (if (consp value) (car value) value)
3577             "\n")
3578     (save-restriction
3579       (narrow-to-region begin (point))
3580       (fill-region-as-paragraph begin (point))
3581       ;; Tapdance around looong Message-IDs.
3582       (forward-line -1)
3583       (when (looking-at "[ \t]*$")
3584         (message-delete-line))
3585       (goto-char begin)
3586       (re-search-forward ":" nil t)
3587       (when (looking-at "\n[ \t]+")
3588         (replace-match " " t t))
3589       (goto-char (point-max)))))
3590
3591 (defun message-shorten-1 (list cut surplus)
3592   "Cut SURPLUS elements out of LIST, beginning with CUTth one."
3593   (setcdr (nthcdr (- cut 2) list)
3594           (nthcdr (+ (- cut 2) surplus 1) list)))
3595
3596 (defun message-shorten-references (header references)
3597   "Trim REFERENCES to be less than 31 Message-ID long, and fold them.
3598 If folding is disallowed, also check that the REFERENCES are less
3599 than 988 characters long, and if they are not, trim them until they are."
3600   (let ((maxcount 31)
3601         (count 0)
3602         (cut 6)
3603         refs)
3604     (with-temp-buffer
3605       (insert references)
3606       (goto-char (point-min))
3607       ;; Cons a list of valid references.
3608       (while (re-search-forward "<[^>]+>" nil t)
3609         (push (match-string 0) refs))
3610       (setq refs (nreverse refs)
3611             count (length refs)))
3612
3613     ;; If the list has more than MAXCOUNT elements, trim it by
3614     ;; removing the CUTth element and the required number of
3615     ;; elements that follow.
3616     (when (> count maxcount)
3617       (let ((surplus (- count maxcount)))
3618         (message-shorten-1 refs cut surplus)
3619         (decf count surplus)))
3620
3621     ;; If folding is disallowed, make sure the total length (including
3622     ;; the spaces between) will be less than MAXSIZE characters.
3623     ;;
3624     ;; Only disallow folding for News messages. At this point the headers
3625     ;; have not been generated, thus we use message-this-is-news directly.
3626     (when (and message-this-is-news message-cater-to-broken-inn)
3627       (let ((maxsize 988)
3628             (totalsize (+ (apply #'+ (mapcar #'length refs))
3629                           (1- count)))
3630             (surplus 0)
3631             (ptr (nthcdr (1- cut) refs)))
3632         ;; Decide how many elements to cut off...
3633         (while (> totalsize maxsize)
3634           (decf totalsize (1+ (length (car ptr))))
3635           (incf surplus)
3636           (setq ptr (cdr ptr)))
3637         ;; ...and do it.
3638         (when (> surplus 0)
3639           (message-shorten-1 refs cut surplus))))
3640
3641     ;; Finally, collect the references back into a string and insert
3642     ;; it into the buffer.
3643     (let ((refstring (mapconcat #'identity refs " ")))
3644       (if (and message-this-is-news message-cater-to-broken-inn)
3645           (insert (capitalize (symbol-name header)) ": "
3646                   refstring "\n")
3647         (message-fill-header header refstring)))))
3648
3649 (defun message-position-point ()
3650   "Move point to where the user probably wants to find it."
3651   (message-narrow-to-headers)
3652   (cond
3653    ((re-search-forward "^[^:]+:[ \t]*$" nil t)
3654     (search-backward ":" )
3655     (widen)
3656     (forward-char 1)
3657     (if (eq (char-after) ? )
3658         (forward-char 1)
3659       (insert " ")))
3660    (t
3661     (goto-char (point-max))
3662     (widen)
3663     (forward-line 1)
3664     (unless (looking-at "$")
3665       (forward-line 2)))
3666    (sit-for 0)))
3667
3668 (defun message-buffer-name (type &optional to group)
3669   "Return a new (unique) buffer name based on TYPE and TO."
3670   (cond
3671    ;; Generate a new buffer name The Message Way.
3672    ((eq message-generate-new-buffers 'unique)
3673     (generate-new-buffer-name
3674      (concat "*" type
3675              (if to
3676                  (concat " to "
3677                          (or (car (mail-extract-address-components to))
3678                              to) "")
3679                "")
3680              (if (and group (not (string= group ""))) (concat " on " group) "")
3681              "*")))
3682    ;; Check whether `message-generate-new-buffers' is a function,
3683    ;; and if so, call it.
3684    ((message-functionp message-generate-new-buffers)
3685     (funcall message-generate-new-buffers type to group))
3686    ((eq message-generate-new-buffers 'unsent)
3687     (generate-new-buffer-name
3688      (concat "*unsent " type
3689              (if to
3690                  (concat " to "
3691                          (or (car (mail-extract-address-components to))
3692                              to) "")
3693                "")
3694              (if (and group (not (string= group ""))) (concat " on " group) "")
3695              "*")))
3696    ;; Use standard name.
3697    (t
3698     (format "*%s message*" type))))
3699
3700 (defun message-pop-to-buffer (name)
3701   "Pop to buffer NAME, and warn if it already exists and is modified."
3702   (let ((buffer (get-buffer name)))
3703     (if (and buffer
3704              (buffer-name buffer))
3705         (progn
3706           (set-buffer (pop-to-buffer buffer))
3707           (when (and (buffer-modified-p)
3708                      (not (y-or-n-p
3709                            "Message already being composed; erase? ")))
3710             (error "Message being composed")))
3711       (set-buffer (pop-to-buffer name)))
3712     (erase-buffer)
3713     (message-mode)))
3714
3715 (defun message-do-send-housekeeping ()
3716   "Kill old message buffers."
3717   ;; We might have sent this buffer already.  Delete it from the
3718   ;; list of buffers.
3719   (setq message-buffer-list (delq (current-buffer) message-buffer-list))
3720   (while (and message-max-buffers
3721               message-buffer-list
3722               (>= (length message-buffer-list) message-max-buffers))
3723     ;; Kill the oldest buffer -- unless it has been changed.
3724     (let ((buffer (pop message-buffer-list)))
3725       (when (and (buffer-name buffer)
3726                  (not (buffer-modified-p buffer)))
3727         (kill-buffer buffer))))
3728   ;; Rename the buffer.
3729   (if message-send-rename-function
3730       (funcall message-send-rename-function)
3731     (when (string-match "\\`\\*\\(sent \\|unsent \\)?\\(.+\\)\\*[^\\*]*" 
3732                         (buffer-name))
3733       (let ((name (match-string 2 (buffer-name)))
3734             to group)
3735         (if (not (or (string-equal name "mail")
3736                      (string-equal name "news")))
3737             (setq name (concat "*sent " name "*"))
3738           (setq to (message-fetch-field "to"))
3739           (setq group (message-fetch-field "newsgroups"))
3740           (setq name
3741                 (cond 
3742                  (to (concat "*sent mail to "
3743                              (or (car (mail-extract-address-components to))
3744                                  to) "*"))
3745                  ((and group (not (string= group "")))
3746                   (concat "*sent news on " group "*"))
3747                  (t "*sent mail*"))))
3748         (unless (string-equal name (buffer-name))
3749           (rename-buffer name t)))))
3750   ;; Push the current buffer onto the list.
3751   (when message-max-buffers
3752     (setq message-buffer-list
3753           (nconc message-buffer-list (list (current-buffer))))))
3754
3755 (defun message-mail-user-agent ()
3756   (let ((mua (cond
3757               ((not message-mail-user-agent) nil)
3758               ((eq message-mail-user-agent t) mail-user-agent)
3759               (t message-mail-user-agent))))
3760     (if (memq mua '(message-user-agent gnus-user-agent))
3761         nil
3762       mua)))
3763
3764 (defun message-setup (headers &optional replybuffer actions switch-function)
3765   (let ((mua (message-mail-user-agent))
3766         subject to field yank-action)
3767     (if (not (and message-this-is-mail mua))
3768         (message-setup-1 headers replybuffer actions)
3769       (if replybuffer
3770           (setq yank-action (list 'insert-buffer replybuffer)))
3771       (setq headers (copy-sequence headers))
3772       (setq field (assq 'Subject headers))
3773       (when field
3774         (setq subject (cdr field))
3775         (setq headers (delq field headers)))
3776       (setq field (assq 'To headers))
3777       (when field
3778         (setq to (cdr field))
3779         (setq headers (delq field headers)))
3780       (let ((mail-user-agent mua))
3781         (compose-mail to subject
3782                       (mapcar (lambda (item)
3783                                 (cons
3784                                  (format "%s" (car item))
3785                                  (cdr item)))
3786                               headers)
3787                       nil switch-function yank-action actions)))))
3788
3789 ;;;(defvar mc-modes-alist)
3790 (defun message-setup-1 (headers &optional replybuffer actions)
3791 ;;;   (when (and (boundp 'mc-modes-alist)
3792 ;;;          (not (assq 'message-mode mc-modes-alist)))
3793 ;;;     (push '(message-mode (encrypt . mc-encrypt-message)
3794 ;;;                      (sign . mc-sign-message))
3795 ;;;       mc-modes-alist))
3796   (when actions
3797     (setq message-send-actions actions))
3798   (setq message-reply-buffer replybuffer)
3799   (goto-char (point-min))
3800   ;; Insert all the headers.
3801   (mail-header-format
3802    (let ((h headers)
3803          (alist message-header-format-alist))
3804      (while h
3805        (unless (assq (caar h) message-header-format-alist)
3806          (push (list (caar h)) alist))
3807        (pop h))
3808      alist)
3809    headers)
3810   (delete-region (point) (progn (forward-line -1) (point)))
3811   (when message-default-headers
3812     (insert message-default-headers)
3813     (or (bolp) (insert ?\n)))
3814   (put-text-property
3815    (point)
3816    (progn
3817      (insert mail-header-separator "\n")
3818      (1- (point)))
3819    'read-only nil)
3820   (forward-line -1)
3821   (when (message-news-p)
3822     (when message-default-news-headers
3823       (insert message-default-news-headers)
3824       (or (bolp) (insert ?\n)))
3825     (when message-generate-headers-first
3826       (message-generate-headers
3827        (delq 'Lines
3828              (delq 'Subject
3829                    (copy-sequence message-required-news-headers))))))
3830   (when (message-mail-p)
3831     (when message-default-mail-headers
3832       (insert message-default-mail-headers)
3833       (or (bolp) (insert ?\n)))
3834     (when message-generate-headers-first
3835       (message-generate-headers
3836        (delq 'Lines
3837              (delq 'Subject
3838                    (copy-sequence message-required-mail-headers))))))
3839   (run-hooks 'message-signature-setup-hook)
3840   (message-insert-signature)
3841   (save-restriction
3842     (message-narrow-to-headers)
3843     (if message-alternative-emails
3844         (message-use-alternative-email-as-from))
3845     (run-hooks 'message-header-setup-hook))
3846   (set-buffer-modified-p nil)
3847   (setq buffer-undo-list nil)
3848   (run-hooks 'message-setup-hook)
3849   (message-position-point)
3850   (undo-boundary))
3851
3852 (defun message-set-auto-save-file-name ()
3853   "Associate the message buffer with a file in the drafts directory."
3854   (when message-auto-save-directory
3855     (unless (file-directory-p
3856              (directory-file-name message-auto-save-directory))
3857       (gnus-make-directory message-auto-save-directory))
3858     (if (gnus-alive-p)
3859         (setq message-draft-article
3860               (nndraft-request-associate-buffer "drafts"))
3861       (setq buffer-file-name (expand-file-name "*message*"
3862                                                message-auto-save-directory))
3863       (setq buffer-auto-save-file-name (make-auto-save-file-name)))
3864     (clear-visited-file-modtime)
3865     (setq buffer-file-coding-system message-draft-coding-system)))
3866
3867 (defun message-disassociate-draft ()
3868   "Disassociate the message buffer from the drafts directory."
3869   (when message-draft-article
3870     (nndraft-request-expire-articles
3871      (list message-draft-article) "drafts" nil t)))
3872
3873 (defun message-insert-headers ()
3874   "Generate the headers for the article."
3875   (interactive)
3876   (save-excursion
3877     (save-restriction
3878       (message-narrow-to-headers)
3879       (when (message-news-p)
3880         (message-generate-headers
3881          (delq 'Lines
3882                (delq 'Subject
3883                      (copy-sequence message-required-news-headers)))))
3884       (when (message-mail-p)
3885         (message-generate-headers
3886          (delq 'Lines
3887                (delq 'Subject
3888                      (copy-sequence message-required-mail-headers))))))))
3889
3890 \f
3891
3892 ;;;
3893 ;;; Commands for interfacing with message
3894 ;;;
3895
3896 ;;;###autoload
3897 (defun message-mail (&optional to subject
3898                                other-headers continue switch-function
3899                                yank-action send-actions)
3900   "Start editing a mail message to be sent.
3901 OTHER-HEADERS is an alist of header/value pairs."
3902   (interactive)
3903   (let ((message-this-is-mail t) replybuffer)
3904     (unless (message-mail-user-agent)
3905       (message-pop-to-buffer (message-buffer-name "mail" to)))
3906     ;; FIXME: message-mail should do something if YANK-ACTION is not
3907     ;; insert-buffer.
3908     (and (consp yank-action) (eq (car yank-action) 'insert-buffer)
3909          (setq replybuffer (nth 1 yank-action)))
3910     (message-setup
3911      (nconc
3912       `((To . ,(or to "")) (Subject . ,(or subject "")))
3913       (when other-headers other-headers))
3914      replybuffer)
3915     ;; FIXME: Should return nil if failure.
3916     t))
3917
3918 ;;;###autoload
3919 (defun message-news (&optional newsgroups subject)
3920   "Start editing a news article to be sent."
3921   (interactive)
3922   (let ((message-this-is-news t))
3923     (message-pop-to-buffer (message-buffer-name "news" nil newsgroups))
3924     (message-setup `((Newsgroups . ,(or newsgroups ""))
3925                      (Subject . ,(or subject ""))))))
3926
3927 (defun message-get-reply-headers (wide &optional to-address)
3928   (let (follow-to mct never-mct from to cc reply-to mrt mft ccalist)
3929     ;; Find all relevant headers we need.
3930     (setq from (message-fetch-field "from")
3931           to (message-fetch-field "to")
3932           cc (message-fetch-field "cc")
3933           mct (message-fetch-field "mail-copies-to")
3934           reply-to (message-fetch-field "reply-to")
3935           mrt (message-fetch-field "mail-reply-to")
3936           mft (and message-use-followup-to
3937                    (message-fetch-field "mail-followup-to")))
3938
3939     ;; Handle special values of Mail-Copies-To.
3940     (when mct
3941       (cond ((or (equal (downcase mct) "never")
3942                  (equal (downcase mct) "nobody"))
3943              (setq never-mct t)
3944              (setq mct nil))
3945             ((or (equal (downcase mct) "always")
3946                  (equal (downcase mct) "poster"))
3947              (setq mct (or mrt reply-to from)))))
3948
3949     (if (and (not mft)
3950              (or (not wide)
3951                  to-address))
3952         (progn
3953           (setq follow-to (list (cons 'To (or to-address mrt reply-to from))))
3954           (when (and (and wide mct)
3955                      (not (member (cons 'To mct) follow-to)))
3956             (push (cons 'Cc mct) follow-to)))
3957       (let (ccalist)
3958         (save-excursion
3959           (message-set-work-buffer)
3960           (if (and mft
3961                    message-use-followup-to
3962                    (or (not (eq message-use-followup-to 'ask))
3963                        (message-y-or-n-p
3964                         (concat "Obey Mail-Followup-To? ") t "\
3965 You should normally obey the Mail-Followup-To: header.  In this
3966 article, it has the value of
3967
3968 " mft "
3969
3970 which directs your response to " (if (string-match "," mft)
3971                                "the specified addresses"
3972                              "that address only") ".
3973
3974 If a message is posted to several mailing lists, Mail-Followup-To is
3975 often used to direct the following discussion to one list only,
3976 because discussions that are spread over several lists tend to be
3977 fragmented and very difficult to follow.
3978
3979 Also, some source/announcement lists are not indented for discussion;
3980 responses here are directed to other addresses.")))
3981               (insert mft)
3982             (unless never-mct
3983               (insert (or mrt reply-to from "")))
3984             (insert (if to (concat (if (bolp) "" ", ") to "") ""))
3985             (insert (if mct (concat (if (bolp) "" ", ") mct) ""))
3986             (insert (if cc (concat (if (bolp) "" ", ") cc) "")))
3987           (goto-char (point-min))
3988           (while (re-search-forward "[ \t]+" nil t)
3989             (replace-match " " t t))
3990           ;; Remove addresses that match `rmail-dont-reply-to-names'.
3991           (let ((rmail-dont-reply-to-names message-dont-reply-to-names))
3992             (insert (prog1 (rmail-dont-reply-to (buffer-string))
3993                       (erase-buffer))))
3994           (goto-char (point-min))
3995           ;; Perhaps "Mail-Copies-To: never" removed the only address?
3996           (when (eobp)
3997             (insert (or mrt reply-to from "")))
3998           (setq ccalist
3999                 (mapcar
4000                  (lambda (addr)
4001                    (cons (mail-strip-quoted-names addr) addr))
4002                  (message-tokenize-header (buffer-string))))
4003           (let ((s ccalist))
4004             (while s
4005               (setq ccalist (delq (assoc (car (pop s)) s) ccalist)))))
4006         (setq follow-to (list (cons 'To (cdr (pop ccalist)))))
4007         (when ccalist
4008           (let ((ccs (cons 'Cc (mapconcat
4009                                 (lambda (addr) (cdr addr)) ccalist ", "))))
4010             (when (string-match "^ +" (cdr ccs))
4011               (setcdr ccs (substring (cdr ccs) (match-end 0))))
4012             (push ccs follow-to)))))
4013     follow-to))
4014
4015
4016 ;;;###autoload
4017 (defun message-reply (&optional to-address wide)
4018   "Start editing a reply to the article in the current buffer."
4019   (interactive)
4020   (require 'gnus-sum)                   ; for gnus-list-identifiers
4021   (let ((cur (current-buffer))
4022         from subject date reply-to to cc
4023         references message-id follow-to
4024         (inhibit-point-motion-hooks t)
4025         (message-this-is-mail t)
4026         gnus-warning)
4027     (save-restriction
4028       (message-narrow-to-head-1)
4029       ;; Allow customizations to have their say.
4030       (if (not wide)
4031           ;; This is a regular reply.
4032           (when (message-functionp message-reply-to-function)
4033             (save-excursion
4034               (setq follow-to (funcall message-reply-to-function))))
4035         ;; This is a followup.
4036         (when (message-functionp message-wide-reply-to-function)
4037           (save-excursion
4038             (setq follow-to
4039                   (funcall message-wide-reply-to-function)))))
4040       (setq message-id (message-fetch-field "message-id" t)
4041             references (message-fetch-field "references")
4042             date (message-fetch-field "date")
4043             from (message-fetch-field "from")
4044             subject (or (message-fetch-field "subject") "none"))
4045     (when gnus-list-identifiers
4046       (setq subject (message-strip-list-identifiers subject)))
4047     (setq subject (concat "Re: " (message-strip-subject-re subject)))
4048
4049     (when (and (setq gnus-warning (message-fetch-field "gnus-warning"))
4050                (string-match "<[^>]+>" gnus-warning))
4051       (setq message-id (match-string 0 gnus-warning)))
4052
4053     (unless follow-to
4054       (setq follow-to (message-get-reply-headers wide to-address))))
4055
4056     (unless (message-mail-user-agent)
4057       (message-pop-to-buffer
4058        (message-buffer-name
4059         (if wide "wide reply" "reply") from
4060         (if wide to-address nil))))
4061
4062     (setq message-reply-headers
4063           (vector 0 subject from date message-id references 0 0 ""))
4064
4065     (message-setup
4066      `((Subject . ,subject)
4067        ,@follow-to
4068        ,@(if (or references message-id)
4069              `((References . ,(concat (or references "") (and references " ")
4070                                       (or message-id ""))))
4071            nil))
4072      cur)))
4073
4074 ;;;###autoload
4075 (defun message-wide-reply (&optional to-address)
4076   "Make a \"wide\" reply to the message in the current buffer."
4077   (interactive)
4078   (message-reply to-address t))
4079
4080 ;;;###autoload
4081 (defun message-followup (&optional to-newsgroups)
4082   "Follow up to the message in the current buffer.
4083 If TO-NEWSGROUPS, use that as the new Newsgroups line."
4084   (interactive)
4085   (require 'gnus-sum)                   ; for gnus-list-identifiers
4086   (let ((cur (current-buffer))
4087         from subject date reply-to mrt mct
4088         references message-id follow-to
4089         (inhibit-point-motion-hooks t)
4090         (message-this-is-news t)
4091         followup-to distribution newsgroups gnus-warning posted-to)
4092     (save-restriction
4093       (narrow-to-region
4094        (goto-char (point-min))
4095        (if (search-forward "\n\n" nil t)
4096            (1- (point))
4097          (point-max)))
4098       (when (message-functionp message-followup-to-function)
4099         (setq follow-to
4100               (funcall message-followup-to-function)))
4101       (setq from (message-fetch-field "from")
4102             date (message-fetch-field "date")
4103             subject (or (message-fetch-field "subject") "none")
4104             references (message-fetch-field "references")
4105             message-id (message-fetch-field "message-id" t)
4106             followup-to (message-fetch-field "followup-to")
4107             newsgroups (message-fetch-field "newsgroups")
4108             posted-to (message-fetch-field "posted-to")
4109             reply-to (message-fetch-field "reply-to")
4110             mrt (message-fetch-field "mail-reply-to")
4111             distribution (message-fetch-field "distribution")
4112             mct (message-fetch-field "mail-copies-to"))
4113       (when (and (setq gnus-warning (message-fetch-field "gnus-warning"))
4114                  (string-match "<[^>]+>" gnus-warning))
4115         (setq message-id (match-string 0 gnus-warning)))
4116       ;; Remove bogus distribution.
4117       (when (and (stringp distribution)
4118                  (let ((case-fold-search t))
4119                    (string-match "world" distribution)))
4120         (setq distribution nil))
4121       (if gnus-list-identifiers
4122           (setq subject (message-strip-list-identifiers subject)))
4123       (setq subject (concat "Re: " (message-strip-subject-re subject)))
4124       (widen))
4125
4126     (message-pop-to-buffer (message-buffer-name "followup" from newsgroups))
4127
4128     (message-setup
4129      `((Subject . ,subject)
4130        ,@(cond
4131           (to-newsgroups
4132            (list (cons 'Newsgroups to-newsgroups)))
4133           (follow-to follow-to)
4134           ((and followup-to message-use-followup-to)
4135            (list
4136             (cond
4137              ((equal (downcase followup-to) "poster")
4138               (if (or (eq message-use-followup-to 'use)
4139                       (message-y-or-n-p "Obey Followup-To: poster? " t "\
4140 You should normally obey the Followup-To: header.
4141
4142 `Followup-To: poster' sends your response via e-mail instead of news.
4143
4144 A typical situation where `Followup-To: poster' is used is when the poster
4145 does not read the newsgroup, so he wouldn't see any replies sent to it."))
4146                   (progn
4147                     (setq message-this-is-news nil)
4148                     (cons 'To (or mrt reply-to from "")))
4149                 (cons 'Newsgroups newsgroups)))
4150              (t
4151               (if (or (equal followup-to newsgroups)
4152                       (not (eq message-use-followup-to 'ask))
4153                       (message-y-or-n-p
4154                        (concat "Obey Followup-To: " followup-to "? ") t "\
4155 You should normally obey the Followup-To: header.
4156
4157         `Followup-To: " followup-to "'
4158 directs your response to " (if (string-match "," followup-to)
4159                                "the specified newsgroups"
4160                              "that newsgroup only") ".
4161
4162 If a message is posted to several newsgroups, Followup-To is often
4163 used to direct the following discussion to one newsgroup only,
4164 because discussions that are spread over several newsgroup tend to
4165 be fragmented and very difficult to follow.
4166
4167 Also, some source/announcement newsgroups are not indented for discussion;
4168 responses here are directed to other newsgroups."))
4169                   (cons 'Newsgroups followup-to)
4170                 (cons 'Newsgroups newsgroups))))))
4171           (posted-to
4172            `((Newsgroups . ,posted-to)))
4173           (t
4174            `((Newsgroups . ,newsgroups))))
4175        ,@(and distribution (list (cons 'Distribution distribution)))
4176        ,@(if (or references message-id)
4177              `((References . ,(concat (or references "") (and references " ")
4178                                       (or message-id "")))))
4179        ,@(when (and mct
4180                     (not (or (equal (downcase mct) "never")
4181                              (equal (downcase mct) "nobody"))))
4182            (list (cons 'Cc (if (or (equal (downcase mct) "always")
4183                                    (equal (downcase mct) "poster"))
4184                                (or mrt reply-to from "")
4185                              mct)))))
4186
4187      cur)
4188
4189     (setq message-reply-headers
4190           (vector 0 subject from date message-id references 0 0 ""))))
4191
4192
4193 ;;;###autoload
4194 (defun message-cancel-news (&optional arg)
4195   "Cancel an article you posted.
4196 If ARG, allow editing of the cancellation message."
4197   (interactive "P")
4198   (unless (message-news-p)
4199     (error "This is not a news article; canceling is impossible"))
4200   (let (from newsgroups message-id distribution buf sender)
4201     (save-excursion
4202       ;; Get header info from original article.
4203       (save-restriction
4204         (message-narrow-to-head-1)
4205         (setq from (message-fetch-field "from")
4206               sender (message-fetch-field "sender")
4207               newsgroups (message-fetch-field "newsgroups")
4208               message-id (message-fetch-field "message-id" t)
4209               distribution (message-fetch-field "distribution")))
4210       ;; Make sure that this article was written by the user.
4211       (unless (or (message-gnksa-enable-p 'cancel-messages)
4212                   (and sender
4213                        (string-equal
4214                         (downcase sender)
4215                         (downcase (message-make-sender))))
4216                   (string-equal
4217                    (downcase (cadr (mail-extract-address-components from)))
4218                    (downcase (cadr (mail-extract-address-components
4219                                     (message-make-from))))))
4220         (error "This article is not yours"))
4221       (when (yes-or-no-p "Do you really want to cancel this article? ")
4222         ;; Make control message.
4223         (if arg
4224             (message-news)
4225           (setq buf (set-buffer (get-buffer-create " *message cancel*"))))
4226         (erase-buffer)
4227         (insert "Newsgroups: " newsgroups "\n"
4228                 "From: " from "\n"
4229                 "Subject: cmsg cancel " message-id "\n"
4230                 "Control: cancel " message-id "\n"
4231                 (if distribution
4232                     (concat "Distribution: " distribution "\n")
4233                   "")
4234                 mail-header-separator "\n"
4235                 message-cancel-message)
4236         (run-hooks 'message-cancel-hook)
4237         (unless arg
4238           (message "Canceling your article...")
4239           (if (let ((message-syntax-checks
4240                      'dont-check-for-anything-just-trust-me))
4241                 (funcall message-send-news-function))
4242               (message "Canceling your article...done"))
4243           (kill-buffer buf))))))
4244
4245 ;;;###autoload
4246 (defun message-supersede ()
4247   "Start composing a message to supersede the current message.
4248 This is done simply by taking the old article and adding a Supersedes
4249 header line with the old Message-ID."
4250   (interactive)
4251   (let ((cur (current-buffer))
4252         (sender (message-fetch-field "sender"))
4253         (from (message-fetch-field "from")))
4254     ;; Check whether the user owns the article that is to be superseded.
4255     (unless (or (message-gnksa-enable-p 'cancel-messages)
4256                 (and sender
4257                      (string-equal
4258                       (downcase sender)
4259                       (downcase (message-make-sender))))
4260                 (string-equal
4261                  (downcase (cadr (mail-extract-address-components from)))
4262                  (downcase (cadr (mail-extract-address-components
4263                                   (message-make-from))))))
4264       (error "This article is not yours"))
4265     ;; Get a normal message buffer.
4266     (message-pop-to-buffer (message-buffer-name "supersede"))
4267     (insert-buffer-substring cur)
4268     (mime-to-mml)
4269     (message-narrow-to-head-1)
4270     ;; Remove unwanted headers.
4271     (when message-ignored-supersedes-headers
4272       (message-remove-header message-ignored-supersedes-headers t))
4273     (goto-char (point-min))
4274     (if (not (re-search-forward "^Message-ID: " nil t))
4275         (error "No Message-ID in this article")
4276       (replace-match "Supersedes: " t t))
4277     (goto-char (point-max))
4278     (insert mail-header-separator)
4279     (widen)
4280     (forward-line 1)))
4281
4282 ;;;###autoload
4283 (defun message-recover ()
4284   "Reread contents of current buffer from its last auto-save file."
4285   (interactive)
4286   (let ((file-name (make-auto-save-file-name)))
4287     (cond ((save-window-excursion
4288              (if (not (eq system-type 'vax-vms))
4289                  (with-output-to-temp-buffer "*Directory*"
4290                    (with-current-buffer standard-output
4291                      (fundamental-mode)) ; for Emacs 20.4+
4292                    (buffer-disable-undo standard-output)
4293                    (let ((default-directory "/"))
4294                      (call-process
4295                       "ls" nil standard-output nil "-l" file-name))))
4296              (yes-or-no-p (format "Recover auto save file %s? " file-name)))
4297            (let ((buffer-read-only nil))
4298              (erase-buffer)
4299              (insert-file-contents file-name nil)))
4300           (t (error "message-recover cancelled")))))
4301
4302 ;;; Washing Subject:
4303
4304 (defun message-wash-subject (subject)
4305   "Remove junk like \"Re:\", \"(fwd)\", etc. added to subject string SUBJECT.
4306 Previous forwarders, replyers, etc. may add it."
4307   (with-temp-buffer
4308     (insert-string subject)
4309     (goto-char (point-min))
4310     ;; strip Re/Fwd stuff off the beginning
4311     (while (re-search-forward
4312             "\\([Rr][Ee]:\\|[Ff][Ww][Dd]\\(\\[[0-9]*\\]\\)?:\\|[Ff][Ww]:\\)" nil t)
4313       (replace-match ""))
4314
4315     ;; and gnus-style forwards [foo@bar.com] subject
4316     (goto-char (point-min))
4317     (while (re-search-forward "\\[[^ \t]*\\(@\\|\\.\\)[^ \t]*\\]" nil t)
4318       (replace-match ""))
4319
4320     ;; and off the end
4321     (goto-char (point-max))
4322     (while (re-search-backward "([Ff][Ww][Dd])" nil t)
4323       (replace-match ""))
4324
4325     ;; and finally, any whitespace that was left-over
4326     (goto-char (point-min))
4327     (while (re-search-forward "^[ \t]+" nil t)
4328       (replace-match ""))
4329     (goto-char (point-max))
4330     (while (re-search-backward "[ \t]+$" nil t)
4331       (replace-match ""))
4332
4333     (buffer-string)))
4334
4335 ;;; Forwarding messages.
4336
4337 (defvar message-forward-decoded-p nil
4338   "Non-nil means the original message is decoded.")
4339
4340 (defun message-forward-subject-author-subject (subject)
4341   "Generate a SUBJECT for a forwarded message.
4342 The form is: [Source] Subject, where if the original message was mail,
4343 Source is the sender, and if the original message was news, Source is
4344 the list of newsgroups is was posted to."
4345   (concat "["
4346            (let ((prefix 
4347                   (or (message-fetch-field
4348                        (if (message-news-p) "newsgroups" "from"))
4349                       "(nowhere)")))
4350              (if message-forward-decoded-p
4351                  prefix
4352                (mail-decode-encoded-word-string prefix)))
4353           "] " subject))
4354
4355 (defun message-forward-subject-fwd (subject)
4356   "Generate a SUBJECT for a forwarded message.
4357 The form is: Fwd: Subject, where Subject is the original subject of
4358 the message."
4359   (concat "Fwd: " subject))
4360
4361 (defun message-make-forward-subject ()
4362   "Return a Subject header suitable for the message in the current buffer."
4363   (save-excursion
4364     (save-restriction
4365       (message-narrow-to-head-1)
4366       (let ((funcs message-make-forward-subject-function)
4367             (subject (message-fetch-field "Subject")))
4368         (setq subject
4369               (if subject
4370                   (if message-forward-decoded-p
4371                       subject
4372                     (mail-decode-encoded-word-string subject))
4373                 ""))
4374         (if message-wash-forwarded-subjects
4375             (setq subject (message-wash-subject subject)))
4376         ;; Make sure funcs is a list.
4377         (and funcs
4378              (not (listp funcs))
4379              (setq funcs (list funcs)))
4380         ;; Apply funcs in order, passing subject generated by previous
4381         ;; func to the next one.
4382         (while funcs
4383           (when (message-functionp (car funcs))
4384             (setq subject (funcall (car funcs) subject)))
4385           (setq funcs (cdr funcs)))
4386         subject))))
4387
4388 (eval-when-compile
4389   (defvar gnus-article-decoded-p))
4390
4391 ;;;###autoload
4392 (defun message-forward (&optional news digest)
4393   "Forward the current message via mail.
4394 Optional NEWS will use news to forward instead of mail.
4395 Optional DIGEST will use digest to forward."
4396   (interactive "P")
4397   (let* ((cur (current-buffer))
4398          (message-forward-decoded-p 
4399           (if (local-variable-p 'gnus-article-decoded-p (current-buffer))
4400               gnus-article-decoded-p  ;; In an article buffer.
4401             message-forward-decoded-p))
4402          (subject (message-make-forward-subject))
4403          art-beg)
4404     (if news
4405         (message-news nil subject)
4406       (message-mail nil subject))
4407     ;; Put point where we want it before inserting the forwarded
4408     ;; message.
4409     (if message-forward-before-signature
4410         (message-goto-body)
4411       (goto-char (point-max)))
4412     (if message-forward-as-mime
4413         (if digest
4414             (insert "\n<#multipart type=digest>\n")
4415           (if message-forward-show-mml
4416               (insert "\n\n<#mml type=message/rfc822 disposition=inline>\n")
4417             (insert "\n\n<#part type=message/rfc822 disposition=inline raw=t>\n")))
4418       (insert "\n-------------------- Start of forwarded message --------------------\n"))
4419     (let ((b (point)) e)
4420       (if digest
4421           (if message-forward-as-mime
4422               (insert-buffer-substring cur)
4423             (mml-insert-buffer cur))
4424         (if (and message-forward-show-mml
4425                  (not message-forward-decoded-p))
4426             (insert
4427              (with-temp-buffer
4428                (mm-disable-multibyte-mule4) ;; Must copy buffer in unibyte mode
4429                (insert
4430                 (with-current-buffer cur
4431                   (mm-string-as-unibyte (buffer-string))))
4432                (mm-enable-multibyte-mule4)
4433                (mime-to-mml)
4434                (goto-char (point-min))
4435                (when (looking-at "From ")
4436                  (replace-match "X-From-Line: "))
4437                (buffer-string)))
4438           (save-restriction
4439             (narrow-to-region (point) (point))
4440             (mml-insert-buffer cur)
4441             (goto-char (point-min))
4442             (when (looking-at "From ")
4443               (replace-match "X-From-Line: "))
4444             (goto-char (point-max)))))
4445       (setq e (point))
4446       (if message-forward-as-mime
4447           (if digest
4448               (insert "<#/multipart>\n")
4449             (if message-forward-show-mml
4450                 (insert "<#/mml>\n")
4451               (insert "<#/part>\n")))
4452         (insert "\n-------------------- End of forwarded message --------------------\n"))
4453       (if (and digest message-forward-as-mime)
4454           (save-restriction
4455             (narrow-to-region b e)
4456             (goto-char b)
4457             (narrow-to-region (point)
4458                               (or (search-forward "\n\n" nil t) (point)))
4459             (delete-region (point-min) (point-max)))
4460         (when (and (not current-prefix-arg)
4461                    message-forward-ignored-headers)
4462           (save-restriction
4463             (narrow-to-region b e)
4464             (goto-char b)
4465             (narrow-to-region (point)
4466                               (or (search-forward "\n\n" nil t) (point)))
4467             (message-remove-header message-forward-ignored-headers t)))))
4468     (message-position-point)))
4469
4470 ;;;###autoload
4471 (defun message-resend (address)
4472   "Resend the current article to ADDRESS."
4473   (interactive
4474    (list (message-read-from-minibuffer "Resend message to: ")))
4475   (message "Resending message to %s..." address)
4476   (save-excursion
4477     (let ((cur (current-buffer))
4478           beg)
4479       ;; We first set up a normal mail buffer.
4480       (unless (message-mail-user-agent)
4481         (set-buffer (get-buffer-create " *message resend*"))
4482         (erase-buffer))
4483       (let ((message-this-is-mail t))
4484         (message-setup `((To . ,address))))
4485       ;; Insert our usual headers.
4486       (message-generate-headers '(From Date To))
4487       (message-narrow-to-headers)
4488       ;; Rename them all to "Resent-*".
4489       (while (re-search-forward "^[A-Za-z]" nil t)
4490         (forward-char -1)
4491         (insert "Resent-"))
4492       (widen)
4493       (forward-line)
4494       (delete-region (point) (point-max))
4495       (setq beg (point))
4496       ;; Insert the message to be resent.
4497       (insert-buffer-substring cur)
4498       (goto-char (point-min))
4499       (search-forward "\n\n")
4500       (forward-char -1)
4501       (save-restriction
4502         (narrow-to-region beg (point))
4503         (message-remove-header message-ignored-resent-headers t)
4504         (goto-char (point-max)))
4505       (insert mail-header-separator)
4506       ;; Rename all old ("Also-")Resent headers.
4507       (while (re-search-backward "^\\(Also-\\)*Resent-" beg t)
4508         (beginning-of-line)
4509         (insert "Also-"))
4510       ;; Quote any "From " lines at the beginning.
4511       (goto-char beg)
4512       (when (looking-at "From ")
4513         (replace-match "X-From-Line: "))
4514       ;; Send it.
4515       (let ((message-inhibit-body-encoding t)
4516             message-required-mail-headers)
4517         (message-send-mail))
4518       (kill-buffer (current-buffer)))
4519     (message "Resending message to %s...done" address)))
4520
4521 ;;;###autoload
4522 (defun message-bounce ()
4523   "Re-mail the current message.
4524 This only makes sense if the current message is a bounce message that
4525 contains some mail you have written which has been bounced back to
4526 you."
4527   (interactive)
4528   (let ((handles (mm-dissect-buffer t))
4529         boundary)
4530     (message-pop-to-buffer (message-buffer-name "bounce"))
4531     (if (stringp (car handles))
4532         ;; This is a MIME bounce.
4533         (mm-insert-part (car (last handles)))
4534       ;; This is a non-MIME bounce, so we try to remove things
4535       ;; manually.
4536       (mm-insert-part handles)
4537       (undo-boundary)
4538       (goto-char (point-min))
4539       (search-forward "\n\n" nil t)
4540       (or (and (re-search-forward message-unsent-separator nil t)
4541                (forward-line 1))
4542           (re-search-forward "^Return-Path:.*\n" nil t))
4543       ;; We remove everything before the bounced mail.
4544       (delete-region
4545        (point-min)
4546        (if (re-search-forward "^[^ \n\t]+:" nil t)
4547            (match-beginning 0)
4548          (point))))
4549     (mm-enable-multibyte)
4550     (mime-to-mml)
4551     (save-restriction
4552       (message-narrow-to-head-1)
4553       (message-remove-header message-ignored-bounced-headers t)
4554       (goto-char (point-max))
4555       (insert mail-header-separator))
4556     (message-position-point)))
4557
4558 ;;;
4559 ;;; Interactive entry points for new message buffers.
4560 ;;;
4561
4562 ;;;###autoload
4563 (defun message-mail-other-window (&optional to subject)
4564   "Like `message-mail' command, but display mail buffer in another window."
4565   (interactive)
4566   (unless (message-mail-user-agent)
4567     (let ((pop-up-windows t)
4568           (special-display-buffer-names nil)
4569           (special-display-regexps nil)
4570           (same-window-buffer-names nil)
4571           (same-window-regexps nil))
4572       (message-pop-to-buffer (message-buffer-name "mail" to))))
4573   (let ((message-this-is-mail t))
4574     (message-setup `((To . ,(or to "")) (Subject . ,(or subject "")))
4575                    nil nil 'switch-to-buffer-other-window)))
4576
4577 ;;;###autoload
4578 (defun message-mail-other-frame (&optional to subject)
4579   "Like `message-mail' command, but display mail buffer in another frame."
4580   (interactive)
4581   (unless (message-mail-user-agent)
4582     (let ((pop-up-frames t)
4583           (special-display-buffer-names nil)
4584           (special-display-regexps nil)
4585           (same-window-buffer-names nil)
4586           (same-window-regexps nil))
4587       (message-pop-to-buffer (message-buffer-name "mail" to))))
4588   (let ((message-this-is-mail t))
4589     (message-setup `((To . ,(or to "")) (Subject . ,(or subject "")))
4590                    nil nil 'switch-to-buffer-other-frame)))
4591
4592 ;;;###autoload
4593 (defun message-news-other-window (&optional newsgroups subject)
4594   "Start editing a news article to be sent."
4595   (interactive)
4596   (let ((pop-up-windows t)
4597         (special-display-buffer-names nil)
4598         (special-display-regexps nil)
4599         (same-window-buffer-names nil)
4600         (same-window-regexps nil))
4601     (message-pop-to-buffer (message-buffer-name "news" nil newsgroups)))
4602   (let ((message-this-is-news t))
4603     (message-setup `((Newsgroups . ,(or newsgroups ""))
4604                      (Subject . ,(or subject ""))))))
4605
4606 ;;;###autoload
4607 (defun message-news-other-frame (&optional newsgroups subject)
4608   "Start editing a news article to be sent."
4609   (interactive)
4610   (let ((pop-up-frames t)
4611         (special-display-buffer-names nil)
4612         (special-display-regexps nil)
4613         (same-window-buffer-names nil)
4614         (same-window-regexps nil))
4615     (message-pop-to-buffer (message-buffer-name "news" nil newsgroups)))
4616   (let ((message-this-is-news t))
4617     (message-setup `((Newsgroups . ,(or newsgroups ""))
4618                      (Subject . ,(or subject ""))))))
4619
4620 ;;; underline.el
4621
4622 ;; This code should be moved to underline.el (from which it is stolen).
4623
4624 ;;;###autoload
4625 (defun bold-region (start end)
4626   "Bold all nonblank characters in the region.
4627 Works by overstriking characters.
4628 Called from program, takes two arguments START and END
4629 which specify the range to operate on."
4630   (interactive "r")
4631   (save-excursion
4632     (let ((end1 (make-marker)))
4633       (move-marker end1 (max start end))
4634       (goto-char (min start end))
4635       (while (< (point) end1)
4636         (or (looking-at "[_\^@- ]")
4637             (insert (char-after) "\b"))
4638         (forward-char 1)))))
4639
4640 ;;;###autoload
4641 (defun unbold-region (start end)
4642   "Remove all boldness (overstruck characters) in the region.
4643 Called from program, takes two arguments START and END
4644 which specify the range to operate on."
4645   (interactive "r")
4646   (save-excursion
4647     (let ((end1 (make-marker)))
4648       (move-marker end1 (max start end))
4649       (goto-char (min start end))
4650       (while (re-search-forward "\b" end1 t)
4651         (if (eq (char-after) (char-after (- (point) 2)))
4652             (delete-char -2))))))
4653
4654 (defalias 'message-exchange-point-and-mark 'exchange-point-and-mark)
4655
4656 ;; Support for toolbar
4657 (eval-when-compile
4658   (defvar tool-bar-map)
4659   (defvar tool-bar-mode))
4660
4661 (defun message-tool-bar-map ()
4662   (or message-tool-bar-map
4663       (setq message-tool-bar-map
4664             (and (fboundp 'tool-bar-add-item-from-menu)
4665                  tool-bar-mode
4666                  (let ((tool-bar-map (copy-keymap tool-bar-map))
4667                        (load-path (mm-image-load-path)))
4668                    ;; Zap some items which aren't so relevant and take
4669                    ;; up space.
4670                    (dolist (key '(print-buffer kill-buffer save-buffer
4671                                                write-file dired open-file))
4672                      (define-key tool-bar-map (vector key) nil))
4673                    (tool-bar-add-item-from-menu
4674                     'message-send-and-exit "mail_send" message-mode-map)
4675                    (tool-bar-add-item-from-menu
4676                     'message-kill-buffer "close" message-mode-map)
4677                    (tool-bar-add-item-from-menu
4678                     'message-dont-send "cancel" message-mode-map)
4679                    (tool-bar-add-item-from-menu
4680                     'mml-attach-file "attach" message-mode-map)
4681                    (tool-bar-add-item-from-menu
4682                     'ispell-message "spell" message-mode-map)
4683                    tool-bar-map)))))
4684
4685 ;;; Group name completion.
4686
4687 (defvar message-newgroups-header-regexp
4688   "^\\(Newsgroups\\|Followup-To\\|Posted-To\\|Gcc\\):"
4689   "Regexp that match headers that lists groups.")
4690
4691 (defun message-tab ()
4692   "Expand group names in Newsgroups and Followup-To headers.
4693 Do a `tab-to-tab-stop' if not in those headers."
4694   (interactive)
4695   (if (let ((mail-abbrev-mode-regexp message-newgroups-header-regexp))
4696         (mail-abbrev-in-expansion-header-p))
4697       (message-expand-group)
4698     (tab-to-tab-stop)))
4699
4700 (defun message-expand-group ()
4701   "Expand the group name under point."
4702   (let* ((b (save-excursion
4703               (save-restriction
4704                 (narrow-to-region
4705                  (save-excursion
4706                    (beginning-of-line)
4707                    (skip-chars-forward "^:")
4708                    (1+ (point)))
4709                  (point))
4710                 (skip-chars-backward "^, \t\n") (point))))
4711          (completion-ignore-case t)
4712          (string (buffer-substring b (progn (skip-chars-forward "^,\t\n ")
4713                                             (point))))
4714          (hashtb (and (boundp 'gnus-active-hashtb) gnus-active-hashtb))
4715          (completions (all-completions string hashtb))
4716          comp)
4717     (delete-region b (point))
4718     (cond
4719      ((= (length completions) 1)
4720       (if (string= (car completions) string)
4721           (progn
4722             (insert string)
4723             (message "Only matching group"))
4724         (insert (car completions))))
4725      ((and (setq comp (try-completion string hashtb))
4726            (not (string= comp string)))
4727       (insert comp))
4728      (t
4729       (insert string)
4730       (if (not comp)
4731           (message "No matching groups")
4732         (save-selected-window
4733           (pop-to-buffer "*Completions*")
4734           (buffer-disable-undo)
4735           (let ((buffer-read-only nil))
4736             (erase-buffer)
4737             (let ((standard-output (current-buffer)))
4738               (display-completion-list (sort completions 'string<)))
4739             (goto-char (point-min))
4740             (delete-region (point) (progn (forward-line 3) (point))))))))))
4741
4742 ;;; Help stuff.
4743
4744 (defun message-talkative-question (ask question show &rest text)
4745   "Call FUNCTION with argument QUESTION; optionally display TEXT... args.
4746 If SHOW is non-nil, the arguments TEXT... are displayed in a temp buffer.
4747 The following arguments may contain lists of values."
4748   (if (and show
4749            (setq text (message-flatten-list text)))
4750       (save-window-excursion
4751         (save-excursion
4752           (with-output-to-temp-buffer " *MESSAGE information message*"
4753             (set-buffer " *MESSAGE information message*")
4754             (fundamental-mode)          ; for Emacs 20.4+
4755             (mapcar 'princ text)
4756             (goto-char (point-min))))
4757         (funcall ask question))
4758     (funcall ask question)))
4759
4760 (defun message-flatten-list (list)
4761   "Return a new, flat list that contains all elements of LIST.
4762
4763 \(message-flatten-list '(1 (2 3 (4 5 (6))) 7))
4764 => (1 2 3 4 5 6 7)"
4765   (cond ((consp list)
4766          (apply 'append (mapcar 'message-flatten-list list)))
4767         (list
4768          (list list))))
4769
4770 (defun message-generate-new-buffer-clone-locals (name &optional varstr)
4771   "Create and return a buffer with name based on NAME using `generate-new-buffer.'
4772 Then clone the local variables and values from the old buffer to the
4773 new one, cloning only the locals having a substring matching the
4774 regexp varstr."
4775   (let ((oldbuf (current-buffer)))
4776     (save-excursion
4777       (set-buffer (generate-new-buffer name))
4778       (message-clone-locals oldbuf varstr)
4779       (current-buffer))))
4780
4781 (defun message-clone-locals (buffer &optional varstr)
4782   "Clone the local variables from BUFFER to the current buffer."
4783   (let ((locals (save-excursion
4784                   (set-buffer buffer)
4785                   (buffer-local-variables)))
4786         (regexp "^gnus\\|^nn\\|^message\\|^user-mail-address"))
4787     (mapcar
4788      (lambda (local)
4789        (when (and (consp local)
4790                   (car local)
4791                   (string-match regexp (symbol-name (car local)))
4792                   (or (null varstr)
4793                       (string-match varstr (symbol-name (car local)))))
4794          (ignore-errors
4795            (set (make-local-variable (car local))
4796                 (cdr local)))))
4797      locals)))
4798
4799 ;;; Miscellaneous functions
4800
4801 (defsubst message-replace-chars-in-string (string from to)
4802   (mm-subst-char-in-string from to string))
4803
4804 ;;;
4805 ;;; MIME functions
4806 ;;;
4807
4808 (defvar message-inhibit-body-encoding nil)
4809
4810 (defun message-encode-message-body ()
4811   (unless message-inhibit-body-encoding
4812     (let ((mail-parse-charset (or mail-parse-charset
4813                                   message-default-charset))
4814           (case-fold-search t)
4815           lines content-type-p)
4816       (message-goto-body)
4817       (save-restriction
4818         (narrow-to-region (point) (point-max))
4819         (let ((new (mml-generate-mime)))
4820           (when new
4821             (delete-region (point-min) (point-max))
4822             (insert new)
4823             (goto-char (point-min))
4824             (if (eq (aref new 0) ?\n)
4825                 (delete-char 1)
4826               (search-forward "\n\n")
4827               (setq lines (buffer-substring (point-min) (1- (point))))
4828               (delete-region (point-min) (point))))))
4829       (save-restriction
4830         (message-narrow-to-headers-or-head)
4831         (message-remove-header "Mime-Version")
4832         (goto-char (point-max))
4833         (insert "MIME-Version: 1.0\n")
4834         (when lines
4835           (insert lines))
4836         (setq content-type-p
4837               (or mml-boundary
4838                   (re-search-backward "^Content-Type:" nil t))))
4839       (save-restriction
4840         (message-narrow-to-headers-or-head)
4841         (message-remove-first-header "Content-Type")
4842         (message-remove-first-header "Content-Transfer-Encoding"))
4843       ;; We always make sure that the message has a Content-Type header.
4844       ;; This is because some broken MTAs and MUAs get awfully confused
4845       ;; when confronted with a message with a MIME-Version header and
4846       ;; without a Content-Type header.  For instance, Solaris'
4847       ;; /usr/bin/mail.
4848       (unless content-type-p
4849         (goto-char (point-min))
4850         (re-search-forward "^MIME-Version:")
4851         (forward-line 1)
4852         (insert "Content-Type: text/plain; charset=us-ascii\n")))))
4853
4854 (defun message-read-from-minibuffer (prompt)
4855   "Read from the minibuffer while providing abbrev expansion."
4856   (if (fboundp 'mail-abbrevs-setup)
4857       (let ((mail-abbrev-mode-regexp "")
4858             (minibuffer-setup-hook 'mail-abbrevs-setup)
4859             (minibuffer-local-map message-minibuffer-local-map))
4860         (read-from-minibuffer prompt))
4861     (let ((minibuffer-setup-hook 'mail-abbrev-minibuffer-setup-hook)
4862           (minibuffer-local-map message-minibuffer-local-map))
4863       (read-string prompt))))
4864
4865 (defun message-use-alternative-email-as-from ()
4866   (require 'mail-utils)
4867   (let* ((fields '("To" "Cc"))
4868          (emails
4869           (split-string
4870            (mail-strip-quoted-names
4871             (mapconcat 'message-fetch-reply-field fields ","))
4872            "[ \f\t\n\r\v,]+"))
4873          email)
4874     (while emails
4875       (if (string-match message-alternative-emails (car emails))
4876           (setq email (car emails)
4877                 emails nil))
4878       (pop emails))
4879     (unless (or (not email) (equal email user-mail-address))
4880       (goto-char (point-max))
4881       (insert "From: " email "\n"))))
4882
4883 (defun message-options-get (symbol)
4884   (cdr (assq symbol message-options)))
4885
4886 (defun message-options-set (symbol value)
4887   (let ((the-cons (assq symbol message-options)))
4888     (if the-cons
4889         (if value
4890             (setcdr the-cons value)
4891           (setq message-options (delq the-cons message-options)))
4892       (and value
4893            (push (cons symbol value) message-options))))
4894   value)
4895
4896 (defun message-options-set-recipient ()
4897   (save-restriction
4898     (message-narrow-to-headers-or-head)
4899     (message-options-set 'message-sender
4900                          (mail-strip-quoted-names
4901                           (message-fetch-field "from")))
4902     (message-options-set 'message-recipients
4903                           (mail-strip-quoted-names
4904                            (message-fetch-field "to")))))
4905
4906 (when (featurep 'xemacs)
4907   (require 'messagexmas)
4908   (message-xmas-redefine))
4909
4910 (provide 'message)
4911
4912 (run-hooks 'message-load-hook)
4913
4914 ;; Local Variables:
4915 ;; coding: iso-8859-1
4916 ;; End:
4917
4918 ;;; message.el ends here