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