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