8f87b3284cd39ffd2f145db1d804355493d67cf2
[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., 51 Franklin Street, Fifth Floor,
29 ;; Boston, MA 02110-1301, 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                    ; Set the mark at the end of the yanked message.
3556                    (push-mark (save-excursion
3557                                 (insert-buffer-substring buffer)
3558                                 (point))))))
3559       ;; Add new IDs to the References field.
3560       (when (and message-yank-add-new-references
3561                  (interactive-p))
3562         (let ((start (point))
3563               (end (mark t))
3564               refs newrefs)
3565           (save-excursion
3566             (save-restriction
3567               (widen)
3568               (setq refs (message-list-references
3569                           nil
3570                           (or (message-make-references)
3571                               (prog2
3572                                   (message-narrow-to-headers)
3573                                   (message-fetch-field "References")
3574                                 (widen)))))
3575               (narrow-to-region start end)
3576               (std11-narrow-to-header)
3577               (unless (equal (setq newrefs
3578                                    (message-list-references
3579                                     (copy-sequence refs)
3580                                     (unless (eq message-yank-add-new-references
3581                                                 'message-id-only)
3582                                       (or (message-fetch-field "References")
3583                                           (message-fetch-field "In-Reply-To")))
3584                                     (message-fetch-field "Message-ID")))
3585                              refs)
3586                 ;; If the References field has been changed, we make it
3587                 ;; visible in the header.
3588                 (when message-reply-headers
3589                   (mail-header-set-message-id message-reply-headers nil)
3590                   (mail-header-set-references message-reply-headers nil))
3591                 (widen)
3592                 (message-narrow-to-headers)
3593                 (if (let ((case-fold-search t))
3594                       (re-search-forward "^References:\\([\t ]+.+\n\\)+"
3595                                          nil t))
3596                     (replace-match "")
3597                   (goto-char (point-max)))
3598                 (mail-header-format
3599                  (list (or (assq 'References message-header-format-alist)
3600                            '(References . message-fill-references)))
3601                  (list (cons 'References
3602                              (mapconcat 'identity
3603                                         (nreverse newrefs) " ")))))))))
3604       (unless arg
3605         (if (and message-suspend-font-lock-when-citing
3606                  (boundp 'font-lock-mode)
3607                  (symbol-value 'font-lock-mode))
3608             (unwind-protect
3609                 (progn
3610                   (sit-for 0)
3611                   (font-lock-mode 0)
3612                   (funcall message-cite-function))
3613               (font-lock-mode 1))
3614           (funcall message-cite-function)))
3615       (message-exchange-point-and-mark)
3616       (unless (bolp)
3617         (insert ?\n))
3618       (unless modified
3619         (setq message-checksum (message-checksum))))))
3620
3621 (defun message-yank-buffer (buffer)
3622   "Insert BUFFER into the current buffer and quote it."
3623   (interactive "bYank buffer: ")
3624   (let ((message-reply-buffer (get-buffer buffer)))
3625     (save-window-excursion
3626       (message-yank-original))))
3627
3628 (defun message-buffers ()
3629   "Return a list of active message buffers."
3630   (let (buffers)
3631     (save-excursion
3632       (dolist (buffer (buffer-list t))
3633         (set-buffer buffer)
3634         (when (and (eq major-mode 'message-mode)
3635                    (null message-sent-message-via))
3636           (push (buffer-name buffer) buffers))))
3637     (nreverse buffers)))
3638
3639 (defun message-cite-original-without-signature ()
3640   "Cite function in the standard Message manner."
3641   (let ((start (point))
3642         (end (mark t))
3643         (functions
3644          (when message-indent-citation-function
3645            (if (listp message-indent-citation-function)
3646                message-indent-citation-function
3647              (list message-indent-citation-function))))
3648         (message-reply-headers (or message-reply-headers
3649                                    (make-mail-header))))
3650     (mail-header-set-from message-reply-headers
3651                           (save-restriction
3652                             (narrow-to-region
3653                              (point)
3654                              (if (search-forward "\n\n" nil t)
3655                                  (1- (point))
3656                                (point-max)))
3657                             (or (message-fetch-field "from")
3658                                 "unknown sender")))
3659     ;; Allow undoing.
3660     (undo-boundary)
3661     (goto-char end)
3662     (when (re-search-backward message-signature-separator start t)
3663       ;; Also peel off any blank lines before the signature.
3664       (forward-line -1)
3665       (while (looking-at "^[ \t]*$")
3666         (forward-line -1))
3667       (forward-line 1)
3668       (delete-region (point) end)
3669       (unless (search-backward "\n\n" start t)
3670         ;; Insert a blank line if it is peeled off.
3671         (insert "\n")))
3672     (goto-char start)
3673     (mapc 'funcall functions)
3674     (when message-citation-line-function
3675       (unless (bolp)
3676         (insert "\n"))
3677       (funcall message-citation-line-function))))
3678
3679 (eval-when-compile (defvar mail-citation-hook))         ;Compiler directive
3680 (defun message-cite-original ()
3681   "Cite function in the standard Message manner."
3682   (if (and (boundp 'mail-citation-hook)
3683            mail-citation-hook)
3684       (run-hooks 'mail-citation-hook)
3685     (let ((start (point))
3686           (end (mark t))
3687           (x-no-archive nil)
3688           (functions
3689            (when message-indent-citation-function
3690              (if (listp message-indent-citation-function)
3691                  message-indent-citation-function
3692                (list message-indent-citation-function))))
3693           (message-reply-headers (or message-reply-headers
3694                                      (make-mail-header))))
3695       (save-restriction
3696         (narrow-to-region (point) (if (search-forward "\n\n" nil t)
3697                                       (1- (point))
3698                                     (point-max)))
3699         (mail-header-set-from message-reply-headers
3700                               (or (message-fetch-field "from")
3701                                   "unknown sender"))
3702         (setq x-no-archive (message-fetch-field "x-no-archive")))
3703       (goto-char start)
3704       (mapc 'funcall functions)
3705       (when message-citation-line-function
3706         (unless (bolp)
3707           (insert "\n"))
3708         (funcall message-citation-line-function))
3709       (when (and x-no-archive
3710                  (not message-cite-articles-with-x-no-archive)
3711                  (string-match "yes" x-no-archive))
3712         (undo-boundary)
3713         (delete-region (point) (mark t))
3714         (insert "> [Quoted text removed due to X-No-Archive]\n")
3715         (forward-line -1)))))
3716
3717 (defun message-insert-citation-line ()
3718   "Insert a simple citation line."
3719   (when message-reply-headers
3720     (insert (mail-header-from message-reply-headers) " writes:\n\n")))
3721
3722 (defun message-position-on-field (header &rest afters)
3723   (let ((case-fold-search t))
3724     (save-restriction
3725       (narrow-to-region
3726        (goto-char (point-min))
3727        (progn
3728          (re-search-forward
3729           (concat "^" (regexp-quote mail-header-separator) "$"))
3730          (match-beginning 0)))
3731       (goto-char (point-min))
3732       (if (re-search-forward (concat "^" (regexp-quote header) ":") nil t)
3733           (progn
3734             (re-search-forward "^[^ \t]" nil 'move)
3735             (beginning-of-line)
3736             (skip-chars-backward "\n")
3737             t)
3738         (while (and afters
3739                     (not (re-search-forward
3740                           (concat "^" (regexp-quote (car afters)) ":")
3741                           nil t)))
3742           (pop afters))
3743         (when afters
3744           (re-search-forward "^[^ \t]" nil 'move)
3745           (beginning-of-line))
3746         (insert header ": \n")
3747         (forward-char -1)
3748         nil))))
3749
3750 (defun message-remove-signature ()
3751   "Remove the signature from the text between point and mark.
3752 The text will also be indented the normal way."
3753   (save-excursion
3754     (let ((start (point))
3755           mark)
3756       (if (not (re-search-forward message-signature-separator (mark t) t))
3757           ;; No signature here, so we just indent the cited text.
3758           (message-indent-citation)
3759         ;; Find the last non-empty line.
3760         (forward-line -1)
3761         (while (looking-at "[ \t]*$")
3762           (forward-line -1))
3763         (forward-line 1)
3764         (setq mark (set-marker (make-marker) (point)))
3765         (goto-char start)
3766         (message-indent-citation)
3767         ;; Enable undoing the deletion.
3768         (undo-boundary)
3769         (delete-region mark (mark t))
3770         (set-marker mark nil)))))
3771
3772 \f
3773
3774 ;;;
3775 ;;; Sending messages
3776 ;;;
3777
3778 ;; Avoid byte-compile warning.
3779 (defvar message-encoding-buffer nil)
3780 (defvar message-edit-buffer nil)
3781 (defvar message-mime-mode nil)
3782
3783 (defun message-send-and-exit (&optional arg)
3784   "Send message like `message-send', then, if no errors, exit from mail buffer."
3785   (interactive "P")
3786   (let ((buf (current-buffer))
3787         (actions message-exit-actions)
3788         (frame (selected-frame))
3789         (org-frame message-original-frame))
3790     (when (and (message-send arg)
3791                (buffer-name buf))
3792       (if message-kill-buffer-on-exit
3793           (kill-buffer buf)
3794         (bury-buffer buf)
3795         (when (eq buf (current-buffer))
3796           (message-bury buf)))
3797       (message-do-actions actions)
3798       (message-delete-frame frame org-frame)
3799       t)))
3800
3801 (defun message-dont-send ()
3802   "Don't send the message you have been editing.
3803 Instead, just auto-save the buffer and then bury it."
3804   (interactive)
3805   (set-buffer-modified-p t)
3806   (save-buffer)
3807   (let ((actions message-postpone-actions)
3808         (frame (selected-frame))
3809         (org-frame message-original-frame))
3810     (message-bury (current-buffer))
3811     (message-do-actions actions)
3812     (message-delete-frame frame org-frame)))
3813
3814 (defun message-kill-buffer ()
3815   "Kill the current buffer."
3816   (interactive)
3817   (when (or (not (buffer-modified-p))
3818             (not message-kill-buffer-query)
3819             (eq t message-kill-buffer-query-function)
3820             (funcall message-kill-buffer-query-function
3821                      "The buffer modified; kill anyway? "))
3822     (let ((actions message-kill-actions)
3823           (draft-article message-draft-article)
3824           (auto-save-file-name buffer-auto-save-file-name)
3825           (file-name buffer-file-name)
3826           (modified (buffer-modified-p))
3827           (frame (selected-frame))
3828           (org-frame message-original-frame))
3829       (setq buffer-file-name nil)
3830       (kill-buffer (current-buffer))
3831       (when (and message-kill-buffer-and-remove-file
3832                  (or (and auto-save-file-name
3833                           (file-exists-p auto-save-file-name))
3834                      (and file-name
3835                           (file-exists-p file-name)))
3836                  (progn
3837                    ;; If the message buffer has lived in a dedicated window,
3838                    ;; `kill-buffer' has killed the frame.  Thus the
3839                    ;; `yes-or-no-p' may show up in a lowered frame.  Make sure
3840                    ;; that the user can see the question by raising the
3841                    ;; current frame:
3842                    (raise-frame)
3843                    (yes-or-no-p (format "Remove the backup file%s? "
3844                                         (if modified " too" "")))))
3845         (ignore-errors
3846           (delete-file auto-save-file-name))
3847         (let ((message-draft-article draft-article))
3848           (message-disassociate-draft)))
3849       (message-do-actions actions)
3850       (message-delete-frame frame org-frame)))
3851   (message ""))
3852
3853 (defun message-mimic-kill-buffer ()
3854   "Kill the current buffer with query.  This is an imitation for
3855 `kill-buffer', but it will delete a message frame."
3856   (interactive)
3857   (let ((bufname (read-buffer (format "Kill buffer: (default %s) "
3858                                       (buffer-name))))
3859         message-kill-buffer-and-remove-file)
3860     (when (or (not bufname)
3861               (string-equal bufname "")
3862               (string-equal bufname (buffer-name)))
3863       (message-kill-buffer))))
3864
3865 (defun message-delete-frame (frame org-frame)
3866   "Delete frame for editing message."
3867   (when (and (static-if (featurep 'xemacs)
3868                  (device-on-window-system-p)
3869                window-system)
3870              (or (and (eq message-delete-frame-on-exit t)
3871                       (select-frame frame)
3872                       (or (eq frame org-frame)
3873                           (prog1
3874                               (y-or-n-p "Delete this frame?")
3875                             (message ""))))
3876                  (and (eq message-delete-frame-on-exit 'ask)
3877                       (select-frame frame)
3878                       (prog1
3879                           (y-or-n-p "Delete this frame?")
3880                         (message "")))))
3881     (delete-frame frame)))
3882
3883 (defun message-bury (buffer)
3884   "Bury this mail BUFFER."
3885   (let ((newbuf (other-buffer buffer)))
3886     (bury-buffer buffer)
3887     (if (and (window-dedicated-p (selected-window))
3888              (not (null (delq (selected-frame) (visible-frame-list)))))
3889         (delete-frame (selected-frame))
3890       (switch-to-buffer newbuf))))
3891
3892 (defun message-send (&optional arg)
3893   "Send the message in the current buffer.
3894 If `message-interactive' is non-nil, wait for success indication or
3895 error messages, and inform user.
3896 Otherwise any failure is reported in a message back to the user from
3897 the mailer.
3898 The usage of ARG is defined by the instance that called Message.
3899 It should typically alter the sending method in some way or other."
3900   (interactive "P")
3901   ;; Disabled test.
3902   (when (or (buffer-modified-p)
3903             (message-check-element 'unchanged)
3904             (y-or-n-p "No changes in the buffer; really send? "))
3905     ;; Make it possible to undo the coming changes.
3906     (undo-boundary)
3907     (let ((inhibit-read-only t))
3908       (put-text-property (point-min) (point-max) 'read-only nil))
3909     (run-hooks 'message-send-hook)
3910     (message-fix-before-sending)
3911     (message message-sending-message)
3912     (let ((message-encoding-buffer
3913            (message-generate-new-buffer-clone-locals " message encoding"))
3914           (message-edit-buffer (current-buffer))
3915           (message-mime-mode mime-edit-mode-flag)
3916           (alist message-send-method-alist)
3917           (success t)
3918           elem sent dont-barf-on-no-method
3919           (message-options message-options))
3920       (unwind-protect
3921           (progn
3922             (message-options-set-recipient)
3923             (save-excursion
3924               (set-buffer message-encoding-buffer)
3925               (erase-buffer)
3926               ;; ;; Avoid copying text props (except hard newlines).
3927               ;; T-gnus change: copy all text props from the editing buffer
3928               ;; into the encoding buffer.
3929               (insert-buffer-substring message-edit-buffer)
3930               (funcall message-encode-function)
3931               (while (and success
3932                           (setq elem (pop alist)))
3933                 (when (funcall (cadr elem))
3934                   (when (and
3935                          (or (not (memq (car elem)
3936                                         message-sent-message-via))
3937                              (message-fetch-field "supersedes")
3938                              (if (or (message-gnksa-enable-p 'multiple-copies)
3939                                      (not (eq (car elem) 'news)))
3940                                  (y-or-n-p
3941                                   (format
3942                                    "Already sent message via %s; resend? "
3943                                    (car elem)))
3944                                (error "Denied posting -- multiple copies")))
3945                          (setq success (funcall (caddr elem) arg)))
3946                     (setq sent t)))))
3947             (unless
3948                 (or
3949                  sent
3950                  (not success)
3951                  (let ((fcc (message-fetch-field "Fcc"))
3952                        (gcc (message-fetch-field "Gcc")))
3953                    (when (or fcc gcc)
3954                      (or
3955                       (eq message-allow-no-recipients 'always)
3956                       (and (not (eq message-allow-no-recipients 'never))
3957                            (setq dont-barf-on-no-method
3958                                  (gnus-y-or-n-p
3959                                   (format "No receiver, perform %s anyway? "
3960                                           (cond ((and fcc gcc) "Fcc and Gcc")
3961                                                 (fcc "Fcc")
3962                                                 (t "Gcc"))))))))))
3963               (error "No methods specified to send by"))
3964             (when (or dont-barf-on-no-method
3965                       (and success sent))
3966               (message-do-fcc)
3967               (save-excursion
3968                 (run-hooks 'message-sent-hook))
3969               (message "Sending...done")
3970               ;; Mark the buffer as unmodified and delete auto-save.
3971               (set-buffer-modified-p nil)
3972               (delete-auto-save-file-if-necessary t)
3973               (message-disassociate-draft)
3974               ;; Delete other mail buffers and stuff.
3975               (message-do-send-housekeeping)
3976               (message-do-actions message-send-actions)
3977               ;; Return success.
3978               t))
3979         (kill-buffer message-encoding-buffer)))))
3980
3981 (defun message-send-via-mail (arg)
3982   "Send the current message via mail."
3983   (message-send-mail arg))
3984
3985 (defun message-send-via-news (arg)
3986   "Send the current message via news."
3987   (message-send-news arg))
3988
3989 (defmacro message-check (type &rest forms)
3990   "Eval FORMS if TYPE is to be checked."
3991   `(or (message-check-element ,type)
3992        (save-excursion
3993          ,@forms)))
3994
3995 (put 'message-check 'lisp-indent-function 1)
3996 (put 'message-check 'edebug-form-spec '(form body))
3997
3998 ;; Advise the function `invisible-region'.
3999 (unless noninteractive
4000   (let (current-load-list)
4001     (eval
4002      `(defadvice invisible-region (around add-mime-edit-invisible (start end)
4003                                           activate)
4004         "Advised by T-gnus Message.
4005 Add the text property `mime-edit-invisible' to an invisible text when
4006 the buffer's major mode is `message-mode'.  The added property will be
4007 used to distinguish whether the invisible text is a MIME part or not."
4008         ,(if (featurep 'xemacs)
4009              '(if (eq ?\n (char-after start))
4010                   (setq start (1+ start)))
4011            '(if (eq ?\n (char-after (1- end)))
4012                 (setq end (1- end))))
4013         (setq ad-return-value
4014               (if (eq 'message-mode major-mode)
4015                   (add-text-properties start end
4016                                        '(invisible t mime-edit-invisible t))
4017                 (put-text-property start end 'invisible t)))))))
4018
4019 (defun message-text-with-property (prop &optional start end reverse)
4020   "Return a list of start and end positions where the text has PROP.
4021 START and END bound the search, they default to `point-min' and
4022 `point-max' respectively.  If REVERSE is non-nil, find text which does
4023 not have PROP."
4024   (unless start
4025     (setq start (point-min)))
4026   (unless end
4027     (setq end (point-max)))
4028   (let (next regions)
4029     (if reverse
4030         (while (and start
4031                     (setq start (text-property-any start end prop nil)))
4032           (setq next (next-single-property-change start prop nil end))
4033           (push (cons start (or next end)) regions)
4034           (setq start next))
4035       (while (and start
4036                   (or (get-text-property start prop)
4037                       (and (setq start (next-single-property-change
4038                                         start prop nil end))
4039                            (get-text-property start prop))))
4040         (setq next (text-property-any start end prop nil))
4041         (push (cons start (or next end)) regions)
4042         (setq start next)))
4043     (nreverse regions)))
4044
4045 (defun message-fix-before-sending ()
4046   "Do various things to make the message nice before sending it."
4047   ;; Make sure there's a newline at the end of the message.
4048   (widen)
4049   (goto-char (point-max))
4050   (unless (bolp)
4051     (insert "\n"))
4052   ;; Make the hidden headers visible.
4053   (widen)
4054   ;; Sort headers before sending the message.
4055   (message-sort-headers)
4056   ;; Make invisible text visible except for mime parts which may be
4057   ;; inserted by the MIME-Edit.
4058   ;; It doesn't seem as if this is useful, since the invisible property
4059   ;; is clobbered by an after-change hook anyhow.
4060   (message-check 'invisible-text
4061     ;; FIXME T-gnus: It should also detect invisible overlays.
4062     (let (from
4063           (to (point-min))
4064           mime-from mime-to hidden-start)
4065       (while (setq from (text-property-any to (point-max) 'invisible t))
4066         (setq to (or (text-property-not-all from (point-max) 'invisible t)
4067                      (point-max))
4068               mime-to from)
4069         (while (setq mime-from (text-property-any mime-to to
4070                                                   'mime-edit-invisible t))
4071           (when (> mime-from mime-to)
4072             (setq hidden-start (or hidden-start mime-to))
4073             (add-text-properties mime-to mime-from
4074                                  '(invisible nil face highlight
4075                                              font-lock-face highlight)))
4076           (setq mime-to (or (text-property-not-all mime-from to
4077                                                    'mime-edit-invisible t)
4078                             to)))
4079         (when (< mime-to to)
4080           (setq hidden-start (or hidden-start mime-to))
4081           (add-text-properties mime-to to
4082                                '(invisible nil face highlight
4083                                            font-lock-face highlight))))
4084       (when hidden-start
4085         (goto-char hidden-start)
4086         (set-window-start (selected-window) (point-at-bol))
4087         (unless (yes-or-no-p
4088                  "Invisible text found and made visible; continue sending? ")
4089           (error "Invisible text found and made visible")))))
4090 ;; The following check is needless to T-gnus since T-gnus determines
4091 ;; a MIME charset forcibly (even if it cannot be determined properly,
4092 ;; the value of the `default-mime-charset-for-write' variable is used).
4093 ;;  (message-check 'illegible-text
4094 ;;    (let (found choice)
4095 ;;      (message-goto-body)
4096 ;;      (skip-chars-forward mm-7bit-chars)
4097 ;;      (while (not (eobp))
4098 ;;      (when (let ((char (char-after)))
4099 ;;              (or (< (mm-char-int char) 128)
4100 ;;                  (and (mm-multibyte-p)
4101 ;;                       (memq (char-charset char)
4102 ;;                             '(eight-bit-control eight-bit-graphic
4103 ;;                                                 control-1))
4104 ;;                       (not (get-text-property
4105 ;;                             (point) 'untranslated-utf-8)))))
4106 ;;        (message-overlay-put (message-make-overlay (point) (1+ (point)))
4107 ;;                             'face 'highlight)
4108 ;;        (setq found t))
4109 ;;      (forward-char)
4110 ;;      (skip-chars-forward mm-7bit-chars))
4111 ;;      (when found
4112 ;;      (setq choice
4113 ;;            (gnus-multiple-choice
4114 ;;             "Non-printable characters found.  Continue sending?"
4115 ;;             '((?d "Remove non-printable characters and send")
4116 ;;               (?r "Replace non-printable characters with dots and send")
4117 ;;               (?i "Ignore non-printable characters and send")
4118 ;;               (?e "Continue editing"))))
4119 ;;      (if (eq choice ?e)
4120 ;;          (error "Non-printable characters"))
4121 ;;      (message-goto-body)
4122 ;;      (skip-chars-forward mm-7bit-chars)
4123 ;;      (while (not (eobp))
4124 ;;        (when (let ((char (char-after)))
4125 ;;                (or (< (mm-char-int char) 128)
4126 ;;                    (and (mm-multibyte-p)
4127 ;;                         ;; Fixme: Wrong for Emacs 22 and for things
4128 ;;                         ;; like undecable utf-8.  Should at least
4129 ;;                         ;; use find-coding-systems-region.
4130 ;;                         (memq (char-charset char)
4131 ;;                               '(eight-bit-control eight-bit-graphic
4132 ;;                                                   control-1))
4133 ;;                         (not (get-text-property
4134 ;;                               (point) 'untranslated-utf-8)))))
4135 ;;          (if (eq choice ?i)
4136 ;;              (message-kill-all-overlays)
4137 ;;            (delete-char 1)
4138 ;;            (when (eq choice ?r)
4139 ;;              (insert "."))))
4140 ;;        (forward-char)
4141 ;;        (skip-chars-forward mm-7bit-chars)))))
4142   )
4143
4144 (defun message-add-action (action &rest types)
4145   "Add ACTION to be performed when doing an exit of type TYPES."
4146   (while types
4147     (add-to-list (intern (format "message-%s-actions" (pop types)))
4148                  action)))
4149
4150 (defun message-delete-action (action &rest types)
4151   "Delete ACTION from lists of actions performed when doing an exit of type TYPES."
4152   (let (var)
4153     (while types
4154       (set (setq var (intern (format "message-%s-actions" (pop types))))
4155            (delq action (symbol-value var))))))
4156
4157 (defun message-do-actions (actions)
4158   "Perform all actions in ACTIONS."
4159   ;; Now perform actions on successful sending.
4160   (dolist (action actions)
4161     (ignore-errors
4162       (cond
4163        ;; A simple function.
4164        ((functionp action)
4165         (funcall action))
4166        ;; Something to be evaled.
4167        (t
4168         (eval action))))))
4169
4170 (defsubst message-maybe-split-and-send-mail ()
4171   "Split a message if necessary, and send it via mail.
4172 Returns nil if sending succeeded, returns any string if sending failed.
4173 This sub function is for exclusive use of `message-send-mail'."
4174   (let ((mime-edit-split-ignored-field-regexp
4175          mime-edit-split-ignored-field-regexp)
4176         (case-fold-search t)
4177         failure)
4178     (while (string-match "Message-ID" mime-edit-split-ignored-field-regexp)
4179       (setq mime-edit-split-ignored-field-regexp
4180             (concat (substring mime-edit-split-ignored-field-regexp
4181                                0 (match-beginning 0))
4182                     "Hey_MIME-Edit,_there_is_an_inviolable_Message_ID"
4183                     "_so_don't_rape_it!"
4184                     (substring mime-edit-split-ignored-field-regexp
4185                                (match-end 0)))))
4186     (setq failure
4187           (or
4188            (catch 'message-sending-mail-failure
4189              (mime-edit-maybe-split-and-send
4190               (function
4191                (lambda ()
4192                  (interactive)
4193                  (save-restriction
4194                    (std11-narrow-to-header mail-header-separator)
4195                    (goto-char (point-min))
4196                    (when (re-search-forward "^Message-ID:" nil t)
4197                      (delete-region (match-end 0) (std11-field-end))
4198                      (insert " " (message-make-message-id))))
4199                  (condition-case err
4200                      (funcall (or message-send-mail-real-function
4201                                   message-send-mail-function))
4202                    (error
4203                     (throw 'message-sending-mail-failure err))))))
4204              nil)
4205            (condition-case err
4206                (progn
4207                  (funcall (or message-send-mail-real-function
4208                               message-send-mail-function))
4209                  nil)
4210              (error err))))
4211     (when failure
4212       (if (eq 'error (car failure))
4213           (cadr failure)
4214         (prin1-to-string failure)))))
4215
4216 (defun message-send-mail-partially ()
4217   "Send mail as message/partial."
4218   ;; replace the header delimiter with a blank line
4219   (goto-char (point-min))
4220   (re-search-forward
4221    (concat "^" (regexp-quote mail-header-separator) "\n"))
4222   (replace-match "\n")
4223   (run-hooks 'message-send-mail-hook)
4224   (let ((p (goto-char (point-min)))
4225         (tembuf (message-generate-new-buffer-clone-locals " message temp"))
4226         (curbuf (current-buffer))
4227         (id (message-make-message-id)) (n 1)
4228         plist total  header required-mail-headers)
4229     (while (not (eobp))
4230       (if (< (point-max) (+ p message-send-mail-partially-limit))
4231           (goto-char (point-max))
4232         (goto-char (+ p message-send-mail-partially-limit))
4233         (beginning-of-line)
4234         (if (<= (point) p) (forward-line 1))) ;; In case of bad message.
4235       (push p plist)
4236       (setq p (point)))
4237     (setq total (length plist))
4238     (push (point-max) plist)
4239     (setq plist (nreverse plist))
4240     (unwind-protect
4241         (save-excursion
4242           (setq p (pop plist))
4243           (while plist
4244             (set-buffer curbuf)
4245             (copy-to-buffer tembuf p (car plist))
4246             (set-buffer tembuf)
4247             (goto-char (point-min))
4248             (if header
4249                 (progn
4250                   (goto-char (point-min))
4251                   (narrow-to-region (point) (point))
4252                   (insert header))
4253               (message-goto-eoh)
4254               (setq header (buffer-substring (point-min) (point)))
4255               (goto-char (point-min))
4256               (narrow-to-region (point) (point))
4257               (insert header)
4258               (message-remove-header "Mime-Version")
4259               (message-remove-header "Content-Type")
4260               (message-remove-header "Content-Transfer-Encoding")
4261               (message-remove-header "Message-ID")
4262               (message-remove-header "Lines")
4263               (goto-char (point-max))
4264               (insert "Mime-Version: 1.0\n")
4265               (setq header (buffer-string)))
4266             (goto-char (point-max))
4267             (insert (format "Content-Type: message/partial; id=\"%s\"; number=%d; total=%d\n\n"
4268                             id n total))
4269             (forward-char -1)
4270             (let ((mail-header-separator ""))
4271               (when (memq 'Message-ID message-required-mail-headers)
4272                 (insert "Message-ID: " (message-make-message-id) "\n"))
4273               (when (memq 'Lines message-required-mail-headers)
4274                 (insert "Lines: " (message-make-lines) "\n"))
4275               (message-goto-subject)
4276               (end-of-line)
4277               (insert (format " (%d/%d)" n total))
4278               (widen)
4279               (mm-with-unibyte-current-buffer
4280                 (funcall (or message-send-mail-real-function
4281                              message-send-mail-function))))
4282             (setq n (+ n 1))
4283             (setq p (pop plist))
4284             (erase-buffer)))
4285       (kill-buffer tembuf))))
4286
4287 (defun message-send-mail (&optional arg)
4288   (require 'mail-utils)
4289   (let* ((tembuf (message-generate-new-buffer-clone-locals " message temp"))
4290          (case-fold-search nil)
4291          (news (message-news-p))
4292          (message-this-is-mail t)
4293          (headers message-required-mail-headers)
4294          failure)
4295     (when message-generate-hashcash
4296       (message "Generating hashcash...")
4297       ;; Wait for calculations already started to finish...
4298       (hashcash-wait-async)
4299       ;; ...and do calculations not already done.  mail-add-payment
4300       ;; will leave existing X-Hashcash headers alone.
4301       (mail-add-payment)
4302       (message "Generating hashcash...done"))
4303     (save-restriction
4304       (message-narrow-to-headers)
4305       ;; Generate the Mail-Followup-To header if the header is not there...
4306       (if (and (message-subscribed-p)
4307                (not (mail-fetch-field "mail-followup-to")))
4308           (setq headers
4309                 (cons
4310                  (cons "Mail-Followup-To" (message-make-mail-followup-to))
4311                  message-required-mail-headers))
4312         ;; otherwise, delete the MFT header if the field is empty
4313         (when (equal "" (mail-fetch-field "mail-followup-to"))
4314           (message-remove-header "^Mail-Followup-To:")))
4315       ;; Insert some headers.
4316       (let ((message-deletable-headers
4317              (if news nil message-deletable-headers)))
4318         (message-generate-headers headers))
4319       ;; Let the user do all of the above.
4320       (run-hooks 'message-header-hook))
4321     (if (not (message-check-mail-syntax))
4322         (progn
4323           (message "")
4324           nil)
4325       (unwind-protect
4326           (save-excursion
4327             (set-buffer tembuf)
4328             (erase-buffer)
4329             ;; ;; Avoid copying text props (except hard newlines).
4330             ;; T-gnus change: copy all text props from the editing buffer
4331             ;; into the encoding buffer.
4332             (insert-buffer-substring message-encoding-buffer)
4333             ;; Remove some headers.
4334             (save-restriction
4335               (message-narrow-to-headers)
4336 ;; We Semi-gnus people have no use for it.
4337 ;;            ;; We (re)generate the Lines header.
4338 ;;            (when (memq 'Lines message-required-mail-headers)
4339 ;;              (message-generate-headers '(Lines)))
4340               (message-remove-header message-ignored-mail-headers t))
4341             (goto-char (point-max))
4342             ;; require one newline at the end.
4343             (or (= (preceding-char) ?\n)
4344                 (insert ?\n))
4345             (message-cleanup-headers)
4346             ;; FIXME: we're inserting the courtesy copy after encoding.
4347             ;; This is wrong if the courtesy copy string contains
4348             ;; non-ASCII characters. -- jh
4349             (when
4350                 (save-restriction
4351                   (message-narrow-to-headers)
4352                   (and news
4353                        (or (message-fetch-field "cc")
4354                            (message-fetch-field "bcc")
4355                            (message-fetch-field "to"))
4356                        (let ((content-type (mime-read-Content-Type)))
4357                          (and
4358                           (or
4359                            (not content-type)
4360                            (and
4361                             (eq 'text (cdr (assq 'type content-type)))
4362                             (eq 'plain (cdr (assq 'subtype content-type)))))
4363                           (not
4364                            (string= "base64"
4365                                     (mime-read-Content-Transfer-Encoding)))))))
4366               (message-insert-courtesy-copy))
4367             (setq failure (message-maybe-split-and-send-mail)))
4368         (kill-buffer tembuf))
4369       (set-buffer message-edit-buffer)
4370       (if failure
4371           (progn
4372             (message "Couldn't send message via mail: %s" failure)
4373             nil)
4374         (push 'mail message-sent-message-via)))))
4375
4376 (defun message-send-mail-with-sendmail ()
4377   "Send off the prepared buffer with sendmail."
4378   (let ((errbuf (if message-interactive
4379                     (message-generate-new-buffer-clone-locals
4380                      " sendmail errors")
4381                   0))
4382         resend-to-addresses delimline)
4383     (unwind-protect
4384         (progn
4385           (let ((case-fold-search t))
4386             (save-restriction
4387               (message-narrow-to-headers)
4388               (setq resend-to-addresses (message-fetch-field "resent-to")))
4389             ;; Change header-delimiter to be what sendmail expects.
4390             (goto-char (point-min))
4391             (re-search-forward
4392              (concat "^" (regexp-quote mail-header-separator) "\n"))
4393             (replace-match "\n")
4394             (backward-char 1)
4395             (setq delimline (point-marker))
4396             (run-hooks 'message-send-mail-hook)
4397             ;; Insert an extra newline if we need it to work around
4398             ;; Sun's bug that swallows newlines.
4399             (goto-char (1+ delimline))
4400             (when (eval message-mailer-swallows-blank-line)
4401               (newline))
4402             (when message-interactive
4403               (with-current-buffer errbuf
4404                 (erase-buffer))))
4405           (let* ((default-directory "/")
4406                  (cpr (as-binary-process
4407                        (apply
4408                         'call-process-region
4409                         (append
4410                          (list (point-min) (point-max)
4411                                (if (boundp 'sendmail-program)
4412                                    sendmail-program
4413                                  "/usr/lib/sendmail")
4414                                nil errbuf nil "-oi")
4415                          ;; Always specify who from,
4416                          ;; since some systems have broken sendmails.
4417                          ;; But some systems are more broken with -f, so
4418                          ;; we'll let users override this.
4419                          (if (null message-sendmail-f-is-evil)
4420                              (list "-f" (message-sendmail-envelope-from)))
4421                          ;; These mean "report errors by mail"
4422                          ;; and "deliver in background".
4423                          (if (null message-interactive) '("-oem" "-odb"))
4424                          ;; Get the addresses from the message
4425                          ;; unless this is a resend.
4426                          ;; We must not do that for a resend
4427                          ;; because we would find the original addresses.
4428                          ;; For a resend, include the specific addresses.
4429                          (if resend-to-addresses
4430                              (list resend-to-addresses)
4431                            '("-t")))))))
4432             (unless (or (null cpr) (and (numberp cpr) (zerop cpr)))
4433               (error "Sending...failed with exit value %d" cpr)))
4434           (when message-interactive
4435             (save-excursion
4436               (set-buffer errbuf)
4437               (goto-char (point-min))
4438               (while (re-search-forward "\n+ *" nil t)
4439                 (replace-match "; "))
4440               (if (not (zerop (buffer-size)))
4441                   (error "Sending...failed to %s"
4442                          (buffer-string))))))
4443       (when (bufferp errbuf)
4444         (kill-buffer errbuf)))))
4445
4446 (defun message-send-mail-with-qmail ()
4447   "Pass the prepared message buffer to qmail-inject.
4448 Refer to the documentation for the variable `message-send-mail-function'
4449 to find out how to use this."
4450   ;; replace the header delimiter with a blank line
4451   (goto-char (point-min))
4452   (re-search-forward
4453    (concat "^" (regexp-quote mail-header-separator) "\n"))
4454   (replace-match "\n")
4455   (backward-char 1)
4456   (run-hooks 'message-send-mail-hook)
4457   ;; send the message
4458   (case
4459       (as-binary-process
4460        (apply
4461         'call-process-region (point-min) (point-max)
4462         message-qmail-inject-program nil nil nil
4463         ;; qmail-inject's default behaviour is to look for addresses on the
4464         ;; command line; if there're none, it scans the headers.
4465         ;; yes, it does The Right Thing w.r.t. Resent-To and it's kin.
4466         ;;
4467         ;; in general, ALL of qmail-inject's defaults are perfect for simply
4468         ;; reading a formatted (i. e., at least a To: or Resent-To header)
4469         ;; message from stdin.
4470         ;;
4471         ;; qmail also has the advantage of not having been raped by
4472         ;; various vendors, so we don't have to allow for that, either --
4473         ;; compare this with message-send-mail-with-sendmail and weep
4474         ;; for sendmail's lost innocence.
4475         ;;
4476         ;; all this is way cool coz it lets us keep the arguments entirely
4477         ;; free for -inject-arguments -- a big win for the user and for us
4478         ;; since we don't have to play that double-guessing game and the user
4479         ;; gets full control (no gestapo'ish -f's, for instance).  --sj
4480         (if (functionp message-qmail-inject-args)
4481             (funcall message-qmail-inject-args)
4482           message-qmail-inject-args)))
4483     ;; qmail-inject doesn't say anything on it's stdout/stderr,
4484     ;; we have to look at the retval instead
4485     (0 nil)
4486     (100 (error "qmail-inject reported permanent failure"))
4487     (111 (error "qmail-inject reported transient failure"))
4488     ;; should never happen
4489     (t   (error "qmail-inject reported unknown failure"))))
4490
4491 (defun message-send-mail-with-mh ()
4492   "Send the prepared message buffer with mh."
4493   (let ((mh-previous-window-config nil)
4494         (name (mh-new-draft-name)))
4495     (setq buffer-file-name name)
4496     ;; MH wants to generate these headers itself.
4497     (when message-mh-deletable-headers
4498       (let ((headers message-mh-deletable-headers))
4499         (while headers
4500           (goto-char (point-min))
4501           (and (re-search-forward
4502                 (concat "^" (symbol-name (car headers)) ": *") nil t)
4503                (message-delete-line))
4504           (pop headers))))
4505     (run-hooks 'message-send-mail-hook)
4506     ;; Pass it on to mh.
4507     (mh-send-letter)))
4508
4509 (defun message-send-mail-with-smtp ()
4510   "Send off the prepared buffer with SMTP."
4511   (require 'smtp) ; XXX
4512   (let ((case-fold-search t)
4513         recipients)
4514     (save-restriction
4515       (message-narrow-to-headers)
4516       (setq recipients
4517             ;; XXX: Should be replaced by better one.
4518             (smtp-deduce-address-list (current-buffer)
4519                                       (point-min) (point-max)))
4520       ;; Remove BCC lines.
4521       (message-remove-header "bcc"))
4522     ;; replace the header delimiter with a blank line.
4523     (goto-char (point-min))
4524     (re-search-forward
4525      (concat "^" (regexp-quote mail-header-separator) "\n"))
4526     (replace-match "\n")
4527     (backward-char 1)
4528     (run-hooks 'message-send-mail-hook)
4529     (if recipients
4530         (smtp-send-buffer user-mail-address recipients (current-buffer))
4531       (error "Sending failed; no recipients"))))
4532
4533 (defsubst message-maybe-split-and-send-news (method)
4534   "Split a message if necessary, and send it via news.
4535 Returns nil if sending succeeded, returns t if sending failed.
4536 This sub function is for exclusive use of `message-send-news'."
4537   (let ((mime-edit-split-ignored-field-regexp
4538          mime-edit-split-ignored-field-regexp)
4539         (case-fold-search t))
4540     (while (string-match "Message-ID" mime-edit-split-ignored-field-regexp)
4541       (setq mime-edit-split-ignored-field-regexp
4542             (concat (substring mime-edit-split-ignored-field-regexp
4543                                0 (match-beginning 0))
4544                     "Hey_MIME-Edit,_there_is_an_inviolable_Message_ID"
4545                     "_so_don't_rape_it!"
4546                     (substring mime-edit-split-ignored-field-regexp
4547                                (match-end 0)))))
4548     (or
4549      (catch 'message-sending-news-failure
4550        (mime-edit-maybe-split-and-send
4551         (function
4552          (lambda ()
4553            (interactive)
4554            (save-restriction
4555              (std11-narrow-to-header mail-header-separator)
4556              (goto-char (point-min))
4557              (when (re-search-forward "^Message-ID:" nil t)
4558                (delete-region (match-end 0) (std11-field-end))
4559                (insert " " (message-make-message-id))))
4560            (unless (funcall message-send-news-function method)
4561              (throw 'message-sending-news-failure t)))))
4562        nil)
4563      (not (funcall message-send-news-function method)))))
4564
4565 (defun message-smtpmail-send-it ()
4566   "Send the prepared message buffer with `smtpmail-send-it'.
4567 This only differs from `smtpmail-send-it' that this command evaluates
4568 `message-send-mail-hook' just before sending a message.  It is useful
4569 if your ISP requires the POP-before-SMTP authentication.  See the Gnus
4570 manual for details."
4571   (run-hooks 'message-send-mail-hook)
4572   (smtpmail-send-it))
4573
4574 (defun message-canlock-generate ()
4575   "Return a string that is non-trivial to guess.
4576 Do not use this for anything important, it is cryptographically weak."
4577   (require 'sha1)
4578   (let (sha1-maximum-internal-length)
4579     (sha1 (concat (message-unique-id)
4580                   (format "%x%x%x" (random) (random t) (random))
4581                   (prin1-to-string (recent-keys))
4582                   (prin1-to-string (garbage-collect))))))
4583
4584 (defun message-canlock-password ()
4585   "The password used by message for cancel locks.
4586 This is the value of `canlock-password', if that option is non-nil.
4587 Otherwise, generate and save a value for `canlock-password' first."
4588   (unless canlock-password
4589     (customize-save-variable 'canlock-password (message-canlock-generate))
4590     (setq canlock-password-for-verify canlock-password))
4591   canlock-password)
4592
4593 (defun message-insert-canlock ()
4594   (when message-insert-canlock
4595     (message-canlock-password)
4596     (canlock-insert-header)))
4597
4598 (defun message-send-news (&optional arg)
4599   (let* ((tembuf (message-generate-new-buffer-clone-locals " *message temp*"))
4600          (case-fold-search nil)
4601          (method (if (functionp message-post-method)
4602                      (funcall message-post-method arg)
4603                    message-post-method))
4604          (newsgroups-field (save-restriction
4605                              (message-narrow-to-headers-or-head)
4606                              (message-fetch-field "Newsgroups")))
4607          (followup-field (save-restriction
4608                            (message-narrow-to-headers-or-head)
4609                            (message-fetch-field "Followup-To")))
4610          ;; BUG: We really need to get the charset for each name in the
4611          ;; Newsgroups and Followup-To lines to allow crossposting
4612          ;; between group namess with incompatible character sets.
4613          ;; -- Per Abrahamsen <abraham@dina.kvl.dk> 2001-10-08.
4614          (group-field-charset
4615           (gnus-group-name-charset method newsgroups-field))
4616          (followup-field-charset
4617           (gnus-group-name-charset method (or followup-field "")))
4618          (mime-field-encoding-method-alist
4619           (append (when group-field-charset
4620                     (list (cons "Newsgroups" group-field-charset)))
4621                   (when followup-field-charset
4622                     (list (cons "Followup-To" followup-field-charset)))
4623                   mime-field-encoding-method-alist))
4624          (message-syntax-checks
4625           (if (and arg
4626                    (listp message-syntax-checks))
4627               (cons '(existing-newsgroups . disabled)
4628                     message-syntax-checks)
4629             message-syntax-checks))
4630          (message-this-is-news t)
4631          result)
4632     (save-restriction
4633       (message-narrow-to-headers)
4634       ;; Insert some headers.
4635       (message-generate-headers message-required-news-headers)
4636       (message-insert-canlock)
4637       ;; Let the user do all of the above.
4638       (run-hooks 'message-header-hook))
4639     ;; Note: This check will be disabled by the ".*" default value for
4640     ;; gnus-group-name-charset-group-alist. -- Pa 2001-10-07.
4641     (when (and group-field-charset
4642                (listp message-syntax-checks))
4643       (setq message-syntax-checks
4644             (cons '(valid-newsgroups . disabled)
4645                   message-syntax-checks)))
4646     (message-cleanup-headers)
4647     (if (not (let ((message-post-method method))
4648                (message-check-news-syntax)))
4649         nil
4650       (unwind-protect
4651           (save-excursion
4652             (set-buffer tembuf)
4653             (buffer-disable-undo)
4654             (erase-buffer)
4655             (insert-buffer-substring message-encoding-buffer)
4656             ;; Remove some headers.
4657             (save-restriction
4658               (message-narrow-to-headers)
4659 ;; We Semi-gnus people have no use for it.
4660 ;;            ;; We (re)generate the Lines header.
4661 ;;            (when (memq 'Lines message-required-mail-headers)
4662 ;;              (message-generate-headers '(Lines)))
4663               ;; Remove some headers.
4664               (message-remove-header message-ignored-news-headers t))
4665             (goto-char (point-max))
4666             ;; require one newline at the end.
4667             (or (= (preceding-char) ?\n)
4668                 (insert ?\n))
4669             (setq result (message-maybe-split-and-send-news method)))
4670         (kill-buffer tembuf))
4671       (set-buffer message-edit-buffer)
4672       (if result
4673           (progn
4674             (message "Couldn't send message via news: %s"
4675                      (nnheader-get-report (car method)))
4676             nil)
4677         (push 'news message-sent-message-via)))))
4678
4679 ;; 1997-09-29 by MORIOKA Tomohiko
4680 (defun message-send-news-with-gnus (method)
4681   (let ((case-fold-search t))
4682     ;; Remove the delimiter.
4683     (goto-char (point-min))
4684     (re-search-forward
4685      (concat "^" (regexp-quote mail-header-separator) "\n"))
4686     (replace-match "\n")
4687     (backward-char 1)
4688     (run-hooks 'message-send-news-hook)
4689     (gnus-open-server method)
4690     (message "Sending news via %s..." (gnus-server-string method))
4691     (gnus-request-post method)
4692     ))
4693
4694 ;;;
4695 ;;; Header generation & syntax checking.
4696 ;;;
4697
4698 (defun message-check-element (type)
4699   "Return non-nil if this TYPE is not to be checked."
4700   (if (eq message-syntax-checks 'dont-check-for-anything-just-trust-me)
4701       t
4702     (let ((able (assq type message-syntax-checks)))
4703       (and (consp able)
4704            (eq (cdr able) 'disabled)))))
4705
4706 (defun message-check-news-syntax ()
4707   "Check the syntax of the message."
4708   (save-excursion
4709     (save-restriction
4710       (widen)
4711       (and
4712        ;; We narrow to the headers and check them first.
4713        (save-excursion
4714          (save-restriction
4715            (message-narrow-to-headers)
4716            (message-check-news-header-syntax)))
4717        ;; Check the body.
4718        (save-excursion
4719          (set-buffer message-edit-buffer)
4720          (message-check-news-body-syntax))))))
4721
4722 (defun message-check-news-header-syntax ()
4723   (and
4724    ;; Check Newsgroups header.
4725    (message-check 'newsgroups
4726      (let ((group (message-fetch-field "newsgroups")))
4727        (or
4728         (and group
4729              (not (string-match "\\`[ \t]*\\'" group)))
4730         (ignore
4731          (message
4732           "The newsgroups field is empty or missing.  Posting is denied.")))))
4733    ;; Check the Subject header.
4734    (message-check 'subject
4735      (let* ((case-fold-search t)
4736             (subject (message-fetch-field "subject")))
4737        (or
4738         (and subject
4739              (not (string-match "\\`[ \t]*\\'" subject)))
4740         (ignore
4741          (message
4742           "The subject field is empty or missing.  Posting is denied.")))))
4743    ;; Check for commands in Subject.
4744    (message-check 'subject-cmsg
4745      (if (string-match "^cmsg " (message-fetch-field "subject"))
4746          (y-or-n-p
4747           "The control code \"cmsg\" is in the subject.  Really post? ")
4748        t))
4749    ;; Check long header lines.
4750    (message-check 'long-header-lines
4751      (let ((start (point))
4752            (header nil)
4753            (length 0)
4754            found)
4755        (while (and (not found)
4756                    (re-search-forward "^\\([^ \t:]+\\): " nil t))
4757          (if (> (- (point) (match-beginning 0)) 998)
4758              (setq found t
4759                    length (- (point) (match-beginning 0)))
4760            (setq header (match-string-no-properties 1)))
4761          (setq start (match-beginning 0))
4762          (forward-line 1))
4763        (if found
4764            (y-or-n-p (format "Your %s header is too long (%d).  Really post? "
4765                              header length))
4766          t)))
4767    ;; Check for multiple identical headers.
4768    (message-check 'multiple-headers
4769      (let (found)
4770        (while (and (not found)
4771                    (re-search-forward "^[^ \t:]+: " nil t))
4772          (save-excursion
4773            (or (re-search-forward
4774                 (concat "^"
4775                         (regexp-quote
4776                          (setq found
4777                                (buffer-substring
4778                                 (match-beginning 0) (- (match-end 0) 2))))
4779                         ":")
4780                 nil t)
4781                (setq found nil))))
4782        (if found
4783            (y-or-n-p (format "Multiple %s headers.  Really post? " found))
4784          t)))
4785    ;; Check for Version and Sendsys.
4786    (message-check 'sendsys
4787      (if (re-search-forward "^Sendsys:\\|^Version:" nil t)
4788          (y-or-n-p
4789           (format "The article contains a %s command.  Really post? "
4790                   (buffer-substring (match-beginning 0)
4791                                     (1- (match-end 0)))))
4792        t))
4793    ;; See whether we can shorten Followup-To.
4794    (message-check 'shorten-followup-to
4795      (let ((newsgroups (message-fetch-field "newsgroups"))
4796            (followup-to (message-fetch-field "followup-to"))
4797            to)
4798        (when (and newsgroups
4799                   (string-match "," newsgroups)
4800                   (not followup-to)
4801                   (not
4802                    (zerop
4803                     (length
4804                      (setq to (completing-read
4805                                "Followups to (default: no Followup-To header) "
4806                                (mapcar #'list
4807                                        (cons "poster"
4808                                              (message-tokenize-header
4809                                               newsgroups)))))))))
4810          (goto-char (point-min))
4811          (insert "Followup-To: " to "\n"))
4812        t))
4813    ;; Check "Shoot me".
4814    (message-check 'shoot
4815      (if (re-search-forward
4816           "Message-ID.*.i-did-not-set--mail-host-address--so-tickle-me" nil t)
4817          (y-or-n-p "You appear to have a misconfigured system.  Really post? ")
4818        t))
4819    ;; Check for Approved.
4820    (message-check 'approved
4821      (if (re-search-forward "^Approved:" nil t)
4822          (y-or-n-p "The article contains an Approved header.  Really post? ")
4823        t))
4824    ;; Check the Message-ID header.
4825    (message-check 'message-id
4826      (let* ((case-fold-search t)
4827             (message-id (message-fetch-field "message-id" t)))
4828        (or (not message-id)
4829            ;; Is there an @ in the ID?
4830            (and (string-match "@" message-id)
4831                 ;; Is there a dot in the ID?
4832                 (string-match "@[^.]*\\." message-id)
4833                 ;; Does the ID end with a dot?
4834                 (not (string-match "\\.>" message-id)))
4835            (y-or-n-p
4836             (format "The Message-ID looks strange: \"%s\".  Really post? "
4837                     message-id)))))
4838    ;; Check the Newsgroups & Followup-To headers.
4839    (message-check 'existing-newsgroups
4840      (let* ((case-fold-search t)
4841             (newsgroups (message-fetch-field "newsgroups"))
4842             (followup-to (message-fetch-field "followup-to"))
4843             (groups (message-tokenize-header
4844                      (if followup-to
4845                          (concat newsgroups "," followup-to)
4846                        newsgroups)))
4847             (post-method (if (functionp message-post-method)
4848                              (funcall message-post-method)
4849                            message-post-method))
4850             ;; KLUDGE to handle nnvirtual groups.  Doing this right
4851             ;; would probably involve a new nnoo function.
4852             ;; -- Per Abrahamsen <abraham@dina.kvl.dk>, 2001-10-17.
4853             (method (if (and (consp post-method)
4854                              (eq (car post-method) 'nnvirtual)
4855                              gnus-message-group-art)
4856                         (let ((group (car (nnvirtual-find-group-art
4857                                            (car gnus-message-group-art)
4858                                            (cdr gnus-message-group-art)))))
4859                           (gnus-find-method-for-group group))
4860                       post-method))
4861             (known-groups
4862              (mapcar (lambda (n)
4863                        (gnus-group-name-decode
4864                         (gnus-group-real-name n)
4865                         (gnus-group-name-charset method n)))
4866                      (gnus-groups-from-server method)))
4867             errors)
4868        (while groups
4869          (when (and (not (equal (car groups) "poster"))
4870                     (not (member (car groups) known-groups))
4871                     (not (member (car groups) errors)))
4872            (push (car groups) errors))
4873          (pop groups))
4874        (cond
4875         ;; Gnus is not running.
4876         ((or (not (and (boundp 'gnus-active-hashtb)
4877                        gnus-active-hashtb))
4878              (not (boundp 'gnus-read-active-file)))
4879          t)
4880         ;; We don't have all the group names.
4881         ((and (or (not gnus-read-active-file)
4882                   (eq gnus-read-active-file 'some))
4883               errors)
4884          (y-or-n-p
4885           (format
4886            "Really use %s possibly unknown group%s: %s? "
4887            (if (= (length errors) 1) "this" "these")
4888            (if (= (length errors) 1) "" "s")
4889            (mapconcat 'identity errors ", "))))
4890         ;; There were no errors.
4891         ((not errors)
4892          t)
4893         ;; There are unknown groups.
4894         (t
4895          (y-or-n-p
4896           (format
4897            "Really post to %s unknown group%s: %s? "
4898            (if (= (length errors) 1) "this" "these")
4899            (if (= (length errors) 1) "" "s")
4900            (mapconcat 'identity errors ", ")))))))
4901    ;; Check continuation headers.
4902    (message-check 'continuation-headers
4903      (goto-char (point-min))
4904      (let ((do-posting t))
4905        (while (re-search-forward "^[^ \t\n][^:\n]*$" nil t)
4906          (if (y-or-n-p "Fix continuation lines? ")
4907              (progn
4908                (goto-char (match-beginning 0))
4909                (insert " "))
4910            (unless (y-or-n-p "Send anyway? ")
4911              (setq do-posting nil))))
4912        do-posting))
4913    ;; Check the Newsgroups & Followup-To headers for syntax errors.
4914    (message-check 'valid-newsgroups
4915      (let ((case-fold-search t)
4916            (headers '("Newsgroups" "Followup-To"))
4917            header error)
4918        (while (and headers (not error))
4919          (when (setq header (mail-fetch-field (car headers)))
4920            (if (or
4921                 (not
4922                  (string-match
4923                   "\\`\\([-+_&.a-zA-Z0-9]+\\)?\\(,[-+_&.a-zA-Z0-9]+\\)*\\'"
4924                   header))
4925                 (memq
4926                  nil (mapcar
4927                       (lambda (g)
4928                         (not (string-match "\\.\\'\\|\\.\\." g)))
4929                       (message-tokenize-header header ","))))
4930                (setq error t)))
4931          (unless error
4932            (pop headers)))
4933        (if (not error)
4934            t
4935          (y-or-n-p
4936           (format "The %s header looks odd: \"%s\".  Really post? "
4937                   (car headers) header)))))
4938    (message-check 'repeated-newsgroups
4939      (let ((case-fold-search t)
4940            (headers '("Newsgroups" "Followup-To"))
4941            header error groups group)
4942        (while (and headers
4943                    (not error))
4944          (when (setq header (mail-fetch-field (pop headers)))
4945            (setq groups (message-tokenize-header header ","))
4946            (while (setq group (pop groups))
4947              (when (member group groups)
4948                (setq error group
4949                      groups nil)))))
4950        (if (not error)
4951            t
4952          (y-or-n-p
4953           (format "Group %s is repeated in headers.  Really post? " error)))))
4954    ;; Check the From header.
4955    (message-check 'from
4956      (let* ((case-fold-search t)
4957             (from (message-fetch-field "from"))
4958             ad)
4959        (cond
4960         ((not from)
4961          (message "There is no From line.  Posting is denied.")
4962          nil)
4963         ((or (not (string-match
4964                    "@[^\\.]*\\."
4965                    (setq ad (nth 1 (std11-extract-address-components
4966                                     from))))) ;larsi@ifi
4967              (string-match "\\.\\." ad) ;larsi@ifi..uio
4968              (string-match "@\\." ad)   ;larsi@.ifi.uio
4969              (string-match "\\.$" ad)   ;larsi@ifi.uio.
4970              (not (string-match "^[^@]+@[^@]+$" ad)) ;larsi.ifi.uio
4971              (string-match "(.*).*(.*)" from)) ;(lars) (lars)
4972          (message
4973           "Denied posting -- the From looks strange: \"%s\"." from)
4974          nil)
4975         ((let ((addresses (rfc822-addresses from)))
4976            (while (and addresses
4977                        (not (eq (string-to-char (car addresses)) ?\()))
4978              (setq addresses (cdr addresses)))
4979            addresses)
4980          (message
4981           "Denied posting -- bad From address: \"%s\"." from)
4982          nil)
4983         (t t))))
4984    ;; Check the Reply-To header.
4985    (message-check 'reply-to
4986      (let* ((case-fold-search t)
4987             (reply-to (message-fetch-field "reply-to"))
4988             ad)
4989        (cond
4990         ((not reply-to)
4991          t)
4992         ((string-match "," reply-to)
4993          (y-or-n-p
4994           (format "Multiple Reply-To addresses: \"%s\". Really post? "
4995                   reply-to)))
4996         ((or (not (string-match
4997                    "@[^\\.]*\\."
4998                    (setq ad (nth 1 (std11-extract-address-components
4999                                     reply-to))))) ;larsi@ifi
5000              (string-match "\\.\\." ad) ;larsi@ifi..uio
5001              (string-match "@\\." ad)   ;larsi@.ifi.uio
5002              (string-match "\\.$" ad)   ;larsi@ifi.uio.
5003              (not (string-match "^[^@]+@[^@]+$" ad)) ;larsi.ifi.uio
5004              (string-match "(.*).*(.*)" reply-to)) ;(lars) (lars)
5005          (y-or-n-p
5006           (format
5007            "The Reply-To looks strange: \"%s\". Really post? "
5008            reply-to)))
5009         (t t))))))
5010
5011 (defun message-check-news-body-syntax ()
5012   (and
5013    ;; Check for long lines.
5014    (message-check 'long-lines
5015      (goto-char (point-min))
5016      (re-search-forward
5017       (concat "^" (regexp-quote mail-header-separator) "$"))
5018      (forward-line 1)
5019      (while (and
5020              (or (looking-at
5021                   mime-edit-tag-regexp)
5022                  (let ((p (point)))
5023                    (end-of-line)
5024                    (< (- (point) p) 80)))
5025              (zerop (forward-line 1))))
5026      (or (bolp)
5027          (eobp)
5028          (y-or-n-p
5029           "You have lines longer than 79 characters.  Really post? ")))
5030    ;; Check whether the article is empty.
5031    (message-check 'empty
5032      (goto-char (point-min))
5033      (re-search-forward
5034       (concat "^" (regexp-quote mail-header-separator) "$"))
5035      (forward-line 1)
5036      (let ((b (point)))
5037        (goto-char (point-max))
5038        (re-search-backward message-signature-separator nil t)
5039        (beginning-of-line)
5040        (or (re-search-backward "[^ \n\t]" b t)
5041            (if (message-gnksa-enable-p 'empty-article)
5042                (y-or-n-p "Empty article.  Really post? ")
5043              (message "Denied posting -- Empty article.")
5044              nil))))
5045    ;; Check for control characters.
5046    (message-check 'control-chars
5047      (if (re-search-forward
5048           (string-as-multibyte "[\000-\007\013\015-\032\034-\037\200-\237]")
5049           nil t)
5050          (y-or-n-p
5051           "The article contains control characters.  Really post? ")
5052        t))
5053    ;; Check 8bit characters.
5054    (message-check '8bit
5055      (message-check-8bit))
5056    ;; Check excessive size.
5057    (message-check 'size
5058      (if (> (buffer-size) 60000)
5059          (y-or-n-p
5060           (format "The article is %d octets long.  Really post? "
5061                   (buffer-size)))
5062        t))
5063    ;; Check whether any new text has been added.
5064    (message-check 'new-text
5065      (or
5066       (not message-checksum)
5067       (not (eq (message-checksum) message-checksum))
5068       (if (message-gnksa-enable-p 'quoted-text-only)
5069           (y-or-n-p
5070            "It looks like no new text has been added.  Really post? ")
5071         (message "Denied posting -- no new text has been added.")
5072         nil)))
5073    ;; Check the length of the signature.
5074    (message-check 'signature
5075      (goto-char (point-max))
5076      (if (> (count-lines (point) (point-max)) 5)
5077          (y-or-n-p
5078           (format
5079            "Your .sig is %d lines; it should be max 4.  Really post? "
5080            (1- (count-lines (point) (point-max)))))
5081        t))
5082    ;; Ensure that text follows last quoted portion.
5083    (message-check 'quoting-style
5084      (goto-char (point-max))
5085      (let ((no-problem t))
5086        (when (search-backward-regexp "^>[^\n]*\n" nil t)
5087          (setq no-problem (search-forward-regexp "^[ \t]*[^>\n]" nil t)))
5088        (if no-problem
5089            t
5090          (if (message-gnksa-enable-p 'quoted-text-only)
5091              (y-or-n-p "Your text should follow quoted text.  Really post? ")
5092            ;; Ensure that
5093            (goto-char (point-min))
5094            (re-search-forward
5095             (concat "^" (regexp-quote mail-header-separator) "$"))
5096            (if (search-forward-regexp "^[ \t]*[^>\n]" nil t)
5097                (y-or-n-p "Your text should follow quoted text.  Really post? ")
5098              (message "Denied posting -- only quoted text.")
5099              nil)))))))
5100
5101 (defun message-check-mail-syntax ()
5102   "Check the syntax of the message."
5103   (save-excursion
5104     (save-restriction
5105       (widen)
5106       (and
5107        ;; We narrow to the headers and check them first.
5108        (save-excursion
5109          (save-restriction
5110            (message-narrow-to-headers)
5111            (message-check-mail-header-syntax)))
5112        ;; Check the body.
5113        (save-excursion
5114          (set-buffer message-edit-buffer)
5115          (message-check-mail-body-syntax))))))
5116
5117 (defun message-check-mail-header-syntax ()
5118   t)
5119
5120 (defun message-check-mail-body-syntax ()
5121   (and
5122    ;; Check 8bit characters.
5123    (message-check '8bit
5124      (message-check-8bit)
5125      )))
5126
5127 (defun message-check-8bit ()
5128   "Check the article contains 8bit characters."
5129   (save-excursion
5130     (set-buffer message-encoding-buffer)
5131     (message-narrow-to-headers)
5132     (let* ((case-fold-search t)
5133            (field-value (message-fetch-field "content-transfer-encoding")))
5134       (if (and field-value
5135                (member (downcase field-value) message-8bit-encoding-list))
5136           t
5137         (widen)
5138         (set-buffer (get-buffer-create " message syntax"))
5139         (erase-buffer)
5140         (goto-char (point-min))
5141         (set-buffer-multibyte nil)
5142         (insert-buffer-substring message-encoding-buffer)
5143         (goto-char (point-min))
5144         (if (re-search-forward "[^\x00-\x7f]" nil t)
5145             (y-or-n-p
5146              "The article contains 8bit characters.  Really post? ")
5147           t)))))
5148
5149 (defun message-checksum ()
5150   "Return a \"checksum\" for the current buffer."
5151   (let ((sum 0))
5152     (save-excursion
5153       (goto-char (point-min))
5154       (re-search-forward
5155        (concat "^" (regexp-quote mail-header-separator) "$"))
5156       (while (not (eobp))
5157         (when (not (looking-at "[ \t\n]"))
5158           (setq sum (logxor (ash sum 1) (if (natnump sum) 0 1)
5159                             (char-after))))
5160         (forward-char 1)))
5161     sum))
5162
5163 (defun message-do-fcc ()
5164   "Process Fcc headers in the current buffer."
5165   (let ((case-fold-search t)
5166         (coding-system-for-write 'raw-text)
5167         list file
5168         (mml-externalize-attachments message-fcc-externalize-attachments))
5169     (save-excursion
5170       (save-restriction
5171         (message-narrow-to-headers)
5172         (setq file (message-fetch-field "fcc" t)))
5173       (when file
5174         (set-buffer (get-buffer-create " *message temp*"))
5175         (erase-buffer)
5176         (insert-buffer-substring message-encoding-buffer)
5177         (save-restriction
5178           (message-narrow-to-headers)
5179           (while (setq file (message-fetch-field "fcc"))
5180             (push file list)
5181             (message-remove-header "fcc" nil t)))
5182         (goto-char (point-min))
5183         (when (re-search-forward
5184                (concat "^" (regexp-quote mail-header-separator) "$")
5185                nil t)
5186           (replace-match "" t t))
5187         ;; Process FCC operations.
5188         (while list
5189           (setq file (pop list))
5190           (if (string-match "^[ \t]*|[ \t]*\\(.*\\)[ \t]*$" file)
5191               ;; Pipe the article to the program in question.
5192               (call-process-region (point-min) (point-max) shell-file-name
5193                                    nil nil nil shell-command-switch
5194                                    (match-string 1 file))
5195             ;; Save the article.
5196             (setq file (expand-file-name file))
5197             (unless (file-exists-p (file-name-directory file))
5198               (make-directory (file-name-directory file) t))
5199             (if (and message-fcc-handler-function
5200                      (not (eq message-fcc-handler-function 'rmail-output)))
5201                 (funcall message-fcc-handler-function file)
5202               (if (and (file-readable-p file) (mail-file-babyl-p file))
5203                   (rmail-output file 1 nil t)
5204                 (let ((mail-use-rfc822 t))
5205                   (rmail-output file 1 t t))))))
5206         (kill-buffer (current-buffer))))))
5207
5208 (defun message-output (filename)
5209   "Append this article to Unix/babyl mail file FILENAME."
5210   (if (and (file-readable-p filename)
5211            (mail-file-babyl-p filename))
5212       (gnus-output-to-rmail filename t)
5213     (gnus-output-to-mail filename t)))
5214
5215 (defun message-cleanup-headers ()
5216   "Do various automatic cleanups of the headers."
5217   ;; Remove empty lines in the header.
5218   (save-restriction
5219     (message-narrow-to-headers)
5220     ;; Remove blank lines.
5221     (while (re-search-forward "^[ \t]*\n" nil t)
5222       (replace-match "" t t))
5223
5224     ;; Correct Newsgroups and Followup-To headers:  Change sequence of
5225     ;; spaces to comma and eliminate spaces around commas.  Eliminate
5226     ;; embedded line breaks.
5227     (goto-char (point-min))
5228     (while (re-search-forward "^\\(Newsgroups\\|Followup-To\\): +" nil t)
5229       (save-restriction
5230         (narrow-to-region
5231          (point)
5232          (if (re-search-forward "^[^ \t]" nil t)
5233              (match-beginning 0)
5234            (forward-line 1)
5235            (point)))
5236         (goto-char (point-min))
5237         (while (re-search-forward "\n[ \t]+" nil t)
5238           (replace-match " " t t))      ;No line breaks (too confusing)
5239         (goto-char (point-min))
5240         (while (re-search-forward "[ \t\n]*,[ \t\n]*\\|[ \t]+" nil t)
5241           (replace-match "," t t))
5242         (goto-char (point-min))
5243         ;; Remove trailing commas.
5244         (when (re-search-forward ",+$" nil t)
5245           (replace-match "" t t))))))
5246
5247 (defun message-make-date (&optional now)
5248   "Make a valid data header.
5249 If NOW, use that time instead."
5250   (let ((system-time-locale "C"))
5251     (format-time-string "%a, %d %b %Y %T %z" now)))
5252
5253 (defun message-make-followup-subject (subject)
5254   "Make a followup Subject."
5255   (cond
5256    ((and (eq message-use-subject-re 'guess)
5257          (string-match message-subject-encoded-re-regexp subject))
5258     subject)
5259    (message-use-subject-re
5260     (concat "Re: " (message-strip-subject-re subject)))
5261    (t subject)))
5262
5263 (defun message-make-message-id ()
5264   "Make a unique Message-ID."
5265   (concat "<" (message-unique-id)
5266           (let ((psubject (save-excursion (message-fetch-field "subject")))
5267                 (psupersedes
5268                  (save-excursion (message-fetch-field "supersedes"))))
5269             (if (or
5270                  (and message-reply-headers
5271                       (mail-header-references message-reply-headers)
5272                       (mail-header-subject message-reply-headers)
5273                       psubject
5274                       (not (string=
5275                             (message-strip-subject-re
5276                              (mail-header-subject message-reply-headers))
5277                             (message-strip-subject-re psubject))))
5278                  (and psupersedes
5279                       (string-match "_-_@" psupersedes)))
5280                 "_-_" ""))
5281           "@" (message-make-fqdn) ">"))
5282
5283 (defvar message-unique-id-char nil)
5284
5285 ;; If you ever change this function, make sure the new version
5286 ;; cannot generate IDs that the old version could.
5287 ;; You might for example insert a "." somewhere (not next to another dot
5288 ;; or string boundary), or modify the "fsf" string.
5289 (defun message-unique-id ()
5290   ;; Don't use microseconds from (current-time), they may be unsupported.
5291   ;; Instead we use this randomly inited counter.
5292   (setq message-unique-id-char
5293         (% (1+ (or message-unique-id-char (logand (random t) (1- (lsh 1 20)))))
5294            ;; (current-time) returns 16-bit ints,
5295            ;; and 2^16*25 just fits into 4 digits i base 36.
5296            (* 25 25)))
5297   (let ((tm (current-time)))
5298     (concat
5299      (if (memq system-type '(ms-dos emx vax-vms))
5300          (let ((user (downcase (user-login-name))))
5301            (while (string-match "[^a-z0-9_]" user)
5302              (aset user (match-beginning 0) ?_))
5303            user)
5304        (message-number-base36 (user-uid) -1))
5305      (message-number-base36 (+ (car tm)
5306                                (lsh (% message-unique-id-char 25) 16)) 4)
5307      (message-number-base36 (+ (nth 1 tm)
5308                                (lsh (/ message-unique-id-char 25) 16)) 4)
5309      ;; Append a given name, because while the generated ID is unique
5310      ;; to this newsreader, other newsreaders might otherwise generate
5311      ;; the same ID via another algorithm.
5312      ".fsf")))
5313
5314 (defun message-number-base36 (num len)
5315   (if (if (< len 0)
5316           (<= num 0)
5317         (= len 0))
5318       ""
5319     (concat (message-number-base36 (/ num 36) (1- len))
5320             (char-to-string (aref "zyxwvutsrqponmlkjihgfedcba9876543210"
5321                                   (% num 36))))))
5322
5323 (defun message-make-organization ()
5324   "Make an Organization header."
5325   (let* ((organization
5326           (when message-user-organization
5327             (if (functionp message-user-organization)
5328                 (funcall message-user-organization)
5329               message-user-organization))))
5330     (with-temp-buffer
5331       (set-buffer-multibyte t)
5332       (cond ((stringp organization)
5333              (insert organization))
5334             ((and (eq t organization)
5335                   message-user-organization-file
5336                   (file-exists-p message-user-organization-file))
5337              (insert-file-contents message-user-organization-file)))
5338       (goto-char (point-min))
5339       (while (re-search-forward "[\t\n]+" nil t)
5340         (replace-match "" t t))
5341       (unless (zerop (buffer-size))
5342         (buffer-string)))))
5343
5344 (defun message-make-lines ()
5345   "Count the number of lines and return numeric string."
5346   (save-excursion
5347     (save-restriction
5348       (widen)
5349       (message-goto-body)
5350       (int-to-string (count-lines (point) (point-max))))))
5351
5352 (defun message-make-references ()
5353   "Return the References header for this message."
5354   (when message-reply-headers
5355     (let ((message-id (mail-header-message-id message-reply-headers))
5356           (references (mail-header-references message-reply-headers))
5357           new-references)
5358       (if (or references message-id)
5359           (concat (or references "") (and references " ")
5360                   (or message-id ""))
5361         nil))))
5362
5363 (defun message-make-in-reply-to ()
5364   "Return the In-Reply-To header for this message."
5365   (when message-reply-headers
5366     (let ((from (mail-header-from message-reply-headers))
5367           (date (mail-header-date message-reply-headers))
5368           (msg-id (mail-header-message-id message-reply-headers)))
5369       (when from
5370         (let ((name (std11-extract-address-components from)))
5371           (concat msg-id (if msg-id " (")
5372                   (or (car name)
5373                       (nth 1 name))
5374                   "'s message of \""
5375                   (if (or (not date) (string= date ""))
5376                       "(unknown date)" date)
5377                   "\"" (if msg-id ")")))))))
5378
5379 (defun message-make-distribution ()
5380   "Make a Distribution header."
5381   (let ((orig-distribution (message-fetch-reply-field "distribution")))
5382     (cond ((functionp message-distribution-function)
5383            (funcall message-distribution-function))
5384           (t orig-distribution))))
5385
5386 (defun message-make-expires ()
5387   "Return an Expires header based on `message-expires'."
5388   (let ((current (current-time))
5389         (future (* 1.0 message-expires 60 60 24)))
5390     ;; Add the future to current.
5391     (setcar current (+ (car current) (round (/ future (expt 2 16)))))
5392     (setcar (cdr current) (+ (nth 1 current) (% (round future) (expt 2 16))))
5393     (message-make-date current)))
5394
5395 (defun message-make-path ()
5396   "Return uucp path."
5397   (let ((login-name (user-login-name)))
5398     (cond ((null message-user-path)
5399            (concat (system-name) "!" login-name))
5400           ((stringp message-user-path)
5401            ;; Support GENERICPATH.  Suggested by vixie@decwrl.dec.com.
5402            (concat message-user-path "!" login-name))
5403           (t login-name))))
5404
5405 (defun message-make-from ()
5406   "Make a From header."
5407   (let* ((style message-from-style)
5408          (login (message-make-address))
5409          (fullname
5410           (or (and (boundp 'user-full-name)
5411                    user-full-name)
5412               (user-full-name))))
5413     (when (string= fullname "&")
5414       (setq fullname (user-login-name)))
5415     (with-temp-buffer
5416       (set-buffer-multibyte t)
5417       (cond
5418        ((or (null style)
5419             (equal fullname ""))
5420         (insert login))
5421        ((or (eq style 'angles)
5422             (and (not (eq style 'parens))
5423                  ;; Use angles if no quoting is needed, or if parens would
5424                  ;; need quoting too.
5425                  (or (not (string-match "[^- !#-'*+/-9=?A-Z^-~]" fullname))
5426                      (let ((tmp (concat fullname nil)))
5427                        (while (string-match "([^()]*)" tmp)
5428                          (aset tmp (match-beginning 0) ?-)
5429                          (aset tmp (1- (match-end 0)) ?-))
5430                        (string-match "[\\()]" tmp)))))
5431         (insert fullname)
5432         (goto-char (point-min))
5433         ;; Look for a character that cannot appear unquoted
5434         ;; according to RFC 822.
5435         (when (re-search-forward "[^- !#-'*+/-9=?A-Z^-~]" nil 1)
5436           ;; Quote fullname, escaping specials.
5437           (goto-char (point-min))
5438           (insert "\"")
5439           (while (re-search-forward "[\"\\]" nil 1)
5440             (replace-match "\\\\\\&" t))
5441           (insert "\""))
5442         (insert " <" login ">"))
5443        (t                               ; 'parens or default
5444         (insert login " (")
5445         (let ((fullname-start (point)))
5446           (insert fullname)
5447           (goto-char fullname-start)
5448           ;; RFC 822 says \ and nonmatching parentheses
5449           ;; must be escaped in comments.
5450           ;; Escape every instance of ()\ ...
5451           (while (re-search-forward "[()\\]" nil 1)
5452             (replace-match "\\\\\\&" t))
5453           ;; ... then undo escaping of matching parentheses,
5454           ;; including matching nested parentheses.
5455           (goto-char fullname-start)
5456           (while (re-search-forward
5457                   "\\(\\=\\|[^\\]\\(\\\\\\\\\\)*\\)\\\\(\\(\\([^\\]\\|\\\\\\\\\\)*\\)\\\\)"
5458                   nil 1)
5459             (replace-match "\\1(\\3)" t)
5460             (goto-char fullname-start)))
5461         (insert ")")))
5462       (buffer-string))))
5463
5464 (defun message-make-sender ()
5465   "Return the \"real\" user address.
5466 This function tries to ignore all user modifications, and
5467 give as trustworthy answer as possible."
5468   (concat (user-login-name) "@" (system-name)))
5469
5470 (defun message-make-address ()
5471   "Make the address of the user."
5472   (or (message-user-mail-address)
5473       (concat (user-login-name) "@" (message-make-domain))))
5474
5475 (defun message-user-mail-address ()
5476   "Return the pertinent part of `user-mail-address'."
5477   (when (and user-mail-address
5478              (string-match "@.*\\." user-mail-address))
5479     (if (string-match " " user-mail-address)
5480         (nth 1 (std11-extract-address-components user-mail-address))
5481       user-mail-address)))
5482
5483 (defun message-sendmail-envelope-from ()
5484   "Return the envelope from."
5485   (cond ((eq message-sendmail-envelope-from 'header)
5486          (nth 1 (std11-extract-address-components
5487                  (message-fetch-field "from"))))
5488         ((stringp message-sendmail-envelope-from)
5489          message-sendmail-envelope-from)
5490         (t
5491          (message-make-address))))
5492
5493 (defun message-make-fqdn ()
5494   "Return user's fully qualified domain name."
5495   (let* ((system-name (system-name))
5496          (user-mail (message-user-mail-address))
5497          (user-domain
5498           (if (and user-mail
5499                    (string-match "@\\(.*\\)\\'" user-mail))
5500               (match-string 1 user-mail)))
5501          (case-fold-search t))
5502     (cond
5503      ((and message-user-fqdn
5504            (stringp message-user-fqdn)
5505            (string-match message-valid-fqdn-regexp message-user-fqdn)
5506            (not (string-match message-bogus-system-names message-user-fqdn)))
5507       message-user-fqdn)
5508      ;; `message-user-fqdn' seems to be valid
5509      ((and (string-match message-valid-fqdn-regexp system-name)
5510            (not (string-match message-bogus-system-names system-name)))
5511       ;; `system-name' returned the right result.
5512       system-name)
5513      ;; Try `mail-host-address'.
5514      ((and (boundp 'mail-host-address)
5515            (stringp mail-host-address)
5516            (string-match message-valid-fqdn-regexp mail-host-address)
5517            (not (string-match message-bogus-system-names mail-host-address)))
5518       mail-host-address)
5519      ;; We try `user-mail-address' as a backup.
5520      ((and user-domain
5521            (stringp user-domain)
5522            (string-match message-valid-fqdn-regexp user-domain)
5523            (not (string-match message-bogus-system-names user-domain)))
5524       user-domain)
5525      ;; Default to this bogus thing.
5526      (t
5527       (concat system-name
5528               ".i-did-not-set--mail-host-address--so-tickle-me")))))
5529
5530 (defun message-make-host-name ()
5531   "Return the name of the host."
5532   (let ((fqdn (message-make-fqdn)))
5533     (string-match "^[^.]+\\." fqdn)
5534     (substring fqdn 0 (1- (match-end 0)))))
5535
5536 (defun message-make-domain ()
5537   "Return the domain name."
5538   (or mail-host-address
5539       (message-make-fqdn)))
5540
5541 (defun message-to-list-only ()
5542   "Send a message to the list only.
5543 Remove all addresses but the list address from To and Cc headers."
5544   (interactive)
5545   (let ((listaddr (message-make-mail-followup-to t)))
5546     (when listaddr
5547       (save-excursion
5548         (message-remove-header "to")
5549         (message-remove-header "cc")
5550         (message-position-on-field "To" "X-Draft-From")
5551         (insert listaddr)))))
5552
5553 (defun message-make-mail-followup-to (&optional only-show-subscribed)
5554   "Return the Mail-Followup-To header.
5555 If passed the optional argument ONLY-SHOW-SUBSCRIBED only return the
5556 subscribed address (and not the additional To and Cc header contents)."
5557   (let* ((case-fold-search t)
5558          (to (message-fetch-field "To"))
5559          (cc (message-fetch-field "cc"))
5560          (msg-recipients (concat to (and to cc ", ") cc))
5561          (recipients
5562           (mapcar 'mail-strip-quoted-names
5563                   (message-tokenize-header msg-recipients)))
5564          (file-regexps
5565           (if message-subscribed-address-file
5566               (let (begin end item re)
5567                 (save-excursion
5568                   (with-temp-buffer
5569                     (insert-file-contents message-subscribed-address-file)
5570                     (while (not (eobp))
5571                       (setq begin (point))
5572                       (forward-line 1)
5573                       (setq end (point))
5574                       (if (bolp) (setq end (1- end)))
5575                       (setq item (regexp-quote (buffer-substring begin end)))
5576                       (if re (setq re (concat re "\\|" item))
5577                         (setq re (concat "\\`\\(" item))))
5578                     (and re (list (concat re "\\)\\'"))))))))
5579          (mft-regexps (apply 'append message-subscribed-regexps
5580                              (mapcar 'regexp-quote
5581                                      message-subscribed-addresses)
5582                              file-regexps
5583                              (mapcar 'funcall
5584                                      message-subscribed-address-functions))))
5585     (save-match-data
5586       (let ((subscribed-lists nil)
5587             (list
5588              (loop for recipient in recipients
5589                when (loop for regexp in mft-regexps
5590                       when (string-match regexp recipient) return t)
5591                return recipient)))
5592         (when list
5593           (if only-show-subscribed
5594               list
5595             msg-recipients))))))
5596
5597 ;; Dummy to avoid byte-compile warning.
5598 (defvar mule-version)
5599 (defvar emacs-beta-version)
5600 (defvar xemacs-codename)
5601 (defvar gnus-inviolable-extended-version)
5602
5603 (defun message-make-user-agent ()
5604   "Return user-agent info if the value `message-user-agent' is non-nil. If the
5605 \"User-Agent\" field has already exist, remove it."
5606   (when message-user-agent
5607     (save-excursion
5608       (goto-char (point-min))
5609       (let ((case-fold-search t))
5610         (when (re-search-forward "^User-Agent:[\t ]*" nil t)
5611           (delete-region (match-beginning 0) (1+ (std11-field-end)))))))
5612   message-user-agent)
5613
5614 (defun message-idna-to-ascii-rhs-1 (header)
5615   "Interactively potentially IDNA encode domain names in HEADER."
5616   (let ((field (message-fetch-field header))
5617         rhs ace  address)
5618     (when field
5619       (dolist (address (mail-header-parse-addresses field))
5620         (setq address (car address)
5621               rhs (downcase (or (cadr (split-string address "@")) ""))
5622               ace (downcase (idna-to-ascii rhs)))
5623         (when (and (not (equal rhs ace))
5624                    (or (not (eq message-use-idna 'ask))
5625                        (y-or-n-p (format "Replace %s with %s? " rhs ace))))
5626           (goto-char (point-min))
5627           (while (re-search-forward (concat "^" header ":") nil t)
5628             (message-narrow-to-field)
5629             (while (search-forward (concat "@" rhs) nil t)
5630               (replace-match (concat "@" ace) t t))
5631             (goto-char (point-max))
5632             (widen)))))))
5633
5634 (defun message-idna-to-ascii-rhs ()
5635   "Possibly IDNA encode non-ASCII domain names in From:, To: and Cc: headers.
5636 See `message-idna-encode'."
5637   (interactive)
5638   (when message-use-idna
5639     (save-excursion
5640       (save-restriction
5641         (message-narrow-to-head)
5642         (message-idna-to-ascii-rhs-1 "From")
5643         (message-idna-to-ascii-rhs-1 "To")
5644         (message-idna-to-ascii-rhs-1 "Reply-To")
5645         (message-idna-to-ascii-rhs-1 "Cc")))))
5646
5647 (defun message-generate-headers (headers)
5648   "Prepare article HEADERS.
5649 Headers already prepared in the buffer are not modified."
5650   (setq headers (append headers message-required-headers))
5651   (save-restriction
5652     (message-narrow-to-headers)
5653     (let* ((Date (message-make-date))
5654            (Message-ID (message-make-message-id))
5655            (Organization (message-make-organization))
5656            (From (message-make-from))
5657            (Path (message-make-path))
5658            (Subject nil)
5659            (Newsgroups nil)
5660            (In-Reply-To (message-make-in-reply-to))
5661            (References (message-make-references))
5662            (To nil)
5663            (Distribution (message-make-distribution))
5664            (Lines (message-make-lines))
5665            (User-Agent (message-make-user-agent))
5666            (Expires (message-make-expires))
5667            (case-fold-search t)
5668            (optionalp nil)
5669            header value elem header-string)
5670       ;; First we remove any old generated headers.
5671       (let ((headers message-deletable-headers))
5672         (unless (buffer-modified-p)
5673           (setq headers (delq 'Message-ID (copy-sequence headers))))
5674         (while headers
5675           (goto-char (point-min))
5676           (and (re-search-forward
5677                 (concat "^" (symbol-name (car headers)) ": *") nil t)
5678                (get-text-property (1+ (match-beginning 0)) 'message-deletable)
5679                (message-delete-line))
5680           (pop headers)))
5681       ;; Go through all the required headers and see if they are in the
5682       ;; articles already.  If they are not, or are empty, they are
5683       ;; inserted automatically - except for Subject, Newsgroups and
5684       ;; Distribution.
5685       (while headers
5686         (goto-char (point-min))
5687         (setq elem (pop headers))
5688         (if (consp elem)
5689             (if (eq (car elem) 'optional)
5690                 (setq header (cdr elem)
5691                       optionalp t)
5692               (setq header (car elem)))
5693           (setq header elem))
5694         (setq header-string  (if (stringp header)
5695                                  header
5696                                (symbol-name header)))
5697         (when (or (not (re-search-forward
5698                         (concat "^"
5699                                 (regexp-quote (downcase header-string))
5700                                 ":")
5701                         nil t))
5702                   (progn
5703                     ;; The header was found.  We insert a space after the
5704                     ;; colon, if there is none.
5705                     (if (/= (char-after) ? ) (insert " ") (forward-char 1))
5706                     ;; Find out whether the header is empty.
5707                     (looking-at "[ \t]*\n[^ \t]")))
5708           ;; So we find out what value we should insert.
5709           (setq value
5710                 (cond
5711                  ((and (consp elem)
5712                        (eq (car elem) 'optional)
5713                        (not (member header-string message-inserted-headers)))
5714                   ;; This is an optional header.  If the cdr of this
5715                   ;; is something that is nil, then we do not insert
5716                   ;; this header.
5717                   (setq header (cdr elem))
5718                   (or (and (functionp (cdr elem))
5719                            (funcall (cdr elem)))
5720                       (and (boundp (cdr elem))
5721                            (symbol-value (cdr elem)))))
5722                  ((consp elem)
5723                   ;; The element is a cons.  Either the cdr is a
5724                   ;; string to be inserted verbatim, or it is a
5725                   ;; function, and we insert the value returned from
5726                   ;; this function.
5727                   (or (and (stringp (cdr elem))
5728                            (cdr elem))
5729                       (and (functionp (cdr elem))
5730                            (funcall (cdr elem)))))
5731                  ((and (boundp header)
5732                        (symbol-value header))
5733                   ;; The element is a symbol.  We insert the value
5734                   ;; of this symbol, if any.
5735                   (symbol-value header))
5736                  ((not (message-check-element header))
5737                   ;; We couldn't generate a value for this header,
5738                   ;; so we just ask the user.
5739                   (read-from-minibuffer
5740                    (format "Empty header for %s; enter value: " header)))))
5741           ;; Finally insert the header.
5742           (when (and value
5743                      (not (equal value "")))
5744             (save-excursion
5745               (if (bolp)
5746                   (progn
5747                     ;; This header didn't exist, so we insert it.
5748                     (goto-char (point-max))
5749                     (let ((formatter
5750                            (cdr (assq header message-header-format-alist))))
5751                       (if formatter
5752                           (funcall formatter header value)
5753                         (insert header-string ": " value))
5754                       (goto-char (message-fill-field))
5755                       ;; We check whether the value was ended by a
5756                       ;; newline.  If not, we insert one.
5757                       (unless (bolp)
5758                         (insert "\n"))
5759                       (forward-line -1)))
5760                 ;; The value of this header was empty, so we clear
5761                 ;; totally and insert the new value.
5762                 (delete-region (point) (point-at-eol))
5763                 ;; If the header is optional, and the header was
5764                 ;; empty, we can't insert it anyway.
5765                 (unless optionalp
5766                   (push header-string message-inserted-headers)
5767                   (insert value)
5768                   (message-fill-field)))
5769               ;; Add the deletable property to the headers that require it.
5770               (and (memq header message-deletable-headers)
5771                    (progn (beginning-of-line) (looking-at "[^:]+: "))
5772                    (add-text-properties
5773                     (point) (match-end 0)
5774                     '(message-deletable t face italic) (current-buffer)))))))
5775       ;; Insert new Sender if the From is strange.
5776       (let ((from (message-fetch-field "from"))
5777             (sender (message-fetch-field "sender"))
5778             (secure-sender (message-make-sender)))
5779         (when (and from
5780                    (not (message-check-element 'sender))
5781                    (not (string=
5782                          (downcase
5783                           (cadr (std11-extract-address-components from)))
5784                          (downcase secure-sender)))
5785                    (or (null sender)
5786                        (not
5787                         (string=
5788                          (downcase
5789                           (cadr (std11-extract-address-components sender)))
5790                          (downcase secure-sender)))))
5791           (goto-char (point-min))
5792           ;; Rename any old Sender headers to Original-Sender.
5793           (when (re-search-forward "^\\(Original-\\)*Sender:" nil t)
5794             (beginning-of-line)
5795             (insert "Original-")
5796             (beginning-of-line))
5797           (when (or (message-news-p)
5798                     (string-match "@.+\\.." secure-sender))
5799             (insert "Sender: " secure-sender "\n"))))
5800       ;; Check for IDNA
5801       (message-idna-to-ascii-rhs))))
5802
5803 (defun message-insert-courtesy-copy ()
5804   "Insert a courtesy message in mail copies of combined messages."
5805   (let (newsgroups)
5806     (save-excursion
5807       (save-restriction
5808         (message-narrow-to-headers)
5809         (when (setq newsgroups (message-fetch-field "newsgroups"))
5810           (goto-char (point-max))
5811           (insert "Posted-To: " newsgroups "\n")))
5812       (forward-line 1)
5813       (when message-courtesy-message
5814         (cond
5815          ((string-match "%s" message-courtesy-message)
5816           (insert (format message-courtesy-message newsgroups)))
5817          (t
5818           (insert message-courtesy-message)))))))
5819
5820 ;;;
5821 ;;; Setting up a message buffer
5822 ;;;
5823
5824 (defun message-skip-to-next-address ()
5825   (let ((end (save-excursion
5826                (message-next-header)
5827                (point)))
5828         quoted char)
5829     (when (looking-at ",")
5830       (forward-char 1))
5831     (while (and (not (= (point) end))
5832                 (or (not (eq char ?,))
5833                     quoted))
5834       (skip-chars-forward "^,\"" (point-max))
5835       (when (eq (setq char (following-char)) ?\")
5836         (setq quoted (not quoted)))
5837       (unless (= (point) end)
5838         (forward-char 1)))
5839     (skip-chars-forward " \t\n")))
5840
5841 (defun message-fill-address (header value)
5842   (insert (capitalize (symbol-name header))
5843           ": "
5844           (if (consp value) (car value) value)
5845           "\n")
5846   (message-fill-field-address))
5847
5848 (defun message-fill-references (header value)
5849   (insert (capitalize (symbol-name header))
5850           ": "
5851           (std11-fill-msg-id-list-string
5852            (if (consp value) (car value) value))))
5853
5854 (defun message-split-line ()
5855   "Split current line, moving portion beyond point vertically down.
5856 If the current line has `message-yank-prefix', insert it on the new line."
5857   (interactive "*")
5858   (condition-case nil
5859       (split-line message-yank-prefix) ;; Emacs 22.1+ supports arg.
5860     (error
5861      (split-line))))
5862
5863 (defun message-insert-header (header value)
5864   (insert (capitalize (symbol-name header))
5865           ": "
5866           (if (consp value) (car value) value)))
5867
5868 (defun message-field-name ()
5869   (save-excursion
5870     (goto-char (point-min))
5871     (when (looking-at "\\([^:]+\\):")
5872       (intern (capitalize (match-string 1))))))
5873
5874 (defun message-fill-field ()
5875   (save-excursion
5876     (save-restriction
5877       (message-narrow-to-field)
5878       (let ((field-name (message-field-name)))
5879         (funcall (or (cadr (assq field-name message-field-fillers))
5880                      'message-fill-field-general)))
5881       (point-max))))
5882
5883 (defun message-fill-field-address ()
5884   (while (not (eobp))
5885     (message-skip-to-next-address)
5886     (let (last)
5887       (if (and (> (current-column) 78)
5888                last)
5889           (progn
5890             (save-excursion
5891               (goto-char last)
5892               (insert "\n\t"))
5893             (setq last (1+ (point))))
5894         (setq last (1+ (point)))))))
5895
5896 (defun message-fill-field-general ()
5897   (let ((begin (point))
5898         (fill-column 78)
5899         (fill-prefix " "))
5900     (while (and (search-forward "\n" nil t)
5901                 (not (eobp)))
5902       (replace-match " " t t))
5903     (fill-region-as-paragraph begin (point-max))
5904     ;; Tapdance around looong Message-IDs.
5905     (forward-line -1)
5906     (when (looking-at "[ \t]*$")
5907       (message-delete-line))
5908     (goto-char begin)
5909     (search-forward ":" nil t)
5910     (when (looking-at "\n[ \t]+")
5911       (replace-match " " t t))
5912     (goto-char (point-max))))
5913
5914 (defun message-shorten-1 (list cut surplus)
5915   "Cut SURPLUS elements out of LIST, beginning with CUTth one."
5916   (setcdr (nthcdr (- cut 2) list)
5917           (nthcdr (+ (- cut 2) surplus 1) list)))
5918
5919 (defun message-shorten-references (header references)
5920   "Trim REFERENCES to be 21 Message-ID long or less, and fold them.
5921 When sending via news, also check that the REFERENCES are less
5922 than 988 characters long, and if they are not, trim them until
5923 they are."
5924   (let ((maxcount 21)
5925         (count 0)
5926         (cut 2)
5927         refs)
5928     (with-temp-buffer
5929       (insert references)
5930       (goto-char (point-min))
5931       ;; Cons a list of valid references.
5932       (while (re-search-forward "<[^>]+>" nil t)
5933         (push (match-string 0) refs))
5934       (setq refs (nreverse refs)
5935             count (length refs)))
5936
5937     ;; If the list has more than MAXCOUNT elements, trim it by
5938     ;; removing the CUTth element and the required number of
5939     ;; elements that follow.
5940     (when (> count maxcount)
5941       (let ((surplus (- count maxcount)))
5942         (message-shorten-1 refs cut surplus)
5943         (decf count surplus)))
5944
5945     ;; When sending via news, make sure the total folded length will
5946     ;; be less than 998 characters.  This is to cater to broken INN
5947     ;; 2.3 which counts the total number of characters in a header
5948     ;; rather than the physical line length of each line, as it should.
5949     ;;
5950     ;; This hack should be removed when it's believed than INN 2.3 is
5951     ;; no longer widely used.
5952     ;;
5953     ;; At this point the headers have not been generated, thus we use
5954     ;; message-this-is-news directly.
5955     (when message-this-is-news
5956       (while (< 998
5957                 (with-temp-buffer
5958                   (message-insert-header
5959                    header (mapconcat #'identity refs " "))
5960                   (buffer-size)))
5961         (message-shorten-1 refs cut 1)))
5962     ;; Finally, collect the references back into a string and insert
5963     ;; it into the buffer.
5964     (message-insert-header header (mapconcat #'identity refs " "))))
5965
5966 (defun message-position-point ()
5967   "Move point to where the user probably wants to find it."
5968   (message-narrow-to-headers)
5969   (cond
5970    ((re-search-forward "^[^:]+:[ \t]*$" nil t)
5971     (search-backward ":" )
5972     (widen)
5973     (forward-char 1)
5974     (if (eq (char-after) ? )
5975         (forward-char 1)
5976       (insert " ")))
5977    (t
5978     (goto-char (point-max))
5979     (widen)
5980     (forward-line 1)
5981     (unless (looking-at "$")
5982       (forward-line 2)))
5983    (sit-for 0)))
5984
5985 (defcustom message-beginning-of-line t
5986   "Whether \\<message-mode-map>\\[message-beginning-of-line]\
5987  goes to beginning of header values."
5988   :version "22.1"
5989   :group 'message-buffers
5990   :link '(custom-manual "(message)Movement")
5991   :type 'boolean)
5992
5993 (defun message-beginning-of-line (&optional n)
5994   "Move point to beginning of header value or to beginning of line.
5995 The prefix argument N is passed directly to `beginning-of-line'.
5996
5997 This command is identical to `beginning-of-line' if point is
5998 outside the message header or if the option `message-beginning-of-line'
5999 is nil.
6000
6001 If point is in the message header and on a (non-continued) header
6002 line, move point to the beginning of the header value or the beginning of line,
6003 whichever is closer.  If point is already at beginning of line, move point to
6004 beginning of header value.  Therefore, repeated calls will toggle point
6005 between beginning of field and beginning of line."
6006   (interactive "p")
6007   (let ((zrs 'zmacs-region-stays))
6008     (when (and (interactive-p) (boundp zrs))
6009       (set zrs t)))
6010   (if (and message-beginning-of-line
6011            (message-point-in-header-p))
6012       (let* ((here (point))
6013              (bol (progn (beginning-of-line n) (point)))
6014              (eol (point-at-eol))
6015              (eoh (re-search-forward ": *" eol t)))
6016         (goto-char
6017          (if (and eoh (or (< eoh here) (= bol here)))
6018              eoh bol)))
6019     (beginning-of-line n)))
6020
6021 (defun message-buffer-name (type &optional to group)
6022   "Return a new (unique) buffer name based on TYPE and TO."
6023   (cond
6024    ;; Generate a new buffer name The Message Way.
6025    ((eq message-generate-new-buffers 'unique)
6026     (generate-new-buffer-name
6027      (concat "*" type
6028              (if to
6029                  (concat " to "
6030                          (or (car (std11-extract-address-components to))
6031                              to) "")
6032                "")
6033              (if (and group (not (string= group ""))) (concat " on " group) "")
6034              "*")))
6035    ;; Check whether `message-generate-new-buffers' is a function,
6036    ;; and if so, call it.
6037    ((functionp message-generate-new-buffers)
6038     (funcall message-generate-new-buffers type to group))
6039    ((eq message-generate-new-buffers 'unsent)
6040     (generate-new-buffer-name
6041      (concat "*unsent " type
6042              (if to
6043                  (concat " to "
6044                          (or (car (std11-extract-address-components to))
6045                              to) "")
6046                "")
6047              (if (and group (not (string= group ""))) (concat " on " group) "")
6048              "*")))
6049    ;; Use standard name.
6050    (t
6051     (format "*%s message*" type))))
6052
6053 (defmacro message-pop-to-buffer-1 (buffer)
6054   `(if pop-up-frames
6055        (let (special-display-buffer-names
6056              special-display-regexps
6057              same-window-buffer-names
6058              same-window-regexps)
6059          (pop-to-buffer ,buffer))
6060      (pop-to-buffer ,buffer)))
6061
6062 (defun message-pop-to-buffer (name)
6063   "Pop to buffer NAME, and warn if it already exists and is modified."
6064   (let ((buffer (get-buffer name))
6065         (pop-up-frames (and (static-if (featurep 'xemacs)
6066                                 (device-on-window-system-p)
6067                               window-system)
6068                             message-use-multi-frames)))
6069     (if (and buffer
6070              (buffer-name buffer))
6071         (progn
6072           (message-pop-to-buffer-1 buffer)
6073           (when (and (buffer-modified-p)
6074                      (not (y-or-n-p
6075                            "Message already being composed; erase? ")))
6076             (error "Message being composed")))
6077       (message-pop-to-buffer-1 name))
6078     (erase-buffer)
6079     (message-mode)
6080     (when pop-up-frames
6081       (set (make-local-variable 'message-original-frame) (selected-frame)))))
6082
6083 (defun message-do-send-housekeeping ()
6084   "Kill old message buffers."
6085   ;; We might have sent this buffer already.  Delete it from the
6086   ;; list of buffers.
6087   (setq message-buffer-list (delq (current-buffer) message-buffer-list))
6088   (while (and message-max-buffers
6089               message-buffer-list
6090               (>= (length message-buffer-list) message-max-buffers))
6091     ;; Kill the oldest buffer -- unless it has been changed.
6092     (let ((buffer (pop message-buffer-list)))
6093       (when (and (buffer-name buffer)
6094                  (not (buffer-modified-p buffer)))
6095         (kill-buffer buffer))))
6096   ;; Rename the buffer.
6097   (if message-send-rename-function
6098       (funcall message-send-rename-function)
6099     ;; Note: mail-abbrevs of XEmacs renames buffer name behind Gnus.
6100     (when (string-match
6101            "\\`\\*\\(sent \\|unsent \\)?\\(.+\\)\\*[^\\*]*\\|\\`mail to "
6102            (buffer-name))
6103       (let ((name (match-string 2 (buffer-name)))
6104             to group)
6105         (if (not (or (null name)
6106                      (string-equal name "mail")
6107                      (string-equal name "posting")))
6108             (setq name (concat "*sent " name "*"))
6109           (message-narrow-to-headers)
6110           (setq to (message-fetch-field "to"))
6111           (setq group (message-fetch-field "newsgroups"))
6112           (widen)
6113           (setq name
6114                 (cond
6115                  (to (concat "*sent mail to "
6116                              (or (car (std11-extract-address-components to))
6117                                  to) "*"))
6118                  ((and group (not (string= group "")))
6119                   (concat "*sent posting on " group "*"))
6120                  (t "*sent mail*"))))
6121         (unless (string-equal name (buffer-name))
6122           (rename-buffer name t)))))
6123   ;; Push the current buffer onto the list.
6124   (when message-max-buffers
6125     (setq message-buffer-list
6126           (nconc message-buffer-list (list (current-buffer))))))
6127
6128 (defun message-mail-user-agent ()
6129   (let ((mua (cond
6130               ((not message-mail-user-agent) nil)
6131               ((eq message-mail-user-agent t) mail-user-agent)
6132               (t message-mail-user-agent))))
6133     (if (memq mua '(message-user-agent gnus-user-agent))
6134         nil
6135       mua)))
6136
6137 (defun message-setup (headers &optional replybuffer actions switch-function)
6138   (let ((mua (message-mail-user-agent))
6139         subject to field yank-action)
6140     (if (not (and message-this-is-mail mua))
6141         (message-setup-1 headers replybuffer actions)
6142       (if replybuffer
6143           (setq yank-action (list 'insert-buffer replybuffer)))
6144       (setq headers (copy-sequence headers))
6145       (setq field (assq 'Subject headers))
6146       (when field
6147         (setq subject (cdr field))
6148         (setq headers (delq field headers)))
6149       (setq field (assq 'To headers))
6150       (when field
6151         (setq to (cdr field))
6152         (setq headers (delq field headers)))
6153       (let ((mail-user-agent mua))
6154         (compose-mail to subject
6155                       (mapcar (lambda (item)
6156                                 (cons
6157                                  (format "%s" (car item))
6158                                  (cdr item)))
6159                               headers)
6160                       nil switch-function yank-action actions)))))
6161
6162 (defun message-headers-to-generate (headers included-headers excluded-headers)
6163   "Return a list that includes all headers from HEADERS.
6164 If INCLUDED-HEADERS is a list, just include those headers.  If if is
6165 t, include all headers.  In any case, headers from EXCLUDED-HEADERS
6166 are not included."
6167   (let ((result nil)
6168         header-name)
6169     (dolist (header headers)
6170       (setq header-name (cond
6171                          ((and (consp header)
6172                                (eq (car header) 'optional))
6173                           ;; On the form (optional . Header)
6174                           (cdr header))
6175                          ((consp header)
6176                           ;; On the form (Header . function)
6177                           (car header))
6178                          (t
6179                           ;; Just a Header.
6180                           header)))
6181       (when (and (not (memq header-name excluded-headers))
6182                  (or (eq included-headers t)
6183                      (memq header-name included-headers)))
6184         (push header result)))
6185     (nreverse result)))
6186
6187 (defun message-setup-1 (headers &optional replybuffer actions)
6188   (dolist (action actions)
6189     (condition-case nil
6190         (add-to-list 'message-send-actions
6191                      `(apply ',(car action) ',(cdr action)))))
6192   (setq message-reply-buffer
6193         (or (message-get-parameter 'reply-buffer)
6194             replybuffer))
6195   (goto-char (point-min))
6196   ;; Insert all the headers.
6197   (mail-header-format
6198    (let ((h headers)
6199          (alist message-header-format-alist))
6200      (while h
6201        (unless (assq (caar h) message-header-format-alist)
6202          (push (list (caar h)) alist))
6203        (pop h))
6204      alist)
6205    headers)
6206   (delete-region (point) (progn (forward-line -1) (point)))
6207   (when message-default-headers
6208     (insert message-default-headers)
6209     (or (bolp) (insert ?\n)))
6210   (insert mail-header-separator "\n")
6211   (forward-line -1)
6212   (when (message-news-p)
6213     (when message-default-news-headers
6214       (insert message-default-news-headers)
6215       (or (bolp) (insert ?\n)))
6216     (when message-generate-headers-first
6217       (message-generate-headers
6218        (message-headers-to-generate
6219         (append message-required-news-headers
6220                 message-required-headers)
6221         message-generate-headers-first
6222         '(Lines Subject)))))
6223   (when (message-mail-p)
6224     (when message-default-mail-headers
6225       (insert message-default-mail-headers)
6226       (or (bolp) (insert ?\n)))
6227     (save-restriction
6228       (message-narrow-to-headers)
6229       (if (and replybuffer
6230                message-alternative-emails)
6231           (message-use-alternative-email-as-from)))
6232     (when message-generate-headers-first
6233       (message-generate-headers
6234        (message-headers-to-generate
6235         (append message-required-mail-headers
6236                 message-required-headers)
6237         message-generate-headers-first
6238         '(Lines Subject)))))
6239   (run-hooks 'message-signature-setup-hook)
6240   (message-insert-signature)
6241   (save-restriction
6242     (message-narrow-to-headers)
6243     (run-hooks 'message-header-setup-hook))
6244   (set-buffer-modified-p nil)
6245   (setq buffer-undo-list nil)
6246   (when message-generate-hashcash
6247     ;; Generate hashcash headers for recipients already known
6248     (mail-add-payment-async))
6249   (run-hooks 'message-setup-hook)
6250   (message-position-point)
6251   (undo-boundary))
6252
6253 (defun message-set-auto-save-file-name ()
6254   "Associate the message buffer with a file in the drafts directory."
6255   (when message-auto-save-directory
6256     (unless (file-directory-p
6257              (directory-file-name message-auto-save-directory))
6258       (make-directory message-auto-save-directory t))
6259     (if (gnus-alive-p)
6260         (setq message-draft-article
6261               (nndraft-request-associate-buffer "drafts"))
6262       (setq buffer-file-name (expand-file-name
6263                               (if (memq system-type
6264                                         '(ms-dos ms-windows windows-nt
6265                                                  cygwin cygwin32 win32 w32
6266                                                  mswindows))
6267                                   "message"
6268                                 "*message*")
6269                               message-auto-save-directory))
6270       (setq buffer-auto-save-file-name (make-auto-save-file-name)))
6271     (clear-visited-file-modtime)
6272     (setq buffer-file-coding-system message-draft-coding-system)))
6273
6274 (defun message-disassociate-draft ()
6275   "Disassociate the message buffer from the drafts directory."
6276   (when message-draft-article
6277     (nndraft-request-expire-articles
6278      (list message-draft-article) "drafts" nil t)))
6279
6280 (defun message-insert-headers ()
6281   "Generate the headers for the article."
6282   (interactive)
6283   (save-excursion
6284     (save-restriction
6285       (message-narrow-to-headers)
6286       (when (message-news-p)
6287         (message-generate-headers
6288          (delq 'Lines
6289                (delq 'Subject
6290                      (copy-sequence message-required-news-headers)))))
6291       (when (message-mail-p)
6292         (message-generate-headers
6293          (delq 'Lines
6294                (delq 'Subject
6295                      (copy-sequence message-required-mail-headers))))))))
6296
6297 \f
6298
6299 ;;;
6300 ;;; Commands for interfacing with message
6301 ;;;
6302
6303 ;;;###autoload
6304 (defun message-mail (&optional to subject
6305                                other-headers continue switch-function
6306                                yank-action send-actions)
6307   "Start editing a mail message to be sent.
6308 OTHER-HEADERS is an alist of header/value pairs."
6309   (interactive)
6310   (let ((message-this-is-mail t) replybuffer)
6311     (unless (message-mail-user-agent)
6312       (message-pop-to-buffer (message-buffer-name "mail" to)))
6313     ;; FIXME: message-mail should do something if YANK-ACTION is not
6314     ;; insert-buffer.
6315     (and (consp yank-action) (eq (car yank-action) 'insert-buffer)
6316          (setq replybuffer (nth 1 yank-action)))
6317     (message-setup
6318      (nconc
6319       `((To . ,(or to "")) (Subject . ,(or subject "")))
6320       (when other-headers other-headers))
6321      replybuffer send-actions)
6322     ;; FIXME: Should return nil if failure.
6323     t))
6324
6325 ;;;###autoload
6326 (defun message-news (&optional newsgroups subject)
6327   "Start editing a news article to be sent."
6328   (interactive)
6329   (let ((message-this-is-news t))
6330     (message-pop-to-buffer (message-buffer-name "posting" nil newsgroups))
6331     (message-setup `((Newsgroups . ,(or newsgroups ""))
6332                      (Subject . ,(or subject ""))))))
6333
6334 (defun message-get-reply-headers (wide &optional to-address address-headers)
6335   (let (follow-to mct never-mct to cc author mft recipients extra)
6336     ;; Find all relevant headers we need.
6337     (save-restriction
6338       (message-narrow-to-headers-or-head)
6339       (let ((mrt (when message-use-mail-reply-to
6340                    (message-fetch-field "mail-reply-to")))
6341             (reply-to (message-fetch-field "reply-to")))
6342         ;; Gmane renames "To".  Look at "Original-To", too, if it is present in
6343         ;; message-header-synonyms.
6344         (setq to (or (message-fetch-field "to")
6345                      (and (loop for synonym in message-header-synonyms
6346                                 when (memq 'Original-To synonym)
6347                                 return t)
6348                           (message-fetch-field "original-to")))
6349               cc (message-fetch-field "cc")
6350               extra (when message-extra-wide-headers
6351                       (mapconcat 'identity
6352                                  (mapcar 'message-fetch-field
6353                                          message-extra-wide-headers)
6354                                  ", "))
6355               mct (when message-use-mail-copies-to
6356                     (message-fetch-field "mail-copies-to"))
6357               author (or mrt
6358                          reply-to
6359                          (message-fetch-field "from")
6360                          "")
6361               mft (when (and (not (or to-address mrt reply-to))
6362                              message-use-mail-followup-to)
6363                     (message-fetch-field "mail-followup-to")))))
6364
6365     (save-match-data
6366       ;; Handle special values of Mail-Copies-To.
6367       (when mct
6368         (cond ((or (equal (downcase mct) "never")
6369                    (equal (downcase mct) "nobody"))
6370                (when (or (not (eq message-use-mail-copies-to 'ask))
6371                          (message-y-or-n-p
6372                           (concat "Obey Mail-Copies-To: never? ") t "\
6373 You should normally obey the Mail-Copies-To: header.
6374
6375         `Mail-Copies-To: " mct "'
6376 directs you not to send your response to the author."))
6377                  (setq never-mct t))
6378                (setq mct nil))
6379               ((or (equal (downcase mct) "always")
6380                    (equal (downcase mct) "poster"))
6381                (if (or (not (eq message-use-mail-copies-to 'ask))
6382                        (message-y-or-n-p
6383                         (concat "Obey Mail-Copies-To: always? ") t "\
6384 You should normally obey the Mail-Copies-To: header.
6385
6386         `Mail-Copies-To: " mct "'
6387 sends a copy of your response to the author."))
6388                    (setq mct author)
6389                  (setq mct nil)))
6390               ((and (eq message-use-mail-copies-to 'ask)
6391                     (not (message-y-or-n-p
6392                           (concat "Obey Mail-Copies-To: " mct " ? ") t "\
6393 You should normally obey the Mail-Copies-To: header.
6394
6395         `Mail-Copies-To: " mct "'
6396 sends a copy of your response to " (if (string-match "," mct)
6397                                        "the specified addresses"
6398                                      "that address") ".")))
6399                (setq mct nil))))
6400
6401       ;; Build (textual) list of new recipient addresses.
6402       (cond
6403        ((not wide)
6404         (setq recipients (concat ", " author)))
6405        (address-headers
6406         (dolist (header address-headers)
6407           (let ((value (message-fetch-field header)))
6408             (when value
6409               (setq recipients (concat recipients ", " value))))))
6410        ((and mft
6411              (string-match "[^ \t,]" mft)
6412              (or (not (eq message-use-mail-followup-to 'ask))
6413                  (message-y-or-n-p "Obey Mail-Followup-To? " t "\
6414 You should normally obey the Mail-Followup-To: header.  In this
6415 article, it has the value of
6416
6417 " mft "
6418
6419 which directs your response to " (if (string-match "," mft)
6420                                      "the specified addresses"
6421                                    "that address only") ".
6422
6423 Most commonly, Mail-Followup-To is used by a mailing list poster to
6424 express that responses should be sent to just the list, and not the
6425 poster as well.
6426
6427 If a message is posted to several mailing lists, Mail-Followup-To may
6428 also be used to direct the following discussion to one list only,
6429 because discussions that are spread over several lists tend to be
6430 fragmented and very difficult to follow.
6431
6432 Also, some source/announcement lists are not intended for discussion;
6433 responses here are directed to other addresses.
6434
6435 You may customize the variable `message-use-mail-followup-to', if you
6436 want to get rid of this query permanently.")))
6437         (setq recipients (concat ", " mft)))
6438        (to-address
6439         (setq recipients (concat ", " to-address))
6440         ;; If the author explicitly asked for a copy, we don't deny it to them.
6441         (if mct (setq recipients (concat recipients ", " mct))))
6442        (t
6443         (setq recipients (if never-mct "" (concat ", " author)))
6444         (if to (setq recipients (concat recipients ", " to)))
6445         (if cc (setq recipients (concat recipients ", " cc)))
6446         (if extra (setq recipients (concat recipients ", " extra)))
6447         (if mct (setq recipients (concat recipients ", " mct)))))
6448       (if (>= (length recipients) 2)
6449           ;; Strip the leading ", ".
6450           (setq recipients (substring recipients 2)))
6451       ;; Squeeze whitespace.
6452       (while (string-match "[ \t][ \t]+" recipients)
6453         (setq recipients (replace-match " " t t recipients)))
6454       ;; Remove addresses that match `rmail-dont-reply-to-names'.
6455       (let ((rmail-dont-reply-to-names message-dont-reply-to-names))
6456         (setq recipients (rmail-dont-reply-to recipients)))
6457       ;; Perhaps "Mail-Copies-To: never" removed the only address?
6458       (if (string-equal recipients "")
6459           (setq recipients author))
6460       ;; Convert string to a list of (("foo@bar" . "Name <Foo@BAR>") ...).
6461       (setq recipients
6462             (mapcar
6463              (lambda (addr)
6464                (cons (downcase (mail-strip-quoted-names addr)) addr))
6465              (message-tokenize-header recipients)))
6466       ;; Remove first duplicates.  (Why not all duplicates?  Is this a bug?)
6467       (let ((s recipients))
6468         (while s
6469           (setq recipients (delq (assoc (car (pop s)) s) recipients))))
6470
6471       ;; Remove hierarchical lists that are contained within each other,
6472       ;; if message-hierarchical-addresses is defined.
6473       (when message-hierarchical-addresses
6474         (let ((plain-addrs (mapcar 'car recipients))
6475               subaddrs recip)
6476           (while plain-addrs
6477             (setq subaddrs (assoc (car plain-addrs)
6478                                   message-hierarchical-addresses)
6479                   plain-addrs (cdr plain-addrs))
6480             (when subaddrs
6481               (setq subaddrs (cdr subaddrs))
6482               (while subaddrs
6483                 (setq recip (assoc (car subaddrs) recipients)
6484                       subaddrs (cdr subaddrs))
6485                 (if recip
6486                     (setq recipients (delq recip recipients))))))))
6487
6488       ;; Build the header alist.  Allow the user to be asked whether
6489       ;; or not to reply to all recipients in a wide reply.
6490       (setq follow-to (list (cons 'To (cdr (pop recipients)))))
6491       (when (and recipients
6492                  (or (not message-wide-reply-confirm-recipients)
6493                      (y-or-n-p "Reply to all recipients? ")))
6494         (setq recipients (mapconcat
6495                           (lambda (addr) (cdr addr)) recipients ", "))
6496         (if (string-match "^ +" recipients)
6497             (setq recipients (substring recipients (match-end 0))))
6498         (push (cons 'Cc recipients) follow-to)))
6499     follow-to))
6500
6501 ;;;###autoload
6502 (defun message-reply (&optional to-address wide)
6503   "Start editing a reply to the article in the current buffer."
6504   (interactive)
6505   (require 'gnus-sum)                   ; for gnus-list-identifiers
6506   (let ((cur (current-buffer))
6507         from subject date
6508         references message-id follow-to
6509         (inhibit-point-motion-hooks t)
6510         (message-this-is-mail t)
6511         gnus-warning in-reply-to)
6512     (save-restriction
6513       (message-narrow-to-head-1)
6514       ;; Allow customizations to have their say.
6515       (if (not wide)
6516           ;; This is a regular reply.
6517           (when (functionp message-reply-to-function)
6518             (save-excursion
6519               (setq follow-to (funcall message-reply-to-function))))
6520         ;; This is a followup.
6521         (when (functionp message-wide-reply-to-function)
6522           (save-excursion
6523             (setq follow-to
6524                   (funcall message-wide-reply-to-function)))))
6525       (setq message-id (message-fetch-field "message-id" t)
6526             references (message-fetch-field "references")
6527             date (message-fetch-field "date")
6528             from (message-fetch-field "from")
6529             subject (or (message-fetch-field "subject") "none"))
6530       (when gnus-list-identifiers
6531         (setq subject (message-strip-list-identifiers subject)))
6532       (setq subject (message-make-followup-subject subject))
6533       (when message-subject-trailing-was-query
6534         (setq subject (message-strip-subject-trailing-was subject)))
6535
6536       (when (and (setq gnus-warning (message-fetch-field "gnus-warning"))
6537                  (string-match "<[^>]+>" gnus-warning))
6538         (setq message-id (match-string 0 gnus-warning)))
6539
6540       (unless follow-to
6541         (setq follow-to (message-get-reply-headers wide to-address)))
6542
6543       ;; Get the references from "In-Reply-To" field if there were
6544       ;; no references and "In-Reply-To" field looks promising.
6545       (unless references
6546         (when (and (setq in-reply-to (message-fetch-field "in-reply-to"))
6547                    (string-match "<[^>]+>" in-reply-to))
6548           (setq references (match-string 0 in-reply-to)))))
6549
6550     (unless (message-mail-user-agent)
6551       (message-pop-to-buffer
6552        (message-buffer-name
6553         (if wide "wide reply" "reply") from
6554         (if wide to-address nil))))
6555
6556     (setq message-reply-headers
6557           (make-full-mail-header-from-decoded-header
6558            0 subject from date message-id references 0 0 ""))
6559
6560     (message-setup
6561      `((Subject . ,subject)
6562        ,@follow-to)
6563      cur)))
6564
6565 ;;;###autoload
6566 (defun message-wide-reply (&optional to-address)
6567   "Make a \"wide\" reply to the message in the current buffer."
6568   (interactive)
6569   (message-reply to-address t))
6570
6571 ;;;###autoload
6572 (defun message-followup (&optional to-newsgroups)
6573   "Follow up to the message in the current buffer.
6574 If TO-NEWSGROUPS, use that as the new Newsgroups line."
6575   (interactive)
6576   (require 'gnus-sum)                   ; for gnus-list-identifiers
6577   (let ((cur (current-buffer))
6578         from subject date reply-to mrt mct mft
6579         references message-id follow-to
6580         (inhibit-point-motion-hooks t)
6581         (message-this-is-news t)
6582         followup-to distribution newsgroups gnus-warning posted-to)
6583     (save-restriction
6584       (message-narrow-to-head)
6585       (when (functionp message-followup-to-function)
6586         (setq follow-to
6587               (funcall message-followup-to-function)))
6588       (setq from (message-fetch-field "from")
6589             date (message-fetch-field "date")
6590             subject (or (message-fetch-field "subject") "none")
6591             references (message-fetch-field "references")
6592             message-id (message-fetch-field "message-id" t)
6593             followup-to (message-fetch-field "followup-to")
6594             newsgroups (message-fetch-field "newsgroups")
6595             posted-to (message-fetch-field "posted-to")
6596             reply-to (message-fetch-field "reply-to")
6597             mrt (when message-use-mail-reply-to
6598                   (message-fetch-field "mail-reply-to"))
6599             distribution (message-fetch-field "distribution")
6600             mct (when message-use-mail-copies-to
6601                   (message-fetch-field "mail-copies-to"))
6602             mft (when message-use-mail-followup-to
6603                   (message-fetch-field "mail-followup-to")))
6604       (when (and (setq gnus-warning (message-fetch-field "gnus-warning"))
6605                  (string-match "<[^>]+>" gnus-warning))
6606         (setq message-id (match-string 0 gnus-warning)))
6607       ;; Remove bogus distribution.
6608       (when (and (stringp distribution)
6609                  (let ((case-fold-search t))
6610                    (string-match "world" distribution)))
6611         (setq distribution nil))
6612       (if gnus-list-identifiers
6613           (setq subject (message-strip-list-identifiers subject)))
6614       (setq subject (message-make-followup-subject subject))
6615       (when message-subject-trailing-was-query
6616         (setq subject (message-strip-subject-trailing-was subject)))
6617       (widen))
6618
6619     ;; Handle special values of Mail-Copies-To.
6620     (when mct
6621       (cond
6622        ((and (or (equal (downcase mct) "never")
6623                  (equal (downcase mct) "nobody")))
6624         (setq mct nil))
6625        ((and (or (equal (downcase mct) "always")
6626                  (equal (downcase mct) "poster")))
6627         (if (or (not (eq message-use-mail-copies-to 'ask))
6628                 (message-y-or-n-p
6629                  (concat "Obey Mail-Copies-To: always? ") t "\
6630 You should normally obey the Mail-Copies-To: header.
6631
6632         `Mail-Copies-To: " mct "'
6633 sends a copy of your response to the author."))
6634             (setq mct (or mrt reply-to from))
6635           (setq mct nil)))
6636        ((and (eq message-use-mail-copies-to 'ask)
6637              (not
6638               (message-y-or-n-p
6639                (concat "Obey Mail-Copies-To: " mct " ? ") t "\
6640 You should normally obey the Mail-Copies-To: header.
6641
6642         `Mail-Copies-To: " mct "'
6643 sends a copy of your response to " (if (string-match "," mct)
6644                                        "the specified addresses"
6645                                      "that address") ".")))
6646         (setq mct nil))))
6647
6648     (unless follow-to
6649       (cond
6650        (to-newsgroups (setq follow-to (list (cons 'Newsgroups to-newsgroups))))
6651        ;; Handle Followup-To.
6652        (followup-to
6653         (cond
6654          ((equal (downcase followup-to) "poster")
6655           (if (or (and followup-to (eq message-use-followup-to 'use))
6656                   (message-y-or-n-p "Obey Followup-To: poster? " t "\
6657 You should normally obey the Followup-To: header.
6658
6659         `Followup-To: poster'
6660 sends your response via e-mail instead of news.
6661
6662 A typical situation where `Followup-To: poster' is used is when the poster
6663 does not read the newsgroup, so he wouldn't see any replies sent to it.
6664
6665 You may customize the variable `message-use-followup-to', if you
6666 want to get rid of this query permanently."))
6667               (setq message-this-is-news nil
6668                     distribution nil
6669                     follow-to (list (cons 'To (or mrt reply-to from ""))))
6670             (setq follow-to (list (cons 'Newsgroups newsgroups)))))
6671          (t
6672           (if (or (equal followup-to newsgroups)
6673                   (not (and followup-to (eq message-use-followup-to 'ask)))
6674                   (message-y-or-n-p
6675                    (concat "Obey Followup-To: " followup-to "? ") t "\
6676 You should normally obey the Followup-To: header.
6677
6678         `Followup-To: " followup-to "'
6679 directs your response to " (if (string-match "," followup-to)
6680                                "the specified newsgroups"
6681                              "that newsgroup only") ".
6682
6683 If a message is posted to several newsgroups, Followup-To is often
6684 used to direct the following discussion to one newsgroup only,
6685 because discussions that are spread over several newsgroup tend to
6686 be fragmented and very difficult to follow.
6687
6688 Also, some source/announcement newsgroups are not intended for discussion;
6689 responses here are directed to other newsgroups.
6690
6691 You may customize the variable `message-use-followup-to', if you
6692 want to get rid of this query permanently."))
6693               (setq follow-to (list (cons 'Newsgroups followup-to)))
6694             (setq follow-to (list (cons 'Newsgroups newsgroups)))))))
6695        ;; Handle Mail-Followup-To, followup via e-mail.
6696        ((and mft
6697              (or (not (eq message-use-mail-followup-to 'ask))
6698                  (message-y-or-n-p
6699                   (concat "Obey Mail-Followup-To: " mft "? ") t "\
6700 You should normally obey the Mail-Followup-To: header.
6701
6702         `Mail-Followup-To: " mft "'
6703 directs your response to " (if (string-match "," mft)
6704                                "the specified addresses"
6705                              "that address only") " instead of news.
6706
6707 A typical situation where Mail-Followup-To is used is when the author thinks
6708 that further discussion should take place only in "
6709                              (if (string-match "," mft)
6710                                  "the specified mailing lists"
6711                                "that mailing list") ".")))
6712         (setq message-this-is-news nil
6713               distribution nil
6714               follow-to (list (cons 'To mft))))
6715        (posted-to (setq follow-to (list (cons 'Newsgroups posted-to))))
6716        (t
6717         (setq follow-to (list (cons 'Newsgroups newsgroups))))))
6718
6719     (message-pop-to-buffer (message-buffer-name "followup" from newsgroups))
6720
6721     (setq message-reply-headers
6722           (make-full-mail-header-from-decoded-header
6723            0 subject from date message-id references 0 0 ""))
6724
6725     (message-setup
6726      `((Subject . ,subject)
6727        ,@follow-to
6728        ,@(and mct (list (cons 'Cc mct)))
6729        ,@(and distribution (list (cons 'Distribution distribution))))
6730      cur)))
6731
6732 (defun message-is-yours-p ()
6733   "Non-nil means current article is yours.
6734 If you have added 'cancel-messages to `message-shoot-gnksa-feet', all articles
6735 are yours except those that have Cancel-Lock header not belonging to you.
6736 Instead of shooting GNKSA feet, you should modify `message-alternative-emails'
6737 regexp to match all of yours addresses."
6738   ;; Canlock-logic as suggested by Per Abrahamsen
6739   ;; <abraham@dina.kvl.dk>
6740   ;;
6741   ;; IF article has cancel-lock THEN
6742   ;;   IF we can verify it THEN
6743   ;;     issue cancel
6744   ;;   ELSE
6745   ;;     error: cancellock: article is not yours
6746   ;; ELSE
6747   ;;   Use old rules, comparing sender...
6748   (save-excursion
6749     (save-restriction
6750       (message-narrow-to-head-1)
6751       (if (message-fetch-field "Cancel-Lock")
6752           (if (null (canlock-verify))
6753               t
6754             (error "Failed to verify Cancel-lock: This article is not yours"))
6755         (let (sender from)
6756           (or
6757            (message-gnksa-enable-p 'cancel-messages)
6758            (and (setq sender (message-fetch-field "sender"))
6759                 (string-equal (downcase sender)
6760                               (downcase (message-make-sender))))
6761            ;; Email address in From field equals to our address
6762            (and (setq from (message-fetch-field "from"))
6763                 (string-equal
6764                  (downcase (cadr (std11-extract-address-components from)))
6765                  (downcase (cadr (std11-extract-address-components
6766                                   (message-make-from))))))
6767            ;; Email address in From field matches
6768            ;; 'message-alternative-emails' regexp
6769            (and from
6770                 message-alternative-emails
6771                 (string-match
6772                  message-alternative-emails
6773                  (cadr (std11-extract-address-components from))))))))))
6774
6775 ;;;###autoload
6776 (defun message-cancel-news (&optional arg)
6777   "Cancel an article you posted.
6778 If ARG, allow editing of the cancellation message."
6779   (interactive "P")
6780   (unless (message-news-p)
6781     (error "This is not a news article; canceling is impossible"))
6782   (let (from newsgroups message-id distribution buf)
6783     (save-excursion
6784       ;; Get header info from original article.
6785       (save-restriction
6786         (message-narrow-to-head-1)
6787         (setq from (message-fetch-field "from")
6788               newsgroups (message-fetch-field "newsgroups")
6789               message-id (message-fetch-field "message-id" t)
6790               distribution (message-fetch-field "distribution")))
6791       ;; Make sure that this article was written by the user.
6792       (unless (message-is-yours-p)
6793         (error "This article is not yours"))
6794       (when (yes-or-no-p "Do you really want to cancel this article? ")
6795         ;; Make control message.
6796         (if arg
6797             (message-news)
6798           (setq buf (set-buffer (get-buffer-create " *message cancel*"))))
6799         (erase-buffer)
6800         (insert "Newsgroups: " newsgroups "\n"
6801                 "From: " from "\n"
6802                 "Subject: cmsg cancel " message-id "\n"
6803                 "Control: cancel " message-id "\n"
6804                 (if distribution
6805                     (concat "Distribution: " distribution "\n")
6806                   "")
6807                 mail-header-separator "\n"
6808                 message-cancel-message)
6809         (run-hooks 'message-cancel-hook)
6810         (unless arg
6811           (message "Canceling your article...")
6812           (if (let ((message-syntax-checks
6813                      'dont-check-for-anything-just-trust-me)
6814                     (message-encoding-buffer (current-buffer))
6815                     (message-edit-buffer (current-buffer)))
6816                 (message-send-news))
6817               (message "Canceling your article...done"))
6818           (kill-buffer buf))))))
6819
6820 (defun message-supersede-setup-for-mime-edit ()
6821   (set (make-local-variable 'message-setup-hook) nil)
6822   (mime-edit-again))
6823
6824 ;;;###autoload
6825 (defun message-supersede ()
6826   "Start composing a message to supersede the current message.
6827 This is done simply by taking the old article and adding a Supersedes
6828 header line with the old Message-ID."
6829   (interactive)
6830   (let ((cur (current-buffer)))
6831     ;; Check whether the user owns the article that is to be superseded.
6832     (unless (message-is-yours-p)
6833       (error "This article is not yours"))
6834     ;; Get a normal message buffer.
6835     (message-pop-to-buffer (message-buffer-name "supersede"))
6836     (insert-buffer-substring cur)
6837     (message-narrow-to-head-1)
6838     ;; Remove unwanted headers.
6839     (when message-ignored-supersedes-headers
6840       (message-remove-header message-ignored-supersedes-headers t))
6841     (goto-char (point-min))
6842     (if (not (re-search-forward "^Message-ID: " nil t))
6843         (error "No Message-ID in this article")
6844       (replace-match "Supersedes: " t t))
6845     (goto-char (point-max))
6846     (insert mail-header-separator)
6847     (widen)
6848     (when message-supersede-setup-function
6849       (funcall message-supersede-setup-function))
6850     (run-hooks 'message-supersede-setup-hook)
6851     (goto-char (point-min))
6852     (search-forward (concat "\n" mail-header-separator "\n") nil t)))
6853
6854 ;;;###autoload
6855 (defun message-recover ()
6856   "Reread contents of current buffer from its last auto-save file."
6857   (interactive)
6858   (let ((file-name (make-auto-save-file-name)))
6859     (cond ((save-window-excursion
6860              (if (not (eq system-type 'vax-vms))
6861                  (with-output-to-temp-buffer "*Directory*"
6862                    (with-current-buffer standard-output
6863                      (fundamental-mode)) ; for Emacs 20.4+
6864                    (buffer-disable-undo standard-output)
6865                    (let ((default-directory "/"))
6866                      (call-process
6867                       "ls" nil standard-output nil "-l" file-name))))
6868              (yes-or-no-p (format "Recover auto save file %s? " file-name)))
6869            (let ((buffer-read-only nil))
6870              (erase-buffer)
6871              (insert-file-contents file-name nil)))
6872           (t (error "message-recover cancelled")))))
6873
6874 ;;; Washing Subject:
6875
6876 (defun message-wash-subject (subject)
6877   "Remove junk like \"Re:\", \"(fwd)\", etc. added to subject string SUBJECT.
6878 Previous forwarders, replyers, etc. may add it."
6879   (with-temp-buffer
6880     (insert subject)
6881     (goto-char (point-min))
6882     ;; strip Re/Fwd stuff off the beginning
6883     (while (re-search-forward
6884             "\\([Rr][Ee]:\\|[Ff][Ww][Dd]\\(\\[[0-9]*\\]\\)?:\\|[Ff][Ww]:\\)" nil t)
6885       (replace-match ""))
6886
6887     ;; and gnus-style forwards [foo@bar.com] subject
6888     (goto-char (point-min))
6889     (while (re-search-forward "\\[[^ \t]*\\(@\\|\\.\\)[^ \t]*\\]" nil t)
6890       (replace-match ""))
6891
6892     ;; and off the end
6893     (goto-char (point-max))
6894     (while (re-search-backward "([Ff][Ww][Dd])" nil t)
6895       (replace-match ""))
6896
6897     ;; and finally, any whitespace that was left-over
6898     (goto-char (point-min))
6899     (while (re-search-forward "^[ \t]+" nil t)
6900       (replace-match ""))
6901     (goto-char (point-max))
6902     (while (re-search-backward "[ \t]+$" nil t)
6903       (replace-match ""))
6904
6905     (buffer-string)))
6906
6907 ;;; Forwarding messages.
6908
6909 (defvar message-forward-decoded-p nil
6910   "Non-nil means the original message is decoded.")
6911
6912 (defun message-forward-subject-name-subject (subject)
6913   "Generate a SUBJECT for a forwarded message.
6914 The form is: [Source] Subject, where if the original message was mail,
6915 Source is the name of the sender, and if the original message was
6916 news, Source is the list of newsgroups is was posted to."
6917   (concat "["
6918           (let ((group (message-fetch-field "newsgroups"))
6919                 from)
6920             (if group
6921                 (gnus-group-decoded-name group)
6922               (or (and (setq from (message-fetch-field "from"))
6923                        (car (std11-extract-address-components
6924                              (nnheader-decode-from from))))
6925                   "(nowhere)")))
6926           "] " subject))
6927
6928 (defun message-forward-subject-author-subject (subject)
6929   "Generate a SUBJECT for a forwarded message.
6930 The form is: [Source] Subject, where if the original message was mail,
6931 Source is the sender, and if the original message was news, Source is
6932 the list of newsgroups is was posted to."
6933   (concat "["
6934           (let ((group (message-fetch-field "newsgroups"))
6935                 from)
6936             (if group
6937                 (gnus-group-decoded-name group)
6938               (if (setq from (message-fetch-field "from"))
6939                   (nnheader-decode-from from)
6940                 "(nowhere)")))
6941           "] " subject))
6942
6943 (defun message-forward-subject-fwd (subject)
6944   "Generate a SUBJECT for a forwarded message.
6945 The form is: Fwd: Subject, where Subject is the original subject of
6946 the message."
6947   (if (string-match "^Fwd: " subject)
6948       subject
6949     (concat "Fwd: " subject)))
6950
6951 (defun message-make-forward-subject ()
6952   "Return a Subject header suitable for the message in the current buffer."
6953   (save-excursion
6954     (save-restriction
6955       (message-narrow-to-head-1)
6956       (let ((funcs message-make-forward-subject-function)
6957             (subject (message-fetch-field "Subject")))
6958         (setq subject
6959               (if subject
6960                   (if message-forward-decoded-p
6961                       subject
6962                     (nnheader-decode-subject subject))
6963                 ""))
6964         (when message-wash-forwarded-subjects
6965           (setq subject (message-wash-subject subject)))
6966         ;; Make sure funcs is a list.
6967         (and funcs
6968              (not (listp funcs))
6969              (setq funcs (list funcs)))
6970         ;; Apply funcs in order, passing subject generated by previous
6971         ;; func to the next one.
6972         (dolist (func funcs)
6973           (when (functionp func)
6974             (setq subject (funcall func subject))))
6975         subject))))
6976
6977 ;;;###autoload
6978 (defun message-forward (&optional news)
6979   "Forward the current message via mail.
6980 Optional NEWS will use news to forward instead of mail."
6981   (interactive "P")
6982   (let ((cur (current-buffer))
6983         (subject (message-make-forward-subject)))
6984     (if news
6985         (message-news nil subject)
6986       (message-mail nil subject))
6987     (message-forward-make-body cur)))
6988
6989 (defun message-forward-make-body-plain (forward-buffer)
6990   (insert
6991    "\n-------------------- Start of forwarded message --------------------\n")
6992   (let ((b (point)) e)
6993     (insert
6994      (with-temp-buffer
6995        (mm-disable-multibyte)
6996        (insert
6997         (with-current-buffer forward-buffer
6998           (mm-with-unibyte-current-buffer (buffer-string))))
6999        (mm-enable-multibyte)
7000        (mime-to-mml)
7001        (goto-char (point-min))
7002        (when (looking-at "From ")
7003          (replace-match "X-From-Line: "))
7004        (buffer-string)))
7005     (setq e (point))
7006     (insert
7007      "\n-------------------- End of forwarded message --------------------\n")
7008     (when message-forward-ignored-headers
7009       (save-restriction
7010         (narrow-to-region b e)
7011         (goto-char b)
7012         (narrow-to-region (point)
7013                           (or (search-forward "\n\n" nil t) (point)))
7014         (message-remove-header message-forward-ignored-headers t)))))
7015
7016 (defun message-forward-make-body-mime (forward-buffer)
7017   (insert "\n\n<#part type=message/rfc822 disposition=inline raw=t>\n")
7018   (let ((b (point)) e)
7019     (save-restriction
7020       (narrow-to-region (point) (point))
7021       (mml-insert-buffer forward-buffer)
7022       (goto-char (point-min))
7023       (when (looking-at "From ")
7024         (replace-match "X-From-Line: "))
7025       (goto-char (point-max)))
7026     (setq e (point))
7027     (insert "<#/part>\n")))
7028
7029 (defun message-forward-make-body-mml (forward-buffer)
7030   (insert "\n\n<#mml type=message/rfc822 disposition=inline>\n")
7031   (let ((b (point)) e)
7032     (if (not message-forward-decoded-p)
7033         (insert
7034          (with-temp-buffer
7035            (mm-disable-multibyte)
7036            (insert
7037             (with-current-buffer forward-buffer
7038               (mm-with-unibyte-current-buffer (buffer-string))))
7039            (mm-enable-multibyte)
7040            (mime-to-mml)
7041            (goto-char (point-min))
7042            (when (looking-at "From ")
7043              (replace-match "X-From-Line: "))
7044            (buffer-string)))
7045       (save-restriction
7046         (narrow-to-region (point) (point))
7047         (mml-insert-buffer forward-buffer)
7048         (goto-char (point-min))
7049         (when (looking-at "From ")
7050           (replace-match "X-From-Line: "))
7051         (goto-char (point-max))))
7052     (setq e (point))
7053     (insert "<#/mml>\n")
7054     (when (and (not message-forward-decoded-p)
7055                message-forward-ignored-headers)
7056       (save-restriction
7057         (narrow-to-region b e)
7058         (goto-char b)
7059         (narrow-to-region (point)
7060                           (or (search-forward "\n\n" nil t) (point)))
7061         (message-remove-header message-forward-ignored-headers t)))))
7062
7063 (defun message-forward-make-body-digest-plain (forward-buffer)
7064   (insert
7065    "\n-------------------- Start of forwarded message --------------------\n")
7066   (let ((b (point)) e)
7067     (mml-insert-buffer forward-buffer)
7068     (setq e (point))
7069     (insert
7070      "\n-------------------- End of forwarded message --------------------\n")))
7071
7072 (defun message-forward-make-body-digest-mime (forward-buffer)
7073   (insert "\n<#multipart type=digest>\n")
7074   (let ((b (point)) e)
7075     (insert-buffer-substring forward-buffer)
7076     (setq e (point))
7077     (insert "<#/multipart>\n")
7078     (save-restriction
7079       (narrow-to-region b e)
7080       (goto-char b)
7081       (narrow-to-region (point)
7082                         (or (search-forward "\n\n" nil t) (point)))
7083       (delete-region (point-min) (point-max)))))
7084
7085 (defun message-forward-make-body-digest (forward-buffer)
7086   (if message-forward-as-mime
7087       (message-forward-make-body-digest-mime forward-buffer)
7088     (message-forward-make-body-digest-plain forward-buffer)))
7089
7090 ;;;###autoload
7091 (defun message-forward-make-body (forward-buffer)
7092   ;; Put point where we want it before inserting the forwarded
7093   ;; message.
7094   ;; Note that this function definition for T-gnus is totally different
7095   ;; from the original Gnus."
7096   (if message-forward-before-signature
7097       (message-goto-body)
7098     (goto-char (point-max)))
7099   ;; Make sure we're at the start of the line.
7100   (unless (bolp)
7101     (insert "\n"))
7102   ;; Narrow to the area we are to insert.
7103   (narrow-to-region (point) (point))
7104   ;; Insert the separators and the forwarded buffer.
7105   (insert message-forward-start-separator)
7106   (let ((art-beg (point)))
7107     (insert-buffer-substring forward-buffer)
7108     (goto-char (point-max))
7109     (insert message-forward-end-separator)
7110     (set-text-properties (point-min) (point-max) nil)
7111     ;; Remove all unwanted headers.
7112     (goto-char art-beg)
7113     (narrow-to-region (point) (if (search-forward "\n\n" nil t)
7114                                   (1- (point))
7115                                 (point)))
7116     (goto-char (point-min))
7117     (message-remove-header message-included-forward-headers t nil t)
7118     (widen)
7119     (message-position-point)))
7120
7121 ;;;###autoload
7122 (defun message-forward-rmail-make-body (forward-buffer)
7123   (save-window-excursion
7124     (set-buffer forward-buffer)
7125     (if (rmail-msg-is-pruned)
7126         (rmail-msg-restore-non-pruned-header)))
7127   (message-forward-make-body forward-buffer))
7128
7129 (eval-when-compile (defvar rmail-enable-mime-composing))
7130
7131 ;; Fixme: Should have defcustom.
7132 ;;;###autoload
7133 (defun message-insinuate-rmail ()
7134   "Let RMAIL use message to forward."
7135   (interactive)
7136   (setq rmail-enable-mime-composing t)
7137   (setq rmail-insert-mime-forwarded-message-function
7138         'message-forward-rmail-make-body))
7139
7140 ;;;###autoload
7141 (defun message-resend (address)
7142   "Resend the current article to ADDRESS."
7143   (interactive
7144    (list (message-read-from-minibuffer "Resend message to: ")))
7145   (message "Resending message to %s..." address)
7146   (save-excursion
7147     (let ((cur (current-buffer))
7148           beg)
7149       ;; We first set up a normal mail buffer.
7150       (unless (message-mail-user-agent)
7151         (set-buffer (get-buffer-create " *message resend*"))
7152         (erase-buffer))
7153       (let ((message-this-is-mail t)
7154             message-setup-hook)
7155         (message-setup `((To . ,address))))
7156       ;; Insert our usual headers.
7157       (message-generate-headers '(From Date To Message-ID))
7158       (message-narrow-to-headers)
7159       ;; Remove X-Draft-From header etc.
7160       (message-remove-header message-ignored-mail-headers t)
7161       ;; Rename them all to "Resent-*".
7162       (goto-char (point-min))
7163       (while (re-search-forward "^[A-Za-z]" nil t)
7164         (forward-char -1)
7165         (insert "Resent-"))
7166       (widen)
7167       (forward-line)
7168       (delete-region (point) (point-max))
7169       (setq beg (point))
7170       ;; Insert the message to be resent.
7171       (insert-buffer-substring cur)
7172       (goto-char (point-min))
7173       (search-forward "\n\n")
7174       (forward-char -1)
7175       (save-restriction
7176         (narrow-to-region beg (point))
7177         (message-remove-header message-ignored-resent-headers t)
7178         (goto-char (point-max)))
7179       (insert mail-header-separator)
7180       ;; Rename all old ("Also-")Resent headers.
7181       (while (re-search-backward "^\\(Also-\\)*Resent-" beg t)
7182         (beginning-of-line)
7183         (insert "Also-"))
7184       ;; Quote any "From " lines at the beginning.
7185       (goto-char beg)
7186       (when (looking-at "From ")
7187         (replace-match "X-From-Line: "))
7188       ;; Send it.
7189       (let ((message-encoding-buffer (current-buffer))
7190             (message-edit-buffer (current-buffer))
7191             message-required-mail-headers)
7192         (message-send-mail))
7193       (kill-buffer (current-buffer)))
7194     (message "Resending message to %s...done" address)))
7195
7196 (defun message-bounce-setup-for-mime-edit ()
7197   (set (make-local-variable 'message-setup-hook) nil)
7198   (mime-edit-again))
7199
7200 ;;;###autoload
7201 (defun message-bounce ()
7202   "Re-mail the current message.
7203 This only makes sense if the current message is a bounce message that
7204 contains some mail you have written which has been bounced back to
7205 you."
7206   (interactive)
7207   (let ((cur (current-buffer))
7208         mime-boundary boundary)
7209     (message-pop-to-buffer (message-buffer-name "bounce"))
7210     (insert-buffer-substring cur)
7211     (undo-boundary)
7212     (message-narrow-to-head)
7213     (if (and (message-fetch-field "MIME-Version")
7214              (setq mime-boundary (message-fetch-field "Content-Type")))
7215         (if (string-match "boundary=\"\\([^\"]+\\)\"" mime-boundary)
7216             (setq mime-boundary (concat (regexp-quote
7217                                          (match-string 1 mime-boundary))
7218                                         " *\nContent-Type: message/rfc822"))
7219           (setq mime-boundary nil)))
7220     (widen)
7221     (goto-char (point-min))
7222     (re-search-forward "\n\n+" nil t)
7223     (setq boundary (point))
7224     ;; We remove everything before the bounced mail.
7225     (if (or (and mime-boundary
7226                  (re-search-forward mime-boundary nil t)
7227                  (forward-line 1))
7228             (re-search-forward message-unsent-separator nil t)
7229             (progn
7230               (search-forward "\n\n" nil 'move)
7231               (re-search-backward "^Return-Path:.*\n" boundary t)))
7232         (progn
7233           (forward-line 1)
7234           (delete-region (point-min)
7235                          (if (re-search-forward "^[^ \n\t]+:" nil t)
7236                              (match-beginning 0)
7237                            (point))))
7238       (when (re-search-backward "^.?From .*\n" nil t)
7239         (delete-region (match-beginning 0) (match-end 0))))
7240     (save-restriction
7241       (message-narrow-to-head-1)
7242       (message-remove-header message-ignored-bounced-headers t)
7243       (goto-char (point-max))
7244       (insert mail-header-separator))
7245     (when message-bounce-setup-function
7246       (funcall message-bounce-setup-function))
7247     (run-hooks 'message-bounce-setup-hook)
7248     (message-position-point)))
7249
7250 ;;;
7251 ;;; Interactive entry points for new message buffers.
7252 ;;;
7253
7254 ;;;###autoload
7255 (defun message-mail-other-window (&optional to subject)
7256   "Like `message-mail' command, but display mail buffer in another window."
7257   (interactive)
7258   (unless (message-mail-user-agent)
7259     (let ((pop-up-windows t)
7260           (special-display-buffer-names nil)
7261           (special-display-regexps nil)
7262           (same-window-buffer-names nil)
7263           (same-window-regexps nil))
7264       (message-pop-to-buffer (message-buffer-name "mail" to))))
7265   (let ((message-this-is-mail t))
7266     (message-setup `((To . ,(or to "")) (Subject . ,(or subject "")))
7267                    nil nil 'switch-to-buffer-other-window)))
7268
7269 ;;;###autoload
7270 (defun message-mail-other-frame (&optional to subject)
7271   "Like `message-mail' command, but display mail buffer in another frame."
7272   (interactive)
7273   (unless (message-mail-user-agent)
7274     (let ((pop-up-frames t)
7275           (special-display-buffer-names nil)
7276           (special-display-regexps nil)
7277           (same-window-buffer-names nil)
7278           (same-window-regexps nil))
7279       (message-pop-to-buffer (message-buffer-name "mail" to))))
7280   (let ((message-this-is-mail t))
7281     (message-setup `((To . ,(or to "")) (Subject . ,(or subject "")))
7282                    nil nil 'switch-to-buffer-other-frame)))
7283
7284 ;;;###autoload
7285 (defun message-news-other-window (&optional newsgroups subject)
7286   "Start editing a news article to be sent."
7287   (interactive)
7288   (let ((pop-up-windows t)
7289         (special-display-buffer-names nil)
7290         (special-display-regexps nil)
7291         (same-window-buffer-names nil)
7292         (same-window-regexps nil))
7293     (message-pop-to-buffer (message-buffer-name "posting" nil newsgroups)))
7294   (let ((message-this-is-news t))
7295     (message-setup `((Newsgroups . ,(or newsgroups ""))
7296                      (Subject . ,(or subject ""))))))
7297
7298 ;;;###autoload
7299 (defun message-news-other-frame (&optional newsgroups subject)
7300   "Start editing a news article to be sent."
7301   (interactive)
7302   (let ((pop-up-frames t)
7303         (special-display-buffer-names nil)
7304         (special-display-regexps nil)
7305         (same-window-buffer-names nil)
7306         (same-window-regexps nil))
7307     (message-pop-to-buffer (message-buffer-name "posting" nil newsgroups)))
7308   (let ((message-this-is-news t))
7309     (message-setup `((Newsgroups . ,(or newsgroups ""))
7310                      (Subject . ,(or subject ""))))))
7311
7312 ;;; underline.el
7313
7314 ;; This code should be moved to underline.el (from which it is stolen).
7315
7316 ;;;###autoload
7317 (defun bold-region (start end)
7318   "Bold all nonblank characters in the region.
7319 Works by overstriking characters.
7320 Called from program, takes two arguments START and END
7321 which specify the range to operate on."
7322   (interactive "r")
7323   (save-excursion
7324     (let ((end1 (make-marker)))
7325       (move-marker end1 (max start end))
7326       (goto-char (min start end))
7327       (while (< (point) end1)
7328         (or (looking-at "[_\^@- ]")
7329             (insert (char-after) "\b"))
7330         (forward-char 1)))))
7331
7332 ;;;###autoload
7333 (defun unbold-region (start end)
7334   "Remove all boldness (overstruck characters) in the region.
7335 Called from program, takes two arguments START and END
7336 which specify the range to operate on."
7337   (interactive "r")
7338   (save-excursion
7339     (let ((end1 (make-marker)))
7340       (move-marker end1 (max start end))
7341       (goto-char (min start end))
7342       (while (search-forward "\b" end1 t)
7343         (if (eq (char-after) (char-after (- (point) 2)))
7344             (delete-char -2))))))
7345
7346 (defun message-exchange-point-and-mark ()
7347   "Exchange point and mark, but don't activate region if it was inactive."
7348   (unless (prog1
7349               (message-mark-active-p)
7350             (exchange-point-and-mark))
7351     (setq mark-active nil)))
7352
7353 (defalias 'message-make-overlay 'make-overlay)
7354 (defalias 'message-delete-overlay 'delete-overlay)
7355 (defalias 'message-overlay-put 'overlay-put)
7356 (defun message-kill-all-overlays ()
7357   (if (featurep 'xemacs)
7358       (map-extents (lambda (extent ignore) (delete-extent extent)))
7359     (mapcar #'delete-overlay (overlays-in (point-min) (point-max)))))
7360
7361 ;; Support for toolbar
7362 (eval-when-compile
7363   (defvar tool-bar-map)
7364   (defvar tool-bar-mode))
7365
7366 (defun message-tool-bar-local-item-from-menu (command icon in-map &optional from-map &rest props)
7367   ;; We need to make tool bar entries in local keymaps with
7368   ;; `tool-bar-local-item-from-menu' in Emacs > 21.3
7369   (if (fboundp 'tool-bar-local-item-from-menu)
7370       ;; This is for Emacs 21.3
7371       (tool-bar-local-item-from-menu command icon in-map from-map props)
7372     (tool-bar-add-item-from-menu command icon from-map props)))
7373
7374 (defun message-tool-bar-map ()
7375   (or message-tool-bar-map
7376       (setq message-tool-bar-map
7377             (and
7378              (condition-case nil (require 'tool-bar) (error nil))
7379              (fboundp 'tool-bar-add-item-from-menu)
7380              tool-bar-mode
7381              (let ((tool-bar-map (copy-keymap tool-bar-map))
7382                    (load-path (mm-image-load-path)))
7383                ;; Zap some items which aren't so relevant and take
7384                ;; up space.
7385                (dolist (key '(print-buffer kill-buffer save-buffer
7386                                            write-file dired open-file))
7387                  (define-key tool-bar-map (vector key) nil))
7388                (message-tool-bar-local-item-from-menu
7389                 'message-send-and-exit "mail_send" tool-bar-map message-mode-map)
7390                (message-tool-bar-local-item-from-menu
7391                 'message-kill-buffer "close" tool-bar-map message-mode-map)
7392                (message-tool-bar-local-item-from-menu
7393                 'message-dont-send "cancel" tool-bar-map message-mode-map)
7394 ;;             (message-tool-bar-local-item-from-menu
7395 ;;              'mime-edit-insert-file "attach"
7396 ;;              tool-bar-map mime-edit-mode-map)
7397                (message-tool-bar-local-item-from-menu
7398                 'ispell-message "spell" tool-bar-map message-mode-map)
7399 ;;             (message-tool-bar-local-item-from-menu
7400 ;;              'mime-edit-preview-message "preview"
7401 ;;              tool-bar-map mime-edit-mode-map)
7402                (message-tool-bar-local-item-from-menu
7403                 'message-insert-importance-high "important"
7404                 tool-bar-map message-mode-map)
7405                (message-tool-bar-local-item-from-menu
7406                 'message-insert-importance-low "unimportant"
7407                 tool-bar-map message-mode-map)
7408                (message-tool-bar-local-item-from-menu
7409                 'message-insert-disposition-notification-to "receipt"
7410                 tool-bar-map message-mode-map)
7411                tool-bar-map)))))
7412
7413 ;;; Group name completion.
7414
7415 (defcustom message-newgroups-header-regexp
7416   "^\\(Newsgroups\\|Followup-To\\|Posted-To\\|Gcc\\):"
7417   "Regexp that match headers that lists groups."
7418   :group 'message
7419   :type 'regexp)
7420
7421 (defcustom message-completion-alist
7422   (list (cons message-newgroups-header-regexp 'message-expand-group)
7423         '("^\\(Resent-\\)?\\(To\\|B?Cc\\):" . message-expand-name)
7424         '("^\\(Reply-To\\|From\\|Mail-Followup-To\\|Mail-Copies-To\\):"
7425           . message-expand-name)
7426         '("^\\(Disposition-Notification-To\\|Return-Receipt-To\\):"
7427           . message-expand-name))
7428   "Alist of (RE . FUN).  Use FUN for completion on header lines matching RE."
7429   :version "22.1"
7430   :group 'message
7431   :type '(alist :key-type regexp :value-type function))
7432
7433 (defcustom message-expand-name-databases
7434   (list 'bbdb 'eudc 'lsdb)
7435   "List of databases to try for name completion (`message-expand-name').
7436 Each element is a symbol and can be `bbdb', `eudc' or `lsdb'."
7437   :group 'message
7438   :type '(set (const bbdb) (const eudc) (const lsdb)))
7439
7440 (defcustom message-expand-name-function
7441   (cond ((and (boundp 'eudc-protocol)
7442               eudc-protocol)
7443          'eudc-expand-inline)
7444         ((fboundp 'bbdb-complete-name)
7445          'bbdb-complete-name)
7446         ((fboundp 'lsdb-complete-name)
7447          'lsdb-complete-name)
7448         (t 'expand-abbrev))
7449   "*A function called to expand addresses in field body.
7450 This variable is semi-obsolete, set it as nil and use
7451 `message-expand-name-databases' instead."
7452   :group 'message
7453   :type '(radio (const :format "Invalidate it: %v\n" nil)
7454                 (function-item :format "eudc: %v\n" eudc-expand-inline)
7455                 (function-item :format "bbdb: %v\n" bbdb-complete-name)
7456                 (function-item :format "lsdb: %v\n" lsdb-complete-name)
7457                 (function :value expand-abbrev)))
7458
7459 (defcustom message-tab-body-function nil
7460   "*Function to execute when `message-tab' (TAB) is executed in the body.
7461 If nil, the function bound in `text-mode-map' or `global-map' is executed."
7462   :version "22.1"
7463   :group 'message
7464   :link '(custom-manual "(message)Various Commands")
7465   :type 'function)
7466
7467 (defun message-tab ()
7468   "Complete names according to `message-completion-alist'.
7469 Execute function specified by `message-tab-body-function' when not in
7470 those headers."
7471   (interactive)
7472   (let ((alist message-completion-alist))
7473     (while (and alist
7474                 (let ((mail-abbrev-mode-regexp (caar alist)))
7475                   (not (mail-abbrev-in-expansion-header-p))))
7476       (setq alist (cdr alist)))
7477     (funcall (or (cdar alist) message-tab-body-function
7478                  (lookup-key text-mode-map "\t")
7479                  (lookup-key global-map "\t")
7480                  'indent-relative))))
7481
7482 (defun message-expand-group ()
7483   "Expand the group name under point."
7484   (let* ((b (save-excursion
7485               (save-restriction
7486                 (narrow-to-region
7487                  (save-excursion
7488                    (beginning-of-line)
7489                    (skip-chars-forward "^:")
7490                    (1+ (point)))
7491                  (point))
7492                 (skip-chars-backward "^, \t\n") (point))))
7493          (completion-ignore-case t)
7494          (string (buffer-substring b (progn (skip-chars-forward "^,\t\n ")
7495                                             (point))))
7496          (hashtb (and (boundp 'gnus-active-hashtb) gnus-active-hashtb))
7497          (completions (all-completions string hashtb))
7498          comp)
7499     (delete-region b (point))
7500     (cond
7501      ((= (length completions) 1)
7502       (if (string= (car completions) string)
7503           (progn
7504             (insert string)
7505             (message "Only matching group"))
7506         (insert (car completions))))
7507      ((and (setq comp (try-completion string hashtb))
7508            (not (string= comp string)))
7509       (insert comp))
7510      (t
7511       (insert string)
7512       (if (not comp)
7513           (message "No matching groups")
7514         (save-selected-window
7515           (pop-to-buffer "*Completions*")
7516           (buffer-disable-undo)
7517           (let ((buffer-read-only nil))
7518             (erase-buffer)
7519             (let ((standard-output (current-buffer)))
7520               (display-completion-list (sort completions 'string<)))
7521             (goto-char (point-min))
7522             (delete-region (point) (progn (forward-line 3) (point))))))))))
7523
7524 (defun message-expand-name ()
7525   (cond (message-expand-name-function
7526          (funcall message-expand-name-function))
7527         ((and (memq 'eudc message-expand-name-databases)
7528               (boundp 'eudc-protocol)
7529               eudc-protocol)
7530          (eudc-expand-inline))
7531         ((and (memq 'bbdb message-expand-name-databases)
7532               (fboundp 'bbdb-complete-name))
7533          (bbdb-complete-name))
7534         ((and (memq 'lsdb message-expand-name-databases)
7535               (fboundp 'lsdb-complete-name))
7536          (lsdb-complete-name))
7537         (t 'expand-abbrev)))
7538
7539 ;;; Help stuff.
7540
7541 (defun message-talkative-question (ask question show &rest text)
7542   "Call FUNCTION with argument QUESTION; optionally display TEXT... args.
7543 If SHOW is non-nil, the arguments TEXT... are displayed in a temp buffer.
7544 The following arguments may contain lists of values."
7545   (if (and show
7546            (setq text (message-flatten-list text)))
7547       (save-window-excursion
7548         (save-excursion
7549           (with-output-to-temp-buffer " *MESSAGE information message*"
7550             (set-buffer " *MESSAGE information message*")
7551             (fundamental-mode)          ; for Emacs 20.4+
7552             (mapcar 'princ text)
7553             (goto-char (point-min))))
7554         (funcall ask question))
7555     (funcall ask question)))
7556
7557 (defun message-flatten-list (list)
7558   "Return a new, flat list that contains all elements of LIST.
7559
7560 \(message-flatten-list '(1 (2 3 (4 5 (6))) 7))
7561 => (1 2 3 4 5 6 7)"
7562   (cond ((consp list)
7563          (apply 'append (mapcar 'message-flatten-list list)))
7564         (list
7565          (list list))))
7566
7567 (defun message-generate-new-buffer-clone-locals (name &optional varstr)
7568   "Create and return a buffer with name based on NAME using `generate-new-buffer'.
7569 Then clone the local variables and values from the old buffer to the
7570 new one, cloning only the locals having a substring matching the
7571 regexp VARSTR."
7572   (let ((oldbuf (current-buffer)))
7573     (save-excursion
7574       (set-buffer (generate-new-buffer name))
7575       (message-clone-locals oldbuf varstr)
7576       (current-buffer))))
7577
7578 (defun message-clone-locals (buffer &optional varstr)
7579   "Clone the local variables from BUFFER to the current buffer."
7580   (let ((locals (save-excursion
7581                   (set-buffer buffer)
7582                   (buffer-local-variables)))
7583         (regexp "^gnus\\|^nn\\|^message\\|^sendmail\\|^smtp\\|^user-mail-address\\|^user-full-name"))
7584     (mapcar
7585      (lambda (local)
7586        (when (and (consp local)
7587                   (car local)
7588                   (string-match regexp (symbol-name (car local)))
7589                   (or (null varstr)
7590                       (string-match varstr (symbol-name (car local)))))
7591          (ignore-errors
7592            (set (make-local-variable (car local))
7593                 (cdr local)))))
7594      locals)))
7595
7596 ;;; @ for MIME Edit mode
7597 ;;;
7598
7599 (defun message-maybe-encode ()
7600   (when message-mime-mode
7601     ;; Inherit the buffer local variable `mime-edit-pgp-processing'.
7602     (let ((pgp-processing (with-current-buffer message-edit-buffer
7603                             mime-edit-pgp-processing)))
7604       (setq mime-edit-pgp-processing pgp-processing))
7605     (run-hooks 'mime-edit-translate-hook)
7606     (if (catch 'mime-edit-error
7607           (save-excursion
7608             (mime-edit-pgp-enclose-buffer)
7609             (mime-edit-translate-body)))
7610         (error "Translation error!"))
7611     (run-hooks 'mime-edit-exit-hook)))
7612
7613 (defun message-mime-insert-article (&optional full-headers)
7614   (interactive "P")
7615   (let ((message-cite-function 'mime-edit-inserted-message-filter)
7616         (message-reply-buffer
7617          (message-get-parameter-with-eval 'original-buffer))
7618         (start (point)))
7619     (message-yank-original nil)
7620     (save-excursion
7621       (narrow-to-region (goto-char start)
7622                         (if (search-forward "\n\n" nil t)
7623                             (1- (point))
7624                           (point-max)))
7625       (goto-char (point-min))
7626       (let ((message-included-forward-headers
7627              (if full-headers "" message-included-forward-headers)))
7628         (message-remove-header message-included-forward-headers t nil t))
7629       (widen))))
7630
7631 (set-alist 'mime-edit-message-inserter-alist
7632            'message-mode (function message-mime-insert-article))
7633
7634 ;;;
7635 ;;; MIME functions
7636 ;;;
7637
7638 (defvar message-inhibit-body-encoding t)
7639
7640 (defun message-encode-message-body ()
7641   (unless message-inhibit-body-encoding
7642     (let ((mail-parse-charset (or mail-parse-charset
7643                                   message-default-charset))
7644           (case-fold-search t)
7645           lines content-type-p)
7646       (message-goto-body)
7647       (save-restriction
7648         (narrow-to-region (point) (point-max))
7649         (let ((new (mml-generate-mime)))
7650           (when new
7651             (delete-region (point-min) (point-max))
7652             (insert new)
7653             (goto-char (point-min))
7654             (if (eq (aref new 0) ?\n)
7655                 (delete-char 1)
7656               (search-forward "\n\n")
7657               (setq lines (buffer-substring (point-min) (1- (point))))
7658               (delete-region (point-min) (point))))))
7659       (save-restriction
7660         (message-narrow-to-headers-or-head)
7661         (message-remove-header "Mime-Version")
7662         (goto-char (point-max))
7663         (insert "MIME-Version: 1.0\n")
7664         (when lines
7665           (insert lines))
7666         (setq content-type-p
7667               (or mml-boundary
7668                   (re-search-backward "^Content-Type:" nil t))))
7669       (save-restriction
7670         (message-narrow-to-headers-or-head)
7671         (message-remove-first-header "Content-Type")
7672         (message-remove-first-header "Content-Transfer-Encoding"))
7673       ;; We always make sure that the message has a Content-Type
7674       ;; header.  This is because some broken MTAs and MUAs get
7675       ;; awfully confused when confronted with a message with a
7676       ;; MIME-Version header and without a Content-Type header.  For
7677       ;; instance, Solaris' /usr/bin/mail.
7678       (unless content-type-p
7679         (goto-char (point-min))
7680         ;; For unknown reason, MIME-Version doesn't exist.
7681         (when (re-search-forward "^MIME-Version:" nil t)
7682           (forward-line 1)
7683           (insert "Content-Type: text/plain; charset=us-ascii\n"))))))
7684
7685 (defun message-read-from-minibuffer (prompt &optional initial-contents)
7686   "Read from the minibuffer while providing abbrev expansion."
7687   (if (fboundp 'mail-abbrevs-setup)
7688       (let ((mail-abbrev-mode-regexp "")
7689             (minibuffer-setup-hook 'mail-abbrevs-setup)
7690             (minibuffer-local-map message-minibuffer-local-map))
7691         (read-from-minibuffer prompt initial-contents))
7692     (let ((minibuffer-setup-hook 'mail-abbrev-minibuffer-setup-hook)
7693           (minibuffer-local-map message-minibuffer-local-map))
7694       (read-string prompt initial-contents))))
7695
7696 (defun message-use-alternative-email-as-from ()
7697   (require 'mail-utils)
7698   (let* ((fields '("To" "Cc" "From"))
7699          (emails
7700           (split-string
7701            (mail-strip-quoted-names
7702             (mapconcat 'message-fetch-reply-field fields ","))
7703            "[ \f\t\n\r\v,]+"))
7704          email)
7705     (while emails
7706       (if (string-match message-alternative-emails (car emails))
7707           (setq email (car emails)
7708                 emails nil))
7709       (pop emails))
7710     (unless (or (not email) (equal email user-mail-address))
7711       (goto-char (point-max))
7712       (insert "From: " (let ((user-mail-address email)) (message-make-from))
7713               "\n"))))
7714
7715 (defun message-options-get (symbol)
7716   (cdr (assq symbol message-options)))
7717
7718 (defun message-options-set (symbol value)
7719   (let ((the-cons (assq symbol message-options)))
7720     (if the-cons
7721         (if value
7722             (setcdr the-cons value)
7723           (setq message-options (delq the-cons message-options)))
7724       (and value
7725            (push (cons symbol value) message-options))))
7726   value)
7727
7728 (defun message-options-set-recipient ()
7729   (save-restriction
7730     (message-narrow-to-headers-or-head)
7731     (message-options-set 'message-sender
7732                          (mail-strip-quoted-names
7733                           (message-fetch-field "from")))
7734     (message-options-set 'message-recipients
7735                          (mail-strip-quoted-names
7736                           (let ((to (message-fetch-field "to"))
7737                                 (cc (message-fetch-field "cc"))
7738                                 (bcc (message-fetch-field "bcc")))
7739                             (concat
7740                              (or to "")
7741                              (if (and to cc) ", ")
7742                              (or cc "")
7743                              (if (and (or to cc) bcc) ", ")
7744                              (or bcc "")))))))
7745
7746 (defun message-hide-headers ()
7747   "Hide headers based on the `message-hidden-headers' variable."
7748   (let ((regexps (if (stringp message-hidden-headers)
7749                      (list message-hidden-headers)
7750                    message-hidden-headers))
7751         (inhibit-point-motion-hooks t)
7752         (after-change-functions nil)
7753         (end-of-headers 0))
7754     (when regexps
7755       (save-excursion
7756         (save-restriction
7757           (message-narrow-to-headers)
7758           (goto-char (point-min))
7759           (while (not (eobp))
7760             (if (not (message-hide-header-p regexps))
7761                 (message-next-header)
7762               (let ((begin (point))
7763                     header header-len)
7764                 (message-next-header)
7765                 (setq header (buffer-substring begin (point))
7766                       header-len (- (point) begin))
7767                 (delete-region begin (point))
7768                 (goto-char (1+ end-of-headers))
7769                 (insert header)
7770                 (setq end-of-headers
7771                       (+ end-of-headers header-len))))))))
7772     (narrow-to-region (1+ end-of-headers) (point-max))))
7773
7774 (defun message-hide-header-p (regexps)
7775   (let ((result nil)
7776         (reverse nil))
7777     (when (eq (car regexps) 'not)
7778       (setq reverse t)
7779       (pop regexps))
7780     (dolist (regexp regexps)
7781       (setq result (or result (looking-at regexp))))
7782     (if reverse
7783         (not result)
7784       result)))
7785
7786 (when (featurep 'xemacs)
7787   (require 'messagexmas)
7788   (message-xmas-redefine))
7789
7790 (provide 'message)
7791
7792 (run-hooks 'message-load-hook)
7793
7794 ;; Local Variables:
7795 ;; coding: iso-8859-1
7796 ;; End:
7797
7798 ;;; message.el ends here