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