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