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