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