Feedback from t-gnus-6_15-quimby branch.
[elisp/gnus.git-] / lisp / message.el
1 ;;; message.el --- composing mail and news messages
2 ;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002
3 ;;        Free Software Foundation, Inc.
4
5 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
6 ;;      MORIOKA Tomohiko <morioka@jaist.ac.jp>
7 ;;      Shuhei KOBAYASHI <shuhei-k@jaist.ac.jp>
8 ;;      Keiichi Suzuki   <kei-suzu@mail.wbs.ne.jp>
9 ;;      Tatsuya Ichikawa <t-ichi@po.shiojiri.ne.jp>
10 ;;      Katsumi Yamaoka  <yamaoka@jpl.org>
11 ;;      Kiyokazu SUTO    <suto@merry.xmath.ous.ac.jp>
12 ;; Keywords: mail, news, MIME
13
14 ;; This file is part of GNU Emacs.
15
16 ;; GNU Emacs is free software; you can redistribute it and/or modify
17 ;; it under the terms of the GNU General Public License as published by
18 ;; the Free Software Foundation; either version 2, or (at your option)
19 ;; any later version.
20
21 ;; GNU Emacs is distributed in the hope that it will be useful,
22 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
23 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24 ;; GNU General Public License for more details.
25
26 ;; You should have received a copy of the GNU General Public License
27 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
28 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
29 ;; Boston, MA 02111-1307, USA.
30
31 ;;; Commentary:
32
33 ;; This mode provides mail-sending facilities from within Emacs.  It
34 ;; consists mainly of large chunks of code from the sendmail.el,
35 ;; gnus-msg.el and rnewspost.el files.
36
37 ;;; Code:
38
39 (eval-when-compile
40   (require 'cl)
41   (require 'smtp)
42   (defvar gnus-list-identifiers)) ; gnus-sum is required where necessary
43 (eval-and-compile
44   (if (boundp 'MULE)
45       (require 'canlock-om)
46     (require 'canlock)))
47 (require 'mailheader)
48 (require 'nnheader)
49 ;; This is apparently necessary even though things are autoloaded:
50 (if (featurep 'xemacs)
51     (require 'mail-abbrevs))
52 (require 'mime-edit)
53 (eval-when-compile (require 'static))
54
55 ;; Avoid byte-compile warnings.
56 (eval-when-compile
57   (require 'mail-parse)
58   (require 'mml))
59
60 (require 'rfc822)
61 (eval-and-compile
62   (autoload 'sha1 "sha1-el")
63   (autoload 'customize-save-variable "cus-edit"));; for Mule 2.
64
65 (defgroup message '((user-mail-address custom-variable)
66                     (user-full-name custom-variable))
67   "Mail and news message composing."
68   :link '(custom-manual "(message)Top")
69   :group 'mail
70   :group 'news)
71
72 (put 'user-mail-address 'custom-type 'string)
73 (put 'user-full-name 'custom-type 'string)
74
75 (defgroup message-various nil
76   "Various Message Variables"
77   :link '(custom-manual "(message)Various Message Variables")
78   :group 'message)
79
80 (defgroup message-buffers nil
81   "Message Buffers"
82   :link '(custom-manual "(message)Message Buffers")
83   :group 'message)
84
85 (defgroup message-sending nil
86   "Message Sending"
87   :link '(custom-manual "(message)Sending Variables")
88   :group 'message)
89
90 (defgroup message-interface nil
91   "Message Interface"
92   :link '(custom-manual "(message)Interface")
93   :group 'message)
94
95 (defgroup message-forwarding nil
96   "Message Forwarding"
97   :link '(custom-manual "(message)Forwarding")
98   :group 'message-interface)
99
100 (defgroup message-insertion nil
101   "Message Insertion"
102   :link '(custom-manual "(message)Insertion")
103   :group 'message)
104
105 (defgroup message-headers nil
106   "Message Headers"
107   :link '(custom-manual "(message)Message Headers")
108   :group 'message)
109
110 (defgroup message-news nil
111   "Composing News Messages"
112   :group 'message)
113
114 (defgroup message-mail nil
115   "Composing Mail Messages"
116   :group 'message)
117
118 (defgroup message-faces nil
119   "Faces used for message composing."
120   :group 'message
121   :group 'faces)
122
123 (defgroup message-frames nil
124   "Message frames"
125   :group 'message)
126
127 (defcustom message-directory "~/Mail/"
128   "*Directory from which all other mail file variables are derived."
129   :group 'message-various
130   :type 'directory)
131
132 (defcustom message-max-buffers 10
133   "*How many buffers to keep before starting to kill them off."
134   :group 'message-buffers
135   :type 'integer)
136
137 (defcustom message-send-rename-function nil
138   "Function called to rename the buffer after sending it."
139   :group 'message-buffers
140   :type '(choice function (const nil)))
141
142 (defcustom message-fcc-handler-function 'message-output
143   "*A function called to save outgoing articles.
144 This function will be called with the name of the file to store the
145 article in.  The default function is `message-output' which saves in Unix
146 mailbox format."
147   :type '(radio (function-item message-output)
148                 (function :tag "Other"))
149   :group 'message-sending)
150
151 (defcustom message-encode-function 'message-maybe-encode
152   "*A function called to encode messages."
153   :group 'message-sending
154   :type 'function)
155
156 (defcustom message-8bit-encoding-list '(8bit binary)
157   "*8bit encoding type in Content-Transfer-Encoding field."
158   :group 'message-sending
159   :type '(repeat (symbol :tag "Type")))
160
161 (defcustom message-fcc-externalize-attachments nil
162   "If non-nil, attachments are included as external parts in Fcc copies."
163   :type 'boolean
164   :group 'message-sending)
165
166 (defcustom message-courtesy-message
167   "The following message is a courtesy copy of an article\nthat has been posted to %s as well.\n\n"
168   "*This is inserted at the start of a mailed copy of a posted message.
169 If the string contains the format spec \"%s\", the Newsgroups
170 the article has been posted to will be inserted there.
171 If this variable is nil, no such courtesy message will be added."
172   :group 'message-sending
173   :type 'string)
174
175 (defcustom message-ignored-bounced-headers "^\\(Received\\|Return-Path\\):"
176   "*Regexp that matches headers to be removed in resent bounced mail."
177   :group 'message-interface
178   :type 'regexp)
179
180 (defcustom message-bounce-setup-function 'message-bounce-setup-for-mime-edit
181   "Function to setup a re-sending bounced message."
182   :group 'message-sending
183   :type 'function)
184
185 ;;;###autoload
186 (defcustom message-from-style 'default
187   "*Specifies how \"From\" headers look.
188
189 If nil, they contain just the return address like:
190         king@grassland.com
191 If `parens', they look like:
192         king@grassland.com (Elvis Parsley)
193 If `angles', they look like:
194         Elvis Parsley <king@grassland.com>
195
196 Otherwise, most addresses look like `angles', but they look like
197 `parens' if `angles' would need quoting and `parens' would not."
198   :type '(choice (const :tag "simple" nil)
199                  (const parens)
200                  (const angles)
201                  (const default))
202   :group 'message-headers)
203
204 (defcustom message-syntax-checks nil
205   ;; Guess this one shouldn't be easy to customize...
206   "*Controls what syntax checks should not be performed on outgoing posts.
207 To disable checking of long signatures, for instance, add
208  `(signature . disabled)' to this list.
209
210 Don't touch this variable unless you really know what you're doing.
211
212 Checks include `subject-cmsg', `multiple-headers', `sendsys',
213 `message-id', `from', `long-lines', `control-chars', `size',
214 `new-text', `quoting-style', `redirected-followup', `signature',
215 `approved', `sender', `empty', `empty-headers', `message-id', `from',
216 `subject', `shorten-followup-to', `existing-newsgroups',
217 `buffer-file-name', `unchanged', `newsgroups', `reply-to'."
218   :group 'message-news
219   :type '(repeat sexp))                 ; Fixme: improve this
220
221 (defcustom message-required-news-headers
222   '(From Newsgroups Subject Date Message-ID
223          (optional . Organization) Lines
224          (optional . User-Agent))
225   "*Headers to be generated or prompted for when posting an article.
226 RFC977 and RFC1036 require From, Date, Newsgroups, Subject,
227 Message-ID.  Organization, Lines, In-Reply-To, Expires, and
228 User-Agent are optional.  If don't you want message to insert some
229 header, remove it from this list."
230   :group 'message-news
231   :group 'message-headers
232   :type '(repeat sexp))
233
234 (defcustom message-required-mail-headers
235   '(From Subject Date (optional . In-Reply-To) Message-ID Lines
236          (optional . User-Agent))
237   "*Headers to be generated or prompted for when mailing a message.
238 It is recommended that From, Date, To, Subject and Message-ID be
239 included.  Organization, Lines and User-Agent are optional."
240   :group 'message-mail
241   :group 'message-headers
242   :type '(repeat sexp))
243
244 (defcustom message-deletable-headers '(Message-ID Date Lines)
245   "Headers to be deleted if they already exist and were generated by message previously."
246   :group 'message-headers
247   :type 'sexp)
248
249 (defcustom message-ignored-news-headers
250   "^NNTP-Posting-Host:\\|^Xref:\\|^[BGF]cc:\\|^Resent-Fcc:\\|^X-Draft-From:\\|^X-Gnus-Agent-Meta-Information:"
251   "*Regexp of headers to be removed unconditionally before posting."
252   :group 'message-news
253   :group 'message-headers
254   :type 'regexp)
255
256 (defcustom message-ignored-mail-headers
257   "^[GF]cc:\\|^Resent-Fcc:\\|^Xref:\\|^X-Draft-From:\\|^X-Gnus-Agent-Meta-Information:"
258   "*Regexp of headers to be removed unconditionally before mailing."
259   :group 'message-mail
260   :group 'message-headers
261   :type 'regexp)
262
263 (defcustom message-ignored-supersedes-headers "^Path:\\|^Date\\|^NNTP-Posting-Host:\\|^Xref:\\|^Lines:\\|^Received:\\|^X-From-Line:\\|^X-Trace:\\|^X-Complaints-To:\\|Return-Path:\\|^Supersedes:\\|^NNTP-Posting-Date:\\|^X-Trace:\\|^X-Complaints-To:\\|^Cancel-Lock:\\|^Cancel-Key:"
264   "*Header lines matching this regexp will be deleted before posting.
265 It's best to delete old Path and Date headers before posting to avoid
266 any confusion."
267   :group 'message-interface
268   :type 'regexp)
269
270 (defcustom message-supersede-setup-function
271   'message-supersede-setup-for-mime-edit
272   "Function to setup a supersede message."
273   :group 'message-sending
274   :type 'function)
275
276 (defcustom message-subject-re-regexp "^[ \t]*\\([Rr][Ee]:[ \t]*\\)*[ \t]*"
277   "*Regexp matching \"Re: \" in the subject line."
278   :group 'message-various
279   :type 'regexp)
280
281 ;;; Some sender agents encode the whole subject including leading "Re: ".
282 ;;; And if followup agent does not decode it for some reason (e.g. unknown
283 ;;; charset) and just add a new "Re: " in front of the encoded-word, the
284 ;;; result will contain multiple "Re: "'s.
285 (defcustom message-subject-encoded-re-regexp
286   (concat
287    "^[ \t]*"
288    (regexp-quote "=?")
289    "[-!#$%&'*+0-9A-Z^_`a-z{|}~]+" ; charset
290    (regexp-quote "?")
291    "\\("
292    "[Bb]" (regexp-quote "?") ; B encoding
293    "\\(\\(CQk\\|CSA\\|IAk\\|ICA\\)[Jg]\\)*" ; \([ \t][ \t][ \t]\)*
294    "\\("
295    "[Uc][km]U6" ; [Rr][Ee]:
296    "\\|"
297    "\\(C[VX]\\|I[FH]\\)J[Fl]O[g-v]" ; [ \t][Rr][Ee]:
298    "\\|"
299    "\\(CQl\\|CSB\\|IAl\\|ICB\\)[Sy][RZ]T[o-r]" ; [ \t][ \t][Rr][Ee]:
300    "\\)"
301    "\\|"
302    "[Qb]" (regexp-quote "?") ; Q encoding
303    "\\(_\\|=09\\|=20\\)*"
304    "\\([Rr]\\|=[57]2\\)\\([Ee]\\|=[46]5\\)\\(:\\|=3[Aa]\\)"
305    "\\)"
306    )
307   "*Regexp matching \"Re: \" in the subject line.
308 Unlike `message-subject-re-regexp', this regexp matches \"Re: \" within
309 an encoded-word."
310   :group 'message-various
311   :type 'regexp)
312
313 (defcustom message-use-subject-re t
314   "*If t, remove any (buggy) \"Re: \"'s from the subject of the precursor
315 and add a new \"Re: \".  If it is nil, use the subject \"as-is\".  If it
316 is the symbol `guess', try to detect \"Re: \" within an encoded-word."
317   :group 'message-various
318   :type '(choice (const :tag "off" nil)
319                  (const :tag "on" t)
320                  (const guess)))
321
322 ;;;###autoload
323 (defcustom message-signature-separator "^-- *$"
324   "Regexp matching the signature separator."
325   :type 'regexp
326   :group 'message-various)
327
328 (defcustom message-signature-separator-for-insertion "-- \n"
329   "*Signature separator. This value will be inserted as signature separator
330 when composing message. Default value is \"-- \\n\". Notice: Changing this
331 value may go against RFC-1036 and draft-ietf-usefor-article-05.txt. "
332   :type 'string
333   :group 'message-insertion)
334
335 (defcustom message-elide-ellipsis "\n[...]\n\n"
336   "*The string which is inserted for elided text."
337   :type 'string
338   :group 'message-various)
339
340 (defcustom message-interactive nil
341   "Non-nil means when sending a message wait for and display errors.
342 nil means let mailer mail back a message to report errors."
343   :group 'message-sending
344   :group 'message-mail
345   :type 'boolean)
346
347 (defcustom message-generate-new-buffers 'unique
348   "*Non-nil means create a new message buffer whenever `message-setup' is called.
349 If this is a function, call that function with three parameters:  The type,
350 the to address and the group name.  (Any of these may be nil.)  The function
351 should return the new buffer name."
352   :group 'message-buffers
353   :type '(choice (const :tag "off" nil)
354                  (const :tag "unique" unique)
355                  (const :tag "unsent" unsent)
356                  (function fun)))
357
358 (defcustom message-kill-buffer-on-exit nil
359   "*Non-nil means that the message buffer will be killed after sending a message."
360   :group 'message-buffers
361   :type 'boolean)
362
363 (defcustom message-kill-buffer-query-function 'yes-or-no-p
364   "*Function used to prompt user whether to kill the message buffer.  If
365 it is t, the buffer will be killed unconditionally."
366   :type '(radio (function-item yes-or-no-p)
367                 (function-item y-or-n-p)
368                 (function-item nnheader-Y-or-n-p)
369                 (function :tag "Other" t))
370   :group 'message-buffers)
371
372 (defcustom message-kill-buffer-and-remove-file t
373   "*Non-nil means that the associated file will be removed before
374 removing the message buffer.  However, it is treated as nil when the
375 command `message-mimic-kill-buffer' is used."
376   :group 'message-buffers
377   :type 'boolean)
378
379 (eval-when-compile
380   (defvar gnus-local-organization))
381 (defcustom message-user-organization
382   (or (and (boundp 'gnus-local-organization)
383            (stringp gnus-local-organization)
384            gnus-local-organization)
385       (getenv "ORGANIZATION")
386       t)
387   "*String to be used as an Organization header.
388 If t, use `message-user-organization-file'."
389   :group 'message-headers
390   :type '(choice string
391                  (const :tag "consult file" t)))
392
393 ;;;###autoload
394 (defcustom message-user-organization-file "/usr/lib/news/organization"
395   "*Local news organization file."
396   :type 'file
397   :group 'message-headers)
398
399 (defcustom message-forward-start-separator
400   (concat (mime-make-tag "message" "rfc822") "\n")
401   "*Delimiter inserted before forwarded messages."
402   :group 'message-forwarding
403   :type 'string)
404
405 (defcustom message-forward-end-separator
406   (concat (mime-make-tag "text" "plain") "\n")
407   "*Delimiter inserted after forwarded messages."
408   :group 'message-forwarding
409   :type 'string)
410
411 (defcustom message-included-forward-headers
412   "^From:\\|^Newsgroups:\\|^Subject:\\|^Date:\\|^\\(Mail-\\)?Followup-To:\\|^\\(Mail-\\)?Reply-To:\\|^Mail-Copies-To:\\|^Organization:\\|^Summary:\\|^Keywords:\\|^To:\\|^Cc:\\|^Posted-To:\\|^Apparently-To:\\|^Gnus-Warning:\\|^Resent-\\|^Message-ID:\\|^References:\\|^Content-\\|^MIME-Version:"
413   "*Regexp matching headers to be included in forwarded messages."
414   :group 'message-forwarding
415   :type 'regexp)
416
417 (defcustom message-make-forward-subject-function
418   'message-forward-subject-author-subject
419   "*List of functions called to generate subject headers for forwarded messages.
420 The subject generated by the previous function is passed into each
421 successive function.
422
423 The provided functions are:
424
425 * `message-forward-subject-author-subject' (Source of article (author or
426       newsgroup)), in brackets followed by the subject
427 * `message-forward-subject-fwd' (Subject of article with 'Fwd:' prepended
428       to it."
429   :group 'message-forwarding
430   :type '(radio (function-item message-forward-subject-author-subject)
431                 (function-item message-forward-subject-fwd)
432                 (repeat :tag "List of functions" function)))
433
434 (defcustom message-forward-as-mime t
435   "*If non-nil, forward messages as an inline/rfc822 MIME section.  Otherwise, directly inline the old message in the forwarded message."
436   :version "21.1"
437   :group 'message-forwarding
438   :type 'boolean)
439
440 (defcustom message-forward-show-mml t
441   "*If non-nil, forward messages are shown as mml.  Otherwise, forward messages are unchanged."
442   :version "21.1"
443   :group 'message-forwarding
444   :type 'boolean)
445
446 (defcustom message-forward-before-signature t
447   "*If non-nil, put forwarded message before signature, else after."
448   :group 'message-forwarding
449   :type 'boolean)
450
451 (defcustom message-wash-forwarded-subjects nil
452   "*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."
453   :group 'message-forwarding
454   :type 'boolean)
455
456 (defcustom message-ignored-resent-headers "^Return-receipt\\|^X-Gnus\\|^Gnus-Warning:"
457   "*All headers that match this regexp will be deleted when resending a message."
458   :group 'message-interface
459   :type 'regexp)
460
461 (defcustom message-forward-ignored-headers "^Content-Transfer-Encoding:\\|^X-Gnus"
462   "*All headers that match this regexp will be deleted when forwarding a message."
463   :version "21.1"
464   :group 'message-forwarding
465   :type '(choice (const :tag "None" nil)
466                  regexp))
467
468 (defcustom message-ignored-cited-headers "."
469   "*Delete these headers from the messages you yank."
470   :group 'message-insertion
471   :type 'regexp)
472
473 (defcustom message-cite-prefix-regexp
474   (if (string-match "[[:digit:]]" "1") ;; support POSIX?
475       "\\([ \t]*[-_.[:word:]]+>+\\|[ \t]*[]>~|:}+]\\)+"
476     ;; ?-, ?_ or ?. MUST NOT be in syntax entry w.
477     "\\([ \t]*\\(\\w\\|[-_.]\\)+>+\\|[ \t]*[]>~|:}+]\\)+")
478   "*Regexp matching the longest possible citation prefix on a line."
479   :group 'message-insertion
480   :type 'regexp)
481
482 (defcustom message-cancel-message "I am canceling my own article.\n"
483   "Message to be inserted in the cancel message."
484   :group 'message-interface
485   :type 'string)
486
487 ;; Useful to set in site-init.el
488 ;;;###autoload
489 (defcustom message-send-mail-function 'message-send-mail-with-sendmail
490   "Function to call to send the current buffer as mail.
491 The headers should be delimited by a line whose contents match the
492 variable `mail-header-separator'.
493
494 Valid values include `message-send-mail-with-sendmail' (the default),
495 `message-send-mail-with-mh', `message-send-mail-with-qmail',
496 `message-send-mail-with-smtp', `smtpmail-send-it' and `feedmail-send-it'.
497
498 See also `send-mail-function'."
499   :type '(radio (function-item message-send-mail-with-sendmail)
500                 (function-item message-send-mail-with-mh)
501                 (function-item message-send-mail-with-qmail)
502                 (function-item message-send-mail-with-smtp)
503                 (function-item smtpmail-send-it)
504                 (function-item feedmail-send-it)
505                 (function :tag "Other"))
506   :group 'message-sending
507   :group 'message-mail)
508
509 ;; 1997-09-29 by MORIOKA Tomohiko
510 (defcustom message-send-news-function 'message-send-news-with-gnus
511   "Function to call to send the current buffer as news.
512 The headers should be delimited by a line whose contents match the
513 variable `mail-header-separator'."
514   :group 'message-sending
515   :group 'message-news
516   :type 'function)
517
518 (defcustom message-reply-to-function nil
519   "If non-nil, function that should return a list of headers.
520 This function should pick out addresses from the To, Cc, and From headers
521 and respond with new To and Cc headers."
522   :group 'message-interface
523   :type '(choice function (const nil)))
524
525 (defcustom message-wide-reply-to-function nil
526   "If non-nil, function that should return a list of headers.
527 This function should pick out addresses from the To, Cc, and From headers
528 and respond with new To and Cc headers."
529   :group 'message-interface
530   :type '(choice function (const nil)))
531
532 (defcustom message-followup-to-function nil
533   "If non-nil, function that should return a list of headers.
534 This function should pick out addresses from the To, Cc, and From headers
535 and respond with new To and Cc headers."
536   :group 'message-interface
537   :type '(choice function (const nil)))
538
539 (defcustom message-use-followup-to 'ask
540   "*Specifies what to do with Followup-To header.
541 If nil, always ignore the header.  If it is t, use its value, but
542 query before using the \"poster\" value.  If it is the symbol `ask',
543 always query the user whether to use the value.  If it is the symbol
544 `use', always use the value."
545   :group 'message-interface
546   :type '(choice (const :tag "ignore" nil)
547                  (const :tag "use & query" t)
548                  (const :tag "maybe" t)
549                  (const :tag "always" use)
550                  (const :tag "ask" ask)))
551
552 (defcustom message-use-mail-copies-to 'ask
553   "*Specifies what to do with Mail-Copies-To header.
554 If nil, always ignore the header.  If it is t, use its value, but
555 query before using the value other than \"always\" or \"never\".
556 If it is the symbol `ask', always query the user whether to use
557 the value.  If it is the symbol `use', always use the value."
558   :group 'message-interface
559   :type '(choice (const :tag "ignore" nil)
560                  (const :tag "maybe" t)
561                  (const :tag "always" use)
562                  (const :tag "ask" ask)))
563
564 ;;; XXX: 'ask and 'use are not implemented yet.
565 (defcustom message-use-mail-reply-to 'ask
566   "*Specifies what to do with Mail-Reply-To/Reply-To header.
567 If nil, always ignore the header.  If it is t or the symbol `use', use
568 its value.  If it is the symbol `ask', always query the user whether to
569 use the value.  Note that if \"Reply-To\" is marked as \"broken\", its value
570 is never used."
571   :group 'message-interface
572   :type '(choice (const :tag "ignore" nil)
573                  (const :tag "maybe" t)
574                  (const :tag "always" use)
575                  (const :tag "ask" ask)))
576
577 (defcustom message-use-mail-followup-to 'use
578   "*Specifies what to do with Mail-Followup-To header.
579 If nil, always ignore the header.  If it is the symbol `ask', always
580 query the user whether to use the value.  If it is t or the symbol
581 `use', always use the value."
582   :group 'message-interface
583   :type '(choice (const :tag "ignore" nil)
584                  (const :tag "maybe" t)
585                  (const :tag "always" use)
586                  (const :tag "ask" ask)))
587
588 (defcustom message-subscribed-address-functions nil
589   "*Specifies functions for determining list subscription.
590 If nil, do not attempt to determine list subscribtion with functions.
591 If non-nil, this variable contains a list of functions which return
592 regular expressions to match lists.  These functions can be used in
593 conjunction with `message-subscribed-regexps' and
594 `message-subscribed-addresses'."
595   :group 'message-interface
596   :type '(repeat sexp))
597
598 (defcustom message-subscribed-address-file nil
599   "*A file containing addresses the user is subscribed to.
600 If nil, do not look at any files to determine list subscriptions.  If
601 non-nil, each line of this file should be a mailing list address."
602   :group 'message-interface
603   :type 'string)
604
605 (defcustom message-subscribed-addresses nil
606   "*Specifies a list of addresses the user is subscribed to.
607 If nil, do not use any predefined list subscriptions.  This list of
608 addresses can be used in conjuction with
609 `message-subscribed-address-functions' and `message-subscribed-regexps'."
610   :group 'message-interface
611   :type '(repeat string))
612
613 (defcustom message-subscribed-regexps nil
614   "*Specifies a list of addresses the user is subscribed to.
615 If nil, do not use any predefined list subscriptions.  This list of
616 regular expressions can be used in conjuction with
617 `message-subscribed-address-functions' and `message-subscribed-addresses'."
618   :group 'message-interface
619   :type '(repeat regexp))
620
621 (defcustom message-allow-no-recipients 'ask
622   "Specifies what to do when there are no recipients other than Gcc/Fcc.
623 If it is the symbol `always', the posting is allowed.  If it is the
624 symbol `never', the posting is not allowed.  If it is the symbol
625 `ask', you are prompted."
626   :group 'message-interface
627   :type '(choice (const always)
628                  (const never)
629                  (const ask)))
630
631 (defcustom message-sendmail-f-is-evil nil
632   "*Non-nil means don't add \"-f username\" to the sendmail command line.
633 Doing so would be even more evil than leaving it out."
634   :group 'message-sending
635   :type 'boolean)
636
637 ;; qmail-related stuff
638 (defcustom message-qmail-inject-program "/var/qmail/bin/qmail-inject"
639   "Location of the qmail-inject program."
640   :group 'message-sending
641   :type 'file)
642
643 (defcustom message-qmail-inject-args nil
644   "Arguments passed to qmail-inject programs.
645 This should be a list of strings, one string for each argument.
646
647 For e.g., if you wish to set the envelope sender address so that bounces
648 go to the right place or to deal with listserv's usage of that address, you
649 might set this variable to '(\"-f\" \"you@some.where\")."
650   :group 'message-sending
651   :type '(repeat string))
652
653 (defvar message-cater-to-broken-inn t
654   "Non-nil means Gnus should not fold the `References' header.
655 Folding `References' makes ancient versions of INN create incorrect
656 NOV lines.")
657
658 (eval-when-compile
659   (defvar gnus-post-method)
660   (defvar gnus-select-method))
661 (defcustom message-post-method
662   (cond ((and (boundp 'gnus-post-method)
663               (listp gnus-post-method)
664               gnus-post-method)
665          gnus-post-method)
666         ((boundp 'gnus-select-method)
667          gnus-select-method)
668         (t '(nnspool "")))
669   "*Method used to post news.
670 Note that when posting from inside Gnus, for instance, this
671 variable isn't used."
672   :group 'message-news
673   :group 'message-sending
674   ;; This should be the `gnus-select-method' widget, but that might
675   ;; create a dependence to `gnus.el'.
676   :type 'sexp)
677
678 (defcustom message-generate-headers-first nil
679   "*If non-nil, generate all required headers before composing.
680 The variables `message-required-news-headers' and
681 `message-required-mail-headers' specify which headers to generate.
682
683 Note that the variable `message-deletable-headers' specifies headers which
684 are to be deleted and then re-generated before sending, so this variable
685 will not have a visible effect for those headers."
686   :group 'message-headers
687   :type 'boolean)
688
689 (defcustom message-setup-hook '(turn-on-mime-edit)
690   "Normal hook, run each time a new outgoing message is initialized.
691 The function `message-setup' runs this hook."
692   :group 'message-various
693   :type 'hook)
694
695 (defcustom message-cancel-hook nil
696   "Hook run when cancelling articles."
697   :group 'message-various
698   :type 'hook)
699
700 (defcustom message-signature-setup-hook nil
701   "Normal hook, run each time a new outgoing message is initialized.
702 It is run after the headers have been inserted and before
703 the signature is inserted."
704   :group 'message-various
705   :type 'hook)
706
707 (defcustom message-bounce-setup-hook nil
708   "Normal hook, run each time a re-sending bounced message is initialized.
709 The function `message-bounce' runs this hook."
710   :group 'message-various
711   :type 'hook)
712
713 (defcustom message-supersede-setup-hook nil
714   "Normal hook, run each time a supersede message is initialized.
715 The function `message-supersede' runs this hook."
716   :group 'message-various
717   :type 'hook)
718
719 (defcustom message-mode-hook nil
720   "Hook run in message mode buffers."
721   :group 'message-various
722   :type 'hook)
723
724 (defcustom message-header-hook '((lambda () (eword-encode-header t)))
725   "Hook run in a message mode buffer narrowed to the headers."
726   :group 'message-various
727   :type 'hook)
728
729 (defcustom message-header-setup-hook nil
730   "Hook called narrowed to the headers when setting up a message buffer."
731   :group 'message-various
732   :type 'hook)
733
734 (defcustom message-minibuffer-local-map
735   (let ((map (make-sparse-keymap 'message-minibuffer-local-map)))
736     (set-keymap-parent map minibuffer-local-map)
737     map)
738   "Keymap for `message-read-from-minibuffer'.")
739
740 ;;;###autoload
741 (defcustom message-citation-line-function 'message-insert-citation-line
742   "*Function called to insert the \"Whomever writes:\" line.
743
744 Note that Gnus provides a feature where the reader can click on
745 `writes:' to hide the cited text.  If you change this line too much,
746 people who read your message will have to change their Gnus
747 configuration.  See the variable `gnus-cite-attribution-suffix'."
748   :type 'function
749   :group 'message-insertion)
750
751 ;;;###autoload
752 (defcustom message-yank-prefix "> "
753   "*Prefix inserted on the lines of yanked messages.
754 Fix `message-cite-prefix-regexp' if it is set to an abnormal value.
755 See also `message-yank-cited-prefix'."
756   :type 'string
757   :group 'message-insertion)
758
759 (defcustom message-yank-add-new-references t
760   "Non-nil means new IDs will be added to \"References\" field when an
761 article is yanked by the command `message-yank-original' interactively.
762 If it is a symbol `message-id-only', only an ID from \"Message-ID\" field
763 is used, otherwise IDs extracted from \"References\", \"In-Reply-To\" and
764 \"Message-ID\" fields are used."
765   :type '(radio (const :tag "Do not add anything" nil)
766                 (const :tag "From Message-Id, References and In-Reply-To fields" t)
767                 (const :tag "From only Message-Id field." message-id-only))
768   :group 'message-insertion)
769
770 (defcustom message-list-references-add-position nil
771   "Integer value means position for adding to \"References\" field when
772 an article is yanked by the command `message-yank-original' interactively."
773   :type '(radio (const :tag "Add to last" nil)
774                 (integer :tag "Position from last ID"))
775   :group 'message-insertion)
776
777 (defcustom message-yank-cited-prefix ">"
778   "*Prefix inserted on cited or empty lines of yanked messages.
779 Fix `message-cite-prefix-regexp' if it is set to an abnormal value.
780 See also `message-yank-prefix'."
781   :type 'string
782   :group 'message-insertion)
783
784 (defcustom message-indentation-spaces 3
785   "*Number of spaces to insert at the beginning of each cited line.
786 Used by `message-yank-original' via `message-yank-cite'."
787   :group 'message-insertion
788   :type 'integer)
789
790 ;;;###autoload
791 (defcustom message-cite-function 'message-cite-original
792   "*Function for citing an original message.
793 Predefined functions include `message-cite-original' and
794 `message-cite-original-without-signature'.
795 Note that `message-cite-original' uses `mail-citation-hook' if that is non-nil."
796   :type '(radio (function-item message-cite-original)
797                 (function-item message-cite-original-without-signature)
798                 (function-item mu-cite-original)
799                 (function-item sc-cite-original)
800                 (function :tag "Other"))
801   :group 'message-insertion)
802
803 ;;;###autoload
804 (defcustom message-suspend-font-lock-when-citing nil
805   "Non-nil means suspend font-lock'ing while citing an original message.
806 Some lazy demand-driven fontification tools (or Emacs itself) have a
807 bug that they often miss a buffer to be fontified.  It will mostly
808 occur when Emacs prompts user for any inputs in the minibuffer.
809 Setting this option to non-nil may help you to avoid unpleasant errors
810 even if it is an add-hoc expedient."
811   :type 'boolean
812   :group 'message-insertion)
813
814 ;;;###autoload
815 (defcustom message-indent-citation-function 'message-indent-citation
816   "*Function for modifying a citation just inserted in the mail buffer.
817 This can also be a list of functions.  Each function can find the
818 citation between (point) and (mark t).  And each function should leave
819 point and mark around the citation text as modified."
820   :type 'function
821   :group 'message-insertion)
822
823 (defvar message-abbrevs-loaded nil)
824
825 ;;;###autoload
826 (defcustom message-signature t
827   "*String to be inserted at the end of the message buffer.
828 If t, the `message-signature-file' file will be inserted instead.
829 If a function, the result from the function will be used instead.
830 If a form, the result from the form will be used instead."
831   :type 'sexp
832   :group 'message-insertion)
833
834 ;;;###autoload
835 (defcustom message-signature-file "~/.signature"
836   "*Name of file containing the text inserted at end of message buffer.
837 Ignored if the named file doesn't exist.
838 If nil, don't insert a signature."
839   :type '(choice file (const :tags "None" nil))
840   :group 'message-insertion)
841
842 (defcustom message-distribution-function nil
843   "*Function called to return a Distribution header."
844   :group 'message-news
845   :group 'message-headers
846   :type '(choice function (const nil)))
847
848 (defcustom message-expires 14
849   "Number of days before your article expires."
850   :group 'message-news
851   :group 'message-headers
852   :link '(custom-manual "(message)News Headers")
853   :type 'integer)
854
855 (defcustom message-user-path nil
856   "If nil, use the NNTP server name in the Path header.
857 If stringp, use this; if non-nil, use no host name (user name only)."
858   :group 'message-news
859   :group 'message-headers
860   :link '(custom-manual "(message)News Headers")
861   :type '(choice (const :tag "nntp" nil)
862                  (string :tag "name")
863                  (sexp :tag "none" :format "%t" t)))
864
865 (defvar message-reply-buffer nil)
866 (defvar message-reply-headers nil)
867 (defvar message-sent-message-via nil)
868 (defvar message-checksum nil)
869 (defvar message-send-actions nil
870   "A list of actions to be performed upon successful sending of a message.")
871 (defvar message-exit-actions nil
872   "A list of actions to be performed upon exiting after sending a message.")
873 (defvar message-kill-actions nil
874   "A list of actions to be performed before killing a message buffer.")
875 (defvar message-postpone-actions nil
876   "A list of actions to be performed after postponing a message.")
877 (defvar message-original-frame nil)
878 (defvar message-parameter-alist nil)
879 (defvar message-startup-parameter-alist nil)
880
881 (define-widget 'message-header-lines 'text
882   "All header lines must be LFD terminated."
883   :format "%{%t%}:%n%v"
884   :valid-regexp "^\\'"
885   :error "All header lines must be newline terminated")
886
887 (defcustom message-default-headers ""
888   "*A string containing header lines to be inserted in outgoing messages.
889 It is inserted before you edit the message, so you can edit or delete
890 these lines."
891   :group 'message-headers
892   :type 'message-header-lines)
893
894 (defcustom message-default-mail-headers ""
895   "*A string of header lines to be inserted in outgoing mails."
896   :group 'message-headers
897   :group 'message-mail
898   :type 'message-header-lines)
899
900 (defcustom message-default-news-headers ""
901   "*A string of header lines to be inserted in outgoing news articles."
902   :group 'message-headers
903   :group 'message-news
904   :type 'message-header-lines)
905
906 ;; Note: could use /usr/ucb/mail instead of sendmail;
907 ;; options -t, and -v if not interactive.
908 (defcustom message-mailer-swallows-blank-line
909   (if (and (string-match "sparc-sun-sunos\\(\\'\\|[^5]\\)"
910                          system-configuration)
911            (file-readable-p "/etc/sendmail.cf")
912            (let ((buffer (get-buffer-create " *temp*")))
913              (unwind-protect
914                  (save-excursion
915                    (set-buffer buffer)
916                    (insert-file-contents "/etc/sendmail.cf")
917                    (goto-char (point-min))
918                    (let ((case-fold-search nil))
919                      (re-search-forward "^OR\\>" nil t)))
920                (kill-buffer buffer))))
921       ;; According to RFC822, "The field-name must be composed of printable
922       ;; ASCII characters (i. e., characters that have decimal values between
923       ;; 33 and 126, except colon)", i. e., any chars except ctl chars,
924       ;; space, or colon.
925       '(looking-at "[ \t]\\|[][!\"#$%&'()*+,-./0-9;<=>?@A-Z\\\\^_`a-z{|}~]+:"))
926   "*Set this non-nil if the system's mailer runs the header and body together.
927 \(This problem exists on Sunos 4 when sendmail is run in remote mode.)
928 The value should be an expression to test whether the problem will
929 actually occur."
930   :group 'message-sending
931   :type 'sexp)
932
933 ;;; XXX: This symbol is overloaded!  See below.
934 (defvar message-user-agent nil
935   "String of the form of PRODUCT/VERSION.  Used for User-Agent header field.")
936
937 (static-when (boundp 'MULE)
938   (require 'reporter));; `define-mail-user-agent' is here.
939
940 ;;;###autoload
941 (define-mail-user-agent 'message-user-agent
942   'message-mail 'message-send-and-exit
943   'message-kill-buffer 'message-send-hook)
944
945 (defvar message-mh-deletable-headers '(Message-ID Date Lines Sender)
946   "If non-nil, delete the deletable headers before feeding to mh.")
947
948 (defvar message-send-method-alist
949   '((news message-news-p message-send-via-news)
950     (mail message-mail-p message-send-via-mail))
951   "Alist of ways to send outgoing messages.
952 Each element has the form
953
954   \(TYPE PREDICATE FUNCTION)
955
956 where TYPE is a symbol that names the method; PREDICATE is a function
957 called without any parameters to determine whether the message is
958 a message of type TYPE; and FUNCTION is a function to be called if
959 PREDICATE returns non-nil.  FUNCTION is called with one parameter --
960 the prefix.")
961
962 (defcustom message-mail-alias-type 'abbrev
963   "*What alias expansion type to use in Message buffers.
964 The default is `abbrev', which uses mailabbrev.  nil switches
965 mail aliases off."
966   :group 'message
967   :link '(custom-manual "(message)Mail Aliases")
968   :type '(choice (const :tag "Use Mailabbrev" abbrev)
969                  (const :tag "No expansion" nil)))
970
971 (defcustom message-auto-save-directory
972   (file-name-as-directory (nnheader-concat message-directory "drafts"))
973   "*Directory where Message auto-saves buffers if Gnus isn't running.
974 If nil, Message won't auto-save."
975   :group 'message-buffers
976   :type '(choice directory (const :tag "Don't auto-save" nil)))
977
978 (defcustom message-buffer-naming-style 'unique
979   "*The way new message buffers are named.
980 Valid valued are `unique' and `unsent'."
981   :version "21.1"
982   :group 'message-buffers
983   :type '(choice (const :tag "unique" unique)
984                  (const :tag "unsent" unsent)))
985
986 (defcustom message-default-charset
987   (and (featurep 'xemacs) (not (featurep 'mule)) 'iso-8859-1)
988   "Default charset used in non-MULE XEmacsen."
989   :version "21.1"
990   :group 'message
991   :type 'symbol)
992
993 (defcustom message-dont-reply-to-names
994   (and (boundp 'rmail-dont-reply-to-names) rmail-dont-reply-to-names)
995   "*A regexp specifying addresses to prune when doing wide replies.
996 A value of nil means exclude your own user name only."
997   :version "21.1"
998   :group 'message
999   :type '(choice (const :tag "Yourself" nil)
1000                  regexp))
1001
1002 (defvar message-shoot-gnksa-feet nil
1003   "*A list of GNKSA feet you are allowed to shoot.
1004 Gnus gives you all the opportunity you could possibly want for
1005 shooting yourself in the foot.  Also, Gnus allows you to shoot the
1006 feet of Good Net-Keeping Seal of Approval. The following are foot
1007 candidates:
1008 `empty-article'     Allow you to post an empty article;
1009 `quoted-text-only'  Allow you to post quoted text only;
1010 `multiple-copies'   Allow you to post multiple copies.")
1011 ;; `cancel-messages'   Allow you to cancel or supersede others' messages.
1012
1013 (defsubst message-gnksa-enable-p (feature)
1014   (or (not (listp message-shoot-gnksa-feet))
1015       (memq feature message-shoot-gnksa-feet)))
1016
1017 ;;; Internal variables.
1018 ;;; Well, not really internal.
1019
1020 (defvar message-mode-syntax-table
1021   (let ((table (copy-syntax-table text-mode-syntax-table)))
1022     (modify-syntax-entry ?% ". " table)
1023     (modify-syntax-entry ?> ". " table)
1024     (modify-syntax-entry ?< ". " table)
1025     table)
1026   "Syntax table used while in Message mode.")
1027
1028 (defface message-header-to-face
1029   '((((class color)
1030       (background dark))
1031      (:foreground "green2" :bold t))
1032     (((class color)
1033       (background light))
1034      (:foreground "MidnightBlue" :bold t))
1035     (t
1036      (:bold t :italic t)))
1037   "Face used for displaying From headers."
1038   :group 'message-faces)
1039
1040 (defface message-header-cc-face
1041   '((((class color)
1042       (background dark))
1043      (:foreground "green4" :bold t))
1044     (((class color)
1045       (background light))
1046      (:foreground "MidnightBlue"))
1047     (t
1048      (:bold t)))
1049   "Face used for displaying Cc headers."
1050   :group 'message-faces)
1051
1052 (defface message-header-subject-face
1053   '((((class color)
1054       (background dark))
1055      (:foreground "green3"))
1056     (((class color)
1057       (background light))
1058      (:foreground "navy blue" :bold t))
1059     (t
1060      (:bold t)))
1061   "Face used for displaying subject headers."
1062   :group 'message-faces)
1063
1064 (defface message-header-newsgroups-face
1065   '((((class color)
1066       (background dark))
1067      (:foreground "yellow" :bold t :italic t))
1068     (((class color)
1069       (background light))
1070      (:foreground "blue4" :bold t :italic t))
1071     (t
1072      (:bold t :italic t)))
1073   "Face used for displaying newsgroups headers."
1074   :group 'message-faces)
1075
1076 (defface message-header-other-face
1077   '((((class color)
1078       (background dark))
1079      (:foreground "#b00000"))
1080     (((class color)
1081       (background light))
1082      (:foreground "steel blue"))
1083     (t
1084      (:bold t :italic t)))
1085   "Face used for displaying newsgroups headers."
1086   :group 'message-faces)
1087
1088 (defface message-header-name-face
1089   '((((class color)
1090       (background dark))
1091      (:foreground "DarkGreen"))
1092     (((class color)
1093       (background light))
1094      (:foreground "cornflower blue"))
1095     (t
1096      (:bold t)))
1097   "Face used for displaying header names."
1098   :group 'message-faces)
1099
1100 (defface message-header-xheader-face
1101   '((((class color)
1102       (background dark))
1103      (:foreground "blue"))
1104     (((class color)
1105       (background light))
1106      (:foreground "blue"))
1107     (t
1108      (:bold t)))
1109   "Face used for displaying X-Header headers."
1110   :group 'message-faces)
1111
1112 (defface message-separator-face
1113   '((((class color)
1114       (background dark))
1115      (:foreground "blue3"))
1116     (((class color)
1117       (background light))
1118      (:foreground "brown"))
1119     (t
1120      (:bold t)))
1121   "Face used for displaying the separator."
1122   :group 'message-faces)
1123
1124 (defface message-cited-text-face
1125   '((((class color)
1126       (background dark))
1127      (:foreground "red"))
1128     (((class color)
1129       (background light))
1130      (:foreground "red"))
1131     (t
1132      (:bold t)))
1133   "Face used for displaying cited text names."
1134   :group 'message-faces)
1135
1136 (defface message-mml-face
1137   '((((class color)
1138       (background dark))
1139      (:foreground "ForestGreen"))
1140     (((class color)
1141       (background light))
1142      (:foreground "ForestGreen"))
1143     (t
1144      (:bold t)))
1145   "Face used for displaying MML."
1146   :group 'message-faces)
1147
1148 (defvar message-font-lock-keywords
1149   (let ((content "[ \t]*\\(.+\\(\n[ \t].*\\)*\\)\n?"))
1150     `((,(concat "^\\([Tt]o:\\)" content)
1151        (1 'message-header-name-face)
1152        (2 'message-header-to-face nil t))
1153       (,(concat "^\\([GBF]?[Cc][Cc]:\\|[Rr]eply-[Tt]o:\\|"
1154                 "[Mm]ail-[Cc]opies-[Tt]o:\\|"
1155                 "[Mm]ail-[Rr]eply-[Tt]o:\\|"
1156                 "[Mm]ail-[Ff]ollowup-[Tt]o:\\)" content)
1157        (1 'message-header-name-face)
1158        (2 'message-header-cc-face nil t))
1159       (,(concat "^\\([Ss]ubject:\\)" content)
1160        (1 'message-header-name-face)
1161        (2 'message-header-subject-face nil t))
1162       (,(concat "^\\([Nn]ewsgroups:\\|Followup-[Tt]o:\\)" content)
1163        (1 'message-header-name-face)
1164        (2 'message-header-newsgroups-face nil t))
1165       (,(concat "^\\([A-Z][^: \n\t]+:\\)" content)
1166        (1 'message-header-name-face)
1167        (2 'message-header-other-face nil t))
1168       (,(concat "^\\(X-[A-Za-z0-9-]+\\|In-Reply-To\\):" content)
1169        (1 'message-header-name-face)
1170        (2 'message-header-name-face))
1171       ,@(if (and mail-header-separator
1172                  (not (equal mail-header-separator "")))
1173             `((,(concat "^\\(" (regexp-quote mail-header-separator) "\\)$")
1174                1 'message-separator-face))
1175           nil)
1176       (,(concat "^\\(" message-cite-prefix-regexp "\\).*")
1177        (0 'message-cited-text-face))
1178       (,mime-edit-tag-regexp
1179        (0 'message-mml-face))))
1180   "Additional expressions to highlight in Message mode.")
1181
1182 ;; XEmacs does it like this.  For Emacs, we have to set the
1183 ;; `font-lock-defaults' buffer-local variable.
1184 (put 'message-mode 'font-lock-defaults '(message-font-lock-keywords t))
1185
1186 (defvar message-face-alist
1187   '((bold . bold-region)
1188     (underline . underline-region)
1189     (default . (lambda (b e)
1190                  (unbold-region b e)
1191                  (ununderline-region b e))))
1192   "Alist of mail and news faces for facemenu.
1193 The cdr of ech entry is a function for applying the face to a region.")
1194
1195 (defcustom message-send-hook nil
1196   "Hook run before sending messages."
1197   :group 'message-various
1198   :options '(ispell-message)
1199   :type 'hook)
1200
1201 (defcustom message-send-mail-hook nil
1202   "Hook run before sending mail messages."
1203   :group 'message-various
1204   :type 'hook)
1205
1206 (defcustom message-send-news-hook nil
1207   "Hook run before sending news messages."
1208   :group 'message-various
1209   :type 'hook)
1210
1211 (defcustom message-sent-hook nil
1212   "Hook run after sending messages."
1213   :group 'message-various
1214   :type 'hook)
1215
1216 (defcustom message-use-multi-frames nil
1217   "Make new frame when sending messages."
1218   :group 'message-frames
1219   :type 'boolean)
1220
1221 (defcustom message-delete-frame-on-exit nil
1222   "Delete frame after sending messages."
1223   :group 'message-frames
1224   :type '(choice (const :tag "off" nil)
1225                  (const :tag "always" t)
1226                  (const :tag "ask" ask)))
1227
1228 (defvar message-draft-coding-system
1229   (cond
1230    ((boundp 'MULE) '*junet*)
1231    ((not (fboundp 'find-coding-system)) nil)
1232    ((find-coding-system 'emacs-mule)
1233     (if (memq system-type '(windows-nt ms-dos ms-windows))
1234         'emacs-mule-dos 'emacs-mule))
1235    ((find-coding-system 'escape-quoted) 'escape-quoted)
1236    ((find-coding-system 'no-conversion) 'no-conversion)
1237    (t nil))
1238   "Coding system to compose mail.")
1239
1240 (defcustom message-send-mail-partially-limit 1000000
1241   "The limitation of messages sent as message/partial.
1242 The lower bound of message size in characters, beyond which the message
1243 should be sent in several parts.  If it is nil, the size is unlimited."
1244   :version "21.1"
1245   :group 'message-buffers
1246   :type '(choice (const :tag "unlimited" nil)
1247                  (integer 1000000)))
1248
1249 (defcustom message-alternative-emails nil
1250   "A regexp to match the alternative email addresses.
1251 The first matched address (not primary one) is used in the From field."
1252   :group 'message-headers
1253   :type '(choice (const :tag "Always use primary" nil)
1254                  regexp))
1255
1256 (defcustom message-mail-user-agent nil
1257   "Like `mail-user-agent'.
1258 Except if it is nil, use Gnus native MUA; if it is t, use
1259 `mail-user-agent'."
1260   :type '(radio (const :tag "Gnus native"
1261                        :format "%t\n"
1262                        nil)
1263                 (const :tag "`mail-user-agent'"
1264                        :format "%t\n"
1265                        t)
1266                 (function-item :tag "Default Emacs mail"
1267                                :format "%t\n"
1268                                sendmail-user-agent)
1269                 (function-item :tag "Emacs interface to MH"
1270                                :format "%t\n"
1271                                mh-e-user-agent)
1272                 (function :tag "Other"))
1273   :version "21.1"
1274   :group 'message)
1275
1276 (defcustom message-wide-reply-confirm-recipients nil
1277   "Whether to confirm a wide reply to multiple email recipients.
1278 If this variable is nil, don't ask whether to reply to all recipients.
1279 If this variable is non-nil, pose the question \"Reply to all
1280 recipients?\" before a wide reply to multiple recipients.  If the user
1281 answers yes, reply to all recipients as usual.  If the user answers
1282 no, only reply back to the author."
1283   :group 'message-headers
1284   :type 'boolean)
1285
1286 (defcustom message-insert-canlock t
1287   "Whether to insert a Cancel-Lock header in news postings."
1288   :group 'message-headers
1289   :type 'boolean)
1290
1291 ;;; Internal variables.
1292
1293 (defvar message-sending-message "Sending...")
1294 (defvar message-buffer-list nil)
1295 (defvar message-this-is-news nil)
1296 (defvar message-this-is-mail nil)
1297 (defvar message-draft-article nil)
1298 (defvar message-mime-part nil)
1299 (defvar message-posting-charset nil)
1300
1301 ;; Byte-compiler warning
1302 (eval-when-compile
1303   (defvar gnus-active-hashtb)
1304   (defvar gnus-read-active-file))
1305
1306 ;;; Regexp matching the delimiter of messages in UNIX mail format
1307 ;;; (UNIX From lines), minus the initial ^.  It should be a copy
1308 ;;; of rmail.el's rmail-unix-mail-delimiter.
1309 (defvar message-unix-mail-delimiter
1310   (let ((time-zone-regexp
1311          (concat "\\([A-Z]?[A-Z]?[A-Z][A-Z]\\( DST\\)?"
1312                  "\\|[-+]?[0-9][0-9][0-9][0-9]"
1313                  "\\|"
1314                  "\\) *")))
1315     (concat
1316      "From "
1317
1318      ;; Many things can happen to an RFC 822 mailbox before it is put into
1319      ;; a `From' line.  The leading phrase can be stripped, e.g.
1320      ;; `Joe <@w.x:joe@y.z>' -> `<@w.x:joe@y.z>'.  The <> can be stripped, e.g.
1321      ;; `<@x.y:joe@y.z>' -> `@x.y:joe@y.z'.  Everything starting with a CRLF
1322      ;; can be removed, e.g.
1323      ;;         From: joe@y.z (Joe      K
1324      ;;                 User)
1325      ;; can yield `From joe@y.z (Joe    K Fri Mar 22 08:11:15 1996', and
1326      ;;         From: Joe User
1327      ;;                 <joe@y.z>
1328      ;; can yield `From Joe User Fri Mar 22 08:11:15 1996'.
1329      ;; The mailbox can be removed or be replaced by white space, e.g.
1330      ;;         From: "Joe User"{space}{tab}
1331      ;;                 <joe@y.z>
1332      ;; can yield `From {space}{tab} Fri Mar 22 08:11:15 1996',
1333      ;; where {space} and {tab} represent the Ascii space and tab characters.
1334      ;; We want to match the results of any of these manglings.
1335      ;; The following regexp rejects names whose first characters are
1336      ;; obviously bogus, but after that anything goes.
1337      "\\([^\0-\b\n-\r\^?].*\\)? "
1338
1339      ;; The time the message was sent.
1340      "\\([^\0-\r \^?]+\\) +"            ; day of the week
1341      "\\([^\0-\r \^?]+\\) +"            ; month
1342      "\\([0-3]?[0-9]\\) +"              ; day of month
1343      "\\([0-2][0-9]:[0-5][0-9]\\(:[0-6][0-9]\\)?\\) *" ; time of day
1344
1345      ;; Perhaps a time zone, specified by an abbreviation, or by a
1346      ;; numeric offset.
1347      time-zone-regexp
1348
1349      ;; The year.
1350      " \\([0-9][0-9]+\\) *"
1351
1352      ;; On some systems the time zone can appear after the year, too.
1353      time-zone-regexp
1354
1355      ;; Old uucp cruft.
1356      "\\(remote from .*\\)?"
1357
1358      "\n"))
1359   "Regexp matching the delimiter of messages in UNIX mail format.")
1360
1361 (defvar message-unsent-separator
1362   (concat "^ *---+ +Unsent message follows +---+ *$\\|"
1363           "^ *---+ +Returned message +---+ *$\\|"
1364           "^Start of returned message$\\|"
1365           "^ *---+ +Original message +---+ *$\\|"
1366           "^ *--+ +begin message +--+ *$\\|"
1367           "^ *---+ +Original message follows +---+ *$\\|"
1368           "^ *---+ +Undelivered message follows +---+ *$\\|"
1369           "^|? *---+ +Message text follows: +---+ *|?$")
1370   "A regexp that matches the separator before the text of a failed message.")
1371
1372 (defvar message-header-format-alist
1373   `((Newsgroups)
1374     (To . message-fill-address)
1375     (Cc . message-fill-address)
1376     (Subject)
1377     (In-Reply-To)
1378     (Fcc)
1379     (Bcc)
1380     (Date)
1381     (Organization)
1382     (Distribution)
1383     (Lines)
1384     (Expires)
1385     (Message-ID)
1386     (References . message-shorten-references)
1387     (User-Agent))
1388   "Alist used for formatting headers.")
1389
1390 (defvar message-options nil
1391   "Some saved answers when sending message.")
1392
1393 (defvar message-send-mail-real-function nil
1394   "Internal send mail function.")
1395
1396 (defvar message-bogus-system-names "^localhost\\."
1397   "The regexp of bogus system names.")
1398
1399 (eval-and-compile
1400   (autoload 'message-setup-toolbar "messagexmas")
1401   (autoload 'mh-new-draft-name "mh-comp")
1402   (autoload 'mh-send-letter "mh-comp")
1403   (autoload 'gnus-point-at-eol "gnus-util")
1404   (autoload 'gnus-point-at-bol "gnus-util")
1405   (autoload 'gnus-output-to-rmail "gnus-util")
1406   (autoload 'gnus-output-to-mail "gnus-util")
1407   (autoload 'mail-abbrev-in-expansion-header-p "mailabbrev")
1408   (autoload 'nndraft-request-associate-buffer "nndraft")
1409   (autoload 'nndraft-request-expire-articles "nndraft")
1410   (autoload 'gnus-open-server "gnus-int")
1411   (autoload 'gnus-request-post "gnus-int")
1412   (autoload 'gnus-copy-article-buffer "gnus-msg")
1413   (autoload 'gnus-alive-p "gnus-util")
1414   (autoload 'gnus-server-string "gnus")
1415   (autoload 'gnus-group-name-charset "gnus-group")
1416   (autoload 'gnus-group-name-decode "gnus-group")
1417   (autoload 'gnus-groups-from-server "gnus")
1418   (autoload 'rmail-output "rmailout")
1419   (autoload 'mu-cite-original "mu-cite"))
1420
1421 \f
1422
1423 ;;;
1424 ;;; Utility functions.
1425 ;;;
1426 (defun message-eval-parameter (parameter)
1427   (condition-case ()
1428       (if (symbolp parameter)
1429           (if (functionp parameter)
1430               (funcall parameter)
1431             (eval parameter))
1432         parameter)
1433     (error nil)))
1434
1435 (defsubst message-get-parameter (key &optional alist)
1436   (unless alist
1437     (setq alist message-parameter-alist))
1438   (cdr (assq key alist)))
1439
1440 (defmacro message-get-parameter-with-eval (key &optional alist)
1441   `(message-eval-parameter (message-get-parameter ,key ,alist)))
1442
1443 (defmacro message-y-or-n-p (question show &rest text)
1444   "Ask QUESTION, displaying remaining args in a temporary buffer if SHOW."
1445   `(message-talkative-question 'y-or-n-p ,question ,show ,@text))
1446
1447 (defmacro message-delete-line (&optional n)
1448   "Delete the current line (and the next N lines)."
1449   `(delete-region (progn (beginning-of-line) (point))
1450                   (progn (forward-line ,(or n 1)) (point))))
1451
1452 (defun message-unquote-tokens (elems)
1453   "Remove double quotes (\") from strings in list ELEMS."
1454   (mapcar (lambda (item)
1455             (while (string-match "^\\(.*\\)\"\\(.*\\)$" item)
1456               (setq item (concat (match-string 1 item)
1457                                  (match-string 2 item))))
1458             item)
1459           elems))
1460
1461 (defun message-tokenize-header (header &optional separator)
1462   "Split HEADER into a list of header elements.
1463 SEPARATOR is a string of characters to be used as separators.  \",\"
1464 is used by default."
1465   (if (not header)
1466       nil
1467     (let ((regexp (format "[%s]+" (or separator ",")))
1468           (beg 1)
1469           (first t)
1470           quoted elems paren)
1471       (save-excursion
1472         (message-set-work-buffer)
1473         (insert header)
1474         (goto-char (point-min))
1475         (while (not (eobp))
1476           (if first
1477               (setq first nil)
1478             (forward-char 1))
1479           (cond ((and (> (point) beg)
1480                       (or (eobp)
1481                           (and (looking-at regexp)
1482                                (not quoted)
1483                                (not paren))))
1484                  (push (buffer-substring beg (point)) elems)
1485                  (setq beg (match-end 0)))
1486                 ((eq (char-after) ?\")
1487                  (setq quoted (not quoted)))
1488                 ((and (eq (char-after) ?\()
1489                       (not quoted))
1490                  (setq paren t))
1491                 ((and (eq (char-after) ?\))
1492                       (not quoted))
1493                  (setq paren nil))))
1494         (nreverse elems)))))
1495
1496 (defun message-mail-file-mbox-p (file)
1497   "Say whether FILE looks like a Unix mbox file."
1498   (when (and (file-exists-p file)
1499              (file-readable-p file)
1500              (file-regular-p file))
1501     (with-temp-buffer
1502       (nnheader-insert-file-contents file)
1503       (goto-char (point-min))
1504       (looking-at message-unix-mail-delimiter))))
1505
1506 (defun message-fetch-field (header &optional not-all)
1507   "The same as `mail-fetch-field', only remove all newlines."
1508   (let* ((inhibit-point-motion-hooks t)
1509          (case-fold-search t)
1510          (value (mail-fetch-field header nil (not not-all))))
1511     (when value
1512       (while (string-match "\n[\t ]+" value)
1513         (setq value (replace-match " " t t value)))
1514       (set-text-properties 0 (length value) nil value)
1515       value)))
1516
1517 (defun message-narrow-to-field ()
1518   "Narrow the buffer to the header on the current line."
1519   (beginning-of-line)
1520   (narrow-to-region
1521    (point)
1522    (progn
1523      (forward-line 1)
1524      (if (re-search-forward "^[^ \n\t]" nil t)
1525          (progn
1526            (beginning-of-line)
1527            (point))
1528        (point-max))))
1529   (goto-char (point-min)))
1530
1531 (defun message-add-header (&rest headers)
1532   "Add the HEADERS to the message header, skipping those already present."
1533   (while headers
1534     (let (hclean)
1535       (unless (string-match "^\\([^:]+\\):[ \t]*[^ \t]" (car headers))
1536         (error "Invalid header `%s'" (car headers)))
1537       (setq hclean (match-string 1 (car headers)))
1538       (save-restriction
1539         (message-narrow-to-headers)
1540         (unless (re-search-forward (concat "^" (regexp-quote hclean) ":") nil t)
1541           (goto-char (point-max))
1542           (if (string-match "\n$" (car headers))
1543               (insert (car headers))
1544             (insert (car headers) ?\n)))))
1545     (setq headers (cdr headers))))
1546
1547
1548 (defun message-fetch-reply-field (header)
1549   "Fetch field HEADER from the message we're replying to."
1550   (let ((buffer (message-eval-parameter message-reply-buffer)))
1551     (when (and buffer
1552                (buffer-name buffer))
1553       (save-excursion
1554         (set-buffer buffer)
1555         (message-fetch-field header)))))
1556
1557 (defun message-set-work-buffer ()
1558   (if (get-buffer " *message work*")
1559       (progn
1560         (set-buffer " *message work*")
1561         (erase-buffer))
1562     (set-buffer (get-buffer-create " *message work*"))
1563     (kill-all-local-variables)))
1564
1565 (defun message-functionp (form)
1566   "Return non-nil if FORM is funcallable."
1567   (or (and (symbolp form) (fboundp form))
1568       (and (listp form) (eq (car form) 'lambda))
1569       (byte-code-function-p form)))
1570
1571 (defun message-strip-list-identifiers (subject)
1572   "Remove list identifiers in `gnus-list-identifiers' from string SUBJECT."
1573   (require 'gnus-sum)                   ; for gnus-list-identifiers
1574   (let ((regexp (if (stringp gnus-list-identifiers)
1575                     gnus-list-identifiers
1576                   (mapconcat 'identity gnus-list-identifiers " *\\|"))))
1577     (if (string-match (concat "\\(\\(\\(Re: +\\)?\\(" regexp
1578                               " *\\)\\)+\\(Re: +\\)?\\)") subject)
1579         (concat (substring subject 0 (match-beginning 1))
1580                 (or (match-string 3 subject)
1581                     (match-string 5 subject))
1582                 (substring subject
1583                            (match-end 1)))
1584       subject)))
1585
1586 (defun message-strip-subject-re (subject)
1587   "Remove \"Re:\" from subject lines in string SUBJECT."
1588   (if (string-match message-subject-re-regexp subject)
1589       (substring subject (match-end 0))
1590     subject))
1591
1592 (defun message-remove-header (header &optional is-regexp first reverse)
1593   "Remove HEADER in the narrowed buffer.
1594 If IS-REGEXP, HEADER is a regular expression.
1595 If FIRST, only remove the first instance of the header.
1596 Return the number of headers removed."
1597   (goto-char (point-min))
1598   (let ((regexp (if is-regexp header (concat "^" (regexp-quote header) ":")))
1599         (number 0)
1600         (case-fold-search t)
1601         last)
1602     (while (and (not (eobp))
1603                 (not last))
1604       (if (if reverse
1605               (not (looking-at regexp))
1606             (looking-at regexp))
1607           (progn
1608             (incf number)
1609             (when first
1610               (setq last t))
1611             (delete-region
1612              (point)
1613              ;; There might be a continuation header, so we have to search
1614              ;; until we find a new non-continuation line.
1615              (progn
1616                (forward-line 1)
1617                (if (re-search-forward "^[^ \t]" nil t)
1618                    (goto-char (match-beginning 0))
1619                  (point-max)))))
1620         (forward-line 1)
1621         (if (re-search-forward "^[^ \t]" nil t)
1622             (goto-char (match-beginning 0))
1623           (goto-char (point-max)))))
1624     number))
1625
1626 (defun message-remove-first-header (header)
1627   "Remove the first instance of HEADER if there is more than one."
1628   (let ((count 0)
1629         (regexp (concat "^" (regexp-quote header) ":")))
1630     (save-excursion
1631       (goto-char (point-min))
1632       (while (re-search-forward regexp nil t)
1633         (incf count)))
1634     (while (> count 1)
1635       (message-remove-header header nil t)
1636       (decf count))))
1637
1638 (defun message-narrow-to-headers ()
1639   "Narrow the buffer to the head of the message."
1640   (widen)
1641   (narrow-to-region
1642    (goto-char (point-min))
1643    (if (re-search-forward
1644         (concat "^" (regexp-quote mail-header-separator) "\n") nil t)
1645        (match-beginning 0)
1646      (point-max)))
1647   (goto-char (point-min)))
1648
1649 (defun message-narrow-to-head-1 ()
1650   "Like `message-narrow-to-head'.  Don't widen."
1651   (narrow-to-region
1652    (goto-char (point-min))
1653    (if (search-forward "\n\n" nil 1)
1654        (1- (point))
1655      (point-max)))
1656   (goto-char (point-min)))
1657
1658 (defun message-narrow-to-head ()
1659   "Narrow the buffer to the head of the message.
1660 Point is left at the beginning of the narrowed-to region."
1661   (widen)
1662   (message-narrow-to-head-1))
1663
1664 (defun message-narrow-to-headers-or-head ()
1665   "Narrow the buffer to the head of the message."
1666   (widen)
1667   (narrow-to-region
1668    (goto-char (point-min))
1669    (cond
1670     ((re-search-forward
1671       (concat "^" (regexp-quote mail-header-separator) "\n") nil t)
1672      (match-beginning 0))
1673     ((search-forward "\n\n" nil t)
1674      (1- (point)))
1675     (t
1676      (point-max))))
1677   (goto-char (point-min)))
1678
1679 (defun message-news-p ()
1680   "Say whether the current buffer contains a news message."
1681   (and (not message-this-is-mail)
1682        (or message-this-is-news
1683            (save-excursion
1684              (save-restriction
1685                (message-narrow-to-headers)
1686                (and (message-fetch-field "newsgroups")
1687                     (not (message-fetch-field "posted-to"))))))))
1688
1689 (defun message-mail-p ()
1690   "Say whether the current buffer contains a mail message."
1691   (and (not message-this-is-news)
1692        (or message-this-is-mail
1693            (save-excursion
1694              (save-restriction
1695                (message-narrow-to-headers)
1696                (or (message-fetch-field "to")
1697                    (message-fetch-field "cc")
1698                    (message-fetch-field "bcc")))))))
1699
1700 (defun message-next-header ()
1701   "Go to the beginning of the next header."
1702   (beginning-of-line)
1703   (or (eobp) (forward-char 1))
1704   (not (if (re-search-forward "^[^ \t]" nil t)
1705            (beginning-of-line)
1706          (goto-char (point-max)))))
1707
1708 (defun message-sort-headers-1 ()
1709   "Sort the buffer as headers using `message-rank' text props."
1710   (goto-char (point-min))
1711   (require 'sort)
1712   (sort-subr
1713    nil 'message-next-header
1714    (lambda ()
1715      (message-next-header)
1716      (unless (bobp)
1717        (forward-char -1)))
1718    (lambda ()
1719      (or (get-text-property (point) 'message-rank)
1720          10000))))
1721
1722 (defun message-sort-headers ()
1723   "Sort the headers of the current message according to `message-header-format-alist'."
1724   (interactive)
1725   (save-excursion
1726     (save-restriction
1727       (let ((max (1+ (length message-header-format-alist)))
1728             rank)
1729         (message-narrow-to-headers)
1730         (while (re-search-forward "^[^ \n]+:" nil t)
1731           (put-text-property
1732            (match-beginning 0) (1+ (match-beginning 0))
1733            'message-rank
1734            (if (setq rank (length (memq (assq (intern (buffer-substring
1735                                                        (match-beginning 0)
1736                                                        (1- (match-end 0))))
1737                                               message-header-format-alist)
1738                                         message-header-format-alist)))
1739                (- max rank)
1740              (1+ max)))))
1741       (message-sort-headers-1))))
1742
1743 \f
1744
1745 ;;;
1746 ;;; Message mode
1747 ;;;
1748
1749 ;;; Set up keymap.
1750
1751 (defvar message-mode-map nil)
1752
1753 (unless message-mode-map
1754   (setq message-mode-map (make-keymap))
1755   (set-keymap-parent message-mode-map text-mode-map)
1756   (define-key message-mode-map "\C-c?" 'describe-mode)
1757
1758   (define-key message-mode-map "\C-c\C-f\C-t" 'message-goto-to)
1759   (define-key message-mode-map "\C-c\C-f\C-o" 'message-goto-from)
1760   (define-key message-mode-map "\C-c\C-f\C-b" 'message-goto-bcc)
1761   (define-key message-mode-map "\C-c\C-f\C-w" 'message-goto-fcc)
1762   (define-key message-mode-map "\C-c\C-f\C-c" 'message-goto-cc)
1763   (define-key message-mode-map "\C-c\C-f\C-s" 'message-goto-subject)
1764   ;; (define-key message-mode-map "\C-c\C-f\C-r" 'message-goto-reply-to)
1765   (define-key message-mode-map "\C-c\C-f\C-r" 'message-goto-mail-reply-to)
1766   (define-key message-mode-map "\C-c\C-f\C-m" 'message-goto-mail-followup-to)
1767   (define-key message-mode-map "\C-c\C-f\C-n" 'message-goto-newsgroups)
1768   (define-key message-mode-map "\C-c\C-f\C-d" 'message-goto-distribution)
1769   (define-key message-mode-map "\C-c\C-f\C-f" 'message-goto-followup-to)
1770   (define-key message-mode-map "\C-c\C-f\C-m" 'message-goto-mail-followup-to)
1771   (define-key message-mode-map "\C-c\C-f\C-k" 'message-goto-keywords)
1772   (define-key message-mode-map "\C-c\C-f\C-u" 'message-goto-summary)
1773   (define-key message-mode-map "\C-c\C-f\C-i" 'message-insert-or-toggle-importance)
1774   (define-key message-mode-map "\C-c\C-b" 'message-goto-body)
1775   (define-key message-mode-map "\C-c\C-i" 'message-goto-signature)
1776   (define-key message-mode-map "\C-c\C-fc" 'message-goto-mail-copies-to)
1777
1778   (define-key message-mode-map "\C-c\C-t" 'message-insert-to)
1779   (define-key message-mode-map "\C-c\C-n" 'message-insert-newsgroups)
1780
1781   (define-key message-mode-map "\C-c\C-u" 'message-insert-or-toggle-importance)
1782   (define-key message-mode-map "\C-c\M-n" 'message-insert-disposition-notification-to)
1783
1784   (define-key message-mode-map "\C-c\C-y" 'message-yank-original)
1785   (define-key message-mode-map "\C-c\M-\C-y" 'message-yank-buffer)
1786   (define-key message-mode-map "\C-c\C-q" 'message-fill-yanked-message)
1787   (define-key message-mode-map "\C-c\C-w" 'message-insert-signature)
1788   (define-key message-mode-map "\C-c\M-h" 'message-insert-headers)
1789   (define-key message-mode-map "\C-c\C-r" 'message-caesar-buffer-body)
1790   (define-key message-mode-map "\C-c\C-o" 'message-sort-headers)
1791   (define-key message-mode-map "\C-c\M-r" 'message-rename-buffer)
1792
1793   (define-key message-mode-map "\C-c\C-c" 'message-send-and-exit)
1794   (define-key message-mode-map "\C-c\C-s" 'message-send)
1795   (define-key message-mode-map "\C-c\C-k" 'message-kill-buffer)
1796   (define-key message-mode-map "\C-c\C-d" 'message-dont-send)
1797   (define-key message-mode-map "\C-c\n" 'gnus-delay-article)
1798
1799   (define-key message-mode-map "\C-c\C-e" 'message-elide-region)
1800   (define-key message-mode-map "\C-c\C-v" 'message-delete-not-region)
1801   (define-key message-mode-map "\C-c\C-z" 'message-kill-to-signature)
1802   (define-key message-mode-map "\M-\r" 'message-newline-and-reformat)
1803   ;;(define-key message-mode-map "\M-q" 'message-fill-paragraph)
1804
1805   (define-key message-mode-map "\C-a" 'message-beginning-of-line)
1806   (define-key message-mode-map "\t" 'message-tab)
1807   (define-key message-mode-map "\M-;" 'comment-region)
1808
1809   (define-key message-mode-map "\C-x\C-s" 'message-save-drafts)
1810   (define-key message-mode-map "\C-xk" 'message-mimic-kill-buffer))
1811
1812 (easy-menu-define
1813  message-mode-menu message-mode-map "Message Menu."
1814  `("Message"
1815    ["Sort Headers" message-sort-headers t]
1816    ["Yank Original" message-yank-original t]
1817    ["Fill Yanked Message" message-fill-yanked-message t]
1818    ["Insert Signature" message-insert-signature t]
1819    ["Caesar (rot13) Message" message-caesar-buffer-body t]
1820    ["Caesar (rot13) Region" message-caesar-region (mark t)]
1821    ["Elide Region" message-elide-region (mark t)]
1822    ["Delete Outside Region" message-delete-not-region (mark t)]
1823    ["Kill To Signature" message-kill-to-signature t]
1824    ["Newline and Reformat" message-newline-and-reformat t]
1825    ["Rename buffer" message-rename-buffer t]
1826    ["Flag As Important" message-insert-importance-high
1827     ,@(if (featurep 'xemacs) '(t)
1828         '(:help "Mark this message as important"))]
1829    ["Flag As Unimportant" message-insert-importance-low
1830     ,@(if (featurep 'xemacs) '(t)
1831         '(:help "Mark this message as unimportant"))]
1832    ["Request Receipt"
1833     message-insert-disposition-notification-to
1834     ,@(if (featurep 'xemacs) '(t)
1835         '(:help "Request a Disposition Notification of this article"))]
1836    ["Spellcheck" ispell-message
1837     ,@(if (featurep 'xemacs) '(t)
1838         '(:help "Spellcheck this message"))]
1839    ["Attach file as MIME" mime-edit-insert-file
1840     ,@(if (featurep 'xemacs) '(t)
1841         '(:help "Attach a file at point"))]
1842    "----"
1843    ["Send Message" message-send-and-exit
1844     ,@(if (featurep 'xemacs) '(t)
1845         '(:help "Send this message"))]
1846    ["Postpone Message" message-dont-send
1847     ,@(if (featurep 'xemacs) '(t)
1848         '(:help "File this draft message and exit"))]
1849    ["Send at Specific Time" gnus-delay-article
1850     ,@(if (featurep 'xemacs) '(t)
1851         '(:help "Ask, then arrange to send message at that time"))]
1852    ["Kill Message" message-kill-buffer
1853     ,@(if (featurep 'xemacs) '(t)
1854         '(:help "Delete this message without sending"))]))
1855
1856 (easy-menu-define
1857  message-mode-field-menu message-mode-map ""
1858  '("Field"
1859    ["Fetch To" message-insert-to t]
1860    ["Fetch Newsgroups" message-insert-newsgroups t]
1861    "----"
1862    ["To" message-goto-to t]
1863    ["From" message-goto-from t]
1864    ["Subject" message-goto-subject t]
1865    ["Cc" message-goto-cc t]
1866    ["Reply-To" message-goto-reply-to t]
1867    ["Mail-Reply-To" message-goto-mail-reply-to t]
1868    ["Mail-Followup-To" message-goto-mail-followup-to t]
1869    ["Mail-Copies-To" message-goto-mail-copies-to t]
1870    ["Summary" message-goto-summary t]
1871    ["Keywords" message-goto-keywords t]
1872    ["Newsgroups" message-goto-newsgroups t]
1873    ["Followup-To" message-goto-followup-to t]
1874    ["Mail-Followup-To" message-goto-mail-followup-to t]
1875    ["Distribution" message-goto-distribution t]
1876    ["Body" message-goto-body t]
1877    ["Signature" message-goto-signature t]))
1878
1879 (defvar message-tool-bar-map nil)
1880
1881 (eval-when-compile
1882   (defvar facemenu-add-face-function)
1883   (defvar facemenu-remove-face-function))
1884
1885 ;;; Forbidden properties
1886 ;;
1887 ;; We use `after-change-functions' to keep special text properties
1888 ;; that interfer with the normal function of message mode out of the
1889 ;; buffer.
1890
1891 (defcustom message-strip-special-text-properties t
1892   "Strip special properties from the message buffer.
1893
1894 Emacs has a number of special text properties which can break message
1895 composing in various ways.  If this option is set, message will strip
1896 these properties from the message composition buffer.  However, some
1897 packages requires these properties to be present in order to work.
1898 If you use one of these packages, turn this option off, and hope the
1899 message composition doesn't break too bad."
1900   :group 'message-various
1901   :type 'boolean)
1902
1903 (defconst message-forbidden-properties
1904   ;; No reason this should be clutter up customize.  We make it a
1905   ;; property list (rather than a list of property symbols), to be
1906   ;; directly useful for `remove-text-properties'.
1907   '(field nil read-only nil intangible nil invisible nil
1908           mouse-face nil modification-hooks nil insert-in-front-hooks nil
1909           insert-behind-hooks nil point-entered nil point-left nil)
1910   ;; Other special properties:
1911   ;; category, face, display: probably doesn't do any harm.
1912   ;; fontified: is used by font-lock.
1913   ;; syntax-table, local-map: I dunno.
1914   ;; We need to add XEmacs names to the list.
1915   "Property list of with properties.forbidden in message buffers.
1916 The values of the properties are ignored, only the property names are used.")
1917
1918 (defun message-tamago-not-in-use-p (pos)
1919   "Return t when tamago version 4 is not in use at the cursor position.
1920 Tamago version 4 is a popular input method for writing Japanese text.
1921 It uses the properties `intangible', `invisible', `modification-hooks'
1922 and `read-only' when translating ascii or kana text to kanji text.
1923 These properties are essential to work, so we should never strip them."
1924   (not (and (boundp 'egg-modefull-mode)
1925             (symbol-value 'egg-modefull-mode)
1926             (or (memq (get-text-property pos 'intangible)
1927                       '(its-part-1 its-part-2))
1928                 (get-text-property pos 'egg-end)
1929                 (get-text-property pos 'egg-lang)
1930                 (get-text-property pos 'egg-start)))))
1931
1932 (defun message-strip-forbidden-properties (begin end &optional old-length)
1933   "Strip forbidden properties between BEGIN and END, ignoring the third arg.
1934 This function is intended to be called from `after-change-functions'.
1935 See also `message-forbidden-properties'."
1936   (when (and message-strip-special-text-properties
1937              (message-tamago-not-in-use-p begin)
1938              ;; Check whether the invisible MIME part is not inserted.
1939              (not (text-property-any begin end 'mime-edit-invisible t)))
1940     (remove-text-properties begin end message-forbidden-properties)))
1941
1942 ;;;###autoload
1943 (define-derived-mode message-mode text-mode "Message"
1944   "Major mode for editing mail and news to be sent.
1945 Like Text Mode but with these additional commands:\\<message-mode-map>
1946 C-c C-s  `message-send' (send the message)  C-c C-c  `message-send-and-exit'
1947 C-c C-d  Postpone sending the message       C-c C-k  Kill the message
1948 C-c C-f  move to a header field (and create it if there isn't):
1949          C-c C-f C-t  move to To        C-c C-f C-s  move to Subject
1950          C-c C-f C-c  move to Cc        C-c C-f C-b  move to Bcc
1951          C-c C-f C-w  move to Fcc       C-c C-f C-r  move to Reply-To
1952          C-c C-f C-u  move to Summary   C-c C-f C-n  move to Newsgroups
1953          C-c C-f C-k  move to Keywords  C-c C-f C-d  move to Distribution
1954          C-c C-f C-f  move to Followup-To
1955          C-c C-f C-m  move to Mail-Followup-To
1956          C-c C-f C-i  cycle through Importance values
1957          C-c C-f c    move to Mail-Copies-To
1958 C-c C-t  `message-insert-to' (add a To header to a news followup)
1959 C-c C-n  `message-insert-newsgroups' (add a Newsgroup header to a news reply)
1960 C-c C-b  `message-goto-body' (move to beginning of message text).
1961 C-c C-i  `message-goto-signature' (move to the beginning of the signature).
1962 C-c C-w  `message-insert-signature' (insert `message-signature-file' file).
1963 C-c C-y  `message-yank-original' (insert current message, if any).
1964 C-c C-q  `message-fill-yanked-message' (fill what was yanked).
1965 C-c C-e  `message-elide-region' (elide the text between point and mark).
1966 C-c C-v  `message-delete-not-region' (remove the text outside the region).
1967 C-c C-z  `message-kill-to-signature' (kill the text up to the signature).
1968 C-c C-r  `message-caesar-buffer-body' (rot13 the message body).
1969 C-c C-u  `message-insert-or-toggle-importance'  (insert or cycle importance).
1970 C-c M-n  `message-insert-disposition-notification-to'  (request receipt).
1971 M-RET    `message-newline-and-reformat' (break the line and reformat)."
1972   (set (make-local-variable 'message-reply-buffer) nil)
1973   (make-local-variable 'message-send-actions)
1974   (make-local-variable 'message-exit-actions)
1975   (make-local-variable 'message-kill-actions)
1976   (make-local-variable 'message-postpone-actions)
1977   (make-local-variable 'message-draft-article)
1978   (setq buffer-offer-save t)
1979   (set (make-local-variable 'facemenu-add-face-function)
1980        (lambda (face end)
1981          (let ((face-fun (cdr (assq face message-face-alist))))
1982            (if face-fun
1983                (funcall face-fun (point) end)
1984              (error "Face %s not configured for %s mode" face mode-name)))
1985          ""))
1986   (set (make-local-variable 'facemenu-remove-face-function) t)
1987   (set (make-local-variable 'message-reply-headers) nil)
1988   (make-local-variable 'message-user-agent)
1989   (make-local-variable 'message-post-method)
1990   (set (make-local-variable 'message-sent-message-via) nil)
1991   (set (make-local-variable 'message-checksum) nil)
1992   (make-local-variable 'message-parameter-alist)
1993   (setq message-parameter-alist
1994         (copy-sequence message-startup-parameter-alist))
1995   (message-setup-fill-variables)
1996   ;; Allow using comment commands to add/remove quoting.
1997   (set (make-local-variable 'comment-start) message-yank-prefix)
1998   (if (featurep 'xemacs)
1999       (message-setup-toolbar)
2000     (set (make-local-variable 'font-lock-defaults)
2001          '(message-font-lock-keywords t))
2002     (if (boundp 'tool-bar-map)
2003         (set (make-local-variable 'tool-bar-map) (message-tool-bar-map))))
2004   (easy-menu-add message-mode-menu message-mode-map)
2005   (easy-menu-add message-mode-field-menu message-mode-map)
2006   ;; make-local-hook is harmless though obsolete in Emacs 21.
2007   ;; Emacs 20 and XEmacs need make-local-hook.
2008   (make-local-hook 'after-change-functions)
2009   ;; Mmmm... Forbidden properties...
2010   (add-hook 'after-change-functions 'message-strip-forbidden-properties
2011             nil 'local)
2012   ;; Allow mail alias things.
2013   (when (eq message-mail-alias-type 'abbrev)
2014     (if (fboundp 'mail-abbrevs-setup)
2015         (mail-abbrevs-setup)
2016       (mail-aliases-setup)))
2017   (message-set-auto-save-file-name)
2018   (set (make-local-variable 'indent-tabs-mode) nil)) ;No tabs for indentation.
2019
2020 (defun message-setup-fill-variables ()
2021   "Setup message fill variables."
2022   (set (make-local-variable 'fill-paragraph-function)
2023        'message-fill-paragraph)
2024   (make-local-variable 'paragraph-separate)
2025   (make-local-variable 'paragraph-start)
2026   (make-local-variable 'adaptive-fill-regexp)
2027   (unless (boundp 'adaptive-fill-first-line-regexp)
2028     (setq adaptive-fill-first-line-regexp nil))
2029   (make-local-variable 'adaptive-fill-first-line-regexp)
2030   (let ((quote-prefix-regexp
2031          ;; User should change message-cite-prefix-regexp if
2032          ;; message-yank-prefix is set to an abnormal value.
2033          (concat "\\(" message-cite-prefix-regexp "\\)[ \t]*")))
2034     (setq paragraph-start
2035           (concat
2036            (regexp-quote mail-header-separator) "$\\|"
2037            "[ \t]*$\\|"                 ; blank lines
2038            "-- $\\|"                    ; signature delimiter
2039            "---+$\\|"                   ; delimiters for forwarded messages
2040            page-delimiter "$\\|"        ; spoiler warnings
2041            ".*wrote:$\\|"               ; attribution lines
2042            quote-prefix-regexp "$"))    ; empty lines in quoted text
2043     (setq paragraph-separate paragraph-start)
2044     (setq adaptive-fill-regexp
2045           (concat quote-prefix-regexp "\\|" adaptive-fill-regexp))
2046     (setq adaptive-fill-first-line-regexp
2047           (concat quote-prefix-regexp "\\|"
2048                   adaptive-fill-first-line-regexp)))
2049   (make-local-variable 'auto-fill-inhibit-regexp)
2050   ;;(setq auto-fill-inhibit-regexp "^[A-Z][^: \n\t]+:")
2051   (setq auto-fill-inhibit-regexp nil)
2052   (make-local-variable 'normal-auto-fill-function)
2053   (setq normal-auto-fill-function 'message-do-auto-fill)
2054   ;; KLUDGE: auto fill might already be turned on in `text-mode-hook'.
2055   ;; In that case, ensure that it uses the right function.  The real
2056   ;; solution would be not to use `define-derived-mode', and run
2057   ;; `text-mode-hook' ourself at the end of the mode.
2058   ;; -- Per Abrahamsen <abraham@dina.kvl.dk> Date: 2001-10-19.
2059   (when auto-fill-function
2060     (setq auto-fill-function normal-auto-fill-function)))
2061
2062 \f
2063
2064 ;;;
2065 ;;; Message mode commands
2066 ;;;
2067
2068 ;;; Movement commands
2069
2070 (defun message-goto-to ()
2071   "Move point to the To header."
2072   (interactive)
2073   (message-position-on-field "To"))
2074
2075 (defun message-goto-from ()
2076   "Move point to the From header."
2077   (interactive)
2078   (message-position-on-field "From"))
2079
2080 (defun message-goto-subject ()
2081   "Move point to the Subject header."
2082   (interactive)
2083   (message-position-on-field "Subject"))
2084
2085 (defun message-goto-cc ()
2086   "Move point to the Cc header."
2087   (interactive)
2088   (message-position-on-field "Cc" "To"))
2089
2090 (defun message-goto-bcc ()
2091   "Move point to the Bcc  header."
2092   (interactive)
2093   (message-position-on-field "Bcc" "Cc" "To"))
2094
2095 (defun message-goto-fcc ()
2096   "Move point to the Fcc header."
2097   (interactive)
2098   (message-position-on-field "Fcc" "To" "Newsgroups"))
2099
2100 (defun message-goto-reply-to ()
2101   "Move point to the Reply-To header."
2102   (interactive)
2103   (message-position-on-field "Reply-To" "Subject"))
2104
2105 (defun message-goto-mail-reply-to ()
2106   "Move point to the Mail-Reply-To header."
2107   (interactive)
2108   (message-position-on-field "Mail-Reply-To" "Subject"))
2109
2110 (defun message-goto-mail-copies-to ()
2111   "Move point to the Mail-Copies-To header.  If the header is newly created,
2112 a string \"never\" is inserted in default."
2113   (interactive)
2114   (unless (message-position-on-field "Mail-Copies-To" "Subject")
2115     (insert "never")
2116     (backward-char 5)))
2117
2118 (defun message-goto-newsgroups ()
2119   "Move point to the Newsgroups header."
2120   (interactive)
2121   (message-position-on-field "Newsgroups"))
2122
2123 (defun message-goto-distribution ()
2124   "Move point to the Distribution header."
2125   (interactive)
2126   (message-position-on-field "Distribution"))
2127
2128 (defun message-goto-followup-to ()
2129   "Move point to the Followup-To header."
2130   (interactive)
2131   (message-position-on-field "Followup-To" "Newsgroups"))
2132
2133 (defun message-goto-mail-followup-to ()
2134   "Move point to the Mail-Followup-To header.  If the header is newly created
2135 and To field contains only one address, the address is inserted in default."
2136   (interactive)
2137   (unless (message-position-on-field "Mail-Followup-To" "Subject")
2138     (let ((start (point))
2139           addresses)
2140       (save-restriction
2141         (message-narrow-to-headers)
2142         (setq addresses (split-string (mail-strip-quoted-names
2143                                        (or (std11-fetch-field "to") ""))
2144                                       "[ \f\t\n\r\v,]+"))
2145         (when (eq 1 (length addresses))
2146           (goto-char start)
2147           (insert (car addresses))
2148           (goto-char start))))))
2149
2150 (defun message-goto-keywords ()
2151   "Move point to the Keywords header."
2152   (interactive)
2153   (message-position-on-field "Keywords" "Subject"))
2154
2155 (defun message-goto-summary ()
2156   "Move point to the Summary header."
2157   (interactive)
2158   (message-position-on-field "Summary" "Subject"))
2159
2160 (defun message-goto-body (&optional interactivep)
2161   "Move point to the beginning of the message body."
2162   (interactive (list t))
2163   (when (and interactivep
2164              (looking-at "[ \t]*\n"))
2165     (expand-abbrev))
2166   (goto-char (point-min))
2167   (or (search-forward (concat "\n" mail-header-separator "\n") nil t)
2168       (search-forward-regexp "[^:]+:\\([^\n]\\|\n[ \t]\\)+\n\n" nil t)))
2169
2170 (defun message-goto-eoh ()
2171   "Move point to the end of the headers."
2172   (interactive)
2173   (message-goto-body)
2174   (forward-line -1))
2175
2176 (defun message-goto-signature ()
2177   "Move point to the beginning of the message signature.
2178 If there is no signature in the article, go to the end and
2179 return nil."
2180   (interactive)
2181   (goto-char (point-min))
2182   (if (re-search-forward message-signature-separator nil t)
2183       (forward-line 1)
2184     (goto-char (point-max))
2185     nil))
2186
2187 \f
2188
2189 (defun message-insert-to (&optional force)
2190   "Insert a To header that points to the author of the article being replied to.
2191 If the original author requested not to be sent mail, the function signals
2192 an error.
2193 With the prefix argument FORCE, insert the header anyway."
2194   (interactive "P")
2195   (let ((co (message-fetch-reply-field "mail-copies-to")))
2196     (when (and (null force)
2197                co
2198                (or (equal (downcase co) "never")
2199                    (equal (downcase co) "nobody")))
2200       (error "The user has requested not to have copies sent via mail")))
2201   (when (and (message-position-on-field "To")
2202              (mail-fetch-field "to")
2203              (not (string-match "\\` *\\'" (mail-fetch-field "to"))))
2204     (insert ", "))
2205   (insert (or (message-fetch-reply-field "mail-reply-to")
2206               (message-fetch-reply-field "reply-to")
2207               (message-fetch-reply-field "from") "")))
2208
2209 (defun message-widen-reply ()
2210   "Widen the reply to include maximum recipients."
2211   (interactive)
2212   (let ((follow-to
2213          (and message-reply-buffer
2214               (buffer-name message-reply-buffer)
2215               (save-excursion
2216                 (set-buffer message-reply-buffer)
2217                 (message-get-reply-headers t)))))
2218     (save-excursion
2219       (save-restriction
2220         (message-narrow-to-headers)
2221         (dolist (elem follow-to)
2222           (message-remove-header (symbol-name (car elem)))
2223           (goto-char (point-min))
2224           (insert (symbol-name (car elem)) ": "
2225                   (cdr elem) "\n"))))))
2226
2227 (defun message-insert-newsgroups ()
2228   "Insert the Newsgroups header from the article being replied to."
2229   (interactive)
2230   (when (and (message-position-on-field "Newsgroups")
2231              (mail-fetch-field "newsgroups")
2232              (not (string-match "\\` *\\'" (mail-fetch-field "newsgroups"))))
2233     (insert ","))
2234   (insert (or (message-fetch-reply-field "newsgroups") "")))
2235
2236 \f
2237
2238 ;;; Various commands
2239
2240 (defun message-delete-not-region (beg end)
2241   "Delete everything in the body of the current message outside of the region."
2242   (interactive "r")
2243   (let (citeprefix)
2244     (save-excursion
2245       (goto-char beg)
2246       ;; snarf citation prefix, if appropriate
2247       (unless (eq (point) (progn (beginning-of-line) (point)))
2248         (when (looking-at message-cite-prefix-regexp)
2249           (setq citeprefix (match-string 0))))
2250       (goto-char end)
2251       (delete-region (point) (if (not (message-goto-signature))
2252                                  (point)
2253                                (forward-line -2)
2254                                (point)))
2255       (insert "\n")
2256       (goto-char beg)
2257       (delete-region beg (progn (message-goto-body)
2258                                 (forward-line 2)
2259                                 (point)))
2260       (when citeprefix
2261         (insert citeprefix))))
2262   (when (message-goto-signature)
2263     (forward-line -2)))
2264
2265 (defun message-kill-to-signature ()
2266   "Deletes all text up to the signature."
2267   (interactive)
2268   (let ((point (point)))
2269     (message-goto-signature)
2270     (unless (eobp)
2271       (forward-line -2))
2272     (kill-region point (point))
2273     (unless (bolp)
2274       (insert "\n"))))
2275
2276 (defun message-newline-and-reformat (&optional arg not-break)
2277   "Insert four newlines, and then reformat if inside quoted text.
2278 Prefix arg means justify as well."
2279   (interactive (list (if current-prefix-arg 'full)))
2280   (let (quoted point beg end leading-space bolp)
2281     (setq point (point))
2282     (beginning-of-line)
2283     (setq beg (point))
2284     (setq bolp (= beg point))
2285     ;; Find first line of the paragraph.
2286     (if not-break
2287         (while (and (not (eobp))
2288                     (not (looking-at message-cite-prefix-regexp))
2289                     (looking-at paragraph-start))
2290           (forward-line 1)))
2291     ;; Find the prefix
2292     (when (looking-at message-cite-prefix-regexp)
2293       (setq quoted (match-string 0))
2294       (goto-char (match-end 0))
2295       (looking-at "[ \t]*")
2296       (setq leading-space (match-string 0)))
2297     (if (and quoted
2298              (not not-break)
2299              (not bolp)
2300              (< (- point beg) (length quoted)))
2301         ;; break inside the cite prefix.
2302         (setq quoted nil
2303               end nil))
2304     (if quoted
2305         (progn
2306           (forward-line 1)
2307           (while (and (not (eobp))
2308                       (not (looking-at paragraph-separate))
2309                       (looking-at message-cite-prefix-regexp)
2310                       (equal quoted (match-string 0)))
2311             (goto-char (match-end 0))
2312             (looking-at "[ \t]*")
2313             (if (> (length leading-space) (length (match-string 0)))
2314                 (setq leading-space (match-string 0)))
2315             (forward-line 1))
2316           (setq end (point))
2317           (goto-char beg)
2318           (while (and (if (bobp) nil (forward-line -1) t)
2319                       (not (looking-at paragraph-start))
2320                       (looking-at message-cite-prefix-regexp)
2321                       (equal quoted (match-string 0)))
2322             (setq beg (point))
2323             (goto-char (match-end 0))
2324             (looking-at "[ \t]*")
2325             (if (> (length leading-space) (length (match-string 0)))
2326                 (setq leading-space (match-string 0)))))
2327       (while (and (not (eobp))
2328                   (not (looking-at paragraph-separate))
2329                   (not (looking-at message-cite-prefix-regexp)))
2330         (forward-line 1))
2331       (setq end (point))
2332       (goto-char beg)
2333       (while (and (if (bobp) nil (forward-line -1) t)
2334                   (not (looking-at paragraph-start))
2335                   (not (looking-at message-cite-prefix-regexp)))
2336         (setq beg (point))))
2337     (goto-char point)
2338     (save-restriction
2339       (narrow-to-region beg end)
2340       (if not-break
2341           (setq point nil)
2342         (if bolp
2343             (newline)
2344           (newline)
2345           (newline))
2346         (setq point (point))
2347         ;; (newline 2) doesn't mark both newline's as hard, so call
2348         ;; newline twice. -jas
2349         (newline)
2350         (newline)
2351         (delete-region (point) (re-search-forward "[ \t]*"))
2352         (when (and quoted (not bolp))
2353           (insert quoted leading-space)))
2354       (if quoted
2355           (let* ((adaptive-fill-regexp
2356                   (regexp-quote (concat quoted leading-space)))
2357                  (adaptive-fill-first-line-regexp
2358                   adaptive-fill-regexp ))
2359             (fill-paragraph arg))
2360         (fill-paragraph arg))
2361       (if point (goto-char point)))))
2362
2363 (defun message-fill-paragraph (&optional arg)
2364   "Like `fill-paragraph'."
2365   (interactive (list (if current-prefix-arg 'full)))
2366   (if (and (boundp 'filladapt-mode) filladapt-mode)
2367       nil
2368     (message-newline-and-reformat arg t)
2369     t))
2370
2371 ;; Is it better to use `mail-header-end'?
2372 (defun message-point-in-header-p ()
2373   "Return t if point is in the header."
2374   (save-excursion
2375     (let ((p (point)))
2376       (goto-char (point-min))
2377       (not (re-search-forward
2378             (concat "^" (regexp-quote mail-header-separator) "\n")
2379             p t)))))
2380
2381 (defun message-do-auto-fill ()
2382   "Like `do-auto-fill', but don't fill in message header."
2383   (unless (message-point-in-header-p)
2384     (do-auto-fill)))
2385
2386 (defun message-insert-signature (&optional force)
2387   "Insert a signature.  See documentation for variable `message-signature'."
2388   (interactive (list 0))
2389   (let* ((signature
2390           (cond
2391            ((and (null message-signature)
2392                  (eq force 0))
2393             (save-excursion
2394               (goto-char (point-max))
2395               (not (re-search-backward message-signature-separator nil t))))
2396            ((and (null message-signature)
2397                  force)
2398             t)
2399            ((message-functionp message-signature)
2400             (funcall message-signature))
2401            ((listp message-signature)
2402             (eval message-signature))
2403            (t message-signature)))
2404          (signature
2405           (cond ((stringp signature)
2406                  signature)
2407                 ((and (eq t signature)
2408                       message-signature-file
2409                       (file-exists-p message-signature-file))
2410                  signature))))
2411     (when signature
2412       (goto-char (point-max))
2413       ;; Insert the signature.
2414       (unless (bolp)
2415         (insert "\n"))
2416       (insert "\n" message-signature-separator-for-insertion)
2417       (unless (bolp)
2418         (insert "\n"))
2419       (if (eq signature t)
2420           (insert-file-contents message-signature-file)
2421         (insert signature))
2422       (goto-char (point-max))
2423       (or (bolp) (insert "\n")))))
2424
2425 (defun message-insert-importance-high ()
2426   "Insert header to mark message as important."
2427   (interactive)
2428   (save-excursion
2429     (message-remove-header "Importance")
2430     (message-goto-eoh)
2431     (insert "Importance: high\n")))
2432
2433 (defun message-insert-importance-low ()
2434   "Insert header to mark message as unimportant."
2435   (interactive)
2436   (save-excursion
2437     (message-remove-header "Importance")
2438     (message-goto-eoh)
2439     (insert "Importance: low\n")))
2440
2441 (defun message-insert-or-toggle-importance ()
2442   "Insert a \"Importance: high\" header, or cycle through the header values.
2443 The three allowed values according to RFC 1327 are `high', `normal'
2444 and `low'."
2445   (interactive)
2446   (save-excursion
2447     (let ((valid '("high" "normal" "low"))
2448           (new "high")
2449           cur)
2450       (when (setq cur (message-fetch-field "Importance"))
2451         (message-remove-header "Importance")
2452         (setq new (cond ((string= cur "high")
2453                          "low")
2454                         ((string= cur "low")
2455                          "normal")
2456                         (t
2457                          "high"))))
2458       (message-goto-eoh)
2459       (insert (format "Importance: %s\n" new)))))
2460
2461 (defun message-insert-disposition-notification-to ()
2462   "Request a disposition notification (return receipt) to this message.
2463 Note that this should not be used in newsgroups."
2464   (interactive)
2465   (save-excursion
2466     (message-remove-header "Disposition-Notification-To")
2467     (message-goto-eoh)
2468     (insert (format "Disposition-Notification-To: %s\n"
2469                     (or (message-fetch-field "From") (message-make-from))))))
2470
2471 (defun message-elide-region (b e)
2472   "Elide the text in the region.
2473 An ellipsis (from `message-elide-ellipsis') will be inserted where the
2474 text was killed."
2475   (interactive "r")
2476   (kill-region b e)
2477   (insert message-elide-ellipsis))
2478
2479 (defvar message-caesar-translation-table nil)
2480
2481 (defun message-caesar-region (b e &optional n)
2482   "Caesar rotate region B to E by N, default 13, for decrypting netnews."
2483   (interactive
2484    (list
2485     (min (point) (or (mark t) (point)))
2486     (max (point) (or (mark t) (point)))
2487     (when current-prefix-arg
2488       (prefix-numeric-value current-prefix-arg))))
2489
2490   (setq n (if (numberp n) (mod n 26) 13)) ;canonize N
2491   (unless (or (zerop n)                 ; no action needed for a rot of 0
2492               (= b e))                  ; no region to rotate
2493     ;; We build the table, if necessary.
2494     (when (or (not message-caesar-translation-table)
2495               (/= (aref message-caesar-translation-table ?a) (+ ?a n)))
2496       (setq message-caesar-translation-table
2497             (message-make-caesar-translation-table n)))
2498     (translate-region b e message-caesar-translation-table)))
2499
2500 (defun message-make-caesar-translation-table (n)
2501   "Create a rot table with offset N."
2502   (let ((i -1)
2503         (table (make-string 256 0)))
2504     (while (< (incf i) 256)
2505       (aset table i i))
2506     (concat
2507      (substring table 0 ?A)
2508      (substring table (+ ?A n) (+ ?A n (- 26 n)))
2509      (substring table ?A (+ ?A n))
2510      (substring table (+ ?A 26) ?a)
2511      (substring table (+ ?a n) (+ ?a n (- 26 n)))
2512      (substring table ?a (+ ?a n))
2513      (substring table (+ ?a 26) 255))))
2514
2515 (defun message-caesar-buffer-body (&optional rotnum)
2516   "Caesar rotate all letters in the current buffer by 13 places.
2517 Used to encode/decode possibly offensive messages (commonly in rec.humor).
2518 With prefix arg, specifies the number of places to rotate each letter forward.
2519 Mail and USENET news headers are not rotated."
2520   (interactive (if current-prefix-arg
2521                    (list (prefix-numeric-value current-prefix-arg))
2522                  (list nil)))
2523   (save-excursion
2524     (save-restriction
2525       (when (message-goto-body)
2526         (narrow-to-region (point) (point-max)))
2527       (message-caesar-region (point-min) (point-max) rotnum))))
2528
2529 (defun message-pipe-buffer-body (program)
2530   "Pipe the message body in the current buffer through PROGRAM."
2531   (save-excursion
2532     (save-restriction
2533       (when (message-goto-body)
2534         (narrow-to-region (point) (point-max)))
2535       (shell-command-on-region
2536        (point-min) (point-max) program nil t))))
2537
2538 (defun message-rename-buffer (&optional enter-string)
2539   "Rename the *message* buffer to \"*message* RECIPIENT\".
2540 If the function is run with a prefix, it will ask for a new buffer
2541 name, rather than giving an automatic name."
2542   (interactive "Pbuffer name: ")
2543   (save-excursion
2544     (save-restriction
2545       (goto-char (point-min))
2546       (narrow-to-region (point)
2547                         (search-forward mail-header-separator nil 'end))
2548       (let* ((mail-to (or
2549                        (if (message-news-p) (message-fetch-field "Newsgroups")
2550                          (message-fetch-field "To"))
2551                        ""))
2552              (mail-trimmed-to
2553               (if (string-match "," mail-to)
2554                   (concat (substring mail-to 0 (match-beginning 0)) ", ...")
2555                 mail-to))
2556              (name-default (concat "*message* " mail-trimmed-to))
2557              (name (if enter-string
2558                        (read-string "New buffer name: " name-default)
2559                      name-default)))
2560         (rename-buffer name t)))))
2561
2562 (defun message-fill-yanked-message (&optional justifyp)
2563   "Fill the paragraphs of a message yanked into this one.
2564 Numeric argument means justify as well."
2565   (interactive "P")
2566   (save-excursion
2567     (goto-char (point-min))
2568     (search-forward (concat "\n" mail-header-separator "\n") nil t)
2569     (let ((fill-prefix message-yank-prefix))
2570       (fill-individual-paragraphs (point) (point-max) justifyp))))
2571
2572 (defun message-indent-citation ()
2573   "Modify text just inserted from a message to be cited.
2574 The inserted text should be the region.
2575 When this function returns, the region is again around the modified text.
2576
2577 Normally, indent each nonblank line `message-indentation-spaces' spaces.
2578 However, if `message-yank-prefix' is non-nil, insert that prefix on each line."
2579   (let ((start (point)))
2580     ;; Remove unwanted headers.
2581     (when message-ignored-cited-headers
2582       (let (all-removed)
2583         (save-restriction
2584           (narrow-to-region
2585            (goto-char start)
2586            (if (search-forward "\n\n" nil t)
2587                (1- (point))
2588              (point)))
2589           (message-remove-header message-ignored-cited-headers t)
2590           (when (= (point-min) (point-max))
2591             (setq all-removed t))
2592           (goto-char (point-max)))
2593         (if all-removed
2594             (goto-char start)
2595           (forward-line 1))))
2596     ;; Delete blank lines at the start of the buffer.
2597     (while (and (point-min)
2598                 (eolp)
2599                 (not (eobp)))
2600       (message-delete-line))
2601     ;; Delete blank lines at the end of the buffer.
2602     (goto-char (point-max))
2603     (unless (bolp)
2604       (insert "\n"))
2605     (while (and (zerop (forward-line -1))
2606                 (looking-at "$"))
2607       (message-delete-line))
2608     ;; Do the indentation.
2609     (if (null message-yank-prefix)
2610         (indent-rigidly start (mark t) message-indentation-spaces)
2611       (save-excursion
2612         (goto-char start)
2613         (while (< (point) (mark t))
2614           (if (or (looking-at ">") (looking-at "^$"))
2615               (insert message-yank-cited-prefix)
2616             (insert message-yank-prefix))
2617           (forward-line 1))))
2618     (goto-char start)))
2619
2620 (defun message-list-references (refs-list &rest refs-strs)
2621   "Add `Message-ID's which appear in REFS-STRS but not in REFS-LIST,
2622 to REFS-LIST."
2623   (let (refs ref id saved-id)
2624     (when (and refs-list
2625                (integerp message-list-references-add-position))
2626       (let ((pos message-list-references-add-position))
2627         (while (and refs-list
2628                     (> pos 0))
2629           (push (pop refs-list) saved-id)
2630           (setq pos (1- pos)))))
2631     (while refs-strs
2632       (when (setq refs (pop refs-strs))
2633         (setq refs (std11-parse-msg-ids (std11-lexical-analyze refs)))
2634         (while refs
2635           (when (eq (car (setq ref (pop refs))) 'msg-id)
2636             (setq id (concat "<" (mapconcat 'cdr (cdr ref) "") ">"))
2637             (or (member id refs-list)
2638                 (member id saved-id)
2639                 (push id refs-list))))))
2640     (while saved-id
2641       (push (pop saved-id) refs-list))
2642     refs-list))
2643
2644 (defvar gnus-article-copy)
2645 (defun message-yank-original (&optional arg)
2646   "Insert the message being replied to, if any.
2647 Puts point before the text and mark after.
2648 Normally indents each nonblank line ARG spaces (default 3).  However,
2649 if `message-yank-prefix' is non-nil, insert that prefix on each line.
2650
2651 This function uses `message-cite-function' to do the actual citing.
2652
2653 Just \\[universal-argument] as argument means don't indent, insert no
2654 prefix, and don't delete any headers.
2655
2656 In addition, if `message-yank-add-new-references' is non-nil and this
2657 command is called interactively, new IDs from the yanked article will
2658 be added to \"References\" field.
2659 \(See also `message-yank-add-new-references'.)"
2660   (interactive "P")
2661   (let ((modified (buffer-modified-p))
2662         (buffer (message-eval-parameter message-reply-buffer))
2663         start end refs)
2664     (when (and buffer
2665                message-cite-function)
2666       (delete-windows-on buffer t)
2667       (insert-buffer buffer) ; mark will be set at the end of article.
2668       (setq start (point)
2669             end (mark t))
2670
2671       ;; Add new IDs to References field.
2672       (when (and message-yank-add-new-references (interactive-p))
2673         (save-excursion
2674           (save-restriction
2675             (message-narrow-to-headers)
2676             (setq refs (message-list-references
2677                         nil
2678                         (message-fetch-field "References")))
2679             (widen)
2680             (narrow-to-region start end)
2681             (std11-narrow-to-header)
2682             (when (setq refs (message-list-references
2683                               refs
2684                               (unless (eq message-yank-add-new-references
2685                                           'message-id-only)
2686                                 (or (message-fetch-field "References")
2687                                     (message-fetch-field "In-Reply-To")))
2688                               (message-fetch-field "Message-ID")))
2689               (widen)
2690               (message-narrow-to-headers)
2691               (goto-char (point-min))
2692               (let ((case-fold-search t))
2693                 (if (re-search-forward "^References:\\([\t ]+.+\n\\)+" nil t)
2694                     (replace-match "")
2695                   (goto-char (point-max))))
2696               (mail-header-format
2697                (list (or (assq 'References message-header-format-alist)
2698                          '(References . message-fill-references)))
2699                (list (cons 'References
2700                            (mapconcat 'identity (nreverse refs) " "))))
2701               (backward-delete-char 1)))))
2702
2703       (unless arg
2704         (if (and message-suspend-font-lock-when-citing
2705                  (boundp 'font-lock-mode)
2706                  (symbol-value 'font-lock-mode))
2707             (unwind-protect
2708                 (progn
2709                   (sit-for 0)
2710                   (font-lock-mode 0)
2711                   (funcall message-cite-function))
2712               (font-lock-mode 1))
2713           (funcall message-cite-function)))
2714       (message-exchange-point-and-mark)
2715       (unless (bolp)
2716         (insert ?\n))
2717       (unless modified
2718         (setq message-checksum (message-checksum))))))
2719
2720 (defun message-yank-buffer (buffer)
2721   "Insert BUFFER into the current buffer and quote it."
2722   (interactive "bYank buffer: ")
2723   (let ((message-reply-buffer buffer))
2724     (save-window-excursion
2725       (message-yank-original))))
2726
2727 (defun message-buffers ()
2728   "Return a list of active message buffers."
2729   (let (buffers)
2730     (save-excursion
2731       (dolist (buffer (buffer-list t))
2732         (set-buffer buffer)
2733         (when (and (eq major-mode 'message-mode)
2734                    (null message-sent-message-via))
2735           (push (buffer-name buffer) buffers))))
2736     (nreverse buffers)))
2737
2738 (defun message-cite-original-without-signature ()
2739   "Cite function in the standard Message manner."
2740   (let ((start (point))
2741         (end (mark t))
2742         (functions
2743          (when message-indent-citation-function
2744            (if (listp message-indent-citation-function)
2745                message-indent-citation-function
2746              (list message-indent-citation-function))))
2747         (message-reply-headers (or message-reply-headers
2748                                    (make-mail-header))))
2749     (mail-header-set-from message-reply-headers
2750                           (save-restriction
2751                             (narrow-to-region
2752                              (point)
2753                              (if (search-forward "\n\n" nil t)
2754                                  (1- (point))
2755                                (point-max)))
2756                             (or (message-fetch-field "from")
2757                                 "unknown sender")))
2758     ;; Allow undoing.
2759     (undo-boundary)
2760     (goto-char end)
2761     (when (re-search-backward message-signature-separator start t)
2762       ;; Also peel off any blank lines before the signature.
2763       (forward-line -1)
2764       (while (looking-at "^[ \t]*$")
2765         (forward-line -1))
2766       (forward-line 1)
2767       (delete-region (point) end)
2768       (unless (search-backward "\n\n" start t)
2769         ;; Insert a blank line if it is peeled off.
2770         (insert "\n")))
2771     (goto-char start)
2772     (while functions
2773       (funcall (pop functions)))
2774     (when message-citation-line-function
2775       (unless (bolp)
2776         (insert "\n"))
2777       (funcall message-citation-line-function))))
2778
2779 (eval-when-compile (defvar mail-citation-hook))         ;Compiler directive
2780 (defun message-cite-original ()
2781   "Cite function in the standard Message manner."
2782   (if (and (boundp 'mail-citation-hook)
2783            mail-citation-hook)
2784       (run-hooks 'mail-citation-hook)
2785     (let ((start (point))
2786           (end (mark t))
2787           (functions
2788            (when message-indent-citation-function
2789              (if (listp message-indent-citation-function)
2790                  message-indent-citation-function
2791                (list message-indent-citation-function))))
2792           (message-reply-headers (or message-reply-headers
2793                                      (make-mail-header))))
2794       (mail-header-set-from message-reply-headers
2795                             (save-restriction
2796                               (narrow-to-region
2797                                (point)
2798                                (if (search-forward "\n\n" nil t)
2799                                    (1- (point))
2800                                  (point-max)))
2801                               (or (message-fetch-field "from")
2802                                   "unknown sender")))
2803       (goto-char start)
2804       (while functions
2805         (funcall (pop functions)))
2806       (when message-citation-line-function
2807         (unless (bolp)
2808           (insert "\n"))
2809         (funcall message-citation-line-function)))))
2810
2811 (defun message-insert-citation-line ()
2812   "Insert a simple citation line."
2813   (when message-reply-headers
2814     (insert (mail-header-from message-reply-headers) " writes:\n\n")))
2815
2816 (defun message-position-on-field (header &rest afters)
2817   (let ((case-fold-search t))
2818     (save-restriction
2819       (narrow-to-region
2820        (goto-char (point-min))
2821        (progn
2822          (re-search-forward
2823           (concat "^" (regexp-quote mail-header-separator) "$"))
2824          (match-beginning 0)))
2825       (goto-char (point-min))
2826       (if (re-search-forward (concat "^" (regexp-quote header) ":") nil t)
2827           (progn
2828             (re-search-forward "^[^ \t]" nil 'move)
2829             (beginning-of-line)
2830             (skip-chars-backward "\n")
2831             t)
2832         (while (and afters
2833                     (not (re-search-forward
2834                           (concat "^" (regexp-quote (car afters)) ":")
2835                           nil t)))
2836           (pop afters))
2837         (when afters
2838           (re-search-forward "^[^ \t]" nil 'move)
2839           (beginning-of-line))
2840         (insert header ": \n")
2841         (forward-char -1)
2842         nil))))
2843
2844 (defun message-remove-signature ()
2845   "Remove the signature from the text between point and mark.
2846 The text will also be indented the normal way."
2847   (save-excursion
2848     (let ((start (point))
2849           mark)
2850       (if (not (re-search-forward message-signature-separator (mark t) t))
2851           ;; No signature here, so we just indent the cited text.
2852           (message-indent-citation)
2853         ;; Find the last non-empty line.
2854         (forward-line -1)
2855         (while (looking-at "[ \t]*$")
2856           (forward-line -1))
2857         (forward-line 1)
2858         (setq mark (set-marker (make-marker) (point)))
2859         (goto-char start)
2860         (message-indent-citation)
2861         ;; Enable undoing the deletion.
2862         (undo-boundary)
2863         (delete-region mark (mark t))
2864         (set-marker mark nil)))))
2865
2866 \f
2867
2868 ;;;
2869 ;;; Sending messages
2870 ;;;
2871
2872 ;; Avoid byte-compile warning.
2873 (defvar message-encoding-buffer nil)
2874 (defvar message-edit-buffer nil)
2875 (defvar message-mime-mode nil)
2876
2877 (defun message-send-and-exit (&optional arg)
2878   "Send message like `message-send', then, if no errors, exit from mail buffer."
2879   (interactive "P")
2880   (let ((buf (current-buffer))
2881         (actions message-exit-actions)
2882         (frame (selected-frame))
2883         (org-frame message-original-frame))
2884     (when (and (message-send arg)
2885                (buffer-name buf))
2886       (if message-kill-buffer-on-exit
2887           (kill-buffer buf)
2888         (bury-buffer buf)
2889         (when (eq buf (current-buffer))
2890           (message-bury buf)))
2891       (message-do-actions actions)
2892       (message-delete-frame frame org-frame)
2893       t)))
2894
2895 (defun message-dont-send ()
2896   "Don't send the message you have been editing."
2897   (interactive)
2898   (message-save-drafts)
2899   (let ((actions message-postpone-actions)
2900         (frame (selected-frame))
2901         (org-frame message-original-frame))
2902     (message-bury (current-buffer))
2903     (message-do-actions actions)
2904     (message-delete-frame frame org-frame)))
2905
2906 (defun message-kill-buffer ()
2907   "Kill the current buffer."
2908   (interactive)
2909   (when (or (not (buffer-modified-p))
2910             (eq t message-kill-buffer-query-function)
2911             (funcall message-kill-buffer-query-function
2912                      "The buffer modified; kill anyway? "))
2913     (let ((actions message-kill-actions)
2914           (draft-article message-draft-article)
2915           (auto-save-file-name buffer-auto-save-file-name)
2916           (file-name buffer-file-name)
2917           (modified (buffer-modified-p))
2918           (frame (selected-frame))
2919           (org-frame message-original-frame))
2920       (setq buffer-file-name nil)
2921       (kill-buffer (current-buffer))
2922       (when (and message-kill-buffer-and-remove-file
2923                  (or (and auto-save-file-name
2924                           (file-exists-p auto-save-file-name))
2925                      (and file-name
2926                           (file-exists-p file-name)))
2927                  (yes-or-no-p (format "Remove the backup file%s? "
2928                                       (if modified " too" ""))))
2929         (ignore-errors
2930           (delete-file auto-save-file-name))
2931         (let ((message-draft-article draft-article))
2932           (message-disassociate-draft)))
2933       (message-do-actions actions)
2934       (message-delete-frame frame org-frame)))
2935   (message ""))
2936
2937 (defun message-mimic-kill-buffer ()
2938   "Kill the current buffer with query.  This is an imitation for
2939 `kill-buffer', but it will delete a message frame."
2940   (interactive)
2941   (let ((bufname (read-buffer (format "Kill buffer: (default %s) "
2942                                       (buffer-name))))
2943         message-kill-buffer-and-remove-file)
2944     (when (or (not bufname)
2945               (string-equal bufname "")
2946               (string-equal bufname (buffer-name)))
2947       (message-kill-buffer))))
2948
2949 (defun message-delete-frame (frame org-frame)
2950   "Delete frame for editing message."
2951   (when (and (or (static-if (featurep 'xemacs)
2952                      (device-on-window-system-p)
2953                    window-system)
2954                  (>= emacs-major-version 20))
2955              (or (and (eq message-delete-frame-on-exit t)
2956                       (select-frame frame)
2957                       (or (eq frame org-frame)
2958                           (prog1
2959                               (y-or-n-p "Delete this frame?")
2960                             (message ""))))
2961                  (and (eq message-delete-frame-on-exit 'ask)
2962                       (select-frame frame)
2963                       (prog1
2964                           (y-or-n-p "Delete this frame?")
2965                         (message "")))))
2966     (delete-frame frame)))
2967
2968 (defun message-bury (buffer)
2969   "Bury this mail BUFFER."
2970   (let ((newbuf (other-buffer buffer)))
2971     (bury-buffer buffer)
2972     (if (and (fboundp 'frame-parameters)
2973              (cdr (assq 'dedicated (frame-parameters)))
2974              (not (null (delq (selected-frame) (visible-frame-list)))))
2975         (delete-frame (selected-frame))
2976       (switch-to-buffer newbuf))))
2977
2978 (defun message-send (&optional arg)
2979   "Send the message in the current buffer.
2980 If `message-interactive' is non-nil, wait for success indication or
2981 error messages, and inform user.
2982 Otherwise any failure is reported in a message back to the user from
2983 the mailer.
2984 The usage of ARG is defined by the instance that called Message.
2985 It should typically alter the sending method in some way or other."
2986   (interactive "P")
2987   ;; Disabled test.
2988   (when (or (buffer-modified-p)
2989             (message-check-element 'unchanged)
2990             (y-or-n-p "No changes in the buffer; really send? "))
2991     ;; Make it possible to undo the coming changes.
2992     (undo-boundary)
2993     (let ((inhibit-read-only t))
2994       (put-text-property (point-min) (point-max) 'read-only nil))
2995     (run-hooks 'message-send-hook)
2996     (message-fix-before-sending)
2997     (message message-sending-message)
2998     (let ((message-encoding-buffer
2999            (message-generate-new-buffer-clone-locals " message encoding"))
3000           (message-edit-buffer (current-buffer))
3001           (message-mime-mode mime-edit-mode-flag)
3002           (alist message-send-method-alist)
3003           (success t)
3004           elem sent dont-barf-on-no-method
3005           (message-options message-options))
3006       (message-options-set-recipient)
3007       (save-excursion
3008         (set-buffer message-encoding-buffer)
3009         (erase-buffer)
3010         ;; ;; Avoid copying text props (except hard newlines).
3011         ;; T-gnus change: copy all text props from the editing buffer
3012         ;; into the encoding buffer.
3013         (insert-buffer message-edit-buffer)
3014         (funcall message-encode-function)
3015         (while (and success
3016                     (setq elem (pop alist)))
3017           (when (funcall (cadr elem))
3018             (when (and (or (not (memq (car elem)
3019                                       message-sent-message-via))
3020                            (if (or (message-gnksa-enable-p 'multiple-copies)
3021                                    (not (eq (car elem) 'news)))
3022                                (y-or-n-p
3023                                 (format
3024                                  "Already sent message via %s; resend? "
3025                                  (car elem)))
3026                              (error "Denied posting -- multiple copies")))
3027                        (setq success (funcall (caddr elem) arg)))
3028               (setq sent t)))))
3029       (unless
3030           (or sent
3031               (not success)
3032               (let ((fcc (message-fetch-field "Fcc"))
3033                     (gcc (message-fetch-field "Gcc")))
3034                 (when (or fcc gcc)
3035                   (or (eq message-allow-no-recipients 'always)
3036                       (and (not (eq message-allow-no-recipients 'never))
3037                            (setq dont-barf-on-no-method
3038                                  (gnus-y-or-n-p
3039                                   (format "No receiver, perform %s anyway? "
3040                                           (cond ((and fcc gcc) "Fcc and Gcc")
3041                                                 (fcc "Fcc")
3042                                                 (t "Gcc"))))))))))
3043         (error "No methods specified to send by"))
3044       (prog1
3045           (when (or dont-barf-on-no-method
3046                     (and success sent))
3047             (message-do-fcc)
3048             (save-excursion
3049               (run-hooks 'message-sent-hook))
3050             (message "Sending...done")
3051             ;; Mark the buffer as unmodified and delete auto-save.
3052             (set-buffer-modified-p nil)
3053             (delete-auto-save-file-if-necessary t)
3054             (message-disassociate-draft)
3055             ;; Delete other mail buffers and stuff.
3056             (message-do-send-housekeeping)
3057             (message-do-actions message-send-actions)
3058             ;; Return success.
3059             t)
3060         (kill-buffer message-encoding-buffer)))))
3061
3062 (defun message-send-via-mail (arg)
3063   "Send the current message via mail."
3064   (message-send-mail arg))
3065
3066 (defun message-send-via-news (arg)
3067   "Send the current message via news."
3068   (message-send-news arg))
3069
3070 (defmacro message-check (type &rest forms)
3071   "Eval FORMS if TYPE is to be checked."
3072   `(or (message-check-element ,type)
3073        (save-excursion
3074          ,@forms)))
3075
3076 (put 'message-check 'lisp-indent-function 1)
3077 (put 'message-check 'edebug-form-spec '(form body))
3078
3079 ;; Advise the function `invisible-region'.
3080 (let (current-load-list)
3081   (eval
3082    `(defadvice invisible-region (around add-mime-edit-invisible (start end)
3083                                         activate)
3084       "Advised by T-gnus Message.
3085 Add the text property `mime-edit-invisible' to an invisible text when
3086 the buffer's major mode is `message-mode'.  The added property will be
3087 used to distinguish whether the invisible text is a MIME part or not."
3088       ,(if (featurep 'xemacs)
3089            '(if (eq ?\n (char-after start))
3090                 (setq start (1+ start)))
3091          '(if (eq ?\n (char-after (1- end)))
3092               (setq end (1- end))))
3093       (setq ad-return-value
3094             (if (eq 'message-mode major-mode)
3095                 (add-text-properties start end
3096                                      '(invisible t mime-edit-invisible t))
3097               (put-text-property start end 'invisible t))))))
3098
3099 (defun message-text-with-property (prop)
3100   "Return a list of all points where the text has PROP."
3101   (let ((points nil)
3102         (point (point-min)))
3103     (save-excursion
3104       (while (< point (point-max))
3105         (when (get-text-property point prop)
3106           (push point points))
3107         (incf point)))
3108     (nreverse points)))
3109
3110 (defun message-fix-before-sending ()
3111   "Do various things to make the message nice before sending it."
3112   ;; Make sure there's a newline at the end of the message.
3113   (widen)
3114   (goto-char (point-max))
3115   (unless (bolp)
3116     (insert "\n"))
3117   ;; Delete all invisible text except for the mime parts which might
3118   ;; be inserted by the MIME-Edit.
3119   (message-check 'invisible-text
3120     (let (from
3121           (to (point-min))
3122           mime-from mime-to hidden-start)
3123       (while (setq from (text-property-any to (point-max) 'invisible t))
3124         (setq to (or (text-property-not-all from (point-max) 'invisible t)
3125                      (point-max))
3126               mime-to from)
3127         (while (setq mime-from (text-property-any mime-to to
3128                                                   'mime-edit-invisible t))
3129           (when (> mime-from mime-to)
3130             (setq hidden-start (or hidden-start mime-to))
3131             (put-text-property mime-to mime-from 'invisible nil))
3132           (setq mime-to (or (text-property-not-all mime-from to
3133                                                    'mime-edit-invisible t)
3134                             to)))
3135         (when (< mime-to to)
3136           (setq hidden-start (or hidden-start mime-to))
3137           (put-text-property mime-to to 'invisible nil)))
3138       (when hidden-start
3139         (goto-char hidden-start)
3140         (set-window-start (selected-window) (gnus-point-at-bol))
3141         (unless (yes-or-no-p
3142                  "Invisible text found and made visible; continue posting? ")
3143           (error "Invisible text found and made visible"))))))
3144
3145 (defun message-add-action (action &rest types)
3146   "Add ACTION to be performed when doing an exit of type TYPES."
3147   (let (var)
3148     (while types
3149       (set (setq var (intern (format "message-%s-actions" (pop types))))
3150            (nconc (symbol-value var) (list action))))))
3151
3152 (defun message-do-actions (actions)
3153   "Perform all actions in ACTIONS."
3154   ;; Now perform actions on successful sending.
3155   (while actions
3156     (ignore-errors
3157       (cond
3158        ;; A simple function.
3159        ((message-functionp (car actions))
3160         (funcall (car actions)))
3161        ;; Something to be evaled.
3162        (t
3163         (eval (car actions)))))
3164     (pop actions)))
3165
3166 (defsubst message-maybe-split-and-send-mail ()
3167   "Split a message if necessary, and send it via mail.
3168 Returns nil if sending succeeded, returns any string if sending failed.
3169 This sub function is for exclusive use of `message-send-mail'."
3170   (let ((mime-edit-split-ignored-field-regexp
3171          mime-edit-split-ignored-field-regexp)
3172         (case-fold-search t)
3173         failure)
3174     (while (string-match "Message-ID" mime-edit-split-ignored-field-regexp)
3175       (setq mime-edit-split-ignored-field-regexp
3176             (concat (substring mime-edit-split-ignored-field-regexp
3177                                0 (match-beginning 0))
3178                     "Hey_MIME-Edit,_there_is_an_inviolable_Message_ID"
3179                     "_so_don't_rape_it!"
3180                     (substring mime-edit-split-ignored-field-regexp
3181                                (match-end 0)))))
3182     (setq failure
3183           (or
3184            (catch 'message-sending-mail-failure
3185              (mime-edit-maybe-split-and-send
3186               (function
3187                (lambda ()
3188                  (interactive)
3189                  (save-restriction
3190                    (std11-narrow-to-header mail-header-separator)
3191                    (goto-char (point-min))
3192                    (when (re-search-forward "^Message-ID:" nil t)
3193                      (delete-region (match-end 0) (std11-field-end))
3194                      (insert " " (message-make-message-id))))
3195                  (condition-case err
3196                      (funcall (or message-send-mail-real-function
3197                                   message-send-mail-function))
3198                    (error
3199                     (throw 'message-sending-mail-failure err))))))
3200              nil)
3201            (condition-case err
3202                (progn
3203                  (funcall (or message-send-mail-real-function
3204                               message-send-mail-function))
3205                  nil)
3206              (error err))))
3207     (when failure
3208       (if (eq 'error (car failure))
3209           (cadr failure)
3210         (prin1-to-string failure)))))
3211
3212 (defun message-send-mail-partially ()
3213   "Send mail as message/partial."
3214   ;; replace the header delimiter with a blank line
3215   (goto-char (point-min))
3216   (re-search-forward
3217    (concat "^" (regexp-quote mail-header-separator) "\n"))
3218   (replace-match "\n")
3219   (run-hooks 'message-send-mail-hook)
3220   (let ((p (goto-char (point-min)))
3221         (tembuf (message-generate-new-buffer-clone-locals " message temp"))
3222         (curbuf (current-buffer))
3223         (id (message-make-message-id)) (n 1)
3224         plist total  header required-mail-headers)
3225     (while (not (eobp))
3226       (if (< (point-max) (+ p message-send-mail-partially-limit))
3227           (goto-char (point-max))
3228         (goto-char (+ p message-send-mail-partially-limit))
3229         (beginning-of-line)
3230         (if (<= (point) p) (forward-line 1))) ;; In case of bad message.
3231       (push p plist)
3232       (setq p (point)))
3233     (setq total (length plist))
3234     (push (point-max) plist)
3235     (setq plist (nreverse plist))
3236     (unwind-protect
3237         (save-excursion
3238           (setq p (pop plist))
3239           (while plist
3240             (set-buffer curbuf)
3241             (copy-to-buffer tembuf p (car plist))
3242             (set-buffer tembuf)
3243             (goto-char (point-min))
3244             (if header
3245                 (progn
3246                   (goto-char (point-min))
3247                   (narrow-to-region (point) (point))
3248                   (insert header))
3249               (message-goto-eoh)
3250               (setq header (buffer-substring (point-min) (point)))
3251               (goto-char (point-min))
3252               (narrow-to-region (point) (point))
3253               (insert header)
3254               (message-remove-header "Mime-Version")
3255               (message-remove-header "Content-Type")
3256               (message-remove-header "Content-Transfer-Encoding")
3257               (message-remove-header "Message-ID")
3258               (message-remove-header "Lines")
3259               (goto-char (point-max))
3260               (insert "Mime-Version: 1.0\n")
3261               (setq header (buffer-substring (point-min) (point-max))))
3262             (goto-char (point-max))
3263             (insert (format "Content-Type: message/partial; id=\"%s\"; number=%d; total=%d\n\n"
3264                             id n total))
3265             (forward-char -1)
3266             (let ((mail-header-separator ""))
3267               (when (memq 'Message-ID message-required-mail-headers)
3268                 (insert "Message-ID: " (message-make-message-id) "\n"))
3269               (when (memq 'Lines message-required-mail-headers)
3270                 (insert "Lines: " (message-make-lines) "\n"))
3271               (message-goto-subject)
3272               (end-of-line)
3273               (insert (format " (%d/%d)" n total))
3274               (widen)
3275               (mm-with-unibyte-current-buffer
3276                 (funcall (or message-send-mail-real-function
3277                              message-send-mail-function))))
3278             (setq n (+ n 1))
3279             (setq p (pop plist))
3280             (erase-buffer)))
3281       (kill-buffer tembuf))))
3282
3283 (defun message-send-mail (&optional arg)
3284   (require 'mail-utils)
3285   (let* ((tembuf (message-generate-new-buffer-clone-locals " message temp"))
3286          (case-fold-search nil)
3287          (news (message-news-p))
3288          (message-this-is-mail t)
3289          (headers message-required-mail-headers)
3290          failure)
3291     (save-restriction
3292       (message-narrow-to-headers)
3293       ;; Generate the Mail-Followup-To header if the header is not there...
3294       (if (and (or message-subscribed-regexps
3295                    message-subscribed-addresses
3296                    message-subscribed-address-file
3297                    message-subscribed-address-functions)
3298                (not (mail-fetch-field "mail-followup-to")))
3299           (setq headers
3300                 (cons
3301                  (cons "Mail-Followup-To" (message-make-mft))
3302                  message-required-mail-headers))
3303         ;; otherwise, delete the MFT header if the field is empty
3304         (when (equal "" (mail-fetch-field "mail-followup-to"))
3305           (message-remove-header "^Mail-Followup-To:")))
3306       ;; Insert some headers.
3307       (let ((message-deletable-headers
3308              (if news nil message-deletable-headers)))
3309         (message-generate-headers headers))
3310       ;; Let the user do all of the above.
3311       (run-hooks 'message-header-hook))
3312     (if (not (message-check-mail-syntax))
3313         (progn
3314           (message "")
3315           nil)
3316       (unwind-protect
3317           (save-excursion
3318             (set-buffer tembuf)
3319             (erase-buffer)
3320             ;; ;; Avoid copying text props (except hard newlines).
3321             ;; T-gnus change: copy all text props from the editing buffer
3322             ;; into the encoding buffer.
3323             (insert-buffer message-encoding-buffer)
3324             ;; Remove some headers.
3325             (save-restriction
3326               (message-narrow-to-headers)
3327 ;; We Semi-gnus people have no use for it.
3328 ;;            ;; We (re)generate the Lines header.
3329 ;;            (when (memq 'Lines message-required-mail-headers)
3330 ;;              (message-generate-headers '(Lines)))
3331               (message-remove-header message-ignored-mail-headers t))
3332             (goto-char (point-max))
3333             ;; require one newline at the end.
3334             (or (= (preceding-char) ?\n)
3335                 (insert ?\n))
3336             (when
3337                 (save-restriction
3338                   (message-narrow-to-headers)
3339                   (and news
3340                        (or (message-fetch-field "cc")
3341                            (message-fetch-field "to"))
3342                        (let ((ct (mime-read-Content-Type)))
3343                          (or (not ct)
3344                              (and (eq 'text (cdr (assq 'type ct)))
3345                                   (eq 'plain (cdr (assq 'subtype ct))))))))
3346               (message-insert-courtesy-copy))
3347             (setq failure (message-maybe-split-and-send-mail)))
3348         (kill-buffer tembuf))
3349       (set-buffer message-edit-buffer)
3350       (if failure
3351           (progn
3352             (message "Couldn't send message via mail: %s" failure)
3353             nil)
3354         (push 'mail message-sent-message-via)))))
3355
3356 (defun message-send-mail-with-sendmail ()
3357   "Send off the prepared buffer with sendmail."
3358   (let ((errbuf (if message-interactive
3359                     (message-generate-new-buffer-clone-locals
3360                      " sendmail errors")
3361                   0))
3362         resend-to-addresses delimline)
3363     (let ((case-fold-search t))
3364       (save-restriction
3365         (message-narrow-to-headers)
3366         (setq resend-to-addresses (message-fetch-field "resent-to")))
3367       ;; Change header-delimiter to be what sendmail expects.
3368       (goto-char (point-min))
3369       (re-search-forward
3370        (concat "^" (regexp-quote mail-header-separator) "\n"))
3371       (replace-match "\n")
3372       (backward-char 1)
3373       (setq delimline (point-marker))
3374       (run-hooks 'message-send-mail-hook)
3375       ;; Insert an extra newline if we need it to work around
3376       ;; Sun's bug that swallows newlines.
3377       (goto-char (1+ delimline))
3378       (when (eval message-mailer-swallows-blank-line)
3379         (newline))
3380       (when message-interactive
3381         (save-excursion
3382           (set-buffer errbuf)
3383           (erase-buffer))))
3384     (let ((default-directory "/"))
3385       (as-binary-process
3386        (apply 'call-process-region
3387               (append (list (point-min) (point-max)
3388                             (if (boundp 'sendmail-program)
3389                                 sendmail-program
3390                               "/usr/lib/sendmail")
3391                             nil errbuf nil "-oi")
3392                       ;; Always specify who from,
3393                       ;; since some systems have broken sendmails.
3394                       ;; But some systems are more broken with -f, so
3395                       ;; we'll let users override this.
3396                       (if (null message-sendmail-f-is-evil)
3397                           (list "-f" (message-make-address)))
3398                       ;; These mean "report errors by mail"
3399                       ;; and "deliver in background".
3400                       (if (null message-interactive) '("-oem" "-odb"))
3401                       ;; Get the addresses from the message
3402                       ;; unless this is a resend.
3403                       ;; We must not do that for a resend
3404                       ;; because we would find the original addresses.
3405                       ;; For a resend, include the specific addresses.
3406                       (if resend-to-addresses
3407                           (list resend-to-addresses)
3408                         '("-t"))))))
3409     (when message-interactive
3410       (save-excursion
3411         (set-buffer errbuf)
3412         (goto-char (point-min))
3413         (while (re-search-forward "\n\n* *" nil t)
3414           (replace-match "; "))
3415         (if (not (zerop (buffer-size)))
3416             (error "Sending...failed to %s"
3417                    (buffer-substring (point-min) (point-max)))))
3418       (when (bufferp errbuf)
3419         (kill-buffer errbuf)))))
3420
3421 (defun message-send-mail-with-qmail ()
3422   "Pass the prepared message buffer to qmail-inject.
3423 Refer to the documentation for the variable `message-send-mail-function'
3424 to find out how to use this."
3425   ;; replace the header delimiter with a blank line
3426   (goto-char (point-min))
3427   (re-search-forward
3428    (concat "^" (regexp-quote mail-header-separator) "\n"))
3429   (replace-match "\n")
3430   (backward-char 1)
3431   (run-hooks 'message-send-mail-hook)
3432   ;; send the message
3433   (case
3434       (as-binary-process
3435        (apply
3436         'call-process-region 1 (point-max) message-qmail-inject-program
3437         nil nil nil
3438         ;; qmail-inject's default behaviour is to look for addresses on the
3439         ;; command line; if there're none, it scans the headers.
3440         ;; yes, it does The Right Thing w.r.t. Resent-To and it's kin.
3441         ;;
3442         ;; in general, ALL of qmail-inject's defaults are perfect for simply
3443         ;; reading a formatted (i. e., at least a To: or Resent-To header)
3444         ;; message from stdin.
3445         ;;
3446         ;; qmail also has the advantage of not having been raped by
3447         ;; various vendors, so we don't have to allow for that, either --
3448         ;; compare this with message-send-mail-with-sendmail and weep
3449         ;; for sendmail's lost innocence.
3450         ;;
3451         ;; all this is way cool coz it lets us keep the arguments entirely
3452         ;; free for -inject-arguments -- a big win for the user and for us
3453         ;; since we don't have to play that double-guessing game and the user
3454         ;; gets full control (no gestapo'ish -f's, for instance).  --sj
3455         message-qmail-inject-args))
3456     ;; qmail-inject doesn't say anything on it's stdout/stderr,
3457     ;; we have to look at the retval instead
3458     (0 nil)
3459     (100 (error "qmail-inject reported permanent failure"))
3460     (111 (error "qmail-inject reported transient failure"))
3461     ;; should never happen
3462     (t   (error "qmail-inject reported unknown failure"))))
3463
3464 (defun message-send-mail-with-mh ()
3465   "Send the prepared message buffer with mh."
3466   (let ((mh-previous-window-config nil)
3467         (name (mh-new-draft-name)))
3468     (setq buffer-file-name name)
3469     ;; MH wants to generate these headers itself.
3470     (when message-mh-deletable-headers
3471       (let ((headers message-mh-deletable-headers))
3472         (while headers
3473           (goto-char (point-min))
3474           (and (re-search-forward
3475                 (concat "^" (symbol-name (car headers)) ": *") nil t)
3476                (message-delete-line))
3477           (pop headers))))
3478     (run-hooks 'message-send-mail-hook)
3479     ;; Pass it on to mh.
3480     (mh-send-letter)))
3481
3482 (defun message-send-mail-with-smtp ()
3483   "Send off the prepared buffer with SMTP."
3484   (require 'smtp) ; XXX
3485   (let ((case-fold-search t)
3486         recipients)
3487     (save-restriction
3488       (message-narrow-to-headers)
3489       (setq recipients
3490             ;; XXX: Should be replaced by better one.
3491             (smtp-deduce-address-list (current-buffer)
3492                                       (point-min) (point-max)))
3493       ;; Remove BCC lines.
3494       (message-remove-header "bcc"))
3495     ;; replace the header delimiter with a blank line.
3496     (goto-char (point-min))
3497     (re-search-forward
3498      (concat "^" (regexp-quote mail-header-separator) "\n"))
3499     (replace-match "\n")
3500     (backward-char 1)
3501     (run-hooks 'message-send-mail-hook)
3502     (if recipients
3503         (static-if (fboundp 'smtp-send-buffer)
3504             (smtp-send-buffer user-mail-address recipients
3505                               (current-buffer))
3506           (let ((result (smtp-via-smtp user-mail-address recipients
3507                                        (current-buffer))))
3508             (unless (eq result t)
3509               (error "Sending failed; %s" result))))
3510       (error "Sending failed; no recipients"))))
3511
3512 (defsubst message-maybe-split-and-send-news (method)
3513   "Split a message if necessary, and send it via news.
3514 Returns nil if sending succeeded, returns t if sending failed.
3515 This sub function is for exclusive use of `message-send-news'."
3516   (let ((mime-edit-split-ignored-field-regexp
3517          mime-edit-split-ignored-field-regexp)
3518         (case-fold-search t))
3519     (while (string-match "Message-ID" mime-edit-split-ignored-field-regexp)
3520       (setq mime-edit-split-ignored-field-regexp
3521             (concat (substring mime-edit-split-ignored-field-regexp
3522                                0 (match-beginning 0))
3523                     "Hey_MIME-Edit,_there_is_an_inviolable_Message_ID"
3524                     "_so_don't_rape_it!"
3525                     (substring mime-edit-split-ignored-field-regexp
3526                                (match-end 0)))))
3527     (or
3528      (catch 'message-sending-news-failure
3529        (mime-edit-maybe-split-and-send
3530         (function
3531          (lambda ()
3532            (interactive)
3533            (save-restriction
3534              (std11-narrow-to-header mail-header-separator)
3535              (goto-char (point-min))
3536              (when (re-search-forward "^Message-ID:" nil t)
3537                (delete-region (match-end 0) (std11-field-end))
3538                (insert " " (message-make-message-id))))
3539            (unless (funcall message-send-news-function method)
3540              (throw 'message-sending-news-failure t)))))
3541        nil)
3542      (not (funcall message-send-news-function method)))))
3543
3544 (defun message-canlock-generate ()
3545   "Return a string that is non-trival to guess.
3546 Do not use this for anything important, it is cryptographically weak."
3547   (sha1 (concat (message-unique-id)
3548                 (format "%x%x%x" (random) (random t) (random))
3549                 (prin1-to-string (recent-keys))
3550                 (prin1-to-string (garbage-collect)))))
3551
3552 (defun message-canlock-password ()
3553   "The password used by message for cancel locks.
3554 This is the value of `canlock-password', if that option is non-nil.
3555 Otherwise, generate and save a value for `canlock-password' first."
3556   (unless canlock-password
3557     (customize-save-variable 'canlock-password (message-canlock-generate)))
3558   canlock-password)
3559
3560 (defun message-insert-canlock ()
3561   (when message-insert-canlock
3562     (message-canlock-password)
3563     (canlock-insert-header)))
3564
3565 (defun message-send-news (&optional arg)
3566   (let* ((tembuf (message-generate-new-buffer-clone-locals " *message temp*"))
3567          (case-fold-search nil)
3568          (method (if (message-functionp message-post-method)
3569                      (funcall message-post-method arg)
3570                    message-post-method))
3571          (newsgroups-field (save-restriction
3572                              (message-narrow-to-headers-or-head)
3573                              (message-fetch-field "Newsgroups")))
3574          (followup-field (save-restriction
3575                            (message-narrow-to-headers-or-head)
3576                            (message-fetch-field "Followup-To")))
3577          ;; BUG: We really need to get the charset for each name in the
3578          ;; Newsgroups and Followup-To lines to allow crossposting
3579          ;; between group namess with incompatible character sets.
3580          ;; -- Per Abrahamsen <abraham@dina.kvl.dk> 2001-10-08.
3581          (group-field-charset
3582           (gnus-group-name-charset method newsgroups-field))
3583          (followup-field-charset
3584           (gnus-group-name-charset method (or followup-field "")))
3585          (mime-field-encoding-method-alist
3586           (append (when group-field-charset
3587                     (list (cons "Newsgroups" group-field-charset)))
3588                   (when followup-field-charset
3589                     (list (cons "Followup-To" followup-field-charset)))
3590                   mime-field-encoding-method-alist))
3591          (message-syntax-checks
3592           (if (and arg
3593                    (listp message-syntax-checks))
3594               (cons '(existing-newsgroups . disabled)
3595                     message-syntax-checks)
3596             message-syntax-checks))
3597          (message-this-is-news t)
3598          result)
3599     (save-restriction
3600       (message-narrow-to-headers)
3601       ;; Insert some headers.
3602       (message-generate-headers message-required-news-headers)
3603       (message-insert-canlock)
3604       ;; Let the user do all of the above.
3605       (run-hooks 'message-header-hook))
3606     ;; Note: This check will be disabled by the ".*" default value for
3607     ;; gnus-group-name-charset-group-alist. -- Pa 2001-10-07.
3608     (when (and group-field-charset
3609                (listp message-syntax-checks))
3610       (setq message-syntax-checks
3611             (cons '(valid-newsgroups . disabled)
3612                   message-syntax-checks)))
3613     (message-cleanup-headers)
3614     (if (not (let ((message-post-method method))
3615                (message-check-news-syntax)))
3616         nil
3617       (unwind-protect
3618           (save-excursion
3619             (set-buffer tembuf)
3620             (buffer-disable-undo)
3621             (erase-buffer)
3622             (insert-buffer message-encoding-buffer)
3623             ;; Remove some headers.
3624             (save-restriction
3625               (message-narrow-to-headers)
3626 ;; We Semi-gnus people have no use for it.
3627 ;;            ;; We (re)generate the Lines header.
3628 ;;            (when (memq 'Lines message-required-mail-headers)
3629 ;;              (message-generate-headers '(Lines)))
3630               ;; Remove some headers.
3631               (message-remove-header message-ignored-news-headers t))
3632             (goto-char (point-max))
3633             ;; require one newline at the end.
3634             (or (= (preceding-char) ?\n)
3635                 (insert ?\n))
3636             (setq result (message-maybe-split-and-send-news method)))
3637         (kill-buffer tembuf))
3638       (set-buffer message-edit-buffer)
3639       (if result
3640           (progn
3641             (message "Couldn't send message via news: %s"
3642                      (nnheader-get-report (car method)))
3643             nil)
3644         (push 'news message-sent-message-via)))))
3645
3646 ;; 1997-09-29 by MORIOKA Tomohiko
3647 (defun message-send-news-with-gnus (method)
3648   (let ((case-fold-search t))
3649     ;; Remove the delimiter.
3650     (goto-char (point-min))
3651     (re-search-forward
3652      (concat "^" (regexp-quote mail-header-separator) "\n"))
3653     (replace-match "\n")
3654     (backward-char 1)
3655     (run-hooks 'message-send-news-hook)
3656     (gnus-open-server method)
3657     (message "Sending news with %s..." (gnus-server-string method))
3658     (gnus-request-post method)
3659     ))
3660
3661 ;;;
3662 ;;; Header generation & syntax checking.
3663 ;;;
3664
3665 (defun message-check-element (type)
3666   "Return non-nil if this TYPE is not to be checked."
3667   (if (eq message-syntax-checks 'dont-check-for-anything-just-trust-me)
3668       t
3669     (let ((able (assq type message-syntax-checks)))
3670       (and (consp able)
3671            (eq (cdr able) 'disabled)))))
3672
3673 (defun message-check-news-syntax ()
3674   "Check the syntax of the message."
3675   (save-excursion
3676     (save-restriction
3677       (widen)
3678       (and
3679        ;; We narrow to the headers and check them first.
3680        (save-excursion
3681          (save-restriction
3682            (message-narrow-to-headers)
3683            (message-check-news-header-syntax)))
3684        ;; Check the body.
3685        (save-excursion
3686          (set-buffer message-edit-buffer)
3687          (message-check-news-body-syntax))))))
3688
3689 (defun message-check-news-header-syntax ()
3690   (and
3691    ;; Check Newsgroups header.
3692    (message-check 'newsgroups
3693      (let ((group (message-fetch-field "newsgroups")))
3694        (or
3695         (and group
3696              (not (string-match "\\`[ \t]*\\'" group)))
3697         (ignore
3698          (message
3699           "The newsgroups field is empty or missing.  Posting is denied.")))))
3700    ;; Check the Subject header.
3701    (message-check 'subject
3702      (let* ((case-fold-search t)
3703             (subject (message-fetch-field "subject")))
3704        (or
3705         (and subject
3706              (not (string-match "\\`[ \t]*\\'" subject)))
3707         (ignore
3708          (message
3709           "The subject field is empty or missing.  Posting is denied.")))))
3710    ;; Check for commands in Subject.
3711    (message-check 'subject-cmsg
3712      (if (string-match "^cmsg " (message-fetch-field "subject"))
3713          (y-or-n-p
3714           "The control code \"cmsg\" is in the subject.  Really post? ")
3715        t))
3716    ;; Check for multiple identical headers.
3717    (message-check 'multiple-headers
3718      (let (found)
3719        (while (and (not found)
3720                    (re-search-forward "^[^ \t:]+: " nil t))
3721          (save-excursion
3722            (or (re-search-forward
3723                 (concat "^"
3724                         (regexp-quote
3725                          (setq found
3726                                (buffer-substring
3727                                 (match-beginning 0) (- (match-end 0) 2))))
3728                         ":")
3729                 nil t)
3730                (setq found nil))))
3731        (if found
3732            (y-or-n-p (format "Multiple %s headers.  Really post? " found))
3733          t)))
3734    ;; Check for Version and Sendsys.
3735    (message-check 'sendsys
3736      (if (re-search-forward "^Sendsys:\\|^Version:" nil t)
3737          (y-or-n-p
3738           (format "The article contains a %s command.  Really post? "
3739                   (buffer-substring (match-beginning 0)
3740                                     (1- (match-end 0)))))
3741        t))
3742    ;; See whether we can shorten Followup-To.
3743    (message-check 'shorten-followup-to
3744      (let ((newsgroups (message-fetch-field "newsgroups"))
3745            (followup-to (message-fetch-field "followup-to"))
3746            to)
3747        (when (and newsgroups
3748                   (string-match "," newsgroups)
3749                   (not followup-to)
3750                   (not
3751                    (zerop
3752                     (length
3753                      (setq to (completing-read
3754                                "Followups to (default: no Followup-To header) "
3755                                (mapcar (lambda (g) (list g))
3756                                        (cons "poster"
3757                                              (message-tokenize-header
3758                                               newsgroups)))))))))
3759          (goto-char (point-min))
3760          (insert "Followup-To: " to "\n"))
3761        t))
3762    ;; Check "Shoot me".
3763    (message-check 'shoot
3764      (if (re-search-forward
3765           "Message-ID.*.i-did-not-set--mail-host-address--so-shoot-me" nil t)
3766          (y-or-n-p "You appear to have a misconfigured system.  Really post? ")
3767        t))
3768    ;; Check for Approved.
3769    (message-check 'approved
3770      (if (re-search-forward "^Approved:" nil t)
3771          (y-or-n-p "The article contains an Approved header.  Really post? ")
3772        t))
3773    ;; Check the Message-ID header.
3774    (message-check 'message-id
3775      (let* ((case-fold-search t)
3776             (message-id (message-fetch-field "message-id" t)))
3777        (or (not message-id)
3778            ;; Is there an @ in the ID?
3779            (and (string-match "@" message-id)
3780                 ;; Is there a dot in the ID?
3781                 (string-match "@[^.]*\\." message-id)
3782                 ;; Does the ID end with a dot?
3783                 (not (string-match "\\.>" message-id)))
3784            (y-or-n-p
3785             (format "The Message-ID looks strange: \"%s\".  Really post? "
3786                     message-id)))))
3787    ;; Check the Newsgroups & Followup-To headers.
3788    (message-check 'existing-newsgroups
3789      (let* ((case-fold-search t)
3790             (newsgroups (message-fetch-field "newsgroups"))
3791             (followup-to (message-fetch-field "followup-to"))
3792             (groups (message-tokenize-header
3793                      (if followup-to
3794                          (concat newsgroups "," followup-to)
3795                        newsgroups)))
3796             (post-method (if (message-functionp message-post-method)
3797                              (funcall message-post-method)
3798                            message-post-method))
3799             ;; KLUDGE to handle nnvirtual groups.  Doing this right
3800             ;; would probably involve a new nnoo function.
3801             ;; -- Per Abrahamsen <abraham@dina.kvl.dk>, 2001-10-17.
3802             (method (if (and (consp post-method)
3803                              (eq (car post-method) 'nnvirtual)
3804                              gnus-message-group-art)
3805                         (let ((group (car (nnvirtual-find-group-art
3806                                            (car gnus-message-group-art)
3807                                            (cdr gnus-message-group-art)))))
3808                           (gnus-find-method-for-group group))
3809                       post-method))
3810             (known-groups
3811              (mapcar (lambda (n)
3812                        (gnus-group-name-decode
3813                         (gnus-group-real-name n)
3814                         (gnus-group-name-charset method n)))
3815                      (gnus-groups-from-server method)))
3816             errors)
3817        (while groups
3818          (unless (or (equal (car groups) "poster")
3819                      (member (car groups) known-groups))
3820            (push (car groups) errors))
3821          (pop groups))
3822        (cond
3823         ;; Gnus is not running.
3824         ((or (not (and (boundp 'gnus-active-hashtb)
3825                        gnus-active-hashtb))
3826              (not (boundp 'gnus-read-active-file)))
3827          t)
3828         ;; We don't have all the group names.
3829         ((and (or (not gnus-read-active-file)
3830                   (eq gnus-read-active-file 'some))
3831               errors)
3832          (y-or-n-p
3833           (format
3834            "Really post to %s possibly unknown group%s: %s? "
3835            (if (= (length errors) 1) "this" "these")
3836            (if (= (length errors) 1) "" "s")
3837            (mapconcat 'identity errors ", "))))
3838         ;; There were no errors.
3839         ((not errors)
3840          t)
3841         ;; There are unknown groups.
3842         (t
3843          (y-or-n-p
3844           (format
3845            "Really post to %s unknown group%s: %s? "
3846            (if (= (length errors) 1) "this" "these")
3847            (if (= (length errors) 1) "" "s")
3848            (mapconcat 'identity errors ", ")))))))
3849    ;; Check the Newsgroups & Followup-To headers for syntax errors.
3850    (message-check 'valid-newsgroups
3851      (let ((case-fold-search t)
3852            (headers '("Newsgroups" "Followup-To"))
3853            header error)
3854        (while (and headers (not error))
3855          (when (setq header (mail-fetch-field (car headers)))
3856            (if (or
3857                 (not
3858                  (string-match
3859                   "\\`\\([-+_&.a-zA-Z0-9]+\\)?\\(,[-+_&.a-zA-Z0-9]+\\)*\\'"
3860                   header))
3861                 (memq
3862                  nil (mapcar
3863                       (lambda (g)
3864                         (not (string-match "\\.\\'\\|\\.\\." g)))
3865                       (message-tokenize-header header ","))))
3866                (setq error t)))
3867          (unless error
3868            (pop headers)))
3869        (if (not error)
3870            t
3871          (y-or-n-p
3872           (format "The %s header looks odd: \"%s\".  Really post? "
3873                   (car headers) header)))))
3874    (message-check 'repeated-newsgroups
3875      (let ((case-fold-search t)
3876            (headers '("Newsgroups" "Followup-To"))
3877            header error groups group)
3878        (while (and headers
3879                    (not error))
3880          (when (setq header (mail-fetch-field (pop headers)))
3881            (setq groups (message-tokenize-header header ","))
3882            (while (setq group (pop groups))
3883              (when (member group groups)
3884                (setq error group
3885                      groups nil)))))
3886        (if (not error)
3887            t
3888          (y-or-n-p
3889           (format "Group %s is repeated in headers.  Really post? " error)))))
3890    ;; Check the From header.
3891    (message-check 'from
3892      (let* ((case-fold-search t)
3893             (from (message-fetch-field "from"))
3894             ad)
3895        (cond
3896         ((not from)
3897          (message "There is no From line.  Posting is denied.")
3898          nil)
3899         ((or (not (string-match
3900                    "@[^\\.]*\\."
3901                    (setq ad (nth 1 (mail-extract-address-components
3902                                     from))))) ;larsi@ifi
3903              (string-match "\\.\\." ad) ;larsi@ifi..uio
3904              (string-match "@\\." ad)   ;larsi@.ifi.uio
3905              (string-match "\\.$" ad)   ;larsi@ifi.uio.
3906              (not (string-match "^[^@]+@[^@]+$" ad)) ;larsi.ifi.uio
3907              (string-match "(.*).*(.*)" from)) ;(lars) (lars)
3908          (message
3909           "Denied posting -- the From looks strange: \"%s\"." from)
3910          nil)
3911         ((let ((addresses (rfc822-addresses from)))
3912            (while (and addresses
3913                        (not (eq (string-to-char (car addresses)) ?\()))
3914              (setq addresses (cdr addresses)))
3915            addresses)
3916          (message
3917           "Denied posting -- bad From address: \"%s\"." from)
3918          nil)
3919         (t t))))
3920    ;; Check the Reply-To header.
3921    (message-check 'reply-to
3922      (let* ((case-fold-search t)
3923             (reply-to (message-fetch-field "reply-to"))
3924             ad)
3925        (cond
3926         ((not reply-to)
3927          t)
3928         ((string-match "," reply-to)
3929          (y-or-n-p
3930           (format "Multiple Reply-To addresses: \"%s\". Really post? "
3931                   reply-to)))
3932         ((or (not (string-match
3933                    "@[^\\.]*\\."
3934                    (setq ad (nth 1 (mail-extract-address-components
3935                                     reply-to))))) ;larsi@ifi
3936              (string-match "\\.\\." ad) ;larsi@ifi..uio
3937              (string-match "@\\." ad)   ;larsi@.ifi.uio
3938              (string-match "\\.$" ad)   ;larsi@ifi.uio.
3939              (not (string-match "^[^@]+@[^@]+$" ad)) ;larsi.ifi.uio
3940              (string-match "(.*).*(.*)" reply-to)) ;(lars) (lars)
3941          (y-or-n-p
3942           (format
3943            "The Reply-To looks strange: \"%s\". Really post? "
3944            reply-to)))
3945         (t t))))))
3946
3947 (defun message-check-news-body-syntax ()
3948   (and
3949    ;; Check for long lines.
3950    (message-check 'long-lines
3951      (goto-char (point-min))
3952      (re-search-forward
3953       (concat "^" (regexp-quote mail-header-separator) "$"))
3954      (forward-line 1)
3955      (while (and
3956              (or (looking-at
3957                   mime-edit-tag-regexp)
3958                  (let ((p (point)))
3959                    (end-of-line)
3960                    (< (- (point) p) 80)))
3961              (zerop (forward-line 1))))
3962      (or (bolp)
3963          (eobp)
3964          (y-or-n-p
3965           "You have lines longer than 79 characters.  Really post? ")))
3966    ;; Check whether the article is empty.
3967    (message-check 'empty
3968      (goto-char (point-min))
3969      (re-search-forward
3970       (concat "^" (regexp-quote mail-header-separator) "$"))
3971      (forward-line 1)
3972      (let ((b (point)))
3973        (goto-char (point-max))
3974        (re-search-backward message-signature-separator nil t)
3975        (beginning-of-line)
3976        (or (re-search-backward "[^ \n\t]" b t)
3977            (if (message-gnksa-enable-p 'empty-article)
3978                (y-or-n-p "Empty article.  Really post? ")
3979              (message "Denied posting -- Empty article.")
3980              nil))))
3981    ;; Check for control characters.
3982    (message-check 'control-chars
3983      (if (re-search-forward "[\000-\007\013\015-\032\034-\037\200-\237]" nil t)
3984          (y-or-n-p
3985           "The article contains control characters.  Really post? ")
3986        t))
3987    ;; Check 8bit characters.
3988    (message-check '8bit
3989      (message-check-8bit))
3990    ;; Check excessive size.
3991    (message-check 'size
3992      (if (> (buffer-size) 60000)
3993          (y-or-n-p
3994           (format "The article is %d octets long.  Really post? "
3995                   (buffer-size)))
3996        t))
3997    ;; Check whether any new text has been added.
3998    (message-check 'new-text
3999      (or
4000       (not message-checksum)
4001       (not (eq (message-checksum) message-checksum))
4002       (if (message-gnksa-enable-p 'quoted-text-only)
4003           (y-or-n-p
4004            "It looks like no new text has been added.  Really post? ")
4005         (message "Denied posting -- no new text has been added.")
4006         nil)))
4007    ;; Check the length of the signature.
4008    (message-check 'signature
4009      (goto-char (point-max))
4010      (if (> (count-lines (point) (point-max)) 5)
4011          (y-or-n-p
4012           (format
4013            "Your .sig is %d lines; it should be max 4.  Really post? "
4014            (1- (count-lines (point) (point-max)))))
4015        t))
4016    ;; Ensure that text follows last quoted portion.
4017    (message-check 'quoting-style
4018      (goto-char (point-max))
4019      (let ((no-problem t))
4020        (when (search-backward-regexp "^>[^\n]*\n" nil t)
4021          (setq no-problem (search-forward-regexp "^[ \t]*[^>\n]" nil t)))
4022        (if no-problem
4023            t
4024          (if (message-gnksa-enable-p 'quoted-text-only)
4025              (y-or-n-p "Your text should follow quoted text.  Really post? ")
4026            ;; Ensure that
4027            (goto-char (point-min))
4028            (re-search-forward
4029             (concat "^" (regexp-quote mail-header-separator) "$"))
4030            (if (search-forward-regexp "^[ \t]*[^>\n]" nil t)
4031                (y-or-n-p "Your text should follow quoted text.  Really post? ")
4032              (message "Denied posting -- only quoted text.")
4033              nil)))))))
4034
4035 (defun message-check-mail-syntax ()
4036   "Check the syntax of the message."
4037   (save-excursion
4038     (save-restriction
4039       (widen)
4040       (and
4041        ;; We narrow to the headers and check them first.
4042        (save-excursion
4043          (save-restriction
4044            (message-narrow-to-headers)
4045            (message-check-mail-header-syntax)))
4046        ;; Check the body.
4047        (save-excursion
4048          (set-buffer message-edit-buffer)
4049          (message-check-mail-body-syntax))))))
4050
4051 (defun message-check-mail-header-syntax ()
4052   t)
4053
4054 (defun message-check-mail-body-syntax ()
4055   (and
4056    ;; Check 8bit characters.
4057    (message-check '8bit
4058      (message-check-8bit)
4059      )))
4060
4061 (defun message-check-8bit ()
4062   "Check the article contains 8bit characters."
4063   (save-excursion
4064     (set-buffer message-encoding-buffer)
4065     (message-narrow-to-headers)
4066     (let* ((case-fold-search t)
4067            (field-value (message-fetch-field "content-transfer-encoding")))
4068       (if (and field-value
4069                (member (downcase field-value) message-8bit-encoding-list))
4070           t
4071         (widen)
4072         (set-buffer (get-buffer-create " message syntax"))
4073         (erase-buffer)
4074         (goto-char (point-min))
4075         (set-buffer-multibyte nil)
4076         (insert-buffer message-encoding-buffer)
4077         (goto-char (point-min))
4078         (if (re-search-forward "[^\x00-\x7f]" nil t)
4079             (y-or-n-p
4080              "The article contains 8bit characters.  Really post? ")
4081           t)))))
4082
4083 (defun message-checksum ()
4084   "Return a \"checksum\" for the current buffer."
4085   (let ((sum 0))
4086     (save-excursion
4087       (goto-char (point-min))
4088       (re-search-forward
4089        (concat "^" (regexp-quote mail-header-separator) "$"))
4090       (while (not (eobp))
4091         (when (not (looking-at "[ \t\n]"))
4092           (setq sum (logxor (ash sum 1) (if (natnump sum) 0 1)
4093                             (char-after))))
4094         (forward-char 1)))
4095     sum))
4096
4097 (defun message-do-fcc ()
4098   "Process Fcc headers in the current buffer."
4099   (let ((case-fold-search t)
4100         (coding-system-for-write 'raw-text)
4101         (output-coding-system 'raw-text)
4102         list file
4103         (mml-externalize-attachments message-fcc-externalize-attachments))
4104     (save-excursion
4105       (save-restriction
4106         (message-narrow-to-headers)
4107         (setq file (message-fetch-field "fcc" t)))
4108       (when file
4109         (set-buffer (get-buffer-create " *message temp*"))
4110         (erase-buffer)
4111         (insert-buffer-substring message-encoding-buffer)
4112         (save-restriction
4113           (message-narrow-to-headers)
4114           (while (setq file (message-fetch-field "fcc"))
4115             (push file list)
4116             (message-remove-header "fcc" nil t)))
4117         (goto-char (point-min))
4118         (when (re-search-forward
4119                (concat "^" (regexp-quote mail-header-separator) "$")
4120                nil t)
4121           (replace-match "" t t))
4122         ;; Process FCC operations.
4123         (while list
4124           (setq file (pop list))
4125           (if (string-match "^[ \t]*|[ \t]*\\(.*\\)[ \t]*$" file)
4126               ;; Pipe the article to the program in question.
4127               (call-process-region (point-min) (point-max) shell-file-name
4128                                    nil nil nil shell-command-switch
4129                                    (match-string 1 file))
4130             ;; Save the article.
4131             (setq file (expand-file-name file))
4132             (unless (file-exists-p (file-name-directory file))
4133               (make-directory (file-name-directory file) t))
4134             (if (and message-fcc-handler-function
4135                      (not (eq message-fcc-handler-function 'rmail-output)))
4136                 (funcall message-fcc-handler-function file)
4137               (if (and (file-readable-p file) (mail-file-babyl-p file))
4138                   (rmail-output file 1 nil t)
4139                 (let ((mail-use-rfc822 t))
4140                   (rmail-output file 1 t t))))))
4141         (kill-buffer (current-buffer))))))
4142
4143 (defun message-output (filename)
4144   "Append this article to Unix/babyl mail file FILENAME."
4145   (if (and (file-readable-p filename)
4146            (mail-file-babyl-p filename))
4147       (gnus-output-to-rmail filename t)
4148     (gnus-output-to-mail filename t)))
4149
4150 (defun message-cleanup-headers ()
4151   "Do various automatic cleanups of the headers."
4152   ;; Remove empty lines in the header.
4153   (save-restriction
4154     (message-narrow-to-headers)
4155     ;; Remove blank lines.
4156     (while (re-search-forward "^[ \t]*\n" nil t)
4157       (replace-match "" t t))
4158
4159     ;; Correct Newsgroups and Followup-To headers:  Change sequence of
4160     ;; spaces to comma and eliminate spaces around commas.  Eliminate
4161     ;; embedded line breaks.
4162     (goto-char (point-min))
4163     (while (re-search-forward "^\\(Newsgroups\\|Followup-To\\): +" nil t)
4164       (save-restriction
4165         (narrow-to-region
4166          (point)
4167          (if (re-search-forward "^[^ \t]" nil t)
4168              (match-beginning 0)
4169            (forward-line 1)
4170            (point)))
4171         (goto-char (point-min))
4172         (while (re-search-forward "\n[ \t]+" nil t)
4173           (replace-match " " t t))      ;No line breaks (too confusing)
4174         (goto-char (point-min))
4175         (while (re-search-forward "[ \t\n]*,[ \t\n]*\\|[ \t]+" nil t)
4176           (replace-match "," t t))
4177         (goto-char (point-min))
4178         ;; Remove trailing commas.
4179         (when (re-search-forward ",+$" nil t)
4180           (replace-match "" t t))))))
4181
4182 (defun message-make-date (&optional now)
4183   "Make a valid data header.
4184 If NOW, use that time instead."
4185   (let* ((now (or now (current-time)))
4186          (zone (nth 8 (decode-time now)))
4187          (sign "+"))
4188     (when (< zone 0)
4189       (setq sign "-")
4190       (setq zone (- zone)))
4191     (concat
4192      ;; The day name of the %a spec is locale-specific.  Pfff.
4193      (format "%s, " (capitalize (car (rassoc (nth 6 (decode-time now))
4194                                              parse-time-weekdays))))
4195      (format-time-string "%d" now)
4196      ;; The month name of the %b spec is locale-specific.  Pfff.
4197      (format " %s "
4198              (capitalize (car (rassoc (nth 4 (decode-time now))
4199                                       parse-time-months))))
4200      (format-time-string "%Y %H:%M:%S " now)
4201      ;; We do all of this because XEmacs doesn't have the %z spec.
4202      (format "%s%02d%02d" sign (/ zone 3600) (/ (% zone 3600) 60)))))
4203
4204 (defun message-make-followup-subject (subject)
4205   "Make a followup Subject."
4206   (cond
4207    ((and (eq message-use-subject-re 'guess)
4208          (string-match message-subject-encoded-re-regexp subject))
4209     subject)
4210    (message-use-subject-re
4211     (concat "Re: " (message-strip-subject-re subject)))
4212    (t subject)))
4213
4214 (defun message-make-message-id ()
4215   "Make a unique Message-ID."
4216   (concat "<" (message-unique-id)
4217           (let ((psubject (save-excursion (message-fetch-field "subject")))
4218                 (psupersedes
4219                  (save-excursion (message-fetch-field "supersedes"))))
4220             (if (or
4221                  (and message-reply-headers
4222                       (mail-header-references message-reply-headers)
4223                       (mail-header-subject message-reply-headers)
4224                       psubject
4225                       (not (string=
4226                             (message-strip-subject-re
4227                              (mail-header-subject message-reply-headers))
4228                             (message-strip-subject-re psubject))))
4229                  (and psupersedes
4230                       (string-match "_-_@" psupersedes)))
4231                 "_-_" ""))
4232           "@" (message-make-fqdn) ">"))
4233
4234 (defvar message-unique-id-char nil)
4235
4236 ;; If you ever change this function, make sure the new version
4237 ;; cannot generate IDs that the old version could.
4238 ;; You might for example insert a "." somewhere (not next to another dot
4239 ;; or string boundary), or modify the "fsf" string.
4240 (defun message-unique-id ()
4241   ;; Don't use microseconds from (current-time), they may be unsupported.
4242   ;; Instead we use this randomly inited counter.
4243   (setq message-unique-id-char
4244         (% (1+ (or message-unique-id-char (logand (random t) (1- (lsh 1 20)))))
4245            ;; (current-time) returns 16-bit ints,
4246            ;; and 2^16*25 just fits into 4 digits i base 36.
4247            (* 25 25)))
4248   (let ((tm (current-time)))
4249     (concat
4250      (if (memq system-type '(ms-dos emx vax-vms))
4251          (let ((user (downcase (user-login-name))))
4252            (while (string-match "[^a-z0-9_]" user)
4253              (aset user (match-beginning 0) ?_))
4254            user)
4255        (message-number-base36 (user-uid) -1))
4256      (message-number-base36 (+ (car tm)
4257                                (lsh (% message-unique-id-char 25) 16)) 4)
4258      (message-number-base36 (+ (nth 1 tm)
4259                                (lsh (/ message-unique-id-char 25) 16)) 4)
4260      ;; Append the newsreader name, because while the generated
4261      ;; ID is unique to this newsreader, other newsreaders might
4262      ;; otherwise generate the same ID via another algorithm.
4263      ".fsf")))
4264
4265 (defun message-number-base36 (num len)
4266   (if (if (< len 0)
4267           (<= num 0)
4268         (= len 0))
4269       ""
4270     (concat (message-number-base36 (/ num 36) (1- len))
4271             (char-to-string (aref "zyxwvutsrqponmlkjihgfedcba9876543210"
4272                                   (% num 36))))))
4273
4274 (defun message-make-organization ()
4275   "Make an Organization header."
4276   (let* ((organization
4277           (when message-user-organization
4278             (if (message-functionp message-user-organization)
4279                 (funcall message-user-organization)
4280               message-user-organization))))
4281     (save-excursion
4282       (message-set-work-buffer)
4283       (cond ((stringp organization)
4284              (insert organization))
4285             ((and (eq t organization)
4286                   message-user-organization-file
4287                   (file-exists-p message-user-organization-file))
4288              (insert-file-contents message-user-organization-file)))
4289       (goto-char (point-min))
4290       (while (re-search-forward "[\t\n]+" nil t)
4291         (replace-match "" t t))
4292       (unless (zerop (buffer-size))
4293         (buffer-string)))))
4294
4295 (defun message-make-lines ()
4296   "Count the number of lines and return numeric string."
4297   (save-excursion
4298     (save-restriction
4299       (widen)
4300       (message-goto-body)
4301       (int-to-string (count-lines (point) (point-max))))))
4302
4303 (defun message-make-in-reply-to ()
4304   "Return the In-Reply-To header for this message."
4305   (when message-reply-headers
4306     (let ((from (mail-header-from message-reply-headers))
4307           (date (mail-header-date message-reply-headers))
4308           (msg-id (mail-header-message-id message-reply-headers)))
4309       (when msg-id
4310         (concat msg-id
4311                 (when from
4312                   (let ((pair (std11-extract-address-components from)))
4313                     (concat "\n ("
4314                             (or (car pair) (cadr pair))
4315                             "'s message of \""
4316                             (if (or (not date) (string= date ""))
4317                                 "(unknown date)" date)
4318                             "\")"))))))))
4319
4320 (defun message-make-distribution ()
4321   "Make a Distribution header."
4322   (let ((orig-distribution (message-fetch-reply-field "distribution")))
4323     (cond ((message-functionp message-distribution-function)
4324            (funcall message-distribution-function))
4325           (t orig-distribution))))
4326
4327 (defun message-make-expires ()
4328   "Return an Expires header based on `message-expires'."
4329   (let ((current (current-time))
4330         (future (* 1.0 message-expires 60 60 24)))
4331     ;; Add the future to current.
4332     (setcar current (+ (car current) (round (/ future (expt 2 16)))))
4333     (setcar (cdr current) (+ (nth 1 current) (% (round future) (expt 2 16))))
4334     (message-make-date current)))
4335
4336 (defun message-make-path ()
4337   "Return uucp path."
4338   (let ((login-name (user-login-name)))
4339     (cond ((null message-user-path)
4340            (concat (system-name) "!" login-name))
4341           ((stringp message-user-path)
4342            ;; Support GENERICPATH.  Suggested by vixie@decwrl.dec.com.
4343            (concat message-user-path "!" login-name))
4344           (t login-name))))
4345
4346 (defun message-make-from ()
4347   "Make a From header."
4348   (let* ((style message-from-style)
4349          (login (message-make-address))
4350          (fullname
4351           (or (and (boundp 'user-full-name)
4352                    user-full-name)
4353               (user-full-name))))
4354     (when (string= fullname "&")
4355       (setq fullname (user-login-name)))
4356     (save-excursion
4357       (message-set-work-buffer)
4358       (cond
4359        ((or (null style)
4360             (equal fullname ""))
4361         (insert login))
4362        ((or (eq style 'angles)
4363             (and (not (eq style 'parens))
4364                  ;; Use angles if no quoting is needed, or if parens would
4365                  ;; need quoting too.
4366                  (or (not (string-match "[^- !#-'*+/-9=?A-Z^-~]" fullname))
4367                      (let ((tmp (concat fullname nil)))
4368                        (while (string-match "([^()]*)" tmp)
4369                          (aset tmp (match-beginning 0) ?-)
4370                          (aset tmp (1- (match-end 0)) ?-))
4371                        (string-match "[\\()]" tmp)))))
4372         (insert fullname)
4373         (insert " <" login ">"))
4374        (t                               ; 'parens or default
4375         (insert login " (")
4376         (let ((fullname-start (point)))
4377           (insert fullname)
4378           (goto-char fullname-start)
4379           ;; RFC 822 says \ and nonmatching parentheses
4380           ;; must be escaped in comments.
4381           ;; Escape every instance of ()\ ...
4382           (while (re-search-forward "[()\\]" nil 1)
4383             (replace-match "\\\\\\&" t))
4384           ;; ... then undo escaping of matching parentheses,
4385           ;; including matching nested parentheses.
4386           (goto-char fullname-start)
4387           (while (re-search-forward
4388                   "\\(\\=\\|[^\\]\\(\\\\\\\\\\)*\\)\\\\(\\(\\([^\\]\\|\\\\\\\\\\)*\\)\\\\)"
4389                   nil 1)
4390             (replace-match "\\1(\\3)" t)
4391             (goto-char fullname-start)))
4392         (insert ")")))
4393       (buffer-string))))
4394
4395 (defun message-make-sender ()
4396   "Return the \"real\" user address.
4397 This function tries to ignore all user modifications, and
4398 give as trustworthy answer as possible."
4399   (concat (user-login-name) "@" (system-name)))
4400
4401 (defun message-make-address ()
4402   "Make the address of the user."
4403   (or (message-user-mail-address)
4404       (concat (user-login-name) "@" (message-make-domain))))
4405
4406 (defun message-user-mail-address ()
4407   "Return the pertinent part of `user-mail-address'."
4408   (when user-mail-address
4409     (if (string-match " " user-mail-address)
4410         (nth 1 (std11-extract-address-components user-mail-address))
4411       user-mail-address)))
4412
4413 (defun message-make-fqdn ()
4414   "Return user's fully qualified domain name."
4415   (let ((system-name (system-name))
4416         (user-mail (message-user-mail-address)))
4417     (cond
4418      ((and (string-match "[^.]\\.[^.]" system-name)
4419            (not (string-match message-bogus-system-names system-name)))
4420       ;; `system-name' returned the right result.
4421       system-name)
4422      ;; Try `mail-host-address'.
4423      ((and (boundp 'mail-host-address)
4424            (stringp mail-host-address)
4425            (string-match "\\." mail-host-address))
4426       mail-host-address)
4427      ;; We try `user-mail-address' as a backup.
4428      ((and user-mail
4429            (string-match "\\." user-mail)
4430            (string-match "@\\(.*\\)\\'" user-mail))
4431       (match-string 1 user-mail))
4432      ;; Default to this bogus thing.
4433      (t
4434       (concat system-name ".i-did-not-set--mail-host-address--so-tickle-me")))))
4435
4436 (defun message-make-host-name ()
4437   "Return the name of the host."
4438   (let ((fqdn (message-make-fqdn)))
4439     (string-match "^[^.]+\\." fqdn)
4440     (substring fqdn 0 (1- (match-end 0)))))
4441
4442 (defun message-make-domain ()
4443   "Return the domain name."
4444   (or mail-host-address
4445       (message-make-fqdn)))
4446
4447 (defun message-make-mft ()
4448   "Return the Mail-Followup-To header."
4449   (let* ((msg-recipients (message-options-get 'message-recipients))
4450          (recipients
4451           (mapcar 'mail-strip-quoted-names
4452                   (message-tokenize-header msg-recipients)))
4453          (file-regexps
4454           (if message-subscribed-address-file
4455               (let (begin end item re)
4456                 (save-excursion
4457                   (with-temp-buffer
4458                     (insert-file-contents message-subscribed-address-file)
4459                     (while (not (eobp))
4460                       (setq begin (point))
4461                       (forward-line 1)
4462                       (setq end (point))
4463                       (if (bolp) (setq end (1- end)))
4464                       (setq item (regexp-quote (buffer-substring begin end)))
4465                       (if re (setq re (concat re "\\|" item))
4466                         (setq re (concat "\\`\\(" item))))
4467                     (and re (list (concat re "\\)\\'"))))))))
4468          (mft-regexps (apply 'append message-subscribed-regexps
4469                              (mapcar 'regexp-quote
4470                                      message-subscribed-addresses)
4471                              file-regexps
4472                              (mapcar 'funcall
4473                                      message-subscribed-address-functions))))
4474     (save-match-data
4475       (when (eval (apply 'append '(or)
4476                          (mapcar
4477                           (function (lambda (regexp)
4478                                       (mapcar
4479                                        (function (lambda (recipient)
4480                                                    `(string-match ,regexp
4481                                                                   ,recipient)))
4482                                        recipients)))
4483                           mft-regexps)))
4484         msg-recipients))))
4485
4486 ;; Dummy to avoid byte-compile warning.
4487 (defvar mule-version)
4488 (defvar emacs-beta-version)
4489 (defvar xemacs-codename)
4490 (defvar gnus-inviolable-extended-version)
4491
4492 (defun message-make-user-agent ()
4493   "Return user-agent info if the value `message-user-agent' is non-nil. If the
4494 \"User-Agent\" field has already exist, it's value will be added in the return
4495 string."
4496   (when message-user-agent
4497     (save-excursion
4498       (goto-char (point-min))
4499       (let ((case-fold-search t)
4500             user-agent start p end)
4501         (if (re-search-forward "^User-Agent:[\t ]*" nil t)
4502             (progn
4503               (setq start (match-beginning 0)
4504                     p (match-end 0)
4505                     end (std11-field-end)
4506                     user-agent (buffer-substring-no-properties p end))
4507               (delete-region start (1+ end))
4508               (concat message-user-agent " " user-agent))
4509           message-user-agent)))))
4510
4511 (defun message-generate-headers (headers)
4512   "Prepare article HEADERS.
4513 Headers already prepared in the buffer are not modified."
4514   (save-restriction
4515     (message-narrow-to-headers)
4516     (let* ((Date (message-make-date))
4517            (Message-ID (message-make-message-id))
4518            (Organization (message-make-organization))
4519            (From (message-make-from))
4520            (Path (message-make-path))
4521            (Subject nil)
4522            (Newsgroups nil)
4523            (In-Reply-To (message-make-in-reply-to))
4524            (To nil)
4525            (Distribution (message-make-distribution))
4526            (Lines (message-make-lines))
4527            (User-Agent (message-make-user-agent))
4528            (Expires (message-make-expires))
4529            (case-fold-search t)
4530            header value elem)
4531       ;; First we remove any old generated headers.
4532       (let ((headers message-deletable-headers))
4533         (unless (buffer-modified-p)
4534           (setq headers (delq 'Message-ID (copy-sequence headers))))
4535         (while headers
4536           (goto-char (point-min))
4537           (and (re-search-forward
4538                 (concat "^" (symbol-name (car headers)) ": *") nil t)
4539                (get-text-property (1+ (match-beginning 0)) 'message-deletable)
4540                (message-delete-line))
4541           (pop headers)))
4542       ;; Go through all the required headers and see if they are in the
4543       ;; articles already.  If they are not, or are empty, they are
4544       ;; inserted automatically - except for Subject, Newsgroups and
4545       ;; Distribution.
4546       (while headers
4547         (goto-char (point-min))
4548         (setq elem (pop headers))
4549         (if (consp elem)
4550             (if (eq (car elem) 'optional)
4551                 (setq header (cdr elem))
4552               (setq header (car elem)))
4553           (setq header elem))
4554         (when (or (not (re-search-forward
4555                         (concat "^"
4556                                 (regexp-quote
4557                                  (downcase
4558                                   (if (stringp header)
4559                                       header
4560                                     (symbol-name header))))
4561                                 ":")
4562                         nil t))
4563                   (progn
4564                     ;; The header was found.  We insert a space after the
4565                     ;; colon, if there is none.
4566                     (if (/= (char-after) ? ) (insert " ") (forward-char 1))
4567                     ;; Find out whether the header is empty...
4568                     (looking-at "[ \t]*\n[^ \t]")))
4569           ;; So we find out what value we should insert.
4570           (setq value
4571                 (cond
4572                  ((and (consp elem) (eq (car elem) 'optional))
4573                   ;; This is an optional header.  If the cdr of this
4574                   ;; is something that is nil, then we do not insert
4575                   ;; this header.
4576                   (setq header (cdr elem))
4577                   (or (and (fboundp (cdr elem)) (funcall (cdr elem)))
4578                       (and (boundp (cdr elem)) (symbol-value (cdr elem)))))
4579                  ((consp elem)
4580                   ;; The element is a cons.  Either the cdr is a
4581                   ;; string to be inserted verbatim, or it is a
4582                   ;; function, and we insert the value returned from
4583                   ;; this function.
4584                   (or (and (stringp (cdr elem)) (cdr elem))
4585                       (and (fboundp (cdr elem)) (funcall (cdr elem)))))
4586                  ((and (boundp header) (symbol-value header))
4587                   ;; The element is a symbol.  We insert the value
4588                   ;; of this symbol, if any.
4589                   (symbol-value header))
4590                  ((not (message-check-element header))
4591                   ;; We couldn't generate a value for this header,
4592                   ;; so we just ask the user.
4593                   (read-from-minibuffer
4594                    (format "Empty header for %s; enter value: " header)))))
4595           ;; Finally insert the header.
4596           (when (and value
4597                      (not (equal value "")))
4598             (save-excursion
4599               (if (bolp)
4600                   (progn
4601                     ;; This header didn't exist, so we insert it.
4602                     (goto-char (point-max))
4603                     (insert (if (stringp header) header (symbol-name header))
4604                             ": " value)
4605                     ;; We check whether the value was ended by a
4606                     ;; newline.  If now, we insert one.
4607                     (unless (bolp)
4608                       (insert "\n"))
4609                     (forward-line -1))
4610                 ;; The value of this header was empty, so we clear
4611                 ;; totally and insert the new value.
4612                 (delete-region (point) (gnus-point-at-eol))
4613                 (insert value)
4614                 (when (bolp)
4615                   (delete-char -1)))
4616               ;; Add the deletable property to the headers that require it.
4617               (and (memq header message-deletable-headers)
4618                    (progn (beginning-of-line) (looking-at "[^:]+: "))
4619                    (add-text-properties
4620                     (point) (match-end 0)
4621                     '(message-deletable t face italic) (current-buffer)))))))
4622       ;; Insert new Sender if the From is strange.
4623       (let ((from (message-fetch-field "from"))
4624             (sender (message-fetch-field "sender"))
4625             (secure-sender (message-make-sender)))
4626         (when (and from
4627                    (not (message-check-element 'sender))
4628                    (not (string=
4629                          (downcase
4630                           (cadr (std11-extract-address-components from)))
4631                          (downcase secure-sender)))
4632                    (or (null sender)
4633                        (not
4634                         (string=
4635                          (downcase
4636                           (cadr (std11-extract-address-components sender)))
4637                          (downcase secure-sender)))))
4638           (goto-char (point-min))
4639           ;; Rename any old Sender headers to Original-Sender.
4640           (when (re-search-forward "^\\(Original-\\)*Sender:" nil t)
4641             (beginning-of-line)
4642             (insert "Original-")
4643             (beginning-of-line))
4644           (when (or (message-news-p)
4645                     (string-match "@.+\\.." secure-sender))
4646             (insert "Sender: " secure-sender "\n")))))))
4647
4648 (defun message-insert-courtesy-copy ()
4649   "Insert a courtesy message in mail copies of combined messages."
4650   (let (newsgroups)
4651     (save-excursion
4652       (save-restriction
4653         (message-narrow-to-headers)
4654         (when (setq newsgroups (message-fetch-field "newsgroups"))
4655           (goto-char (point-max))
4656           (insert "Posted-To: " newsgroups "\n")))
4657       (forward-line 1)
4658       (when message-courtesy-message
4659         (cond
4660          ((string-match "%s" message-courtesy-message)
4661           (insert (format message-courtesy-message newsgroups)))
4662          (t
4663           (insert message-courtesy-message)))))))
4664
4665 ;;;
4666 ;;; Setting up a message buffer
4667 ;;;
4668
4669 (defun message-fill-address (header value)
4670   (save-restriction
4671     (narrow-to-region (point) (point))
4672     (insert (capitalize (symbol-name header))
4673             ": "
4674             (if (consp value) (car value) value)
4675             "\n")
4676     (narrow-to-region (point-min) (1- (point-max)))
4677     (let (quoted last)
4678       (goto-char (point-min))
4679       (while (not (eobp))
4680         (skip-chars-forward "^,\"" (point-max))
4681         (if (or (eq (char-after) ?,)
4682                 (eobp))
4683             (when (not quoted)
4684               (if (and (> (current-column) 78)
4685                        last)
4686                   (save-excursion
4687                     (goto-char last)
4688                     (looking-at "[ \t]*")
4689                     (replace-match "\n " t t)))
4690               (setq last (1+ (point))))
4691           (setq quoted (not quoted)))
4692         (unless (eobp)
4693           (forward-char 1))))
4694     (goto-char (point-max))
4695     (widen)
4696     (forward-line 1)))
4697
4698 (defun message-fill-references (header value)
4699   (insert (capitalize (symbol-name header))
4700           ": "
4701           (std11-fill-msg-id-list-string
4702            (if (consp value) (car value) value))
4703           "\n"))
4704
4705 (defun message-fill-header (header value)
4706   (let ((begin (point))
4707         (fill-column 78)
4708         (fill-prefix " "))
4709     (insert (capitalize (symbol-name header))
4710             ": "
4711             (if (consp value) (car value) value)
4712             "\n")
4713     (save-restriction
4714       (narrow-to-region begin (point))
4715       (fill-region-as-paragraph begin (point))
4716       ;; Tapdance around looong Message-IDs.
4717       (forward-line -1)
4718       (when (looking-at "[ \t]*$")
4719         (message-delete-line))
4720       (goto-char begin)
4721       (re-search-forward ":" nil t)
4722       (when (looking-at "\n[ \t]+")
4723         (replace-match " " t t))
4724       (goto-char (point-max)))))
4725
4726 (defun message-shorten-1 (list cut surplus)
4727   "Cut SURPLUS elements out of LIST, beginning with CUTth one."
4728   (setcdr (nthcdr (- cut 2) list)
4729           (nthcdr (+ (- cut 2) surplus 1) list)))
4730
4731 (defun message-shorten-references (header references)
4732   "Trim REFERENCES to be 21 Message-ID long or less, and fold them.
4733 If folding is disallowed, also check that the REFERENCES are less
4734 than 988 characters long, and if they are not, trim them until they are."
4735   (let ((maxcount 21)
4736         (count 0)
4737         (cut 2)
4738         refs)
4739     (with-temp-buffer
4740       (insert references)
4741       (goto-char (point-min))
4742       ;; Cons a list of valid references.
4743       (while (re-search-forward "<[^>]+>" nil t)
4744         (push (match-string 0) refs))
4745       (setq refs (nreverse refs)
4746             count (length refs)))
4747
4748     ;; If the list has more than MAXCOUNT elements, trim it by
4749     ;; removing the CUTth element and the required number of
4750     ;; elements that follow.
4751     (when (> count maxcount)
4752       (let ((surplus (- count maxcount)))
4753         (message-shorten-1 refs cut surplus)
4754         (decf count surplus)))
4755
4756     ;; If folding is disallowed, make sure the total length (including
4757     ;; the spaces between) will be less than MAXSIZE characters.
4758     ;;
4759     ;; Only disallow folding for News messages. At this point the headers
4760     ;; have not been generated, thus we use message-this-is-news directly.
4761     (when (and message-this-is-news message-cater-to-broken-inn)
4762       (let ((maxsize 988)
4763             (totalsize (+ (apply #'+ (mapcar #'length refs))
4764                           (1- count)))
4765             (surplus 0)
4766             (ptr (nthcdr (1- cut) refs)))
4767         ;; Decide how many elements to cut off...
4768         (while (> totalsize maxsize)
4769           (decf totalsize (1+ (length (car ptr))))
4770           (incf surplus)
4771           (setq ptr (cdr ptr)))
4772         ;; ...and do it.
4773         (when (> surplus 0)
4774           (message-shorten-1 refs cut surplus))))
4775
4776     ;; Finally, collect the references back into a string and insert
4777     ;; it into the buffer.
4778     (let ((refstring (mapconcat #'identity refs " ")))
4779       (if (and message-this-is-news message-cater-to-broken-inn)
4780           (insert (capitalize (symbol-name header)) ": "
4781                   refstring "\n")
4782         (message-fill-header header refstring)))))
4783
4784 (defun message-position-point ()
4785   "Move point to where the user probably wants to find it."
4786   (message-narrow-to-headers)
4787   (cond
4788    ((re-search-forward "^[^:]+:[ \t]*$" nil t)
4789     (search-backward ":" )
4790     (widen)
4791     (forward-char 1)
4792     (if (eq (char-after) ? )
4793         (forward-char 1)
4794       (insert " ")))
4795    (t
4796     (goto-char (point-max))
4797     (widen)
4798     (forward-line 1)
4799     (unless (looking-at "$")
4800       (forward-line 2)))
4801    (sit-for 0)))
4802
4803 (defun message-beginning-of-line (&optional n)
4804   "Move point to beginning of header value or to beginning of line."
4805   (interactive "p")
4806   (if (message-point-in-header-p)
4807       (let* ((here (point))
4808              (bol (progn (beginning-of-line n) (point)))
4809              (eol (gnus-point-at-eol))
4810              (eoh (re-search-forward ": *" eol t)))
4811         (if (or (not eoh) (equal here eoh))
4812             (goto-char bol)
4813           (goto-char eoh)))
4814     (beginning-of-line n)))
4815
4816 (defun message-buffer-name (type &optional to group)
4817   "Return a new (unique) buffer name based on TYPE and TO."
4818   (cond
4819    ;; Generate a new buffer name The Message Way.
4820    ((eq message-generate-new-buffers 'unique)
4821     (generate-new-buffer-name
4822      (concat "*" type
4823              (if to
4824                  (concat " to "
4825                          (or (car (std11-extract-address-components to))
4826                              to) "")
4827                "")
4828              (if (and group (not (string= group ""))) (concat " on " group) "")
4829              "*")))
4830    ;; Check whether `message-generate-new-buffers' is a function,
4831    ;; and if so, call it.
4832    ((message-functionp message-generate-new-buffers)
4833     (funcall message-generate-new-buffers type to group))
4834    ((eq message-generate-new-buffers 'unsent)
4835     (generate-new-buffer-name
4836      (concat "*unsent " type
4837              (if to
4838                  (concat " to "
4839                          (or (car (mail-extract-address-components to))
4840                              to) "")
4841                "")
4842              (if (and group (not (string= group ""))) (concat " on " group) "")
4843              "*")))
4844    ;; Use standard name.
4845    (t
4846     (format "*%s message*" type))))
4847
4848 (defmacro message-pop-to-buffer-1 (buffer)
4849   `(if pop-up-frames
4850        (let (special-display-buffer-names
4851              special-display-regexps
4852              same-window-buffer-names
4853              same-window-regexps)
4854          (pop-to-buffer ,buffer))
4855      (pop-to-buffer ,buffer)))
4856
4857 (defun message-pop-to-buffer (name)
4858   "Pop to buffer NAME, and warn if it already exists and is modified."
4859   (let ((buffer (get-buffer name))
4860         (pop-up-frames (and (or (static-if (featurep 'xemacs)
4861                                     (device-on-window-system-p)
4862                                   window-system)
4863                                 (>= emacs-major-version 20))
4864                             message-use-multi-frames)))
4865     (if (and buffer
4866              (buffer-name buffer))
4867         (progn
4868           (message-pop-to-buffer-1 buffer)
4869           (when (and (buffer-modified-p)
4870                      (not (y-or-n-p
4871                            "Message already being composed; erase? ")))
4872             (error "Message being composed")))
4873       (message-pop-to-buffer-1 name))
4874     (erase-buffer)
4875     (message-mode)
4876     (when pop-up-frames
4877       (set (make-local-variable 'message-original-frame) (selected-frame)))))
4878
4879 (defun message-do-send-housekeeping ()
4880   "Kill old message buffers."
4881   ;; We might have sent this buffer already.  Delete it from the
4882   ;; list of buffers.
4883   (setq message-buffer-list (delq (current-buffer) message-buffer-list))
4884   (while (and message-max-buffers
4885               message-buffer-list
4886               (>= (length message-buffer-list) message-max-buffers))
4887     ;; Kill the oldest buffer -- unless it has been changed.
4888     (let ((buffer (pop message-buffer-list)))
4889       (when (and (buffer-name buffer)
4890                  (not (buffer-modified-p buffer)))
4891         (kill-buffer buffer))))
4892   ;; Rename the buffer.
4893   (if message-send-rename-function
4894       (funcall message-send-rename-function)
4895     ;; Note: mail-abbrevs of XEmacs renames buffer name behind Gnus.
4896     (when (string-match
4897            "\\`\\*\\(sent \\|unsent \\)?\\(.+\\)\\*[^\\*]*\\|\\`mail to "
4898            (buffer-name))
4899       (let ((name (match-string 2 (buffer-name)))
4900             to group)
4901         (if (not (or (null name)
4902                      (string-equal name "mail")
4903                      (string-equal name "posting")))
4904             (setq name (concat "*sent " name "*"))
4905           (message-narrow-to-headers)
4906           (setq to (message-fetch-field "to"))
4907           (setq group (message-fetch-field "newsgroups"))
4908           (widen)
4909           (setq name
4910                 (cond
4911                  (to (concat "*sent mail to "
4912                              (or (car (mail-extract-address-components to))
4913                                  to) "*"))
4914                  ((and group (not (string= group "")))
4915                   (concat "*sent posting on " group "*"))
4916                  (t "*sent mail*"))))
4917         (unless (string-equal name (buffer-name))
4918           (rename-buffer name t)))))
4919   ;; Push the current buffer onto the list.
4920   (when message-max-buffers
4921     (setq message-buffer-list
4922           (nconc message-buffer-list (list (current-buffer))))))
4923
4924 (defun message-mail-user-agent ()
4925   (let ((mua (cond
4926               ((not message-mail-user-agent) nil)
4927               ((eq message-mail-user-agent t) mail-user-agent)
4928               (t message-mail-user-agent))))
4929     (if (memq mua '(message-user-agent gnus-user-agent))
4930         nil
4931       mua)))
4932
4933 (defun message-setup (headers &optional replybuffer actions switch-function)
4934   (let ((mua (message-mail-user-agent))
4935         subject to field yank-action)
4936     (if (not (and message-this-is-mail mua))
4937         (message-setup-1 headers replybuffer actions)
4938       (if replybuffer
4939           (setq yank-action (list 'insert-buffer replybuffer)))
4940       (setq headers (copy-sequence headers))
4941       (setq field (assq 'Subject headers))
4942       (when field
4943         (setq subject (cdr field))
4944         (setq headers (delq field headers)))
4945       (setq field (assq 'To headers))
4946       (when field
4947         (setq to (cdr field))
4948         (setq headers (delq field headers)))
4949       (let ((mail-user-agent mua))
4950         (compose-mail to subject
4951                       (mapcar (lambda (item)
4952                                 (cons
4953                                  (format "%s" (car item))
4954                                  (cdr item)))
4955                               headers)
4956                       nil switch-function yank-action actions)))))
4957
4958 (defun message-setup-1 (headers &optional replybuffer actions)
4959   (dolist (action actions)
4960     (condition-case nil
4961         (add-to-list 'message-send-actions
4962                      `(apply ',(car action) ',(cdr action)))))
4963   (setq message-reply-buffer
4964         (or (message-get-parameter 'reply-buffer)
4965             replybuffer))
4966   (goto-char (point-min))
4967   ;; Insert all the headers.
4968   (mail-header-format
4969    (let ((h headers)
4970          (alist message-header-format-alist))
4971      (while h
4972        (unless (assq (caar h) message-header-format-alist)
4973          (push (list (caar h)) alist))
4974        (pop h))
4975      alist)
4976    headers)
4977   (delete-region (point) (progn (forward-line -1) (point)))
4978   (when message-default-headers
4979     (insert message-default-headers)
4980     (or (bolp) (insert ?\n)))
4981   (put-text-property
4982    (point)
4983    (progn
4984      (insert mail-header-separator "\n")
4985      (1- (point)))
4986    'read-only nil)
4987   (forward-line -1)
4988   (when (message-news-p)
4989     (when message-default-news-headers
4990       (insert message-default-news-headers)
4991       (or (bolp) (insert ?\n)))
4992     (when message-generate-headers-first
4993       (message-generate-headers
4994        (delq 'Lines
4995              (delq 'Subject
4996                    (copy-sequence message-required-news-headers))))))
4997   (when (message-mail-p)
4998     (when message-default-mail-headers
4999       (insert message-default-mail-headers)
5000       (or (bolp) (insert ?\n)))
5001     (when message-generate-headers-first
5002       (message-generate-headers
5003        (delq 'Lines
5004              (delq 'Subject
5005                    (copy-sequence message-required-mail-headers))))))
5006   (run-hooks 'message-signature-setup-hook)
5007   (message-insert-signature)
5008   (save-restriction
5009     (message-narrow-to-headers)
5010     (if message-alternative-emails
5011         (message-use-alternative-email-as-from))
5012     (run-hooks 'message-header-setup-hook))
5013   (set-buffer-modified-p nil)
5014   (setq buffer-undo-list nil)
5015   (run-hooks 'message-setup-hook)
5016   (message-position-point)
5017   (undo-boundary))
5018
5019 (defun message-set-auto-save-file-name ()
5020   "Associate the message buffer with a file in the drafts directory."
5021   (when message-auto-save-directory
5022     (unless (file-directory-p
5023              (directory-file-name message-auto-save-directory))
5024       (gnus-make-directory message-auto-save-directory))
5025     (if (gnus-alive-p)
5026         (setq message-draft-article
5027               (nndraft-request-associate-buffer "drafts"))
5028       (setq buffer-file-name (expand-file-name
5029                               (if (memq system-type
5030                                         '(ms-dos ms-windows windows-nt
5031                                                  cygwin32 win32 w32
5032                                                  mswindows))
5033                                   "message"
5034                                 "*message*")
5035                               message-auto-save-directory))
5036       (setq buffer-auto-save-file-name (make-auto-save-file-name)))
5037     (clear-visited-file-modtime)
5038     (static-if (boundp 'MULE)
5039         (set-file-coding-system message-draft-coding-system)
5040       (setq buffer-file-coding-system message-draft-coding-system))))
5041
5042 (defun message-disassociate-draft ()
5043   "Disassociate the message buffer from the drafts directory."
5044   (when message-draft-article
5045     (nndraft-request-expire-articles
5046      (list message-draft-article) "drafts" nil t)))
5047
5048 (defun message-insert-headers ()
5049   "Generate the headers for the article."
5050   (interactive)
5051   (save-excursion
5052     (save-restriction
5053       (message-narrow-to-headers)
5054       (when (message-news-p)
5055         (message-generate-headers
5056          (delq 'Lines
5057                (delq 'Subject
5058                      (copy-sequence message-required-news-headers)))))
5059       (when (message-mail-p)
5060         (message-generate-headers
5061          (delq 'Lines
5062                (delq 'Subject
5063                      (copy-sequence message-required-mail-headers))))))))
5064
5065 \f
5066
5067 ;;;
5068 ;;; Commands for interfacing with message
5069 ;;;
5070
5071 ;;;###autoload
5072 (defun message-mail (&optional to subject
5073                                other-headers continue switch-function
5074                                yank-action send-actions)
5075   "Start editing a mail message to be sent.
5076 OTHER-HEADERS is an alist of header/value pairs."
5077   (interactive)
5078   (let ((message-this-is-mail t) replybuffer)
5079     (unless (message-mail-user-agent)
5080       (message-pop-to-buffer (message-buffer-name "mail" to)))
5081     ;; FIXME: message-mail should do something if YANK-ACTION is not
5082     ;; insert-buffer.
5083     (and (consp yank-action) (eq (car yank-action) 'insert-buffer)
5084          (setq replybuffer (nth 1 yank-action)))
5085     (message-setup
5086      (nconc
5087       `((To . ,(or to "")) (Subject . ,(or subject "")))
5088       (when other-headers other-headers))
5089      replybuffer send-actions)
5090     ;; FIXME: Should return nil if failure.
5091     t))
5092
5093 ;;;###autoload
5094 (defun message-news (&optional newsgroups subject)
5095   "Start editing a news article to be sent."
5096   (interactive)
5097   (let ((message-this-is-news t))
5098     (message-pop-to-buffer (message-buffer-name "posting" nil newsgroups))
5099     (message-setup `((Newsgroups . ,(or newsgroups ""))
5100                      (Subject . ,(or subject ""))))))
5101
5102 (defun message-get-reply-headers (wide &optional to-address)
5103   (let (follow-to mct never-mct to cc author mft recipients)
5104     ;; Find all relevant headers we need.
5105     (let ((mrt (when message-use-mail-reply-to
5106                  (message-fetch-field "mail-reply-to")))
5107           (reply-to (message-fetch-field "reply-to")))
5108       (setq to (message-fetch-field "to")
5109             cc (message-fetch-field "cc")
5110             mct (when message-use-mail-copies-to
5111                   (message-fetch-field "mail-copies-to"))
5112             author (or mrt
5113                        reply-to
5114                        (message-fetch-field "from")
5115                        "")
5116             mft (when (and (not (or to-address mrt reply-to))
5117                            message-use-mail-followup-to)
5118                   (message-fetch-field "mail-followup-to"))))
5119
5120     (save-match-data
5121       ;; Handle special values of Mail-Copies-To.
5122       (when mct
5123         (cond ((or (equal (downcase mct) "never")
5124                    (equal (downcase mct) "nobody"))
5125                (when (or (not (eq message-use-mail-copies-to 'ask))
5126                          (message-y-or-n-p
5127                           (concat "Obey Mail-Copies-To: never? ") t "\
5128 You should normally obey the Mail-Copies-To: header.
5129
5130         `Mail-Copies-To: " mct "'
5131 directs you not to send your response to the author."))
5132                  (setq never-mct t))
5133                (setq mct nil))
5134               ((or (equal (downcase mct) "always")
5135                    (equal (downcase mct) "poster"))
5136                (if (or (not (eq message-use-mail-copies-to 'ask))
5137                        (message-y-or-n-p
5138                         (concat "Obey Mail-Copies-To: always? ") t "\
5139 You should normally obey the Mail-Copies-To: header.
5140
5141         `Mail-Copies-To: " mct "'
5142 sends a copy of your response to the author."))
5143                    (setq mct author)
5144                  (setq mct nil)))
5145               ((and (eq message-use-mail-copies-to 'ask)
5146                     (not (message-y-or-n-p
5147                           (concat "Obey Mail-Copies-To: " mct " ? ") t "\
5148 You should normally obey the Mail-Copies-To: header.
5149
5150         `Mail-Copies-To: " mct "'
5151 sends a copy of your response to " (if (string-match "," mct)
5152                                        "the specified addresses"
5153                                      "that address") ".")))
5154                (setq mct nil))))
5155
5156       ;; Build (textual) list of new recipient addresses.
5157       (cond
5158        ((not wide)
5159         (setq recipients (concat ", " author)))
5160        ((and mft
5161              (string-match "[^ \t,]" mft)
5162              (or (not (eq message-use-mail-followup-to 'ask))
5163                  (message-y-or-n-p "Obey Mail-Followup-To? " t "\
5164 You should normally obey the Mail-Followup-To: header.  In this
5165 article, it has the value of
5166
5167 " mft "
5168
5169 which directs your response to " (if (string-match "," mft)
5170                                      "the specified addresses"
5171                                    "that address only") ".
5172
5173 Most commonly, Mail-Followup-To is used by a mailing list poster to
5174 express that responses should be sent to just the list, and not the
5175 poster as well.
5176
5177 If a message is posted to several mailing lists, Mail-Followup-To may
5178 also be used to direct the following discussion to one list only,
5179 because discussions that are spread over several lists tend to be
5180 fragmented and very difficult to follow.
5181
5182 Also, some source/announcement lists are not intended for discussion;
5183 responses here are directed to other addresses.")))
5184         (setq recipients (concat ", " mft)))
5185        (to-address
5186         (setq recipients (concat ", " to-address))
5187         ;; If the author explicitly asked for a copy, we don't deny it to them.
5188         (if mct (setq recipients (concat recipients ", " mct))))
5189        (t
5190         (setq recipients (if never-mct "" (concat ", " author)))
5191         (if to  (setq recipients (concat recipients ", " to)))
5192         (if cc  (setq recipients (concat recipients ", " cc)))
5193         (if mct (setq recipients (concat recipients ", " mct)))))
5194       (if (>= (length recipients) 2)
5195           ;; Strip the leading ", ".
5196           (setq recipients (substring recipients 2)))
5197       ;; Squeeze whitespace.
5198       (while (string-match "[ \t][ \t]+" recipients)
5199         (setq recipients (replace-match " " t t recipients)))
5200       ;; Remove addresses that match `rmail-dont-reply-to-names'.
5201       (let ((rmail-dont-reply-to-names message-dont-reply-to-names))
5202         (setq recipients (rmail-dont-reply-to recipients)))
5203       ;; Perhaps "Mail-Copies-To: never" removed the only address?
5204       (if (string-equal recipients "")
5205           (setq recipients author))
5206       ;; Convert string to a list of (("foo@bar" . "Name <foo@bar>") ...).
5207       (setq recipients
5208             (mapcar
5209              (lambda (addr)
5210                (cons (mail-strip-quoted-names addr) addr))
5211              (message-tokenize-header recipients)))
5212       ;; Remove first duplicates.  (Why not all duplicates?  Is this a bug?)
5213       (let ((s recipients))
5214         (while s
5215           (setq recipients (delq (assoc (car (pop s)) s) recipients))))
5216       ;; Build the header alist.  Allow the user to be asked whether
5217       ;; or not to reply to all recipients in a wide reply.
5218       (setq follow-to (list (cons 'To (cdr (pop recipients)))))
5219       (when (and recipients
5220                  (or (not message-wide-reply-confirm-recipients)
5221                      (y-or-n-p "Reply to all recipients? ")))
5222         (setq recipients (mapconcat
5223                           (lambda (addr) (cdr addr)) recipients ", "))
5224         (if (string-match "^ +" recipients)
5225             (setq recipients (substring recipients (match-end 0))))
5226         (push (cons 'Cc recipients) follow-to)))
5227     follow-to))
5228
5229 ;;;###autoload
5230 (defun message-reply (&optional to-address wide)
5231   "Start editing a reply to the article in the current buffer."
5232   (interactive)
5233   (require 'gnus-sum)                   ; for gnus-list-identifiers
5234   (let ((cur (current-buffer))
5235         from subject date
5236         references message-id follow-to
5237         (inhibit-point-motion-hooks t)
5238         (message-this-is-mail t)
5239         gnus-warning in-reply-to)
5240     (save-restriction
5241       (message-narrow-to-head-1)
5242       ;; Allow customizations to have their say.
5243       (if (not wide)
5244           ;; This is a regular reply.
5245           (when (message-functionp message-reply-to-function)
5246             (save-excursion
5247               (setq follow-to (funcall message-reply-to-function))))
5248         ;; This is a followup.
5249         (when (message-functionp message-wide-reply-to-function)
5250           (save-excursion
5251             (setq follow-to
5252                   (funcall message-wide-reply-to-function)))))
5253       (setq message-id (message-fetch-field "message-id" t)
5254             references (message-fetch-field "references")
5255             date (message-fetch-field "date")
5256             from (message-fetch-field "from")
5257             subject (or (message-fetch-field "subject") "none"))
5258       (when gnus-list-identifiers
5259         (setq subject (message-strip-list-identifiers subject)))
5260       (setq subject (message-make-followup-subject subject))
5261
5262       (when (and (setq gnus-warning (message-fetch-field "gnus-warning"))
5263                  (string-match "<[^>]+>" gnus-warning))
5264         (setq message-id (match-string 0 gnus-warning)))
5265
5266       (unless follow-to
5267         (setq follow-to (message-get-reply-headers wide to-address)))
5268
5269       ;; Get the references from "In-Reply-To" field if there were
5270       ;; no references and "In-Reply-To" field looks promising.
5271       (unless references
5272         (when (and (setq in-reply-to (message-fetch-field "in-reply-to"))
5273                    (string-match "<[^>]+>" in-reply-to))
5274           (setq references (match-string 0 in-reply-to)))))
5275
5276     (unless (message-mail-user-agent)
5277       (message-pop-to-buffer
5278        (message-buffer-name
5279         (if wide "wide reply" "reply") from
5280         (if wide to-address nil))))
5281
5282     (setq message-reply-headers
5283           (make-full-mail-header-from-decoded-header
5284            0 subject from date message-id references 0 0 ""))
5285
5286     (message-setup
5287      `((Subject . ,subject)
5288        ,@follow-to
5289        ,@(if (or references message-id)
5290              `((References . ,(concat (or references "") (and references " ")
5291                                       (or message-id ""))))
5292            nil))
5293      cur)))
5294
5295 ;;;###autoload
5296 (defun message-wide-reply (&optional to-address)
5297   "Make a \"wide\" reply to the message in the current buffer."
5298   (interactive)
5299   (message-reply to-address t))
5300
5301 ;;;###autoload
5302 (defun message-followup (&optional to-newsgroups)
5303   "Follow up to the message in the current buffer.
5304 If TO-NEWSGROUPS, use that as the new Newsgroups line."
5305   (interactive)
5306   (require 'gnus-sum)                   ; for gnus-list-identifiers
5307   (let ((cur (current-buffer))
5308         from subject date reply-to mrt mct mft
5309         references message-id follow-to
5310         (inhibit-point-motion-hooks t)
5311         (message-this-is-news t)
5312         followup-to distribution newsgroups gnus-warning posted-to)
5313     (save-restriction
5314       (message-narrow-to-head)
5315       (when (message-functionp message-followup-to-function)
5316         (setq follow-to
5317               (funcall message-followup-to-function)))
5318       (setq from (message-fetch-field "from")
5319             date (message-fetch-field "date")
5320             subject (or (message-fetch-field "subject") "none")
5321             references (message-fetch-field "references")
5322             message-id (message-fetch-field "message-id" t)
5323             followup-to (message-fetch-field "followup-to")
5324             newsgroups (message-fetch-field "newsgroups")
5325             posted-to (message-fetch-field "posted-to")
5326             reply-to (message-fetch-field "reply-to")
5327             mrt (when message-use-mail-reply-to
5328                   (message-fetch-field "mail-reply-to"))
5329             distribution (message-fetch-field "distribution")
5330             mct (when message-use-mail-copies-to
5331                   (message-fetch-field "mail-copies-to"))
5332             mft (when message-use-mail-followup-to
5333                   (message-fetch-field "mail-followup-to")))
5334       (when (and (setq gnus-warning (message-fetch-field "gnus-warning"))
5335                  (string-match "<[^>]+>" gnus-warning))
5336         (setq message-id (match-string 0 gnus-warning)))
5337       ;; Remove bogus distribution.
5338       (when (and (stringp distribution)
5339                  (let ((case-fold-search t))
5340                    (string-match "world" distribution)))
5341         (setq distribution nil))
5342       (if gnus-list-identifiers
5343           (setq subject (message-strip-list-identifiers subject)))
5344       (setq subject (message-make-followup-subject subject))
5345       (widen))
5346
5347     ;; Handle special values of Mail-Copies-To.
5348     (when mct
5349       (cond
5350        ((and (or (equal (downcase mct) "never")
5351                  (equal (downcase mct) "nobody")))
5352         (setq mct nil))
5353        ((and (or (equal (downcase mct) "always")
5354                  (equal (downcase mct) "poster")))
5355         (if (or (not (eq message-use-mail-copies-to 'ask))
5356                 (message-y-or-n-p
5357                  (concat "Obey Mail-Copies-To: always? ") t "\
5358 You should normally obey the Mail-Copies-To: header.
5359
5360         `Mail-Copies-To: " mct "'
5361 sends a copy of your response to the author."))
5362             (setq mct (or mrt reply-to from))
5363           (setq mct nil)))
5364        ((and (eq message-use-mail-copies-to 'ask)
5365              (not
5366               (message-y-or-n-p
5367                (concat "Obey Mail-Copies-To: " mct " ? ") t "\
5368 You should normally obey the Mail-Copies-To: header.
5369
5370         `Mail-Copies-To: " mct "'
5371 sends a copy of your response to " (if (string-match "," mct)
5372                                        "the specified addresses"
5373                                      "that address") ".")))
5374         (setq mct nil))))
5375
5376     (unless follow-to
5377       (cond
5378        (to-newsgroups (setq follow-to (list (cons 'Newsgroups to-newsgroups))))
5379        ;; Handle Followup-To.
5380        (followup-to
5381         (cond
5382          ((equal (downcase followup-to) "poster")
5383           (if (or (and followup-to (eq message-use-followup-to 'use))
5384                   (message-y-or-n-p "Obey Followup-To: poster? " t "\
5385 You should normally obey the Followup-To: header.
5386
5387         `Followup-To: poster'
5388 sends your response via e-mail instead of news.
5389
5390 A typical situation where `Followup-To: poster' is used is when the author
5391 does not read the newsgroup, so he wouldn't see any replies sent to it."))
5392               (setq message-this-is-news nil
5393                     distribution nil
5394                     follow-to (list (cons 'To (or mrt reply-to from ""))))
5395             (setq follow-to (list (cons 'Newsgroups newsgroups)))))
5396          (t
5397           (if (or (equal followup-to newsgroups)
5398                   (not (and followup-to (eq message-use-followup-to 'ask)))
5399                   (message-y-or-n-p
5400                    (concat "Obey Followup-To: " followup-to "? ") t "\
5401 You should normally obey the Followup-To: header.
5402
5403         `Followup-To: " followup-to "'
5404 directs your response to " (if (string-match "," followup-to)
5405                                "the specified newsgroups"
5406                              "that newsgroup only") ".
5407
5408 If a message is posted to several newsgroups, Followup-To is often
5409 used to direct the following discussion to one newsgroup only,
5410 because discussions that are spread over several newsgroup tend to
5411 be fragmented and very difficult to follow.
5412
5413 Also, some source/announcement newsgroups are not intended for discussion;
5414 responses here are directed to other newsgroups."))
5415               (setq follow-to (list (cons 'Newsgroups followup-to)))
5416             (setq follow-to (list (cons 'Newsgroups newsgroups)))))))
5417        ;; Handle Mail-Followup-To, followup via e-mail.
5418        ((and mft
5419              (or (not (eq message-use-mail-followup-to 'ask))
5420                  (message-y-or-n-p
5421                   (concat "Obey Mail-Followup-To: " mft "? ") t "\
5422 You should normally obey the Mail-Followup-To: header.
5423
5424         `Mail-Followup-To: " mft "'
5425 directs your response to " (if (string-match "," mft)
5426                                "the specified addresses"
5427                              "that address only") " instead of news.
5428
5429 A typical situation where Mail-Followup-To is used is when the author thinks
5430 that further discussion should take place only in "
5431                              (if (string-match "," mft)
5432                                  "the specified mailing lists"
5433                                "that mailing list") ".")))
5434         (setq message-this-is-news nil
5435               distribution nil
5436               follow-to (list (cons 'To mft))))
5437        (posted-to (setq follow-to (list (cons 'Newsgroups posted-to))))
5438        (t
5439         (setq follow-to (list (cons 'Newsgroups newsgroups))))))
5440
5441     (message-pop-to-buffer (message-buffer-name "followup" from newsgroups))
5442
5443     (message-setup
5444      `((Subject . ,subject)
5445        ,@follow-to
5446        ,@(and mct (list (cons 'Cc mct)))
5447        ,@(and distribution (list (cons 'Distribution distribution)))
5448        ,@(if (or references message-id)
5449              `((References . ,(concat (or references "") (and references " ")
5450                                       (or message-id ""))))))
5451      cur)
5452
5453     (setq message-reply-headers
5454           (make-full-mail-header-from-decoded-header
5455            0 subject from date message-id references 0 0 ""))))
5456
5457 ;;;###autoload
5458 (defun message-cancel-news (&optional arg)
5459   "Cancel an article you posted.
5460 If ARG, allow editing of the cancellation message."
5461   (interactive "P")
5462   (unless (message-news-p)
5463     (error "This is not a news article; canceling is impossible"))
5464   (let (from newsgroups message-id distribution buf sender)
5465     (save-excursion
5466       ;; Get header info from original article.
5467       (save-restriction
5468         (message-narrow-to-head-1)
5469         (setq from (message-fetch-field "from")
5470               sender (message-fetch-field "sender")
5471               newsgroups (message-fetch-field "newsgroups")
5472               message-id (message-fetch-field "message-id" t)
5473               distribution (message-fetch-field "distribution")))
5474       ;; Make sure that this article was written by the user.
5475       (unless (or
5476                ;; Canlock-logic as suggested by Per Abrahamsen
5477                ;; <abraham@dina.kvl.dk>
5478                ;;
5479                ;; IF article has cancel-lock THEN
5480                ;;   IF we can verify it THEN
5481                ;;     issue cancel
5482                ;;   ELSE
5483                ;;     error: cancellock: article is not yours
5484                ;; ELSE
5485                ;;   Use old rules, comparing sender...
5486                (if (message-fetch-field "Cancel-Lock")
5487                    (if (null (canlock-verify))
5488                        t
5489                      (error "Failed to verify Cancel-lock: This article is not yours"))
5490                  nil)
5491                (message-gnksa-enable-p 'cancel-messages)
5492                (and sender
5493                     (string-equal
5494                      (downcase sender)
5495                      (downcase (message-make-sender))))
5496                (string-equal
5497                 (downcase (cadr (std11-extract-address-components from)))
5498                 (downcase (cadr (std11-extract-address-components
5499                                  (message-make-from))))))
5500         (error "This article is not yours"))
5501       (when (yes-or-no-p "Do you really want to cancel this article? ")
5502         ;; Make control message.
5503         (if arg
5504             (message-news)
5505           (setq buf (set-buffer (get-buffer-create " *message cancel*"))))
5506         (erase-buffer)
5507         (insert "Newsgroups: " newsgroups "\n"
5508                 "From: " from "\n"
5509                 "Subject: cmsg cancel " message-id "\n"
5510                 "Control: cancel " message-id "\n"
5511                 (if distribution
5512                     (concat "Distribution: " distribution "\n")
5513                   "")
5514                 mail-header-separator "\n"
5515                 message-cancel-message)
5516         (run-hooks 'message-cancel-hook)
5517         (unless arg
5518           (message "Canceling your article...")
5519           (if (let ((message-syntax-checks
5520                      'dont-check-for-anything-just-trust-me)
5521                     (message-encoding-buffer (current-buffer))
5522                     (message-edit-buffer (current-buffer)))
5523                 (message-send-news))
5524               (message "Canceling your article...done"))
5525           (kill-buffer buf))))))
5526
5527 (defun message-supersede-setup-for-mime-edit ()
5528   (set (make-local-variable 'message-setup-hook) nil)
5529   (mime-edit-again))
5530
5531 ;;;###autoload
5532 (defun message-supersede ()
5533   "Start composing a message to supersede the current message.
5534 This is done simply by taking the old article and adding a Supersedes
5535 header line with the old Message-ID."
5536   (interactive)
5537   (let ((cur (current-buffer))
5538         (sender (message-fetch-field "sender"))
5539         (from (message-fetch-field "from")))
5540     ;; Check whether the user owns the article that is to be superseded.
5541     (unless (or
5542              ;; Canlock-logic as suggested by Per Abrahamsen
5543              ;; <abraham@dina.kvl.dk>
5544              ;;
5545              ;; IF article has cancel-lock THEN
5546              ;;   IF we can verify it THEN
5547              ;;     issue cancel
5548              ;;   ELSE
5549              ;;     error: cancellock: article is not yours
5550              ;; ELSE
5551              ;;   Use old rules, comparing sender...
5552              (if (message-fetch-field "Cancel-Lock")
5553                  (if (null (canlock-verify))
5554                      t
5555                    (error "Failed to verify Cancel-lock: This article is not yours"))
5556                nil)
5557              (message-gnksa-enable-p 'cancel-messages)
5558              (and sender
5559                   (string-equal
5560                    (downcase sender)
5561                    (downcase (message-make-sender))))
5562              (string-equal
5563               (downcase (cadr (std11-extract-address-components from)))
5564               (downcase (cadr (std11-extract-address-components
5565                                (message-make-from))))))
5566       (error "This article is not yours"))
5567     ;; Get a normal message buffer.
5568     (message-pop-to-buffer (message-buffer-name "supersede"))
5569     (insert-buffer-substring cur)
5570     (message-narrow-to-head-1)
5571     ;; Remove unwanted headers.
5572     (when message-ignored-supersedes-headers
5573       (message-remove-header message-ignored-supersedes-headers t))
5574     (goto-char (point-min))
5575     (if (not (re-search-forward "^Message-ID: " nil t))
5576         (error "No Message-ID in this article")
5577       (replace-match "Supersedes: " t t))
5578     (goto-char (point-max))
5579     (insert mail-header-separator)
5580     (widen)
5581     (when message-supersede-setup-function
5582       (funcall message-supersede-setup-function))
5583     (run-hooks 'message-supersede-setup-hook)
5584     (goto-char (point-min))
5585     (search-forward (concat "\n" mail-header-separator "\n") nil t)))
5586
5587 ;;;###autoload
5588 (defun message-recover ()
5589   "Reread contents of current buffer from its last auto-save file."
5590   (interactive)
5591   (let ((file-name (make-auto-save-file-name)))
5592     (cond ((save-window-excursion
5593              (if (not (eq system-type 'vax-vms))
5594                  (with-output-to-temp-buffer "*Directory*"
5595                    (with-current-buffer standard-output
5596                      (fundamental-mode)) ; for Emacs 20.4+
5597                    (buffer-disable-undo standard-output)
5598                    (let ((default-directory "/"))
5599                      (call-process
5600                       "ls" nil standard-output nil "-l" file-name))))
5601              (yes-or-no-p (format "Recover auto save file %s? " file-name)))
5602            (let ((buffer-read-only nil))
5603              (erase-buffer)
5604              (insert-file-contents file-name nil)))
5605           (t (error "message-recover cancelled")))))
5606
5607 ;;; Washing Subject:
5608
5609 (defun message-wash-subject (subject)
5610   "Remove junk like \"Re:\", \"(fwd)\", etc. added to subject string SUBJECT.
5611 Previous forwarders, replyers, etc. may add it."
5612   (with-temp-buffer
5613     (insert subject)
5614     (goto-char (point-min))
5615     ;; strip Re/Fwd stuff off the beginning
5616     (while (re-search-forward
5617             "\\([Rr][Ee]:\\|[Ff][Ww][Dd]\\(\\[[0-9]*\\]\\)?:\\|[Ff][Ww]:\\)" nil t)
5618       (replace-match ""))
5619
5620     ;; and gnus-style forwards [foo@bar.com] subject
5621     (goto-char (point-min))
5622     (while (re-search-forward "\\[[^ \t]*\\(@\\|\\.\\)[^ \t]*\\]" nil t)
5623       (replace-match ""))
5624
5625     ;; and off the end
5626     (goto-char (point-max))
5627     (while (re-search-backward "([Ff][Ww][Dd])" nil t)
5628       (replace-match ""))
5629
5630     ;; and finally, any whitespace that was left-over
5631     (goto-char (point-min))
5632     (while (re-search-forward "^[ \t]+" nil t)
5633       (replace-match ""))
5634     (goto-char (point-max))
5635     (while (re-search-backward "[ \t]+$" nil t)
5636       (replace-match ""))
5637
5638     (buffer-string)))
5639
5640 ;;; Forwarding messages.
5641
5642 (defvar message-forward-decoded-p nil
5643   "Non-nil means the original message is decoded.")
5644
5645 (defun message-forward-subject-author-subject (subject)
5646   "Generate a SUBJECT for a forwarded message.
5647 The form is: [Source] Subject, where if the original message was mail,
5648 Source is the sender, and if the original message was news, Source is
5649 the list of newsgroups is was posted to."
5650   (concat "["
5651           (let ((prefix (message-fetch-field "newsgroups")))
5652             (or prefix
5653                 (and (setq prefix (message-fetch-field "from"))
5654                      (nnheader-decode-from prefix))
5655                 "(nowhere)"))
5656           "] " subject))
5657
5658 (defun message-forward-subject-fwd (subject)
5659   "Generate a SUBJECT for a forwarded message.
5660 The form is: Fwd: Subject, where Subject is the original subject of
5661 the message."
5662   (concat "Fwd: " subject))
5663
5664 (defun message-make-forward-subject ()
5665   "Return a Subject header suitable for the message in the current buffer."
5666   (save-excursion
5667     (save-restriction
5668       (message-narrow-to-head-1)
5669       (let ((funcs message-make-forward-subject-function)
5670             (subject (message-fetch-field "Subject")))
5671         (setq subject
5672               (if subject
5673                   (if message-forward-decoded-p
5674                       subject
5675                     (nnheader-decode-subject subject))
5676                 ""))
5677         (if message-wash-forwarded-subjects
5678             (setq subject (message-wash-subject subject)))
5679         ;; Make sure funcs is a list.
5680         (and funcs
5681              (not (listp funcs))
5682              (setq funcs (list funcs)))
5683         ;; Apply funcs in order, passing subject generated by previous
5684         ;; func to the next one.
5685         (while funcs
5686           (when (message-functionp (car funcs))
5687             (setq subject (funcall (car funcs) subject)))
5688           (setq funcs (cdr funcs)))
5689         subject))))
5690
5691 ;;;###autoload
5692 (defun message-forward (&optional news)
5693   "Forward the current message via mail.
5694 Optional NEWS will use news to forward instead of mail."
5695   (interactive "P")
5696   (let ((cur (current-buffer))
5697         (subject (message-make-forward-subject)))
5698     (if news
5699         (message-news nil subject)
5700       (message-mail nil subject))
5701     (message-forward-make-body cur)))
5702
5703 ;;;###autoload
5704 (defun message-forward-make-body (forward-buffer)
5705   ;; Put point where we want it before inserting the forwarded
5706   ;; message.
5707   (if message-forward-before-signature
5708       (message-goto-body)
5709     (goto-char (point-max)))
5710   ;; Make sure we're at the start of the line.
5711   (unless (bolp)
5712     (insert "\n"))
5713   ;; Narrow to the area we are to insert.
5714   (narrow-to-region (point) (point))
5715   ;; Insert the separators and the forwarded buffer.
5716   (insert message-forward-start-separator)
5717   (let ((art-beg (point)))
5718     (insert-buffer-substring forward-buffer)
5719     (goto-char (point-max))
5720     (insert message-forward-end-separator)
5721     (set-text-properties (point-min) (point-max) nil)
5722     ;; Remove all unwanted headers.
5723     (goto-char art-beg)
5724     (narrow-to-region (point) (if (search-forward "\n\n" nil t)
5725                                   (1- (point))
5726                                 (point)))
5727     (goto-char (point-min))
5728     (message-remove-header message-included-forward-headers t nil t)
5729     (widen)
5730     (message-position-point)))
5731
5732 ;;;###autoload
5733 (defun message-forward-rmail-make-body (forward-buffer)
5734   (save-window-excursion
5735     (set-buffer forward-buffer)
5736     (let (rmail-enable-mime)
5737       (rmail-toggle-header 0)))
5738   (message-forward-make-body forward-buffer))
5739
5740 ;;;###autoload
5741 (defun message-insinuate-rmail ()
5742   "Let RMAIL uses message to forward."
5743   (interactive)
5744   (setq rmail-enable-mime-composing t)
5745   (setq rmail-insert-mime-forwarded-message-function
5746         'message-forward-rmail-make-body))
5747
5748 ;;;###autoload
5749 (defun message-resend (address)
5750   "Resend the current article to ADDRESS."
5751   (interactive
5752    (list (message-read-from-minibuffer "Resend message to: ")))
5753   (message "Resending message to %s..." address)
5754   (save-excursion
5755     (let ((cur (current-buffer))
5756           beg)
5757       ;; We first set up a normal mail buffer.
5758       (unless (message-mail-user-agent)
5759         (set-buffer (get-buffer-create " *message resend*"))
5760         (erase-buffer)
5761         (let ((message-this-is-mail t)
5762               ;; avoid to turn-on-mime-edit
5763               message-setup-hook)
5764           (message-setup `((To . ,address)))))
5765       ;; Insert our usual headers.
5766       (message-generate-headers '(From Date To))
5767       (message-narrow-to-headers)
5768       ;; Rename them all to "Resent-*".
5769       (while (re-search-forward "^[A-Za-z]" nil t)
5770         (forward-char -1)
5771         (insert "Resent-"))
5772       (widen)
5773       (forward-line)
5774       (delete-region (point) (point-max))
5775       (setq beg (point))
5776       ;; Insert the message to be resent.
5777       (insert-buffer-substring cur)
5778       (goto-char (point-min))
5779       (search-forward "\n\n")
5780       (forward-char -1)
5781       (save-restriction
5782         (narrow-to-region beg (point))
5783         (message-remove-header message-ignored-resent-headers t)
5784         (goto-char (point-max)))
5785       (insert mail-header-separator)
5786       ;; Rename all old ("Also-")Resent headers.
5787       (while (re-search-backward "^\\(Also-\\)*Resent-" beg t)
5788         (beginning-of-line)
5789         (insert "Also-"))
5790       ;; Quote any "From " lines at the beginning.
5791       (goto-char beg)
5792       (when (looking-at "From ")
5793         (replace-match "X-From-Line: "))
5794       ;; Send it.
5795       (let ((message-encoding-buffer (current-buffer))
5796             (message-edit-buffer (current-buffer))
5797             message-required-mail-headers)
5798         (message-send-mail))
5799       (kill-buffer (current-buffer)))
5800     (message "Resending message to %s...done" address)))
5801
5802 (defun message-bounce-setup-for-mime-edit ()
5803   (set (make-local-variable 'message-setup-hook) nil)
5804   (mime-edit-again))
5805
5806 ;;;###autoload
5807 (defun message-bounce ()
5808   "Re-mail the current message.
5809 This only makes sense if the current message is a bounce message that
5810 contains some mail you have written which has been bounced back to
5811 you."
5812   (interactive)
5813   (let ((cur (current-buffer))
5814         boundary)
5815     (message-pop-to-buffer (message-buffer-name "bounce"))
5816     (insert-buffer-substring cur)
5817     (undo-boundary)
5818     (message-narrow-to-head)
5819     (if (and (message-fetch-field "MIME-Version")
5820              (setq boundary (message-fetch-field "Content-Type")))
5821         (if (string-match "boundary=\"\\([^\"]+\\)\"" boundary)
5822             (setq boundary (concat (match-string 1 boundary) " *\n"
5823                                    "Content-Type: message/rfc822"))
5824           (setq boundary nil)))
5825     (widen)
5826     (goto-char (point-min))
5827     (search-forward "\n\n" nil t)
5828     (if (or (and boundary
5829                  (re-search-forward boundary nil t)
5830                  (forward-line 2))
5831             (and (re-search-forward message-unsent-separator nil t)
5832                  (forward-line 1))
5833             (re-search-forward "^Return-Path:.*\n" nil t))
5834         ;; We remove everything before the bounced mail.
5835         (delete-region
5836          (point-min)
5837          (if (re-search-forward "^[^ \n\t]+:" nil t)
5838              (match-beginning 0)
5839            (point)))
5840       (when (re-search-backward "^.?From .*\n" nil t)
5841         (delete-region (match-beginning 0) (match-end 0))))
5842     (save-restriction
5843       (message-narrow-to-head-1)
5844       (message-remove-header message-ignored-bounced-headers t)
5845       (goto-char (point-max))
5846       (insert mail-header-separator))
5847     (when message-bounce-setup-function
5848       (funcall message-bounce-setup-function))
5849     (run-hooks 'message-bounce-setup-hook)
5850     (message-position-point)))
5851
5852 ;;;
5853 ;;; Interactive entry points for new message buffers.
5854 ;;;
5855
5856 ;;;###autoload
5857 (defun message-mail-other-window (&optional to subject)
5858   "Like `message-mail' command, but display mail buffer in another window."
5859   (interactive)
5860   (unless (message-mail-user-agent)
5861     (let ((pop-up-windows t)
5862           (special-display-buffer-names nil)
5863           (special-display-regexps nil)
5864           (same-window-buffer-names nil)
5865           (same-window-regexps nil))
5866       (message-pop-to-buffer (message-buffer-name "mail" to))))
5867   (let ((message-this-is-mail t))
5868     (message-setup `((To . ,(or to "")) (Subject . ,(or subject "")))
5869                    nil nil 'switch-to-buffer-other-window)))
5870
5871 ;;;###autoload
5872 (defun message-mail-other-frame (&optional to subject)
5873   "Like `message-mail' command, but display mail buffer in another frame."
5874   (interactive)
5875   (unless (message-mail-user-agent)
5876     (let ((pop-up-frames t)
5877           (special-display-buffer-names nil)
5878           (special-display-regexps nil)
5879           (same-window-buffer-names nil)
5880           (same-window-regexps nil))
5881       (message-pop-to-buffer (message-buffer-name "mail" to))))
5882   (let ((message-this-is-mail t))
5883     (message-setup `((To . ,(or to "")) (Subject . ,(or subject "")))
5884                    nil nil 'switch-to-buffer-other-frame)))
5885
5886 ;;;###autoload
5887 (defun message-news-other-window (&optional newsgroups subject)
5888   "Start editing a news article to be sent."
5889   (interactive)
5890   (let ((pop-up-windows t)
5891         (special-display-buffer-names nil)
5892         (special-display-regexps nil)
5893         (same-window-buffer-names nil)
5894         (same-window-regexps nil))
5895     (message-pop-to-buffer (message-buffer-name "posting" nil newsgroups)))
5896   (let ((message-this-is-news t))
5897     (message-setup `((Newsgroups . ,(or newsgroups ""))
5898                      (Subject . ,(or subject ""))))))
5899
5900 ;;;###autoload
5901 (defun message-news-other-frame (&optional newsgroups subject)
5902   "Start editing a news article to be sent."
5903   (interactive)
5904   (let ((pop-up-frames t)
5905         (special-display-buffer-names nil)
5906         (special-display-regexps nil)
5907         (same-window-buffer-names nil)
5908         (same-window-regexps nil))
5909     (message-pop-to-buffer (message-buffer-name "posting" nil newsgroups)))
5910   (let ((message-this-is-news t))
5911     (message-setup `((Newsgroups . ,(or newsgroups ""))
5912                      (Subject . ,(or subject ""))))))
5913
5914 ;;; underline.el
5915
5916 ;; This code should be moved to underline.el (from which it is stolen).
5917
5918 ;;;###autoload
5919 (defun bold-region (start end)
5920   "Bold all nonblank characters in the region.
5921 Works by overstriking characters.
5922 Called from program, takes two arguments START and END
5923 which specify the range to operate on."
5924   (interactive "r")
5925   (save-excursion
5926     (let ((end1 (make-marker)))
5927       (move-marker end1 (max start end))
5928       (goto-char (min start end))
5929       (while (< (point) end1)
5930         (or (looking-at "[_\^@- ]")
5931             (insert (char-after) "\b"))
5932         (forward-char 1)))))
5933
5934 ;;;###autoload
5935 (defun unbold-region (start end)
5936   "Remove all boldness (overstruck characters) in the region.
5937 Called from program, takes two arguments START and END
5938 which specify the range to operate on."
5939   (interactive "r")
5940   (save-excursion
5941     (let ((end1 (make-marker)))
5942       (move-marker end1 (max start end))
5943       (goto-char (min start end))
5944       (while (re-search-forward "\b" end1 t)
5945         (if (eq (char-after) (char-after (- (point) 2)))
5946             (delete-char -2))))))
5947
5948 (defalias 'message-exchange-point-and-mark 'exchange-point-and-mark)
5949
5950 ;; Support for toolbar
5951 (eval-when-compile
5952   (defvar tool-bar-map)
5953   (defvar tool-bar-mode))
5954
5955 (defun message-tool-bar-map ()
5956   (or message-tool-bar-map
5957       (setq message-tool-bar-map
5958             (and (fboundp 'tool-bar-add-item-from-menu)
5959                  tool-bar-mode
5960                  (let ((tool-bar-map (copy-keymap tool-bar-map))
5961                        (load-path (mm-image-load-path)))
5962                    ;; Zap some items which aren't so relevant and take
5963                    ;; up space.
5964                    (dolist (key '(print-buffer kill-buffer save-buffer
5965                                                write-file dired open-file))
5966                      (define-key tool-bar-map (vector key) nil))
5967                    (tool-bar-add-item-from-menu
5968                     'message-send-and-exit "mail_send" message-mode-map)
5969                    (tool-bar-add-item-from-menu
5970                     'message-kill-buffer "close" message-mode-map)
5971                    (tool-bar-add-item-from-menu
5972                     'message-dont-send "cancel" message-mode-map)
5973                    (tool-bar-add-item-from-menu
5974                     'mime-edit-insert-file "attach" message-mode-map)
5975                    (tool-bar-add-item-from-menu
5976                     'ispell-message "spell" message-mode-map)
5977                    (tool-bar-add-item-from-menu
5978                     'message-insert-importance-high "important"
5979                     message-mode-map)
5980                    (tool-bar-add-item-from-menu
5981                     'message-insert-importance-low "unimportant"
5982                     message-mode-map)
5983                    (tool-bar-add-item-from-menu
5984                     'message-insert-disposition-notification-to "receipt"
5985                     message-mode-map)
5986                    tool-bar-map)))))
5987
5988 ;;; Group name completion.
5989
5990 (defcustom message-newgroups-header-regexp
5991   "^\\(Newsgroups\\|Followup-To\\|Posted-To\\|Gcc\\):"
5992   "Regexp that match headers that lists groups."
5993   :group 'message
5994   :type 'regexp)
5995
5996 (defcustom message-completion-alist
5997   (list (cons message-newgroups-header-regexp 'message-expand-group)
5998         '("^\\(Resent-\\)?\\(To\\|B?Cc\\):" . message-expand-name))
5999   "Alist of (RE . FUN).  Use FUN for completion on header lines matching RE."
6000   :group 'message
6001   :type '(alist :key-type regexp :value-type function))
6002
6003 (defcustom message-tab-body-function nil
6004   "*Function to execute when `message-tab' (TAB) is executed in the body.
6005 If nil, the function bound in `text-mode-map' or `global-map' is executed."
6006   :group 'message
6007   :type 'function)
6008
6009 (defun message-tab ()
6010   "Complete names according to `message-completion-alist'.
6011 Execute function specified by `message-tab-body-function' when not in
6012 those headers."
6013   (interactive)
6014   (let ((alist message-completion-alist))
6015     (while (and alist
6016                 (let ((mail-abbrev-mode-regexp (caar alist)))
6017                   (not (mail-abbrev-in-expansion-header-p))))
6018       (setq alist (cdr alist)))
6019     (funcall (or (cdar alist) message-tab-body-function
6020                  (lookup-key text-mode-map "\t")
6021                  (lookup-key global-map "\t")
6022                  'indent-relative))))
6023
6024 (defun message-expand-group ()
6025   "Expand the group name under point."
6026   (let* ((b (save-excursion
6027               (save-restriction
6028                 (narrow-to-region
6029                  (save-excursion
6030                    (beginning-of-line)
6031                    (skip-chars-forward "^:")
6032                    (1+ (point)))
6033                  (point))
6034                 (skip-chars-backward "^, \t\n") (point))))
6035          (completion-ignore-case t)
6036          (string (buffer-substring b (progn (skip-chars-forward "^,\t\n ")
6037                                             (point))))
6038          (hashtb (and (boundp 'gnus-active-hashtb) gnus-active-hashtb))
6039          (completions (all-completions string hashtb))
6040          comp)
6041     (delete-region b (point))
6042     (cond
6043      ((= (length completions) 1)
6044       (if (string= (car completions) string)
6045           (progn
6046             (insert string)
6047             (message "Only matching group"))
6048         (insert (car completions))))
6049      ((and (setq comp (try-completion string hashtb))
6050            (not (string= comp string)))
6051       (insert comp))
6052      (t
6053       (insert string)
6054       (if (not comp)
6055           (message "No matching groups")
6056         (save-selected-window
6057           (pop-to-buffer "*Completions*")
6058           (buffer-disable-undo)
6059           (let ((buffer-read-only nil))
6060             (erase-buffer)
6061             (let ((standard-output (current-buffer)))
6062               (display-completion-list (sort completions 'string<)))
6063             (goto-char (point-min))
6064             (delete-region (point) (progn (forward-line 3) (point))))))))))
6065
6066 (defun message-expand-name ()
6067   (if (fboundp 'bbdb-complete-name)
6068       (bbdb-complete-name)
6069     (expand-abbrev)))
6070
6071 ;;; Help stuff.
6072
6073 (defun message-talkative-question (ask question show &rest text)
6074   "Call FUNCTION with argument QUESTION; optionally display TEXT... args.
6075 If SHOW is non-nil, the arguments TEXT... are displayed in a temp buffer.
6076 The following arguments may contain lists of values."
6077   (if (and show
6078            (setq text (message-flatten-list text)))
6079       (save-window-excursion
6080         (save-excursion
6081           (with-output-to-temp-buffer " *MESSAGE information message*"
6082             (set-buffer " *MESSAGE information message*")
6083             (fundamental-mode)          ; for Emacs 20.4+
6084             (mapcar 'princ text)
6085             (goto-char (point-min))))
6086         (funcall ask question))
6087     (funcall ask question)))
6088
6089 (defun message-flatten-list (list)
6090   "Return a new, flat list that contains all elements of LIST.
6091
6092 \(message-flatten-list '(1 (2 3 (4 5 (6))) 7))
6093 => (1 2 3 4 5 6 7)"
6094   (cond ((consp list)
6095          (apply 'append (mapcar 'message-flatten-list list)))
6096         (list
6097          (list list))))
6098
6099 (defun message-generate-new-buffer-clone-locals (name &optional varstr)
6100   "Create and return a buffer with name based on NAME using `generate-new-buffer.'
6101 Then clone the local variables and values from the old buffer to the
6102 new one, cloning only the locals having a substring matching the
6103 regexp varstr."
6104   (let ((oldbuf (current-buffer)))
6105     (save-excursion
6106       (set-buffer (generate-new-buffer name))
6107       (message-clone-locals oldbuf varstr)
6108       (current-buffer))))
6109
6110 (defun message-clone-locals (buffer &optional varstr)
6111   "Clone the local variables from BUFFER to the current buffer."
6112   (let ((locals (save-excursion
6113                   (set-buffer buffer)
6114                   (buffer-local-variables)))
6115         (regexp
6116          "^\\(gnus\\|nn\\|message\\|user-\\(mail-address\\|full-name\\)\\)"))
6117     (mapcar
6118      (lambda (local)
6119        (when (and (consp local)
6120                   (car local)
6121                   (string-match regexp (symbol-name (car local)))
6122                   (or (null varstr)
6123                       (string-match varstr (symbol-name (car local)))))
6124          (ignore-errors
6125            (set (make-local-variable (car local))
6126                 (cdr local)))))
6127      locals)))
6128
6129
6130 ;;; @ for MIME Edit mode
6131 ;;;
6132
6133 (defun message-maybe-encode ()
6134   (when message-mime-mode
6135     ;; Inherit the buffer local variable `mime-edit-pgp-processing'.
6136     (let ((pgp-processing (with-current-buffer message-edit-buffer
6137                             mime-edit-pgp-processing)))
6138       (setq mime-edit-pgp-processing pgp-processing))
6139     (run-hooks 'mime-edit-translate-hook)
6140     (if (catch 'mime-edit-error
6141           (save-excursion
6142             (mime-edit-pgp-enclose-buffer)
6143             (mime-edit-translate-body)))
6144         (error "Translation error!"))
6145     (run-hooks 'mime-edit-exit-hook)))
6146
6147 (defun message-mime-insert-article (&optional full-headers)
6148   (interactive "P")
6149   (let ((message-cite-function 'mime-edit-inserted-message-filter)
6150         (message-reply-buffer
6151          (message-get-parameter-with-eval 'original-buffer))
6152         (start (point)))
6153     (message-yank-original nil)
6154     (save-excursion
6155       (narrow-to-region (goto-char start)
6156                         (if (search-forward "\n\n" nil t)
6157                             (1- (point))
6158                           (point-max)))
6159       (goto-char (point-min))
6160       (let ((message-included-forward-headers
6161              (if full-headers "" message-included-forward-headers)))
6162         (message-remove-header message-included-forward-headers t nil t))
6163       (widen))))
6164
6165 (set-alist 'mime-edit-message-inserter-alist
6166            'message-mode (function message-mime-insert-article))
6167
6168 ;;; Miscellaneous functions
6169
6170 ;; stolen (and renamed) from nnheader.el
6171 (static-if (fboundp 'subst-char-in-string)
6172     (defsubst message-replace-chars-in-string (string from to)
6173       (subst-char-in-string from to string))
6174   (defun message-replace-chars-in-string (string from to)
6175     "Replace characters in STRING from FROM to TO."
6176     (let ((string (substring string 0)) ;Copy string.
6177           (len (length string))
6178           (idx 0))
6179       ;; Replace all occurrences of FROM with TO.
6180       (while (< idx len)
6181         (when (= (aref string idx) from)
6182           (aset string idx to))
6183         (setq idx (1+ idx)))
6184       string)))
6185
6186 ;;;
6187 ;;; MIME functions
6188 ;;;
6189
6190 (defvar message-inhibit-body-encoding t)
6191
6192 (defun message-encode-message-body ()
6193   (unless message-inhibit-body-encoding
6194     (let ((mail-parse-charset (or mail-parse-charset
6195                                   message-default-charset))
6196           (case-fold-search t)
6197           lines content-type-p)
6198       (message-goto-body)
6199       (save-restriction
6200         (narrow-to-region (point) (point-max))
6201         (let ((new (mml-generate-mime)))
6202           (when new
6203             (delete-region (point-min) (point-max))
6204             (insert new)
6205             (goto-char (point-min))
6206             (if (eq (aref new 0) ?\n)
6207                 (delete-char 1)
6208               (search-forward "\n\n")
6209               (setq lines (buffer-substring (point-min) (1- (point))))
6210               (delete-region (point-min) (point))))))
6211       (save-restriction
6212         (message-narrow-to-headers-or-head)
6213         (message-remove-header "Mime-Version")
6214         (goto-char (point-max))
6215         (insert "MIME-Version: 1.0\n")
6216         (when lines
6217           (insert lines))
6218         (setq content-type-p
6219               (or mml-boundary
6220                   (re-search-backward "^Content-Type:" nil t))))
6221       (save-restriction
6222         (message-narrow-to-headers-or-head)
6223         (message-remove-first-header "Content-Type")
6224         (message-remove-first-header "Content-Transfer-Encoding"))
6225       ;; We always make sure that the message has a Content-Type
6226       ;; header.  This is because some broken MTAs and MUAs get
6227       ;; awfully confused when confronted with a message with a
6228       ;; MIME-Version header and without a Content-Type header.  For
6229       ;; instance, Solaris' /usr/bin/mail.
6230       (unless content-type-p
6231         (goto-char (point-min))
6232         ;; For unknown reason, MIME-Version doesn't exist.
6233         (when (re-search-forward "^MIME-Version:" nil t)
6234           (forward-line 1)
6235           (insert "Content-Type: text/plain; charset=us-ascii\n"))))))
6236
6237 (defun message-read-from-minibuffer (prompt &optional initial-contents)
6238   "Read from the minibuffer while providing abbrev expansion."
6239   (if (fboundp 'mail-abbrevs-setup)
6240       (let ((mail-abbrev-mode-regexp "")
6241             (minibuffer-setup-hook 'mail-abbrevs-setup)
6242             (minibuffer-local-map message-minibuffer-local-map))
6243         (read-from-minibuffer prompt initial-contents))
6244     (let ((minibuffer-setup-hook 'mail-abbrev-minibuffer-setup-hook)
6245           (minibuffer-local-map message-minibuffer-local-map))
6246       (read-string prompt initial-contents))))
6247
6248 (defun message-use-alternative-email-as-from ()
6249   (require 'mail-utils)
6250   (let* ((fields '("To" "Cc"))
6251          (emails
6252           (split-string
6253            (mail-strip-quoted-names
6254             (mapconcat 'message-fetch-reply-field fields ","))
6255            "[ \f\t\n\r\v,]+"))
6256          email)
6257     (while emails
6258       (if (string-match message-alternative-emails (car emails))
6259           (setq email (car emails)
6260                 emails nil))
6261       (pop emails))
6262     (unless (or (not email) (equal email user-mail-address))
6263       (goto-char (point-max))
6264       (insert "From: " email "\n"))))
6265
6266 (defun message-options-get (symbol)
6267   (cdr (assq symbol message-options)))
6268
6269 (defun message-options-set (symbol value)
6270   (let ((the-cons (assq symbol message-options)))
6271     (if the-cons
6272         (if value
6273             (setcdr the-cons value)
6274           (setq message-options (delq the-cons message-options)))
6275       (and value
6276            (push (cons symbol value) message-options))))
6277   value)
6278
6279 (defun message-options-set-recipient ()
6280   (save-restriction
6281     (message-narrow-to-headers-or-head)
6282     (message-options-set 'message-sender
6283                          (mail-strip-quoted-names
6284                           (message-fetch-field "from")))
6285     (message-options-set 'message-recipients
6286                          (mail-strip-quoted-names
6287                           (let ((to (message-fetch-field "to"))
6288                                 (cc (message-fetch-field "cc"))
6289                                 (bcc (message-fetch-field "bcc")))
6290                             (concat
6291                              (or to "")
6292                              (if (and to cc) ", ")
6293                              (or cc "")
6294                              (if (and (or to cc) bcc) ", ")
6295                              (or bcc "")))))))
6296
6297 (when (featurep 'xemacs)
6298   (require 'messagexmas)
6299   (message-xmas-redefine))
6300
6301 (defun message-save-drafts ()
6302   "Postponing the message."
6303   (interactive)
6304   (message "Saving %s..." buffer-file-name)
6305   (let ((reply-headers message-reply-headers)
6306         (buffer (current-buffer)))
6307     (with-temp-file buffer-file-name
6308       (insert-buffer buffer)
6309       (setq message-reply-headers reply-headers)
6310       (message-generate-headers '((optional . In-Reply-To)))
6311       (mime-edit-translate-buffer))
6312     (set-buffer-modified-p nil))
6313   (message "Saving %s...done" buffer-file-name))
6314
6315 (provide 'message)
6316
6317 (run-hooks 'message-load-hook)
6318
6319 ;; Local Variables:
6320 ;; coding: iso-8859-1
6321 ;; End:
6322
6323 ;;; message.el ends here