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