Sync with Gnus.
[elisp/gnus.git-] / lisp / message.el
1 ;;; message.el --- composing mail and news messages
2 ;; Copyright (C) 1996, 1997, 1998, 1999, 2000
3 ;;        Free Software Foundation, Inc.
4
5 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
6 ;;      MORIOKA Tomohiko <morioka@jaist.ac.jp>
7 ;;      Shuhei KOBAYASHI <shuhei-k@jaist.ac.jp>
8 ;;      Keiichi Suzuki   <kei-suzu@mail.wbs.ne.jp>
9 ;;      Tatsuya Ichikawa <t-ichi@po.shiojiri.ne.jp>
10 ;;      Katsumi Yamaoka  <yamaoka@jpl.org>
11 ;;      Kiyokazu SUTO    <suto@merry.xmath.ous.ac.jp>
12 ;; Keywords: mail, news, MIME
13
14 ;; This file is part of GNU Emacs.
15
16 ;; GNU Emacs is free software; you can redistribute it and/or modify
17 ;; it under the terms of the GNU General Public License as published by
18 ;; the Free Software Foundation; either version 2, or (at your option)
19 ;; any later version.
20
21 ;; GNU Emacs is distributed in the hope that it will be useful,
22 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
23 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24 ;; GNU General Public License for more details.
25
26 ;; You should have received a copy of the GNU General Public License
27 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
28 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
29 ;; Boston, MA 02111-1307, USA.
30
31 ;;; Commentary:
32
33 ;; This mode provides mail-sending facilities from within Emacs.  It
34 ;; consists mainly of large chunks of code from the sendmail.el,
35 ;; gnus-msg.el and rnewspost.el files.
36
37 ;;; Code:
38
39 (eval-when-compile
40   (require 'cl)
41   (require 'smtp)
42   (defvar gnus-list-identifiers))       ; gnus-sum is required where necessary
43
44 (require 'mailheader)
45 (require 'nnheader)
46 (require 'mime-edit)
47 (eval-when-compile (require 'static))
48
49 ;; Avoid byte-compile warnings.
50 (eval-when-compile
51   (require 'mail-parse)
52   (require 'mml))
53
54 (defgroup message '((user-mail-address custom-variable)
55                     (user-full-name custom-variable))
56   "Mail and news message composing."
57   :link '(custom-manual "(message)Top")
58   :group 'mail
59   :group 'news)
60
61 (put 'user-mail-address 'custom-type 'string)
62 (put 'user-full-name 'custom-type 'string)
63
64 (defgroup message-various nil
65   "Various Message Variables"
66   :link '(custom-manual "(message)Various Message Variables")
67   :group 'message)
68
69 (defgroup message-buffers nil
70   "Message Buffers"
71   :link '(custom-manual "(message)Message Buffers")
72   :group 'message)
73
74 (defgroup message-sending nil
75   "Message Sending"
76   :link '(custom-manual "(message)Sending Variables")
77   :group 'message)
78
79 (defgroup message-interface nil
80   "Message Interface"
81   :link '(custom-manual "(message)Interface")
82   :group 'message)
83
84 (defgroup message-forwarding nil
85   "Message Forwarding"
86   :link '(custom-manual "(message)Forwarding")
87   :group 'message-interface)
88
89 (defgroup message-insertion nil
90   "Message Insertion"
91   :link '(custom-manual "(message)Insertion")
92   :group 'message)
93
94 (defgroup message-headers nil
95   "Message Headers"
96   :link '(custom-manual "(message)Message Headers")
97   :group 'message)
98
99 (defgroup message-news nil
100   "Composing News Messages"
101   :group 'message)
102
103 (defgroup message-mail nil
104   "Composing Mail Messages"
105   :group 'message)
106
107 (defgroup message-faces nil
108   "Faces used for message composing."
109   :group 'message
110   :group 'faces)
111
112 (defgroup message-frames nil
113   "Message frames"
114   :group 'message)
115
116 (defcustom message-directory "~/Mail/"
117   "*Directory from which all other mail file variables are derived."
118   :group 'message-various
119   :type 'directory)
120
121 (defcustom message-max-buffers 10
122   "*How many buffers to keep before starting to kill them off."
123   :group 'message-buffers
124   :type 'integer)
125
126 (defcustom message-send-rename-function nil
127   "Function called to rename the buffer after sending it."
128   :group 'message-buffers
129   :type 'function)
130
131 (defcustom message-fcc-handler-function 'message-output
132   "*A function called to save outgoing articles.
133 This function will be called with the name of the file to store the
134 article in.  The default function is `message-output' which saves in Unix
135 mailbox format."
136   :type '(radio (function-item message-output)
137                 (function :tag "Other"))
138   :group 'message-sending)
139
140 (defcustom message-encode-function 'message-maybe-encode
141   "*A function called to encode messages."
142   :group 'message-sending
143   :type 'function)
144
145 (defcustom message-8bit-encoding-list '(8bit binary)
146   "*8bit encoding type in Content-Transfer-Encoding field."
147   :group 'message-sending
148   :type '(repeat (symbol :tag "Type")))
149
150 (defcustom message-courtesy-message
151   "The following message is a courtesy copy of an article\nthat has been posted to %s as well.\n\n"
152   "*This is inserted at the start of a mailed copy of a posted message.
153 If the string contains the format spec \"%s\", the Newsgroups
154 the article has been posted to will be inserted there.
155 If this variable is nil, no such courtesy message will be added."
156   :group 'message-sending
157   :type 'string)
158
159 (defcustom message-ignored-bounced-headers "^\\(Received\\|Return-Path\\):"
160   "*Regexp that matches headers to be removed in resent bounced mail."
161   :group 'message-interface
162   :type 'regexp)
163
164 (defcustom message-bounce-setup-function 'message-bounce-setup-for-mime-edit
165   "Function to setup a re-sending bounced message."
166   :group 'message-sending
167   :type 'function)
168
169 ;;;###autoload
170 (defcustom message-from-style 'default
171   "*Specifies how \"From\" headers look.
172
173 If `nil', they contain just the return address like:
174         king@grassland.com
175 If `parens', they look like:
176         king@grassland.com (Elvis Parsley)
177 If `angles', they look like:
178         Elvis Parsley <king@grassland.com>
179
180 Otherwise, most addresses look like `angles', but they look like
181 `parens' if `angles' would need quoting and `parens' would not."
182   :type '(choice (const :tag "simple" nil)
183                  (const parens)
184                  (const angles)
185                  (const default))
186   :group 'message-headers)
187
188 (defcustom message-syntax-checks nil
189   ;; Guess this one shouldn't be easy to customize...
190   "*Controls what syntax checks should not be performed on outgoing posts.
191 To disable checking of long signatures, for instance, add
192  `(signature . disabled)' to this list.
193
194 Don't touch this variable unless you really know what you're doing.
195
196 Checks include subject-cmsg multiple-headers sendsys message-id from
197 long-lines control-chars size new-text quoting-style
198 redirected-followup signature approved sender empty empty-headers
199 message-id from subject shorten-followup-to existing-newsgroups
200 buffer-file-name unchanged newsgroups."
201   :group 'message-news
202   :type '(repeat sexp))
203
204 (defcustom message-required-news-headers
205   '(From Newsgroups Subject Date Message-ID
206          (optional . Organization) Lines
207          (optional . User-Agent))
208   "*Headers to be generated or prompted for when posting an article.
209 RFC977 and RFC1036 require From, Date, Newsgroups, Subject,
210 Message-ID.  Organization, Lines, In-Reply-To, Expires, and
211 User-Agent are optional.  If don't you want message to insert some
212 header, remove it from this list."
213   :group 'message-news
214   :group 'message-headers
215   :type '(repeat sexp))
216
217 (defcustom message-required-mail-headers
218   '(From Subject Date (optional . In-Reply-To) Message-ID Lines
219          (optional . User-Agent))
220   "*Headers to be generated or prompted for when mailing a message.
221 RFC822 required that From, Date, To, Subject and Message-ID be
222 included.  Organization, Lines and User-Agent are optional."
223   :group 'message-mail
224   :group 'message-headers
225   :type '(repeat sexp))
226
227 (defcustom message-deletable-headers '(Message-ID Date Lines)
228   "Headers to be deleted if they already exist and were generated by message previously."
229   :group 'message-headers
230   :type 'sexp)
231
232 (defcustom message-ignored-news-headers
233   "^NNTP-Posting-Host:\\|^Xref:\\|^[BGF]cc:\\|^Resent-Fcc:"
234   "*Regexp of headers to be removed unconditionally before posting."
235   :group 'message-news
236   :group 'message-headers
237   :type 'regexp)
238
239 (defcustom message-ignored-mail-headers "^[GF]cc:\\|^Resent-Fcc:\\|^Xref:"
240   "*Regexp of headers to be removed unconditionally before mailing."
241   :group 'message-mail
242   :group 'message-headers
243   :type 'regexp)
244
245 (defcustom message-ignored-supersedes-headers "^Path:\\|^Date\\|^NNTP-Posting-Host:\\|^Xref:\\|^Lines:\\|^Received:\\|^X-From-Line:\\|^X-Trace:\\|^X-Complaints-To:\\|Return-Path:\\|^Supersedes:"
246   "*Header lines matching this regexp will be deleted before posting.
247 It's best to delete old Path and Date headers before posting to avoid
248 any confusion."
249   :group 'message-interface
250   :type 'regexp)
251
252 (defcustom message-supersede-setup-function
253   'message-supersede-setup-for-mime-edit
254   "Function to setup a supersede message."
255   :group 'message-sending
256   :type 'function)
257
258 (defcustom message-subject-re-regexp "^[ \t]*\\([Rr][Ee]:[ \t]*\\)*[ \t]*"
259   "*Regexp matching \"Re: \" in the subject line."
260   :group 'message-various
261   :type 'regexp)
262
263 ;;; Some sender agents encode the whole subject including leading "Re: ".
264 ;;; And if followup agent does not decode it for some reason (e.g. unknown
265 ;;; charset) and just add a new "Re: " in front of the encoded-word, the
266 ;;; result will contain multiple "Re: "'s.
267 (defcustom message-subject-encoded-re-regexp
268   (concat
269    "^[ \t]*"
270    (regexp-quote "=?")
271    "[-!#$%&'*+0-9A-Z^_`a-z{|}~]+" ; charset
272    (regexp-quote "?")
273    "\\("
274    "[Bb]" (regexp-quote "?") ; B encoding
275    "\\(\\(CQk\\|CSA\\|IAk\\|ICA\\)[Jg]\\)*" ; \([ \t][ \t][ \t]\)*
276    "\\("
277    "[Uc][km]U6" ; [Rr][Ee]:
278    "\\|"
279    "\\(C[VX]\\|I[FH]\\)J[Fl]O[g-v]" ; [ \t][Rr][Ee]:
280    "\\|"
281    "\\(CQl\\|CSB\\|IAl\\|ICB\\)[Sy][RZ]T[o-r]" ; [ \t][ \t][Rr][Ee]:
282    "\\)"
283    "\\|"
284    "[Qb]" (regexp-quote "?") ; Q encoding
285    "\\(_\\|=09\\|=20\\)*"
286    "\\([Rr]\\|=[57]2\\)\\([Ee]\\|=[46]5\\)\\(:\\|=3[Aa]\\)"
287    "\\)"
288    )
289   "*Regexp matching \"Re: \" in the subject line.
290 Unlike `message-subject-re-regexp', this regexp matches \"Re: \" within
291 an encoded-word."
292   :group 'message-various
293   :type 'regexp)
294
295 (defcustom message-use-subject-re t
296   "*If t, remove any (buggy) \"Re: \"'s from the subject of the precursor
297 and add a new \"Re: \".  If it is nil, use the subject \"as-is\".  If it
298 is the symbol `guess', try to detect \"Re: \" within an encoded-word."
299   :group 'message-various
300   :type '(choice (const :tag "off" nil)
301                  (const :tag "on" t)
302                  (const guess)))
303
304 ;;;###autoload
305 (defcustom message-signature-separator "^-- *$"
306   "Regexp matching the signature separator."
307   :type 'regexp
308   :group 'message-various)
309
310 (defcustom message-elide-ellipsis "\n[...]\n\n"
311   "*The string which is inserted for elided text."
312   :type 'string
313   :group 'message-various)
314
315 (defcustom message-interactive nil
316   "Non-nil means when sending a message wait for and display errors.
317 nil means let mailer mail back a message to report errors."
318   :group 'message-sending
319   :group 'message-mail
320   :type 'boolean)
321
322 (defcustom message-generate-new-buffers 'unique
323   "*Non-nil means that a new message buffer will be created whenever `message-setup' is called.
324 If this is a function, call that function with three parameters:  The type,
325 the to address and the group name.  (Any of these may be nil.)  The function
326 should return the new buffer name."
327   :group 'message-buffers
328   :type '(choice (const :tag "off" nil)
329                  (const :tag "unique" unique)
330                  (const :tag "unsent" unsent)
331                  (function fun)))
332
333 (defcustom message-kill-buffer-on-exit nil
334   "*Non-nil means that the message buffer will be killed after sending a message."
335   :group 'message-buffers
336   :type 'boolean)
337
338 (defcustom message-kill-buffer-query-function 'yes-or-no-p
339   "*A function called to query the user whether to kill buffer anyway or not.
340 If it is t, the buffer will be killed peremptorily."
341   :type '(radio (function-item yes-or-no-p)
342                 (function-item y-or-n-p)
343                 (function-item nnheader-Y-or-n-p)
344                 (function :tag "Other" t))
345   :group 'message-buffers)
346
347 (defvar gnus-local-organization)
348 (defcustom message-user-organization
349   (or (and (boundp 'gnus-local-organization)
350            (stringp gnus-local-organization)
351            gnus-local-organization)
352       (getenv "ORGANIZATION")
353       t)
354   "*String to be used as an Organization header.
355 If t, use `message-user-organization-file'."
356   :group 'message-headers
357   :type '(choice string
358                  (const :tag "consult file" t)))
359
360 ;;;###autoload
361 (defcustom message-user-organization-file "/usr/lib/news/organization"
362   "*Local news organization file."
363   :type 'file
364   :group 'message-headers)
365
366 (defcustom message-forward-start-separator
367   (concat (mime-make-tag "message" "rfc822") "\n")
368   "*Delimiter inserted before forwarded messages."
369   :group 'message-forwarding
370   :type 'string)
371
372 (defcustom message-forward-end-separator
373   (concat (mime-make-tag "text" "plain") "\n")
374   "*Delimiter inserted after forwarded messages."
375   :group 'message-forwarding
376   :type 'string)
377
378 (defcustom message-included-forward-headers
379   "^From:\\|^Newsgroups:\\|^Subject:\\|^Date:\\|^\\(Mail-\\)?Followup-To:\\|^\\(Mail-\\)?Reply-To:\\|^Mail-Copies-To:\\|^Organization:\\|^Summary:\\|^Keywords:\\|^To:\\|^Cc:\\|^Posted-To:\\|^Apparently-To:\\|^Gnus-Warning:\\|^Resent-\\|^Message-ID:\\|^References:\\|^Content-\\|^MIME-Version:"
380   "*Regexp matching headers to be included in forwarded messages."
381   :group 'message-forwarding
382   :type 'regexp)
383
384 (defcustom message-make-forward-subject-function
385   'message-forward-subject-author-subject
386   "*A list of functions that are called to generate a subject header for forwarded messages.
387 The subject generated by the previous function is passed into each
388 successive function.
389
390 The provided functions are:
391
392 * message-forward-subject-author-subject (Source of article (author or
393       newsgroup)), in brackets followed by the subject
394 * message-forward-subject-fwd (Subject of article with 'Fwd:' prepended
395       to it."
396   :group 'message-forwarding
397   :type '(radio (function-item message-forward-subject-author-subject)
398                 (function-item message-forward-subject-fwd)))
399
400 (defcustom message-forward-as-mime t
401   "*If non-nil, forward messages as an inline/rfc822 MIME section.  Otherwise, directly inline the old message in the forwarded message."
402   :group 'message-forwarding
403   :type 'boolean)
404
405 (defcustom message-forward-show-mml t
406   "*If non-nil, forward messages are shown as mml.  Otherwise, forward messages are unchanged."
407   :group 'message-forwarding
408   :type 'boolean)
409
410 (defcustom message-forward-before-signature t
411   "*If non-nil, put forwarded message before signature, else after."
412   :group 'message-forwarding
413   :type 'boolean)
414
415 (defcustom message-wash-forwarded-subjects nil
416   "*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."
417   :group 'message-forwarding
418   :type 'boolean)
419
420 (defcustom message-ignored-resent-headers "^Return-receipt\\|^X-Gnus\\|^Gnus-Warning:"
421   "*All headers that match this regexp will be deleted when resending a message."
422   :group 'message-interface
423   :type 'regexp)
424
425 (defcustom message-forward-ignored-headers "^Content-Transfer-Encoding:\\|^X-Gnus"
426   "*All headers that match this regexp will be deleted when forwarding a message."
427   :group 'message-forwarding
428   :type '(choice (const :tag "None" nil)
429                  regexp))
430
431 (defcustom message-ignored-cited-headers "."
432   "*Delete these headers from the messages you yank."
433   :group 'message-insertion
434   :type 'regexp)
435
436 (defcustom message-cancel-message "I am canceling my own article.\n"
437   "Message to be inserted in the cancel message."
438   :group 'message-interface
439   :type 'string)
440
441 ;; Useful to set in site-init.el
442 ;;;###autoload
443 (defcustom message-send-mail-function 'message-send-mail-with-sendmail
444   "Function to call to send the current buffer as mail.
445 The headers should be delimited by a line whose contents match the
446 variable `mail-header-separator'.
447
448 Valid values include `message-send-mail-with-sendmail' (the default),
449 `message-send-mail-with-mh', `message-send-mail-with-qmail' and
450 `message-send-mail-with-smtp'."
451   :type '(radio (function-item message-send-mail-with-sendmail)
452                 (function-item message-send-mail-with-mh)
453                 (function-item message-send-mail-with-qmail)
454                 (function-item message-send-mail-with-smtp)
455                 (function :tag "Other"))
456   :group 'message-sending
457   :group 'message-mail)
458
459 ;; 1997-09-29 by MORIOKA Tomohiko
460 (defcustom message-send-news-function 'message-send-news-with-gnus
461   "Function to call to send the current buffer as news.
462 The headers should be delimited by a line whose contents match the
463 variable `mail-header-separator'."
464   :group 'message-sending
465   :group 'message-news
466   :type 'function)
467
468 (defcustom message-reply-to-function nil
469   "Function that should return a list of headers.
470 This function should pick out addresses from the To, Cc, and From headers
471 and respond with new To and Cc headers."
472   :group 'message-interface
473   :type 'function)
474
475 (defcustom message-wide-reply-to-function nil
476   "Function that should return a list of headers.
477 This function should pick out addresses from the To, Cc, and From headers
478 and respond with new To and Cc headers."
479   :group 'message-interface
480   :type 'function)
481
482 (defcustom message-followup-to-function nil
483   "Function that should return a list of headers.
484 This function should pick out addresses from the To, Cc, and From headers
485 and respond with new To and Cc headers."
486   :group 'message-interface
487   :type 'function)
488
489 (defcustom message-use-followup-to 'ask
490   "*Specifies what to do with Followup-To header.
491 If nil, always ignore the header.  If it is t, use its value, but
492 query before using the \"poster\" value.  If it is the symbol `ask',
493 always query the user whether to use the value.  If it is the symbol
494 `use', always use the value."
495   :group 'message-interface
496   :type '(choice (const :tag "ignore" nil)
497                  (const :tag "maybe" t)
498                  (const :tag "always" use)
499                  (const :tag "ask" ask)))
500
501 (defcustom message-use-mail-copies-to 'ask
502   "*Specifies what to do with Mail-Copies-To header.
503 If nil, always ignore the header.  If it is t, use its value, but
504 query before using the value other than \"always\" or \"never\".
505 If it is the symbol `ask', always query the user whether to use
506 the value.  If it is the symbol `use', always use the value."
507   :group 'message-interface
508   :type '(choice (const :tag "ignore" nil)
509                  (const :tag "maybe" t)
510                  (const :tag "always" use)
511                  (const :tag "ask" ask)))
512
513 (defcustom message-use-mail-followup-to 'ask
514   "*Specifies what to do with Mail-Followup-To header.
515 If nil, always ignore the header.  If it is the symbol `ask', always
516 query the user whether to use the value.  If it is t or the symbol
517 `use', always use the value."
518   :group 'message-interface
519   :type '(choice (const :tag "ignore" nil)
520                  (const :tag "maybe" t)
521                  (const :tag "always" use)
522                  (const :tag "ask" ask)))
523
524 ;;; XXX: 'ask and 'use are not implemented yet.
525 (defcustom message-use-mail-reply-to 'ask
526   "*Specifies what to do with Mail-Reply-To/Reply-To header.
527 If nil, always ignore the header.  If it is t or the symbol `use', use
528 its value.  If it is the symbol `ask', always query the user whether to
529 use the value.  Note that if \"Reply-To\" is marked as \"broken\", its value
530 is never used."
531   :group 'message-interface
532   :type '(choice (const :tag "ignore" nil)
533                  (const :tag "maybe" t)
534                  (const :tag "always" use)
535                  (const :tag "ask" ask)))
536
537 (defcustom message-sendmail-f-is-evil nil
538   "*Non-nil means that \"-f username\" should not be added to the sendmail command line.
539 Doing so would be even more evil than leaving it out."
540   :group 'message-sending
541   :type 'boolean)
542
543 ;; qmail-related stuff
544 (defcustom message-qmail-inject-program "/var/qmail/bin/qmail-inject"
545   "Location of the qmail-inject program."
546   :group 'message-sending
547   :type 'file)
548
549 (defcustom message-qmail-inject-args nil
550   "Arguments passed to qmail-inject programs.
551 This should be a list of strings, one string for each argument.
552
553 For e.g., if you wish to set the envelope sender address so that bounces
554 go to the right place or to deal with listserv's usage of that address, you
555 might set this variable to '(\"-f\" \"you@some.where\")."
556   :group 'message-sending
557   :type '(repeat string))
558
559 (defvar message-cater-to-broken-inn t
560   "Non-nil means Gnus should not fold the `References' header.
561 Folding `References' makes ancient versions of INN create incorrect
562 NOV lines.")
563
564 (defvar gnus-post-method)
565 (defvar gnus-select-method)
566 (defcustom message-post-method
567   (cond ((and (boundp 'gnus-post-method)
568               (listp gnus-post-method)
569               gnus-post-method)
570          gnus-post-method)
571         ((boundp 'gnus-select-method)
572          gnus-select-method)
573         (t '(nnspool "")))
574   "*Method used to post news.
575 Note that when posting from inside Gnus, for instance, this
576 variable isn't used."
577   :group 'message-news
578   :group 'message-sending
579   ;; This should be the `gnus-select-method' widget, but that might
580   ;; create a dependence to `gnus.el'.
581   :type 'sexp)
582
583 (defcustom message-generate-headers-first nil
584   "*If non-nil, generate all possible headers before composing."
585   :group 'message-headers
586   :type 'boolean)
587
588 (defcustom message-setup-hook '(turn-on-mime-edit)
589   "Normal hook, run each time a new outgoing message is initialized.
590 The function `message-setup' runs this hook."
591   :group 'message-various
592   :type 'hook)
593
594 (defcustom message-cancel-hook nil
595   "Hook run when cancelling articles."
596   :group 'message-various
597   :type 'hook)
598
599 (defcustom message-signature-setup-hook nil
600   "Normal hook, run each time a new outgoing message is initialized.
601 It is run after the headers have been inserted and before
602 the signature is inserted."
603   :group 'message-various
604   :type 'hook)
605
606 (defcustom message-bounce-setup-hook nil
607   "Normal hook, run each time a re-sending bounced message is initialized.
608 The function `message-bounce' runs this hook."
609   :group 'message-various
610   :type 'hook)
611
612 (defcustom message-supersede-setup-hook nil
613   "Normal hook, run each time a supersede message is initialized.
614 The function `message-supersede' runs this hook."
615   :group 'message-various
616   :type 'hook)
617
618 (defcustom message-mode-hook nil
619   "Hook run in message mode buffers."
620   :group 'message-various
621   :type 'hook)
622
623 (defcustom message-header-hook '((lambda () (eword-encode-header t)))
624   "Hook run in a message mode buffer narrowed to the headers."
625   :group 'message-various
626   :type 'hook)
627
628 (defcustom message-header-setup-hook nil
629   "Hook called narrowed to the headers when setting up a message buffer."
630   :group 'message-various
631   :type 'hook)
632
633 ;;;###autoload
634 (defcustom message-citation-line-function 'message-insert-citation-line
635   "*Function called to insert the \"Whomever writes:\" line."
636   :type 'function
637   :group 'message-insertion)
638
639 ;;;###autoload
640 (defcustom message-yank-prefix "> "
641   "*Prefix inserted on the lines of yanked messages."
642   :type 'string
643   :group 'message-insertion)
644
645 (defcustom message-yank-add-new-references t
646   "Non-nil means new IDs will be added to \"References\" field when an
647 article is yanked by the command `message-yank-original' interactively.
648 If it is a symbol `message-id-only', only an ID from \"Message-ID\" field
649 is used, otherwise IDs extracted from \"References\", \"In-Reply-To\" and
650 \"Message-ID\" fields are used."
651   :type '(radio (const :tag "Do not add anything" nil)
652                 (const :tag "From Message-Id, References and In-Reply-To fields" t)
653                 (const :tag "From only Message-Id field." message-id-only))
654   :group 'message-insertion)
655
656 (defcustom message-list-references-add-position nil
657   "Integer value means position for adding to \"References\" field when
658 an article is yanked by the command `message-yank-original' interactively."
659   :type '(radio (const :tag "Add to last" nil)
660                 (integer :tag "Position from last ID"))
661   :group 'message-insertion)
662
663 (defcustom message-indentation-spaces 3
664   "*Number of spaces to insert at the beginning of each cited line.
665 Used by `message-yank-original' via `message-yank-cite'."
666   :group 'message-insertion
667   :type 'integer)
668
669 ;;;###autoload
670 (defcustom message-cite-function 'message-cite-original
671   "*Function for citing an original message.
672 Predefined functions include `message-cite-original' and
673 `message-cite-original-without-signature'.
674 Note that `message-cite-original' uses `mail-citation-hook' if that is non-nil."
675   :type '(radio (function-item message-cite-original)
676                 (function-item message-cite-original-without-signature)
677                 (function-item mu-cite-original)
678                 (function-item sc-cite-original)
679                 (function :tag "Other"))
680   :group 'message-insertion)
681
682 ;;;###autoload
683 (defcustom message-indent-citation-function 'message-indent-citation
684   "*Function for modifying a citation just inserted in the mail buffer.
685 This can also be a list of functions.  Each function can find the
686 citation between (point) and (mark t).  And each function should leave
687 point and mark around the citation text as modified."
688   :type 'function
689   :group 'message-insertion)
690
691 (defvar message-abbrevs-loaded nil)
692
693 ;;;###autoload
694 (defcustom message-signature t
695   "*String to be inserted at the end of the message buffer.
696 If t, the `message-signature-file' file will be inserted instead.
697 If a function, the result from the function will be used instead.
698 If a form, the result from the form will be used instead."
699   :type 'sexp
700   :group 'message-insertion)
701
702 ;;;###autoload
703 (defcustom message-signature-file "~/.signature"
704   "*File containing the text inserted at end of message buffer."
705   :type 'file
706   :group 'message-insertion)
707
708 (defcustom message-distribution-function nil
709   "*Function called to return a Distribution header."
710   :group 'message-news
711   :group 'message-headers
712   :type 'function)
713
714 (defcustom message-expires 14
715   "Number of days before your article expires."
716   :group 'message-news
717   :group 'message-headers
718   :link '(custom-manual "(message)News Headers")
719   :type 'integer)
720
721 (defcustom message-user-path nil
722   "If nil, use the NNTP server name in the Path header.
723 If stringp, use this; if non-nil, use no host name (user name only)."
724   :group 'message-news
725   :group 'message-headers
726   :link '(custom-manual "(message)News Headers")
727   :type '(choice (const :tag "nntp" nil)
728                  (string :tag "name")
729                  (sexp :tag "none" :format "%t" t)))
730
731 (defvar message-reply-buffer nil)
732 (defvar message-reply-headers nil)
733 (defvar message-sent-message-via nil)
734 (defvar message-checksum nil)
735 (defvar message-send-actions nil
736   "A list of actions to be performed upon successful sending of a message.")
737 (defvar message-exit-actions nil
738   "A list of actions to be performed upon exiting after sending a message.")
739 (defvar message-kill-actions nil
740   "A list of actions to be performed before killing a message buffer.")
741 (defvar message-postpone-actions nil
742   "A list of actions to be performed after postponing a message.")
743 (defvar message-original-frame nil)
744 (defvar message-parameter-alist nil)
745 (defvar message-startup-parameter-alist nil)
746
747 (define-widget 'message-header-lines 'text
748   "All header lines must be LFD terminated."
749   :format "%t:%n%v"
750   :valid-regexp "^\\'"
751   :error "All header lines must be newline terminated")
752
753 (defcustom message-default-headers ""
754   "*A string containing header lines to be inserted in outgoing messages.
755 It is inserted before you edit the message, so you can edit or delete
756 these lines."
757   :group 'message-headers
758   :type 'message-header-lines)
759
760 (defcustom message-default-mail-headers ""
761   "*A string of header lines to be inserted in outgoing mails."
762   :group 'message-headers
763   :group 'message-mail
764   :type 'message-header-lines)
765
766 (defcustom message-default-news-headers ""
767   "*A string of header lines to be inserted in outgoing news articles."
768   :group 'message-headers
769   :group 'message-news
770   :type 'message-header-lines)
771
772 ;; Note: could use /usr/ucb/mail instead of sendmail;
773 ;; options -t, and -v if not interactive.
774 (defcustom message-mailer-swallows-blank-line
775   (if (and (string-match "sparc-sun-sunos\\(\\'\\|[^5]\\)"
776                          system-configuration)
777            (file-readable-p "/etc/sendmail.cf")
778            (let ((buffer (get-buffer-create " *temp*")))
779              (unwind-protect
780                  (save-excursion
781                    (set-buffer buffer)
782                    (insert-file-contents "/etc/sendmail.cf")
783                    (goto-char (point-min))
784                    (let ((case-fold-search nil))
785                      (re-search-forward "^OR\\>" nil t)))
786                (kill-buffer buffer))))
787       ;; According to RFC822, "The field-name must be composed of printable
788       ;; ASCII characters (i. e., characters that have decimal values between
789       ;; 33 and 126, except colon)", i. e., any chars except ctl chars,
790       ;; space, or colon.
791       '(looking-at "[ \t]\\|[][!\"#$%&'()*+,-./0-9;<=>?@A-Z\\\\^_`a-z{|}~]+:"))
792   "*Set this non-nil if the system's mailer runs the header and body together.
793 \(This problem exists on Sunos 4 when sendmail is run in remote mode.)
794 The value should be an expression to test whether the problem will
795 actually occur."
796   :group 'message-sending
797   :type 'sexp)
798
799 ;;; XXX: This symbol is overloaded!  See below.
800 (defvar message-user-agent nil
801   "String of the form of PRODUCT/VERSION.  Used for User-Agent header field.")
802
803 ;; Ignore errors in case this is used in Emacs 19.
804 ;; Don't use ignore-errors because this is copied into loaddefs.el.
805 ;;;###autoload
806 (ignore-errors
807   (define-mail-user-agent 'message-user-agent
808     'message-mail 'message-send-and-exit
809     'message-kill-buffer 'message-send-hook))
810
811 (defvar message-mh-deletable-headers '(Message-ID Date Lines Sender)
812   "If non-nil, delete the deletable headers before feeding to mh.")
813
814 (defvar message-send-method-alist
815   '((news message-news-p message-send-via-news)
816     (mail message-mail-p message-send-via-mail))
817   "Alist of ways to send outgoing messages.
818 Each element has the form
819
820   \(TYPE PREDICATE FUNCTION)
821
822 where TYPE is a symbol that names the method; PREDICATE is a function
823 called without any parameters to determine whether the message is
824 a message of type TYPE; and FUNCTION is a function to be called if
825 PREDICATE returns non-nil.  FUNCTION is called with one parameter --
826 the prefix.")
827
828 (defvar message-mail-alias-type 'abbrev
829   "*What alias expansion type to use in Message buffers.
830 The default is `abbrev', which uses mailabbrev.  nil switches
831 mail aliases off.")
832
833 (defcustom message-auto-save-directory
834   (nnheader-concat message-directory "drafts/")
835   "*Directory where Message auto-saves buffers if Gnus isn't running.
836 If nil, Message won't auto-save."
837   :group 'message-buffers
838   :type 'directory)
839
840 (defcustom message-buffer-naming-style 'unique
841   "*The way new message buffers are named.
842 Valid valued are `unique' and `unsent'."
843   :group 'message-buffers
844   :type '(choice (const :tag "unique" unique)
845                  (const :tag "unsent" unsent)))
846
847 (defcustom message-default-charset nil
848   "Default charset used in non-MULE XEmacsen."
849   :group 'message
850   :type 'symbol)
851
852 (defcustom message-dont-reply-to-names 
853   (and (boundp 'rmail-dont-reply-to-names) rmail-dont-reply-to-names)
854   "*A regexp specifying names to prune when doing wide replies.
855 A value of nil means exclude your own name only."
856   :group 'message
857   :type '(choice (const :tag "Yourself" nil)
858                  regexp))
859
860 ;;; Internal variables.
861 ;;; Well, not really internal.
862
863 (defvar message-mode-syntax-table
864   (let ((table (copy-syntax-table text-mode-syntax-table)))
865     (modify-syntax-entry ?% ". " table)
866     (modify-syntax-entry ?> ". " table)
867     (modify-syntax-entry ?< ". " table)
868     table)
869   "Syntax table used while in Message mode.")
870
871 (defvar message-mode-abbrev-table text-mode-abbrev-table
872   "Abbrev table used in Message mode buffers.
873 Defaults to `text-mode-abbrev-table'.")
874 (defgroup message-headers nil
875   "Message headers."
876   :link '(custom-manual "(message)Variables")
877   :group 'message)
878
879 (defface message-header-to-face
880   '((((class color)
881       (background dark))
882      (:foreground "green2" :bold t))
883     (((class color)
884       (background light))
885      (:foreground "MidnightBlue" :bold t))
886     (t
887      (:bold t :italic t)))
888   "Face used for displaying From headers."
889   :group 'message-faces)
890
891 (defface message-header-cc-face
892   '((((class color)
893       (background dark))
894      (:foreground "green4" :bold t))
895     (((class color)
896       (background light))
897      (:foreground "MidnightBlue"))
898     (t
899      (:bold t)))
900   "Face used for displaying Cc headers."
901   :group 'message-faces)
902
903 (defface message-header-subject-face
904   '((((class color)
905       (background dark))
906      (:foreground "green3"))
907     (((class color)
908       (background light))
909      (:foreground "navy blue" :bold t))
910     (t
911      (:bold t)))
912   "Face used for displaying subject headers."
913   :group 'message-faces)
914
915 (defface message-header-newsgroups-face
916   '((((class color)
917       (background dark))
918      (:foreground "yellow" :bold t :italic t))
919     (((class color)
920       (background light))
921      (:foreground "blue4" :bold t :italic t))
922     (t
923      (:bold t :italic t)))
924   "Face used for displaying newsgroups headers."
925   :group 'message-faces)
926
927 (defface message-header-other-face
928   '((((class color)
929       (background dark))
930      (:foreground "#b00000"))
931     (((class color)
932       (background light))
933      (:foreground "steel blue"))
934     (t
935      (:bold t :italic t)))
936   "Face used for displaying newsgroups headers."
937   :group 'message-faces)
938
939 (defface message-header-name-face
940   '((((class color)
941       (background dark))
942      (:foreground "DarkGreen"))
943     (((class color)
944       (background light))
945      (:foreground "cornflower blue"))
946     (t
947      (:bold t)))
948   "Face used for displaying header names."
949   :group 'message-faces)
950
951 (defface message-header-xheader-face
952   '((((class color)
953       (background dark))
954      (:foreground "blue"))
955     (((class color)
956       (background light))
957      (:foreground "blue"))
958     (t
959      (:bold t)))
960   "Face used for displaying X-Header headers."
961   :group 'message-faces)
962
963 (defface message-separator-face
964   '((((class color)
965       (background dark))
966      (:foreground "blue3"))
967     (((class color)
968       (background light))
969      (:foreground "brown"))
970     (t
971      (:bold t)))
972   "Face used for displaying the separator."
973   :group 'message-faces)
974
975 (defface message-cited-text-face
976   '((((class color)
977       (background dark))
978      (:foreground "red"))
979     (((class color)
980       (background light))
981      (:foreground "red"))
982     (t
983      (:bold t)))
984   "Face used for displaying cited text names."
985   :group 'message-faces)
986
987 (defface message-mml-face
988   '((((class color)
989       (background dark))
990      (:foreground "ForestGreen"))
991     (((class color)
992       (background light))
993      (:foreground "ForestGreen"))
994     (t
995      (:bold t)))
996   "Face used for displaying MML."
997   :group 'message-faces)
998
999 (defvar message-font-lock-fence-open-regexp "[+|]"
1000   "*Regexp that matches fence open string.")
1001
1002 (defvar message-font-lock-fence-close-regexp "|"
1003   "*Regexp that matches fence close string.")
1004
1005 (defvar message-font-lock-fence-open-position nil
1006   "*Cons of SYMBOL of a function or a variable and a number of OFFSET that
1007 indicate the fence open position.  If it is non-nil,
1008 `message-font-lock-fence-open-regexp' is not used for searching for the
1009 fence open position.  If SYMBOL is a function, it is called with one argument
1010 last cursor position and should return the fence open position as a number
1011 or a marker.  If SYMBOL is a variable symbol, the value is examined with
1012 `symbol-value'.  OFFSET is added to the position to compensate the value.
1013 For example, the following combinations of variable symbol and offset value
1014 can be used:
1015
1016 Egg v3: '(egg:*region-start* . -1)
1017 Canna:  '(canna:*region-start* . 0)
1018 ")
1019
1020 (defvar message-font-lock-fence-close-position nil
1021   "*Cons of SYMBOL of a function or a variable and a number of OFFSET that
1022 indicate the fence close position.  If it is non-nil,
1023 `message-font-lock-fence-close-regexp' is not used for searching for the
1024 fence close position.  If SYMBOL is a function, it is called with one argument
1025 last cursor position and should return the fence close position as a number
1026 or a marker.  If SYMBOL is a variable symbol, the value is examined with
1027 `symbol-value'.  OFFSET is added to the position to compensate the value.
1028 For example, the following combinations of variable symbol and offset value
1029 can be used:
1030
1031 Egg v3: '(egg:*region-end* . 0)
1032 Canna:  '(canna:*region-end* . 0)
1033 ")
1034
1035 (defvar message-font-lock-cited-text-regexp
1036   "^[\t ]*\\([^\000- :>|}\177]*\\)[:>|}].*"
1037   "*Regexp that matches cited text.  It should have a grouping for the
1038 citation prefix which is ended at the beginning of citation mark string.")
1039
1040 (defvar message-font-lock-citation-name-max-column 10
1041   "*Maximun number of column for citation name for fontifying.")
1042
1043 (defvar message-font-lock-last-position nil
1044   "Internal buffer local variable to save the last cursor position
1045 before fontifying.")
1046
1047 (eval-after-load "font-lock"
1048   '(defadvice font-lock-after-change-function
1049      (before message-font-lock-save-last-position activate)
1050      "Save last cursor position before fontifying."
1051      (if (eq 'message-mode major-mode)
1052          (setq message-font-lock-last-position (point)))))
1053
1054 (defun message-font-lock-cited-text-matcher (limit)
1055   "Search for a cited text containing `message-font-lock-cited-text-regexp'
1056 forward.  Argument LIMIT bounds the search.  If a cited text is found, it
1057 returns t and sets match data 1 and 2, otherwise it returns nil.  Normally,
1058 match data 2 has zero length, but if the FENCE (for input method) is detected
1059 in matched text, result is divided into match data 1 and 2 across the FENCE.
1060 See also the documentations for the following variables:
1061  `message-font-lock-fence-open-regexp'
1062  `message-font-lock-fence-close-regexp'
1063  `message-font-lock-fence-open-position'
1064  `message-font-lock-fence-close-position'
1065 "
1066   (prog1
1067       (when (re-search-forward message-font-lock-cited-text-regexp limit t)
1068         (let* ((start0 (match-beginning 0))
1069                (end0 (match-end 0))
1070                (cite-mark (match-end 1))
1071                (should-fontify
1072                 (progn
1073                   (goto-char cite-mark)
1074                   (<= (current-column)
1075                       message-font-lock-citation-name-max-column)))
1076                end1 start2)
1077           (and
1078            should-fontify
1079            message-font-lock-last-position
1080            (>= message-font-lock-last-position start0)
1081            (<= message-font-lock-last-position end0)
1082            (cond
1083             (message-font-lock-fence-open-position
1084              (let* ((symbol (car message-font-lock-fence-open-position))
1085                     (open
1086                      (cond ((functionp symbol)
1087                             (funcall symbol message-font-lock-last-position))
1088                            ((and (symbolp symbol)
1089                                  (boundp symbol))
1090                             (symbol-value symbol)))))
1091                (when (markerp open)
1092                  (setq open (marker-position open)))
1093                (and (numberp open)
1094                     (setq open
1095                           (+ open
1096                              (cdr message-font-lock-fence-open-position)))
1097                     (>= message-font-lock-last-position open)
1098                     (goto-char open)
1099                     (or (not message-font-lock-fence-open-regexp)
1100                         (looking-at message-font-lock-fence-open-regexp))
1101                     (setq end1 open))))
1102             (message-font-lock-fence-open-regexp
1103              (goto-char message-font-lock-last-position)
1104              (when (re-search-backward
1105                     message-font-lock-fence-open-regexp start0 t)
1106                (setq end1 (match-beginning 0)))))
1107            (setq should-fontify
1108                  (and message-font-lock-fence-open-position
1109                       (not (eq cite-mark end1))))
1110            (cond
1111             (message-font-lock-fence-close-position
1112              (let* ((symbol (car message-font-lock-fence-close-position))
1113                     (close
1114                      (cond ((functionp symbol)
1115                             (funcall symbol message-font-lock-last-position))
1116                            ((and (symbolp symbol)
1117                                  (boundp symbol))
1118                             (symbol-value symbol)))))
1119                (when (markerp close)
1120                  (setq close (marker-position close)))
1121                (and (numberp close)
1122                     (setq close
1123                           (+ close
1124                              (cdr message-font-lock-fence-close-position)))
1125                     (<= message-font-lock-last-position close)
1126                     (setq start2 close))))
1127             (message-font-lock-fence-close-regexp
1128              (goto-char message-font-lock-last-position)
1129              (when (looking-at message-font-lock-fence-close-regexp)
1130                (setq start2 (match-end 0)))))
1131            (setq should-fontify
1132                  (and (not (and (not message-font-lock-fence-open-position)
1133                                 (eq cite-mark end1)))
1134                       (not (eq cite-mark start2)))))
1135           (goto-char end0)
1136           (when should-fontify
1137             (if start2
1138                 (store-match-data (list start0 end0 start0 end1 start2 end0))
1139               (store-match-data (list start0 end0 start0 end0 end0 end0)))
1140             t)))
1141     (setq message-font-lock-last-position nil)))
1142
1143 (defvar message-font-lock-keywords-1
1144   (let ((content "[ \t]*\\(.+\\(\n[ \t].*\\)*\\)"))
1145     `((,(concat "^\\([Tt]o:\\)" content)
1146        (1 'message-header-name-face)
1147        (2 'message-header-to-face nil t))
1148       (,(concat "^\\([GBF]?[Cc][Cc]:\\|[Rr]eply-[Tt]o:\\|"
1149                 "[Mm]ail-[Cc]opies-[Tt]o:\\|"
1150                 "[Mm]ail-[Rr]eply-[Tt]o:\\|"
1151                 "[Mm]ail-[Ff]ollowup-[Tt]o:\\)" content)
1152        (1 'message-header-name-face)
1153        (2 'message-header-cc-face nil t))
1154       (,(concat "^\\([Ss]ubject:\\)" content)
1155        (1 'message-header-name-face)
1156        (2 'message-header-subject-face nil t))
1157       (,(concat "^\\([Nn]ewsgroups:\\|Followup-[Tt]o:\\)" content)
1158        (1 'message-header-name-face)
1159        (2 'message-header-newsgroups-face nil t))
1160       (,(concat "^\\([A-Z][^: \n\t]+:\\)" content)
1161        (1 'message-header-name-face)
1162        (2 'message-header-other-face nil t))
1163       (,(concat "^\\(X-[A-Za-z0-9-]+\\|In-Reply-To\\):" content)
1164        (1 'message-header-name-face)
1165        (2 'message-header-name-face))
1166       ,@(if (and mail-header-separator
1167                  (not (equal mail-header-separator "")))
1168             `((,(concat "^\\(" (regexp-quote mail-header-separator) "\\)$")
1169                1 'message-separator-face))
1170           nil))))
1171
1172 (defvar message-font-lock-keywords-2
1173   (append message-font-lock-keywords-1
1174           '((message-font-lock-cited-text-matcher
1175              (1 'message-cited-text-face)
1176              (2 'message-cited-text-face))
1177             ("<#/?\\(multipart\\|part\\|external\\).*>"
1178              (0 'message-mml-face)))))
1179
1180 (defvar message-font-lock-keywords message-font-lock-keywords-2
1181   "Additional expressions to highlight in Message mode.")
1182
1183 ;; XEmacs does it like this.  For Emacs, we have to set the
1184 ;; `font-lock-defaults' buffer-local variable.
1185 (put 'message-mode 'font-lock-defaults
1186      '((message-font-lock-keywords
1187         message-font-lock-keywords-1
1188         message-font-lock-keywords-2)
1189        nil nil nil nil
1190        (font-lock-mark-block-function . mark-paragraph)))
1191
1192 (defvar message-face-alist
1193   '((bold . bold-region)
1194     (underline . underline-region)
1195     (default . (lambda (b e)
1196                  (unbold-region b e)
1197                  (ununderline-region b e))))
1198   "Alist of mail and news faces for facemenu.
1199 The cdr of ech entry is a function for applying the face to a region.")
1200
1201 (defcustom message-send-hook nil
1202   "Hook run before sending messages."
1203   :group 'message-various
1204   :options '(ispell-message)
1205   :type 'hook)
1206
1207 (defcustom message-send-mail-hook nil
1208   "Hook run before sending mail messages."
1209   :group 'message-various
1210   :type 'hook)
1211
1212 (defcustom message-send-news-hook nil
1213   "Hook run before sending news messages."
1214   :group 'message-various
1215   :type 'hook)
1216
1217 (defcustom message-sent-hook nil
1218   "Hook run after sending messages."
1219   :group 'message-various
1220   :type 'hook)
1221
1222 (defcustom message-use-multi-frames nil
1223   "Make new frame when sending messages."
1224   :group 'message-frames
1225   :type 'boolean)
1226
1227 (defcustom message-delete-frame-on-exit nil
1228   "Delete frame after sending messages."
1229   :group 'message-frames
1230   :type '(choice (const :tag "off" nil)
1231                  (const :tag "always" t)
1232                  (const :tag "ask" ask)))
1233
1234 (defvar message-draft-coding-system
1235   (cond
1236    ((boundp 'MULE) '*junet*)
1237    ((not (fboundp 'find-coding-system)) nil)
1238    ((find-coding-system 'emacs-mule)
1239     (if (memq system-type '(windows-nt ms-dos ms-windows))
1240         'emacs-mule-dos 'emacs-mule))
1241    ((find-coding-system 'escape-quoted) 'escape-quoted)
1242    ((find-coding-system 'no-conversion) 'no-conversion)
1243    (t nil))
1244   "Coding system to compose mail.")
1245
1246 (defcustom message-send-mail-partially-limit 1000000
1247   "The limitation of messages sent as message/partial.
1248 The lower bound of message size in characters, beyond which the message 
1249 should be sent in several parts. If it is nil, the size is unlimited."
1250   :group 'message-buffers
1251   :type '(choice (const :tag "unlimited" nil)
1252                  (integer 1000000)))
1253
1254 ;;; Internal variables.
1255
1256 (defvar message-buffer-list nil)
1257 (defvar message-this-is-news nil)
1258 (defvar message-this-is-mail nil)
1259 (defvar message-draft-article nil)
1260 (defvar message-mime-part nil)
1261 (defvar message-posting-charset nil)
1262
1263 ;; Byte-compiler warning
1264 (defvar gnus-active-hashtb)
1265 (defvar gnus-read-active-file)
1266
1267 ;;; Regexp matching the delimiter of messages in UNIX mail format
1268 ;;; (UNIX From lines), minus the initial ^.  It should be a copy
1269 ;;; of rmail.el's rmail-unix-mail-delimiter.
1270 (defvar message-unix-mail-delimiter
1271   (let ((time-zone-regexp
1272          (concat "\\([A-Z]?[A-Z]?[A-Z][A-Z]\\( DST\\)?"
1273                  "\\|[-+]?[0-9][0-9][0-9][0-9]"
1274                  "\\|"
1275                  "\\) *")))
1276     (concat
1277      "From "
1278
1279      ;; Many things can happen to an RFC 822 mailbox before it is put into
1280      ;; a `From' line.  The leading phrase can be stripped, e.g.
1281      ;; `Joe <@w.x:joe@y.z>' -> `<@w.x:joe@y.z>'.  The <> can be stripped, e.g.
1282      ;; `<@x.y:joe@y.z>' -> `@x.y:joe@y.z'.  Everything starting with a CRLF
1283      ;; can be removed, e.g.
1284      ;;         From: joe@y.z (Joe      K
1285      ;;                 User)
1286      ;; can yield `From joe@y.z (Joe    K Fri Mar 22 08:11:15 1996', and
1287      ;;         From: Joe User
1288      ;;                 <joe@y.z>
1289      ;; can yield `From Joe User Fri Mar 22 08:11:15 1996'.
1290      ;; The mailbox can be removed or be replaced by white space, e.g.
1291      ;;         From: "Joe User"{space}{tab}
1292      ;;                 <joe@y.z>
1293      ;; can yield `From {space}{tab} Fri Mar 22 08:11:15 1996',
1294      ;; where {space} and {tab} represent the Ascii space and tab characters.
1295      ;; We want to match the results of any of these manglings.
1296      ;; The following regexp rejects names whose first characters are
1297      ;; obviously bogus, but after that anything goes.
1298      "\\([^\0-\b\n-\r\^?].*\\)? "
1299
1300      ;; The time the message was sent.
1301      "\\([^\0-\r \^?]+\\) +"            ; day of the week
1302      "\\([^\0-\r \^?]+\\) +"            ; month
1303      "\\([0-3]?[0-9]\\) +"              ; day of month
1304      "\\([0-2][0-9]:[0-5][0-9]\\(:[0-6][0-9]\\)?\\) *" ; time of day
1305
1306      ;; Perhaps a time zone, specified by an abbreviation, or by a
1307      ;; numeric offset.
1308      time-zone-regexp
1309
1310      ;; The year.
1311      " \\([0-9][0-9]+\\) *"
1312
1313      ;; On some systems the time zone can appear after the year, too.
1314      time-zone-regexp
1315
1316      ;; Old uucp cruft.
1317      "\\(remote from .*\\)?"
1318
1319      "\n"))
1320   "Regexp matching the delimiter of messages in UNIX mail format.")
1321
1322 (defvar message-unsent-separator
1323   (concat "^ *---+ +Unsent message follows +---+ *$\\|"
1324           "^ *---+ +Returned message +---+ *$\\|"
1325           "^Start of returned message$\\|"
1326           "^ *---+ +Original message +---+ *$\\|"
1327           "^ *--+ +begin message +--+ *$\\|"
1328           "^ *---+ +Original message follows +---+ *$\\|"
1329           "^ *---+ +Undelivered message follows +---+ *$\\|"
1330           "^|? *---+ +Message text follows: +---+ *|?$")
1331   "A regexp that matches the separator before the text of a failed message.")
1332
1333 (defvar message-header-format-alist
1334   `((Newsgroups)
1335     (To . message-fill-address)
1336     (Cc . message-fill-address)
1337     (Subject)
1338     (In-Reply-To)
1339     (Fcc)
1340     (Bcc)
1341     (Date)
1342     (Organization)
1343     (Distribution)
1344     (Lines)
1345     (Expires)
1346     (Message-ID)
1347     (References . message-shorten-references)
1348     (User-Agent))
1349   "Alist used for formatting headers.")
1350
1351 (eval-and-compile
1352   (autoload 'message-setup-toolbar "messagexmas")
1353   (autoload 'mh-new-draft-name "mh-comp")
1354   (autoload 'mh-send-letter "mh-comp")
1355   (autoload 'gnus-point-at-eol "gnus-util")
1356   (autoload 'gnus-point-at-bol "gnus-util")
1357   (autoload 'gnus-output-to-rmail "gnus-util")
1358   (autoload 'gnus-output-to-mail "gnus-util")
1359   (autoload 'mail-abbrev-in-expansion-header-p "mailabbrev")
1360   (autoload 'nndraft-request-associate-buffer "nndraft")
1361   (autoload 'nndraft-request-expire-articles "nndraft")
1362   (autoload 'gnus-open-server "gnus-int")
1363   (autoload 'gnus-request-post "gnus-int")
1364   (autoload 'gnus-copy-article-buffer "gnus-msg")
1365   (autoload 'gnus-alive-p "gnus-util")
1366   (autoload 'gnus-group-name-charset "gnus-group")
1367   (autoload 'rmail-output "rmail")
1368   (autoload 'mu-cite-original "mu-cite"))
1369
1370 \f
1371
1372 ;;;
1373 ;;; Utility functions.
1374 ;;;
1375 (defun message-eval-parameter (parameter)
1376   (condition-case ()
1377       (if (symbolp parameter)
1378           (if (functionp parameter)
1379               (funcall parameter)
1380             (eval parameter))
1381         parameter)
1382     (error nil)))
1383
1384 (defsubst message-get-parameter (key &optional alist)
1385   (unless alist
1386     (setq alist message-parameter-alist))
1387   (cdr (assq key alist)))
1388
1389 (defmacro message-get-parameter-with-eval (key &optional alist)
1390   `(message-eval-parameter (message-get-parameter ,key ,alist)))
1391
1392 (defmacro message-y-or-n-p (question show &rest text)
1393   "Ask QUESTION, displaying the rest of the arguments in a temp. buffer if SHOW"
1394   `(message-talkative-question 'y-or-n-p ,question ,show ,@text))
1395
1396 ;; Delete the current line (and the next N lines.);
1397 (defmacro message-delete-line (&optional n)
1398   `(delete-region (progn (beginning-of-line) (point))
1399                   (progn (forward-line ,(or n 1)) (point))))
1400
1401 (defun message-unquote-tokens (elems)
1402   "Remove double quotes (\") from strings in list."
1403   (mapcar (lambda (item)
1404             (while (string-match "^\\(.*\\)\"\\(.*\\)$" item)
1405               (setq item (concat (match-string 1 item) 
1406                                  (match-string 2 item))))
1407             item)
1408           elems))
1409
1410 (defun message-tokenize-header (header &optional separator)
1411   "Split HEADER into a list of header elements.
1412 SEPARATOR is a string of characters to be used as separators.  \",\"
1413 is used by default."
1414   (if (not header)
1415       nil
1416     (let ((regexp (format "[%s]+" (or separator ",")))
1417           (beg 1)
1418           (first t)
1419           quoted elems paren)
1420       (save-excursion
1421         (message-set-work-buffer)
1422         (insert header)
1423         (goto-char (point-min))
1424         (while (not (eobp))
1425           (if first
1426               (setq first nil)
1427             (forward-char 1))
1428           (cond ((and (> (point) beg)
1429                       (or (eobp)
1430                           (and (looking-at regexp)
1431                                (not quoted)
1432                                (not paren))))
1433                  (push (buffer-substring beg (point)) elems)
1434                  (setq beg (match-end 0)))
1435                 ((eq (char-after) ?\")
1436                  (setq quoted (not quoted)))
1437                 ((and (eq (char-after) ?\()
1438                       (not quoted))
1439                  (setq paren t))
1440                 ((and (eq (char-after) ?\))
1441                       (not quoted))
1442                  (setq paren nil))))
1443         (nreverse elems)))))
1444
1445 (defun message-mail-file-mbox-p (file)
1446   "Say whether FILE looks like a Unix mbox file."
1447   (when (and (file-exists-p file)
1448              (file-readable-p file)
1449              (file-regular-p file))
1450     (with-temp-buffer
1451       (nnheader-insert-file-contents file)
1452       (goto-char (point-min))
1453       (looking-at message-unix-mail-delimiter))))
1454
1455 (defun message-fetch-field (header &optional not-all)
1456   "The same as `mail-fetch-field', only remove all newlines."
1457   (let* ((inhibit-point-motion-hooks t)
1458          (case-fold-search t)
1459          (value (mail-fetch-field header nil (not not-all))))
1460     (when value
1461       (while (string-match "\n[\t ]+" value)
1462         (setq value (replace-match " " t t value)))
1463       (set-text-properties 0 (length value) nil value)
1464       value)))
1465
1466 (defun message-narrow-to-field ()
1467   "Narrow the buffer to the header on the current line."
1468   (beginning-of-line)
1469   (narrow-to-region
1470    (point)
1471    (progn
1472      (forward-line 1)
1473      (if (re-search-forward "^[^ \n\t]" nil t)
1474          (progn
1475            (beginning-of-line)
1476            (point))
1477        (point-max))))
1478   (goto-char (point-min)))
1479
1480 (defun message-add-header (&rest headers)
1481   "Add the HEADERS to the message header, skipping those already present."
1482   (while headers
1483     (let (hclean)
1484       (unless (string-match "^\\([^:]+\\):[ \t]*[^ \t]" (car headers))
1485         (error "Invalid header `%s'" (car headers)))
1486       (setq hclean (match-string 1 (car headers)))
1487       (save-restriction
1488         (message-narrow-to-headers)
1489         (unless (re-search-forward (concat "^" (regexp-quote hclean) ":") nil t)
1490           (insert (car headers) ?\n))))
1491     (setq headers (cdr headers))))
1492
1493
1494 (defun message-fetch-reply-field (header)
1495   "Fetch FIELD from the message we're replying to."
1496   (let ((buffer (message-eval-parameter message-reply-buffer)))
1497     (when (and buffer
1498                (buffer-name buffer))
1499       (save-excursion
1500         (set-buffer buffer)
1501         (message-fetch-field header)))))
1502
1503 (defun message-set-work-buffer ()
1504   (if (get-buffer " *message work*")
1505       (progn
1506         (set-buffer " *message work*")
1507         (erase-buffer))
1508     (set-buffer (get-buffer-create " *message work*"))
1509     (kill-all-local-variables)))
1510
1511 (defun message-functionp (form)
1512   "Return non-nil if FORM is funcallable."
1513   (or (and (symbolp form) (fboundp form))
1514       (and (listp form) (eq (car form) 'lambda))
1515       (byte-code-function-p form)))
1516
1517 (defun message-strip-list-identifiers (subject)
1518   "Remove list identifiers in `gnus-list-identifiers'."
1519   (require 'gnus-sum)                   ; for gnus-list-identifiers
1520   (let ((regexp (if (stringp gnus-list-identifiers)
1521                     gnus-list-identifiers
1522                   (mapconcat 'identity gnus-list-identifiers " *\\|"))))
1523     (if (string-match (concat "\\(\\(\\(Re: +\\)?\\(" regexp 
1524                                 " *\\)\\)+\\(Re: +\\)?\\)") subject)
1525         (concat (substring subject 0 (match-beginning 1))
1526                 (or (match-string 3 subject)
1527                     (match-string 5 subject))
1528                 (substring subject
1529                            (match-end 1)))
1530       subject)))
1531
1532 (defun message-strip-subject-re (subject)
1533   "Remove \"Re:\" from subject lines."
1534   (if (string-match message-subject-re-regexp subject)
1535       (substring subject (match-end 0))
1536     subject))
1537
1538 (defun message-remove-header (header &optional is-regexp first reverse)
1539   "Remove HEADER in the narrowed buffer.
1540 If REGEXP, HEADER is a regular expression.
1541 If FIRST, only remove the first instance of the header.
1542 Return the number of headers removed."
1543   (goto-char (point-min))
1544   (let ((regexp (if is-regexp header (concat "^" (regexp-quote header) ":")))
1545         (number 0)
1546         (case-fold-search t)
1547         last)
1548     (while (and (not (eobp))
1549                 (not last))
1550       (if (if reverse
1551               (not (looking-at regexp))
1552             (looking-at regexp))
1553           (progn
1554             (incf number)
1555             (when first
1556               (setq last t))
1557             (delete-region
1558              (point)
1559              ;; There might be a continuation header, so we have to search
1560              ;; until we find a new non-continuation line.
1561              (progn
1562                (forward-line 1)
1563                (if (re-search-forward "^[^ \t]" nil t)
1564                    (goto-char (match-beginning 0))
1565                  (point-max)))))
1566         (forward-line 1)
1567         (if (re-search-forward "^[^ \t]" nil t)
1568             (goto-char (match-beginning 0))
1569           (goto-char (point-max)))))
1570     number))
1571
1572 (defun message-remove-first-header (header)
1573   "Remove the first instance of HEADER if there is more than one."
1574   (let ((count 0)
1575         (regexp (concat "^" (regexp-quote header) ":")))
1576     (save-excursion
1577       (goto-char (point-min))
1578       (while (re-search-forward regexp nil t)
1579         (incf count)))
1580     (while (> count 1)
1581       (message-remove-header header nil t)
1582       (decf count))))
1583
1584 (defun message-narrow-to-headers ()
1585   "Narrow the buffer to the head of the message."
1586   (widen)
1587   (narrow-to-region
1588    (goto-char (point-min))
1589    (if (re-search-forward
1590         (concat "^" (regexp-quote mail-header-separator) "\n") nil t)
1591        (match-beginning 0)
1592      (point-max)))
1593   (goto-char (point-min)))
1594
1595 (defun message-narrow-to-head ()
1596   "Narrow the buffer to the head of the message.
1597 Point is left at the beginning of the narrowed-to region."
1598   (widen)
1599   (narrow-to-region
1600    (goto-char (point-min))
1601    (if (search-forward "\n\n" nil 1)
1602        (1- (point))
1603      (point-max)))
1604   (goto-char (point-min)))
1605
1606 (defun message-narrow-to-headers-or-head ()
1607   "Narrow the buffer to the head of the message."
1608   (widen)
1609   (narrow-to-region
1610    (goto-char (point-min))
1611    (cond
1612     ((re-search-forward
1613       (concat "^" (regexp-quote mail-header-separator) "\n") nil t)
1614      (match-beginning 0))
1615     ((search-forward "\n\n" nil t)
1616      (1- (point)))
1617     (t
1618      (point-max))))
1619   (goto-char (point-min)))
1620
1621 (defun message-news-p ()
1622   "Say whether the current buffer contains a news message."
1623   (and (not message-this-is-mail)
1624        (or message-this-is-news
1625            (save-excursion
1626              (save-restriction
1627                (message-narrow-to-headers)
1628                (and (message-fetch-field "newsgroups")
1629                     (not (message-fetch-field "posted-to"))))))))
1630
1631 (defun message-mail-p ()
1632   "Say whether the current buffer contains a mail message."
1633   (and (not message-this-is-news)
1634        (or message-this-is-mail
1635            (save-excursion
1636              (save-restriction
1637                (message-narrow-to-headers)
1638                (or (message-fetch-field "to")
1639                    (message-fetch-field "cc")
1640                    (message-fetch-field "bcc")))))))
1641
1642 (defun message-next-header ()
1643   "Go to the beginning of the next header."
1644   (beginning-of-line)
1645   (or (eobp) (forward-char 1))
1646   (not (if (re-search-forward "^[^ \t]" nil t)
1647            (beginning-of-line)
1648          (goto-char (point-max)))))
1649
1650 (defun message-sort-headers-1 ()
1651   "Sort the buffer as headers using `message-rank' text props."
1652   (goto-char (point-min))
1653   (require 'sort)
1654   (sort-subr
1655    nil 'message-next-header
1656    (lambda ()
1657      (message-next-header)
1658      (unless (bobp)
1659        (forward-char -1)))
1660    (lambda ()
1661      (or (get-text-property (point) 'message-rank)
1662          10000))))
1663
1664 (defun message-sort-headers ()
1665   "Sort the headers of the current message according to `message-header-format-alist'."
1666   (interactive)
1667   (save-excursion
1668     (save-restriction
1669       (let ((max (1+ (length message-header-format-alist)))
1670             rank)
1671         (message-narrow-to-headers)
1672         (while (re-search-forward "^[^ \n]+:" nil t)
1673           (put-text-property
1674            (match-beginning 0) (1+ (match-beginning 0))
1675            'message-rank
1676            (if (setq rank (length (memq (assq (intern (buffer-substring
1677                                                        (match-beginning 0)
1678                                                        (1- (match-end 0))))
1679                                               message-header-format-alist)
1680                                         message-header-format-alist)))
1681                (- max rank)
1682              (1+ max)))))
1683       (message-sort-headers-1))))
1684
1685 \f
1686
1687 ;;;
1688 ;;; Message mode
1689 ;;;
1690
1691 ;;; Set up keymap.
1692
1693 (defvar message-mode-map nil)
1694
1695 (unless message-mode-map
1696   (setq message-mode-map (make-keymap))
1697   (set-keymap-parent message-mode-map text-mode-map)
1698   (define-key message-mode-map "\C-c?" 'describe-mode)
1699
1700   (define-key message-mode-map "\C-c\C-f\C-t" 'message-goto-to)
1701   (define-key message-mode-map "\C-c\C-f\C-b" 'message-goto-bcc)
1702   (define-key message-mode-map "\C-c\C-f\C-w" 'message-goto-fcc)
1703   (define-key message-mode-map "\C-c\C-f\C-c" 'message-goto-cc)
1704   (define-key message-mode-map "\C-c\C-f\C-s" 'message-goto-subject)
1705   ;; (define-key message-mode-map "\C-c\C-f\C-r" 'message-goto-reply-to)
1706   (define-key message-mode-map "\C-c\C-f\C-r" 'message-goto-mail-reply-to)
1707   (define-key message-mode-map "\C-c\C-f\C-m" 'message-goto-mail-followup-to)
1708   (define-key message-mode-map "\C-c\C-f\C-n" 'message-goto-newsgroups)
1709   (define-key message-mode-map "\C-c\C-f\C-d" 'message-goto-distribution)
1710   (define-key message-mode-map "\C-c\C-f\C-f" 'message-goto-followup-to)
1711   (define-key message-mode-map "\C-c\C-f\C-k" 'message-goto-keywords)
1712   (define-key message-mode-map "\C-c\C-f\C-u" 'message-goto-summary)
1713   (define-key message-mode-map "\C-c\C-b" 'message-goto-body)
1714   (define-key message-mode-map "\C-c\C-i" 'message-goto-signature)
1715   (define-key message-mode-map "\C-c\C-fc" 'message-goto-mail-copies-to)
1716
1717   (define-key message-mode-map "\C-c\C-t" 'message-insert-to)
1718   (define-key message-mode-map "\C-c\C-n" 'message-insert-newsgroups)
1719
1720   (define-key message-mode-map "\C-c\C-y" 'message-yank-original)
1721   (define-key message-mode-map "\C-c\M-\C-y" 'message-yank-buffer)
1722   (define-key message-mode-map "\C-c\C-q" 'message-fill-yanked-message)
1723   (define-key message-mode-map "\C-c\C-w" 'message-insert-signature)
1724   (define-key message-mode-map "\C-c\M-h" 'message-insert-headers)
1725   (define-key message-mode-map "\C-c\C-r" 'message-caesar-buffer-body)
1726   (define-key message-mode-map "\C-c\C-o" 'message-sort-headers)
1727   (define-key message-mode-map "\C-c\M-r" 'message-rename-buffer)
1728
1729   (define-key message-mode-map "\C-c\C-c" 'message-send-and-exit)
1730   (define-key message-mode-map "\C-c\C-s" 'message-send)
1731   (define-key message-mode-map "\C-c\C-k" 'message-kill-buffer)
1732   (define-key message-mode-map "\C-c\C-d" 'message-dont-send)
1733
1734   (define-key message-mode-map "\C-c\C-e" 'message-elide-region)
1735   (define-key message-mode-map "\C-c\C-v" 'message-delete-not-region)
1736   (define-key message-mode-map "\C-c\C-z" 'message-kill-to-signature)
1737   (define-key message-mode-map "\M-\r" 'message-newline-and-reformat)
1738
1739   (define-key message-mode-map "\t" 'message-tab)
1740
1741   (define-key message-mode-map "\C-x\C-s" 'message-save-drafts)
1742   (define-key message-mode-map "\C-xk" 'message-mimic-kill-buffer))
1743
1744 (easy-menu-define
1745  message-mode-menu message-mode-map "Message Menu."
1746  '("Message"
1747    ["Sort Headers" message-sort-headers t]
1748    ["Yank Original" message-yank-original t]
1749    ["Fill Yanked Message" message-fill-yanked-message t]
1750    ["Insert Signature" message-insert-signature t]
1751    ["Caesar (rot13) Message" message-caesar-buffer-body t]
1752    ["Caesar (rot13) Region" message-caesar-region (mark t)]
1753    ["Elide Region" message-elide-region (mark t)]
1754    ["Delete Outside Region" message-delete-not-region (mark t)]
1755    ["Kill To Signature" message-kill-to-signature t]
1756    ["Newline and Reformat" message-newline-and-reformat t]
1757    ["Rename buffer" message-rename-buffer t]
1758    ["Spellcheck" ispell-message t]
1759    ["Attach file as MIME" mime-edit-insert-file t]
1760    "----"
1761    ["Send Message" message-send-and-exit t]
1762    ["Abort Message" message-dont-send t]
1763    ["Kill Message" message-kill-buffer t]))
1764
1765 (easy-menu-define
1766  message-mode-field-menu message-mode-map ""
1767  '("Field"
1768    ["Fetch To" message-insert-to t]
1769    ["Fetch Newsgroups" message-insert-newsgroups t]
1770    "----"
1771    ["To" message-goto-to t]
1772    ["Subject" message-goto-subject t]
1773    ["Cc" message-goto-cc t]
1774    ["Reply-To" message-goto-reply-to t]
1775    ["Mail-Reply-To" message-goto-mail-reply-to t]
1776    ["Mail-Followup-To" message-goto-mail-followup-to t]
1777    ["Mail-Copies-To" message-goto-mail-copies-to t]
1778    ["Summary" message-goto-summary t]
1779    ["Keywords" message-goto-keywords t]
1780    ["Newsgroups" message-goto-newsgroups t]
1781    ["Followup-To" message-goto-followup-to t]
1782    ["Distribution" message-goto-distribution t]
1783    ["Body" message-goto-body t]
1784    ["Signature" message-goto-signature t]))
1785
1786 (defvar facemenu-add-face-function)
1787 (defvar facemenu-remove-face-function)
1788
1789 ;;;###autoload
1790 (defun message-mode ()
1791   "Major mode for editing mail and news to be sent.
1792 Like Text Mode but with these additional commands:
1793 C-c C-s  message-send (send the message)    C-c C-c  message-send-and-exit
1794 C-c C-d  Pospone sending the message        C-c C-k  Kill the message
1795 C-c C-f  move to a header field (and create it if there isn't):
1796          C-c C-f C-t  move to To        C-c C-f C-s  move to Subject
1797          C-c C-f C-c  move to Cc        C-c C-f C-b  move to Bcc
1798          C-c C-f C-w  move to Fcc       C-c C-f C-r  move to Reply-To
1799          C-c C-f C-u  move to Summary   C-c C-f C-n  move to Newsgroups
1800          C-c C-f C-k  move to Keywords  C-c C-f C-d  move to Distribution
1801          C-c C-f C-m  move to Mail-Followup-To
1802          C-c C-f C-f  move to Followup-To
1803 C-c C-t  message-insert-to (add a To header to a news followup)
1804 C-c C-n  message-insert-newsgroups (add a Newsgroup header to a news reply)
1805 C-c C-b  message-goto-body (move to beginning of message text).
1806 C-c C-i  message-goto-signature (move to the beginning of the signature).
1807 C-c C-w  message-insert-signature (insert `message-signature-file' file).
1808 C-c C-y  message-yank-original (insert current message, if any).
1809 C-c C-q  message-fill-yanked-message (fill what was yanked).
1810 C-c C-e  message-elide-region (elide the text between point and mark).
1811 C-c C-v  message-delete-not-region (remove the text outside the region).
1812 C-c C-z  message-kill-to-signature (kill the text up to the signature).
1813 C-c C-r  message-caesar-buffer-body (rot13 the message body).
1814 M-RET    message-newline-and-reformat (break the line and reformat)."
1815   (interactive)
1816   (kill-all-local-variables)
1817   (set (make-local-variable 'message-reply-buffer) nil)
1818   (make-local-variable 'message-send-actions)
1819   (make-local-variable 'message-exit-actions)
1820   (make-local-variable 'message-kill-actions)
1821   (make-local-variable 'message-postpone-actions)
1822   (make-local-variable 'message-draft-article)
1823   (make-local-hook 'kill-buffer-hook)
1824   (set-syntax-table message-mode-syntax-table)
1825   (use-local-map message-mode-map)
1826   (setq local-abbrev-table message-mode-abbrev-table)
1827   (setq major-mode 'message-mode)
1828   (setq mode-name "Message")
1829   (setq buffer-offer-save t)
1830   (make-local-variable 'facemenu-add-face-function)
1831   (make-local-variable 'facemenu-remove-face-function)
1832   (setq facemenu-add-face-function
1833         (lambda (face end)
1834           (let ((face-fun (cdr (assq face message-face-alist))))
1835             (if face-fun
1836                 (funcall face-fun (point) end)
1837               (error "Face %s not configured for %s mode" face mode-name)))
1838           "")
1839         facemenu-remove-face-function t)
1840   (make-local-variable 'paragraph-separate)
1841   (make-local-variable 'paragraph-start)
1842   ;; `-- ' precedes the signature.  `-----' appears at the start of the
1843   ;; lines that delimit forwarded messages.
1844   ;; Lines containing just >= 3 dashes, perhaps after whitespace,
1845   ;; are also sometimes used and should be separators.
1846   (setq paragraph-start
1847         (concat (regexp-quote mail-header-separator)
1848                 "$\\|[ \t]*[a-z0-9A-Z]*>+[ \t]*$\\|[ \t]*$\\|"
1849                 "-- $\\|---+$\\|"
1850                 page-delimiter
1851                 ;;!!! Uhm... shurely this can't be right?
1852                 "[> " (regexp-quote message-yank-prefix) "]+$"))
1853   (setq paragraph-separate paragraph-start)
1854   (make-local-variable 'message-reply-headers)
1855   (setq message-reply-headers nil)
1856   (make-local-variable 'message-user-agent)
1857   (make-local-variable 'message-post-method)
1858   (set (make-local-variable 'message-sent-message-via) nil)
1859   (set (make-local-variable 'message-checksum) nil)
1860   (make-local-variable 'message-parameter-alist)
1861   (setq message-parameter-alist
1862         (copy-sequence message-startup-parameter-alist))
1863   ;;(when (fboundp 'mail-hist-define-keys)
1864   ;;  (mail-hist-define-keys))
1865   (if (featurep 'xemacs)
1866       (message-setup-toolbar)
1867     (set (make-local-variable 'font-lock-defaults)
1868          '((message-font-lock-keywords
1869             message-font-lock-keywords-1
1870             message-font-lock-keywords-2)
1871            nil nil nil nil
1872            (font-lock-mark-block-function . mark-paragraph))))
1873   (set (make-local-variable 'message-font-lock-last-position) nil)
1874   (easy-menu-add message-mode-menu message-mode-map)
1875   (easy-menu-add message-mode-field-menu message-mode-map)
1876   ;; Allow mail alias things.
1877   (when (eq message-mail-alias-type 'abbrev)
1878     (if (fboundp 'mail-abbrevs-setup)
1879         (mail-abbrevs-setup)
1880       (mail-aliases-setup)))
1881   (message-set-auto-save-file-name)
1882   (make-local-variable 'adaptive-fill-regexp)
1883   (setq adaptive-fill-regexp
1884         (concat "[ \t]*[-a-z0-9A-Z]*\\(>[ \t]*\\)+[ \t]*\\|"
1885                 adaptive-fill-regexp))
1886   (unless (boundp 'adaptive-fill-first-line-regexp)
1887     (setq adaptive-fill-first-line-regexp nil))
1888   (make-local-variable 'adaptive-fill-first-line-regexp)
1889   (setq adaptive-fill-first-line-regexp
1890         (concat "[ \t]*[-a-z0-9A-Z]*\\(>[ \t]*\\)+[ \t]*\\|"
1891                 adaptive-fill-first-line-regexp))
1892   (make-local-variable 'auto-fill-inhibit-regexp)
1893   (setq auto-fill-inhibit-regexp "^[A-Z][^: \n\t]+:")
1894   (make-local-variable 'indent-tabs-mode) ;Turn off tabs for indentation.
1895   (setq indent-tabs-mode nil)
1896   (run-hooks 'text-mode-hook 'message-mode-hook))
1897
1898 \f
1899
1900 ;;;
1901 ;;; Message mode commands
1902 ;;;
1903
1904 ;;; Movement commands
1905
1906 (defun message-goto-to ()
1907   "Move point to the To header."
1908   (interactive)
1909   (message-position-on-field "To"))
1910
1911 (defun message-goto-subject ()
1912   "Move point to the Subject header."
1913   (interactive)
1914   (message-position-on-field "Subject"))
1915
1916 (defun message-goto-cc ()
1917   "Move point to the Cc header."
1918   (interactive)
1919   (message-position-on-field "Cc" "To"))
1920
1921 (defun message-goto-bcc ()
1922   "Move point to the Bcc  header."
1923   (interactive)
1924   (message-position-on-field "Bcc" "Cc" "To"))
1925
1926 (defun message-goto-fcc ()
1927   "Move point to the Fcc header."
1928   (interactive)
1929   (message-position-on-field "Fcc" "To" "Newsgroups"))
1930
1931 (defun message-goto-reply-to ()
1932   "Move point to the Reply-To header."
1933   (interactive)
1934   (message-position-on-field "Reply-To" "Subject"))
1935
1936 (defun message-goto-mail-reply-to ()
1937   "Move point to the Mail-Reply-To header."
1938   (interactive)
1939   (message-position-on-field "Mail-Reply-To" "Subject"))
1940
1941 (defun message-goto-mail-followup-to ()
1942   "Move point to the Mail-Followup-To header.  If the header is newly created
1943 and To field contains only one address, the address is inserted in default."
1944   (interactive)
1945   (unless (message-position-on-field "Mail-Followup-To" "Subject")
1946     (let ((start (point))
1947           addresses)
1948       (save-restriction
1949         (message-narrow-to-headers)
1950         (setq addresses (split-string (mail-strip-quoted-names
1951                                        (or (std11-fetch-field "to") ""))
1952                                       "[ \f\t\n\r\v,]+"))
1953         (when (eq 1 (length addresses))
1954           (goto-char start)
1955           (insert (car addresses))
1956           (goto-char start))))))
1957
1958 (defun message-goto-mail-copies-to ()
1959   "Move point to the Mail-Copies-To header.  If the header is newly created,
1960 a string \"never\" is inserted in default."
1961   (interactive)
1962   (unless (message-position-on-field "Mail-Copies-To" "Subject")
1963     (insert "never")
1964     (backward-char 5)))
1965
1966 (defun message-goto-newsgroups ()
1967   "Move point to the Newsgroups header."
1968   (interactive)
1969   (message-position-on-field "Newsgroups"))
1970
1971 (defun message-goto-distribution ()
1972   "Move point to the Distribution header."
1973   (interactive)
1974   (message-position-on-field "Distribution"))
1975
1976 (defun message-goto-followup-to ()
1977   "Move point to the Followup-To header."
1978   (interactive)
1979   (message-position-on-field "Followup-To" "Newsgroups"))
1980
1981 (defun message-goto-keywords ()
1982   "Move point to the Keywords header."
1983   (interactive)
1984   (message-position-on-field "Keywords" "Subject"))
1985
1986 (defun message-goto-summary ()
1987   "Move point to the Summary header."
1988   (interactive)
1989   (message-position-on-field "Summary" "Subject"))
1990
1991 (defun message-goto-body ()
1992   "Move point to the beginning of the message body."
1993   (interactive)
1994   (if (looking-at "[ \t]*\n") (expand-abbrev))
1995   (goto-char (point-min))
1996   (or (search-forward (concat "\n" mail-header-separator "\n") nil t)
1997       (search-forward "\n\n" nil t)))
1998
1999 (defun message-goto-eoh ()
2000   "Move point to the end of the headers."
2001   (interactive)
2002   (message-goto-body)
2003   (forward-line -1))
2004
2005 (defun message-goto-signature ()
2006   "Move point to the beginning of the message signature.
2007 If there is no signature in the article, go to the end and
2008 return nil."
2009   (interactive)
2010   (goto-char (point-min))
2011   (if (re-search-forward message-signature-separator nil t)
2012       (forward-line 1)
2013     (goto-char (point-max))
2014     nil))
2015
2016 \f
2017
2018 (defun message-insert-to (&optional force)
2019   "Insert a To header that points to the author of the article being replied to.
2020 If the original author requested not to be sent mail, the function signals
2021 an error.
2022 With the prefix argument FORCE, insert the header anyway."
2023   (interactive "P")
2024   (let ((co (message-fetch-reply-field "mail-copies-to")))
2025     (when (and (null force)
2026                co
2027                (or (equal (downcase co) "never")
2028                    (equal (downcase co) "nobody")))
2029       (error "The user has requested not to have copies sent via mail")))
2030   (when (and (message-position-on-field "To")
2031              (mail-fetch-field "to")
2032              (not (string-match "\\` *\\'" (mail-fetch-field "to"))))
2033     (insert ", "))
2034   (insert (or (message-fetch-reply-field "reply-to")
2035               (message-fetch-reply-field "from") "")))
2036
2037 (defun message-widen-reply ()
2038   "Widen the reply to include maximum recipients."
2039   (interactive)
2040   (let ((follow-to
2041          (and message-reply-buffer
2042               (buffer-name message-reply-buffer)
2043               (save-excursion
2044                 (set-buffer message-reply-buffer)
2045                 (message-get-reply-headers t)))))
2046     (save-excursion
2047       (save-restriction
2048         (message-narrow-to-headers)
2049         (dolist (elem follow-to)
2050           (message-remove-header (symbol-name (car elem)))
2051           (goto-char (point-min))
2052           (insert (symbol-name (car elem)) ": "
2053                   (cdr elem) "\n"))))))
2054
2055 (defun message-insert-newsgroups ()
2056   "Insert the Newsgroups header from the article being replied to."
2057   (interactive)
2058   (when (and (message-position-on-field "Newsgroups")
2059              (mail-fetch-field "newsgroups")
2060              (not (string-match "\\` *\\'" (mail-fetch-field "newsgroups"))))
2061     (insert ","))
2062   (insert (or (message-fetch-reply-field "newsgroups") "")))
2063
2064 \f
2065
2066 ;;; Various commands
2067
2068 (defun message-delete-not-region (beg end)
2069   "Delete everything in the body of the current message that is outside of the region."
2070   (interactive "r")
2071   (save-excursion
2072     (goto-char end)
2073     (delete-region (point) (if (not (message-goto-signature))
2074                                (point)
2075                              (forward-line -2)
2076                              (point)))
2077     (insert "\n")
2078     (goto-char beg)
2079     (delete-region beg (progn (message-goto-body)
2080                               (forward-line 2)
2081                               (point))))
2082   (when (message-goto-signature)
2083     (forward-line -2)))
2084
2085 (defun message-kill-to-signature ()
2086   "Deletes all text up to the signature."
2087   (interactive)
2088   (let ((point (point)))
2089     (message-goto-signature)
2090     (unless (eobp)
2091       (forward-line -2))
2092     (kill-region point (point))
2093     (unless (bolp)
2094       (insert "\n"))))
2095
2096 (defun message-newline-and-reformat ()
2097   "Insert four newlines, and then reformat if inside quoted text."
2098   (interactive)
2099   (let ((prefix "[]>»|:}+ \t]*")
2100         (supercite-thing "[-._a-zA-Z0-9]*[>]+[ \t]*")
2101         quoted point)
2102     (unless (bolp)
2103       (save-excursion
2104         (beginning-of-line)
2105         (when (looking-at (concat prefix
2106                                   supercite-thing))
2107           (setq quoted (match-string 0))))
2108       (insert "\n"))
2109     (setq point (point))
2110     (insert "\n\n\n")
2111     (delete-region (point) (re-search-forward "[ \t]*"))
2112     (when quoted
2113       (insert quoted))
2114     (fill-paragraph nil)
2115     (goto-char point)
2116     (forward-line 1)))
2117
2118 (defun message-insert-signature (&optional force)
2119   "Insert a signature.  See documentation for the `message-signature' variable."
2120   (interactive (list 0))
2121   (let* ((signature
2122           (cond
2123            ((and (null message-signature)
2124                  (eq force 0))
2125             (save-excursion
2126               (goto-char (point-max))
2127               (not (re-search-backward message-signature-separator nil t))))
2128            ((and (null message-signature)
2129                  force)
2130             t)
2131            ((message-functionp message-signature)
2132             (funcall message-signature))
2133            ((listp message-signature)
2134             (eval message-signature))
2135            (t message-signature)))
2136          (signature
2137           (cond ((stringp signature)
2138                  signature)
2139                 ((and (eq t signature)
2140                       message-signature-file
2141                       (file-exists-p message-signature-file))
2142                  signature))))
2143     (when signature
2144       (goto-char (point-max))
2145       ;; Insert the signature.
2146       (unless (bolp)
2147         (insert "\n"))
2148       (insert "\n-- \n")
2149       (if (eq signature t)
2150           (insert-file-contents message-signature-file)
2151         (insert signature))
2152       (goto-char (point-max))
2153       (or (bolp) (insert "\n")))))
2154
2155 (defun message-elide-region (b e)
2156   "Elide the text between point and mark.
2157 An ellipsis (from `message-elide-ellipsis') will be inserted where the
2158 text was killed."
2159   (interactive "r")
2160   (kill-region b e)
2161   (insert message-elide-ellipsis))
2162
2163 (defvar message-caesar-translation-table nil)
2164
2165 (defun message-caesar-region (b e &optional n)
2166   "Caesar rotation of region by N, default 13, for decrypting netnews."
2167   (interactive
2168    (list
2169     (min (point) (or (mark t) (point)))
2170     (max (point) (or (mark t) (point)))
2171     (when current-prefix-arg
2172       (prefix-numeric-value current-prefix-arg))))
2173
2174   (setq n (if (numberp n) (mod n 26) 13)) ;canonize N
2175   (unless (or (zerop n)                 ; no action needed for a rot of 0
2176               (= b e))                  ; no region to rotate
2177     ;; We build the table, if necessary.
2178     (when (or (not message-caesar-translation-table)
2179               (/= (aref message-caesar-translation-table ?a) (+ ?a n)))
2180       (setq message-caesar-translation-table
2181             (message-make-caesar-translation-table n)))
2182     (translate-region b e message-caesar-translation-table)))
2183
2184 (defun message-make-caesar-translation-table (n)
2185   "Create a rot table with offset N."
2186   (let ((i -1)
2187         (table (make-string 256 0)))
2188     (while (< (incf i) 256)
2189       (aset table i i))
2190     (concat
2191      (substring table 0 ?A)
2192      (substring table (+ ?A n) (+ ?A n (- 26 n)))
2193      (substring table ?A (+ ?A n))
2194      (substring table (+ ?A 26) ?a)
2195      (substring table (+ ?a n) (+ ?a n (- 26 n)))
2196      (substring table ?a (+ ?a n))
2197      (substring table (+ ?a 26) 255))))
2198
2199 (defun message-caesar-buffer-body (&optional rotnum)
2200   "Caesar rotates all letters in the current buffer by 13 places.
2201 Used to encode/decode possiblyun offensive messages (commonly in net.jokes).
2202 With prefix arg, specifies the number of places to rotate each letter forward.
2203 Mail and USENET news headers are not rotated."
2204   (interactive (if current-prefix-arg
2205                    (list (prefix-numeric-value current-prefix-arg))
2206                  (list nil)))
2207   (save-excursion
2208     (save-restriction
2209       (when (message-goto-body)
2210         (narrow-to-region (point) (point-max)))
2211       (message-caesar-region (point-min) (point-max) rotnum))))
2212
2213 (defun message-pipe-buffer-body (program)
2214   "Pipe the message body in the current buffer through PROGRAM."
2215   (save-excursion
2216     (save-restriction
2217       (when (message-goto-body)
2218         (narrow-to-region (point) (point-max)))
2219       (shell-command-on-region
2220        (point-min) (point-max) program nil t))))
2221
2222 (defun message-rename-buffer (&optional enter-string)
2223   "Rename the *message* buffer to \"*message* RECIPIENT\".
2224 If the function is run with a prefix, it will ask for a new buffer
2225 name, rather than giving an automatic name."
2226   (interactive "Pbuffer name: ")
2227   (save-excursion
2228     (save-restriction
2229       (goto-char (point-min))
2230       (narrow-to-region (point)
2231                         (search-forward mail-header-separator nil 'end))
2232       (let* ((mail-to (or
2233                        (if (message-news-p) (message-fetch-field "Newsgroups")
2234                          (message-fetch-field "To"))
2235                        ""))
2236              (mail-trimmed-to
2237               (if (string-match "," mail-to)
2238                   (concat (substring mail-to 0 (match-beginning 0)) ", ...")
2239                 mail-to))
2240              (name-default (concat "*message* " mail-trimmed-to))
2241              (name (if enter-string
2242                        (read-string "New buffer name: " name-default)
2243                      name-default)))
2244         (rename-buffer name t)))))
2245
2246 (defun message-fill-yanked-message (&optional justifyp)
2247   "Fill the paragraphs of a message yanked into this one.
2248 Numeric argument means justify as well."
2249   (interactive "P")
2250   (save-excursion
2251     (goto-char (point-min))
2252     (search-forward (concat "\n" mail-header-separator "\n") nil t)
2253     (let ((fill-prefix message-yank-prefix))
2254       (fill-individual-paragraphs (point) (point-max) justifyp))))
2255
2256 (defun message-indent-citation ()
2257   "Modify text just inserted from a message to be cited.
2258 The inserted text should be the region.
2259 When this function returns, the region is again around the modified text.
2260
2261 Normally, indent each nonblank line `message-indentation-spaces' spaces.
2262 However, if `message-yank-prefix' is non-nil, insert that prefix on each line."
2263   (let ((start (point)))
2264     ;; Remove unwanted headers.
2265     (when message-ignored-cited-headers
2266       (let (all-removed)
2267         (save-restriction
2268           (narrow-to-region
2269            (goto-char start)
2270            (if (search-forward "\n\n" nil t)
2271                (1- (point))
2272              (point)))
2273           (message-remove-header message-ignored-cited-headers t)
2274           (when (= (point-min) (point-max))
2275             (setq all-removed t))
2276           (goto-char (point-max)))
2277         (if all-removed
2278             (goto-char start)
2279           (forward-line 1))))
2280     ;; Delete blank lines at the start of the buffer.
2281     (while (and (point-min)
2282                 (eolp)
2283                 (not (eobp)))
2284       (message-delete-line))
2285     ;; Delete blank lines at the end of the buffer.
2286     (goto-char (point-max))
2287     (unless (bolp)
2288       (insert "\n"))
2289     (while (and (zerop (forward-line -1))
2290                 (looking-at "$"))
2291       (message-delete-line))
2292     ;; Do the indentation.
2293     (if (null message-yank-prefix)
2294         (indent-rigidly start (mark t) message-indentation-spaces)
2295       (save-excursion
2296         (goto-char start)
2297         (while (< (point) (mark t))
2298           (insert message-yank-prefix)
2299           (forward-line 1))))
2300     (goto-char start)))
2301
2302 (defun message-list-references (refs-list &rest refs-strs)
2303   "Add `Message-ID's which appear in REFS-STRS but not in REFS-LIST,
2304 to REFS-LIST."
2305   (let (refs ref id saved-id)
2306     (when (and refs-list
2307                (integerp message-list-references-add-position))
2308       (let ((pos message-list-references-add-position))
2309         (while (and refs-list
2310                     (> pos 0))
2311           (push (pop refs-list) saved-id)
2312           (setq pos (1- pos)))))
2313     (while refs-strs
2314       (when (setq refs (pop refs-strs))
2315         (setq refs (std11-parse-msg-ids (std11-lexical-analyze refs)))
2316         (while refs
2317           (when (eq (car (setq ref (pop refs))) 'msg-id)
2318             (setq id (concat "<" (mapconcat 'cdr (cdr ref) "") ">"))
2319             (or (member id refs-list)
2320                 (member id saved-id)
2321                 (push id refs-list))))))
2322     (while saved-id
2323       (push (pop saved-id) refs-list))
2324     refs-list))
2325
2326 (defvar gnus-article-copy)
2327 (defun message-yank-original (&optional arg)
2328   "Insert the message being replied to, if any.
2329 Puts point before the text and mark after.
2330 Normally indents each nonblank line ARG spaces (default 3).  However,
2331 if `message-yank-prefix' is non-nil, insert that prefix on each line.
2332
2333 This function uses `message-cite-function' to do the actual citing.
2334
2335 Just \\[universal-argument] as argument means don't indent, insert no
2336 prefix, and don't delete any headers.
2337
2338 In addition, if `message-yank-add-new-references' is non-nil and this
2339 command is called interactively, new IDs from the yanked article will
2340 be added to \"References\" field.
2341 \(See also `message-yank-add-new-references'.)"
2342   (interactive "P")
2343   (let ((modified (buffer-modified-p))
2344         (buffer (message-eval-parameter message-reply-buffer))
2345         start end refs)
2346     (when (and buffer
2347                message-cite-function)
2348       (delete-windows-on buffer t)
2349       (insert-buffer buffer) ; mark will be set at the end of article.
2350       (setq start (point)
2351             end (mark t))
2352
2353       ;; Add new IDs to References field.
2354       (when (and message-yank-add-new-references (interactive-p))
2355         (save-excursion
2356           (save-restriction
2357             (message-narrow-to-headers)
2358             (setq refs (message-list-references
2359                         nil
2360                         (message-fetch-field "References")))
2361             (widen)
2362             (narrow-to-region start end)
2363             (std11-narrow-to-header)
2364             (when (setq refs (message-list-references
2365                               refs
2366                               (unless (eq message-yank-add-new-references
2367                                           'message-id-only)
2368                                 (or (message-fetch-field "References")
2369                                     (message-fetch-field "In-Reply-To")))
2370                               (message-fetch-field "Message-ID")))
2371               (widen)
2372               (message-narrow-to-headers)
2373               (goto-char (point-min))
2374               (let ((case-fold-search t))
2375                 (if (re-search-forward "^References:\\([\t ]+.+\n\\)+" nil t)
2376                     (replace-match "")
2377                   (goto-char (point-max))))
2378               (mail-header-format
2379                (list (or (assq 'References message-header-format-alist)
2380                          '(References . message-fill-references)))
2381                (list (cons 'References
2382                            (mapconcat 'identity (nreverse refs) " "))))
2383               (backward-delete-char 1)))))
2384
2385       (funcall message-cite-function)
2386       (message-exchange-point-and-mark)
2387       (unless (bolp)
2388         (insert ?\n))
2389       (unless modified
2390         (setq message-checksum (message-checksum))))))
2391
2392 (defun message-yank-buffer (buffer)
2393   "Insert BUFFER into the current buffer and quote it."
2394   (interactive "bYank buffer: ")
2395   (let ((message-reply-buffer buffer))
2396     (save-window-excursion
2397       (message-yank-original))))
2398
2399 (defun message-buffers ()
2400   "Return a list of active message buffers."
2401   (let (buffers)
2402     (save-excursion
2403       (dolist (buffer (buffer-list t))
2404         (set-buffer buffer)
2405         (when (and (eq major-mode 'message-mode)
2406                    (null message-sent-message-via))
2407           (push (buffer-name buffer) buffers))))
2408     (nreverse buffers)))
2409
2410 (defun message-cite-original-without-signature ()
2411   "Cite function in the standard Message manner."
2412   (let ((start (point))
2413         (end (mark t))
2414         (functions
2415          (when message-indent-citation-function
2416            (if (listp message-indent-citation-function)
2417                message-indent-citation-function
2418              (list message-indent-citation-function)))))
2419     ;; Allow undoing.
2420     (undo-boundary)
2421     (goto-char end)
2422     (when (re-search-backward message-signature-separator start t)
2423       ;; Also peel off any blank lines before the signature.
2424       (forward-line -1)
2425       (while (looking-at "^[ \t]*$")
2426         (forward-line -1))
2427       (forward-line 1)
2428       (delete-region (point) end))
2429     (goto-char start)
2430     (while functions
2431       (funcall (pop functions)))
2432     (when message-citation-line-function
2433       (unless (bolp)
2434         (insert "\n"))
2435       (funcall message-citation-line-function))))
2436
2437 (defvar mail-citation-hook)             ;Compiler directive
2438 (defun message-cite-original ()
2439   "Cite function in the standard Message manner."
2440   (if (and (boundp 'mail-citation-hook)
2441            mail-citation-hook)
2442       (run-hooks 'mail-citation-hook)
2443     (let ((start (point))
2444           (end (mark t))
2445           (functions
2446            (when message-indent-citation-function
2447              (if (listp message-indent-citation-function)
2448                  message-indent-citation-function
2449                (list message-indent-citation-function))))
2450           (message-reply-headers (or message-reply-headers
2451                                      (make-mail-header))))
2452       (mail-header-set-from message-reply-headers
2453                             (save-restriction
2454                               (narrow-to-region
2455                                (point)
2456                                (if (search-forward "\n\n" nil t)
2457                                    (1- (point))
2458                                  (point-max)))
2459                               (or (message-fetch-field "from")
2460                                   "unknown sender")))
2461       (goto-char start)
2462       (while functions
2463         (funcall (pop functions)))
2464       (when message-citation-line-function
2465         (unless (bolp)
2466           (insert "\n"))
2467         (funcall message-citation-line-function)))))
2468
2469 (defun message-insert-citation-line ()
2470   "Function that inserts a simple citation line."
2471   (when message-reply-headers
2472     (insert (mail-header-from message-reply-headers) " writes:\n\n")))
2473
2474 (defun message-position-on-field (header &rest afters)
2475   (let ((case-fold-search t))
2476     (save-restriction
2477       (narrow-to-region
2478        (goto-char (point-min))
2479        (progn
2480          (re-search-forward
2481           (concat "^" (regexp-quote mail-header-separator) "$"))
2482          (match-beginning 0)))
2483       (goto-char (point-min))
2484       (if (re-search-forward (concat "^" (regexp-quote header) ":") nil t)
2485           (progn
2486             (re-search-forward "^[^ \t]" nil 'move)
2487             (beginning-of-line)
2488             (skip-chars-backward "\n")
2489             t)
2490         (while (and afters
2491                     (not (re-search-forward
2492                           (concat "^" (regexp-quote (car afters)) ":")
2493                           nil t)))
2494           (pop afters))
2495         (when afters
2496           (re-search-forward "^[^ \t]" nil 'move)
2497           (beginning-of-line))
2498         (insert header ": \n")
2499         (forward-char -1)
2500         nil))))
2501
2502 (defun message-remove-signature ()
2503   "Remove the signature from the text between point and mark.
2504 The text will also be indented the normal way."
2505   (save-excursion
2506     (let ((start (point))
2507           mark)
2508       (if (not (re-search-forward message-signature-separator (mark t) t))
2509           ;; No signature here, so we just indent the cited text.
2510           (message-indent-citation)
2511         ;; Find the last non-empty line.
2512         (forward-line -1)
2513         (while (looking-at "[ \t]*$")
2514           (forward-line -1))
2515         (forward-line 1)
2516         (setq mark (set-marker (make-marker) (point)))
2517         (goto-char start)
2518         (message-indent-citation)
2519         ;; Enable undoing the deletion.
2520         (undo-boundary)
2521         (delete-region mark (mark t))
2522         (set-marker mark nil)))))
2523
2524 \f
2525
2526 ;;;
2527 ;;; Sending messages
2528 ;;;
2529
2530 ;; Avoid byte-compile warning.
2531 (defvar message-encoding-buffer nil)
2532 (defvar message-edit-buffer nil)
2533 (defvar message-mime-mode nil)
2534
2535 (defun message-send-and-exit (&optional arg)
2536   "Send message like `message-send', then, if no errors, exit from mail buffer."
2537   (interactive "P")
2538   (let ((buf (current-buffer))
2539         (actions message-exit-actions)
2540         (frame (selected-frame))
2541         (org-frame message-original-frame))
2542     (when (and (message-send arg)
2543                (buffer-name buf))
2544       (if message-kill-buffer-on-exit
2545           (kill-buffer buf)
2546         (bury-buffer buf)
2547         (when (eq buf (current-buffer))
2548           (message-bury buf)))
2549       (message-do-actions actions)
2550       (message-delete-frame frame org-frame)
2551       t)))
2552
2553 (defun message-dont-send ()
2554   "Don't send the message you have been editing."
2555   (interactive)
2556   (message-save-drafts)
2557   (let ((actions message-postpone-actions)
2558         (frame (selected-frame))
2559         (org-frame message-original-frame))
2560     (message-bury (current-buffer))
2561     (message-do-actions actions)
2562     (message-delete-frame frame org-frame)))
2563
2564 (defun message-kill-buffer ()
2565   "Kill the current buffer."
2566   (interactive)
2567   (when (or (not (buffer-modified-p))
2568             (eq t message-kill-buffer-query-function)
2569             (funcall message-kill-buffer-query-function
2570                      "The buffer modified; kill anyway? "))
2571     (let ((actions message-kill-actions)
2572           (frame (selected-frame))
2573           (org-frame message-original-frame))
2574       (setq buffer-file-name nil)
2575       (kill-buffer (current-buffer))
2576       (message-do-actions actions)
2577       (message-delete-frame frame org-frame)))
2578   (message ""))
2579
2580 (defun message-mimic-kill-buffer ()
2581   "Kill the current buffer with query."
2582   (interactive)
2583   (unless (eq 'message-mode major-mode)
2584     (error "%s must be invoked from a message buffer." this-command))
2585   (let ((command this-command)
2586         (bufname (read-buffer (format "Kill buffer: (default %s) "
2587                                       (buffer-name)))))
2588     (if (or (not bufname)
2589             (string-equal bufname "")
2590             (string-equal bufname (buffer-name)))
2591         (message-kill-buffer)
2592       (message "%s must be invoked only for the current buffer." command))))
2593
2594 (defun message-delete-frame (frame org-frame)
2595   "Delete frame for editing message."
2596   (when (and (or (static-if (featurep 'xemacs)
2597                      (device-on-window-system-p)
2598                    window-system)
2599                  (>= emacs-major-version 20))
2600              (or (and (eq message-delete-frame-on-exit t)
2601                       (select-frame frame)
2602                       (or (eq frame org-frame)
2603                           (prog1
2604                               (y-or-n-p "Delete this frame?")
2605                             (message ""))))
2606                  (and (eq message-delete-frame-on-exit 'ask)
2607                       (select-frame frame)
2608                       (prog1
2609                           (y-or-n-p "Delete this frame?")
2610                         (message "")))))
2611     (delete-frame frame)))
2612
2613 (defun message-bury (buffer)
2614   "Bury this mail buffer."
2615   (let ((newbuf (other-buffer buffer)))
2616     (bury-buffer buffer)
2617     (if (and (fboundp 'frame-parameters)
2618              (cdr (assq 'dedicated (frame-parameters)))
2619              (not (null (delq (selected-frame) (visible-frame-list)))))
2620         (delete-frame (selected-frame))
2621       (switch-to-buffer newbuf))))
2622
2623 (defun message-send (&optional arg)
2624   "Send the message in the current buffer.
2625 If `message-interactive' is non-nil, wait for success indication or
2626 error messages, and inform user.
2627 Otherwise any failure is reported in a message back to the user from
2628 the mailer.
2629 The usage of ARG is defined by the instance that called Message.
2630 It should typically alter the sending method in some way or other."
2631   (interactive "P")
2632   ;; Disabled test.
2633   (when (or (buffer-modified-p)
2634             (message-check-element 'unchanged)
2635             (y-or-n-p "No changes in the buffer; really send? "))
2636     ;; Make it possible to undo the coming changes.
2637     (undo-boundary)
2638     (let ((inhibit-read-only t))
2639       (put-text-property (point-min) (point-max) 'read-only nil))
2640     (run-hooks 'message-send-hook)
2641     (message-fix-before-sending)
2642     (message "Sending...")
2643     (let ((message-encoding-buffer
2644            (message-generate-new-buffer-clone-locals " message encoding"))
2645           (message-edit-buffer (current-buffer))
2646           (message-mime-mode mime-edit-mode-flag)
2647           (alist message-send-method-alist)
2648           (success t)
2649           elem sent)
2650       (save-excursion
2651         (set-buffer message-encoding-buffer)
2652         (erase-buffer)
2653         (insert-buffer message-edit-buffer)
2654         (funcall message-encode-function)
2655         (while (and success
2656                     (setq elem (pop alist)))
2657           (when (or (not (funcall (cadr elem)))
2658                     (and (or (not (memq (car elem)
2659                                         message-sent-message-via))
2660                              (y-or-n-p
2661                               (format
2662                                "Already sent message via %s; resend? "
2663                                (car elem))))
2664                          (setq success (funcall (caddr elem) arg))))
2665             (setq sent t))))
2666       (unless (or sent (not success))
2667         (error "No methods specified to send by"))
2668       (prog1
2669           (when (and success sent)
2670             (message-do-fcc)
2671             (save-excursion
2672               (run-hooks 'message-sent-hook))
2673             (message "Sending...done")
2674             ;; Mark the buffer as unmodified and delete autosave.
2675             (set-buffer-modified-p nil)
2676             (delete-auto-save-file-if-necessary t)
2677             (message-disassociate-draft)
2678             ;; Delete other mail buffers and stuff.
2679             (message-do-send-housekeeping)
2680             (message-do-actions message-send-actions)
2681             ;; Return success.
2682             t)
2683         (kill-buffer message-encoding-buffer)))))
2684
2685 (defun message-send-via-mail (arg)
2686   "Send the current message via mail."
2687   (message-send-mail arg))
2688
2689 (defun message-send-via-news (arg)
2690   "Send the current message via news."
2691   (message-send-news arg))
2692
2693 (defmacro message-check (type &rest forms)
2694   "Eval FORMS if TYPE is to be checked."
2695   `(or (message-check-element ,type)
2696        (save-excursion
2697          ,@forms)))
2698
2699 (put 'message-check 'lisp-indent-function 1)
2700 (put 'message-check 'edebug-form-spec '(form body))
2701
2702 ;; This function will be used by MIME-Edit when inserting invisible parts.
2703 (defun message-invisible-region (start end)
2704   (if (featurep 'xemacs)
2705       (if (save-excursion
2706             (goto-char start)
2707             (eq (following-char) ?\n))
2708           (setq start (1+ start)))
2709     (if (save-excursion
2710           (goto-char (1- end))
2711           (eq (following-char) ?\n))
2712         (setq end (1- end))))
2713   (put-text-property start end 'invisible t)
2714   (if (eq 'message-mode major-mode)
2715       (put-text-property start end 'message-invisible t)))
2716
2717 (eval-after-load "invisible"
2718   '(defalias 'invisible-region 'message-invisible-region))
2719
2720 (defun message-fix-before-sending ()
2721   "Do various things to make the message nice before sending it."
2722   ;; Make sure there's a newline at the end of the message.
2723   (widen)
2724   (goto-char (point-max))
2725   (unless (bolp)
2726     (insert "\n"))
2727   ;; Expose all invisible text with the property `message-invisible'.
2728   ;; We should believe that the things might be created by MIME-Edit.
2729   (let (start)
2730     (while (setq start (text-property-any (point-min) (point-max)
2731                                           'message-invisible t))
2732       (remove-text-properties start
2733                               (or (text-property-not-all start (point-max)
2734                                                          'message-invisible t)
2735                                   (point-max))
2736                               '(invisible nil message-invisible nil))))
2737   ;; Expose all invisible text.
2738   (message-check 'invisible-text
2739     (when (text-property-any (point-min) (point-max) 'invisible t)
2740       (put-text-property (point-min) (point-max) 'invisible nil)
2741       (unless (yes-or-no-p
2742                "Invisible text found and made visible; continue posting? ")
2743         (error "Invisible text found and made visible")))))
2744
2745 (defun message-add-action (action &rest types)
2746   "Add ACTION to be performed when doing an exit of type TYPES."
2747   (let (var)
2748     (while types
2749       (set (setq var (intern (format "message-%s-actions" (pop types))))
2750            (nconc (symbol-value var) (list action))))))
2751
2752 (defun message-do-actions (actions)
2753   "Perform all actions in ACTIONS."
2754   ;; Now perform actions on successful sending.
2755   (while actions
2756     (ignore-errors
2757       (cond
2758        ;; A simple function.
2759        ((message-functionp (car actions))
2760         (funcall (car actions)))
2761        ;; Something to be evaled.
2762        (t
2763         (eval (car actions)))))
2764     (pop actions)))
2765
2766 (defsubst message-maybe-split-and-send-mail ()
2767   "Split a message if necessary, and send it via mail.
2768 Returns nil if sending succeeded, returns any string if sending failed.
2769 This sub function is for exclusive use of `message-send-mail'."
2770   (let ((mime-edit-split-ignored-field-regexp
2771          mime-edit-split-ignored-field-regexp)
2772         (case-fold-search t)
2773         failure)
2774     (while (string-match "Message-ID" mime-edit-split-ignored-field-regexp)
2775       (setq mime-edit-split-ignored-field-regexp
2776             (concat (substring mime-edit-split-ignored-field-regexp
2777                                0 (match-beginning 0))
2778                     "Hey_MIME-Edit,_there_is_an_inviolable_Message_ID"
2779                     "_so_don't_rape_it!"
2780                     (substring mime-edit-split-ignored-field-regexp
2781                                (match-end 0)))))
2782     (setq failure
2783           (or
2784            (catch 'message-sending-mail-failure
2785              (mime-edit-maybe-split-and-send
2786               (function
2787                (lambda ()
2788                  (interactive)
2789                  (save-restriction
2790                    (std11-narrow-to-header mail-header-separator)
2791                    (goto-char (point-min))
2792                    (when (re-search-forward "^Message-ID:" nil t)
2793                      (delete-region (match-end 0) (std11-field-end))
2794                      (insert " " (message-make-message-id))))
2795                  (condition-case err
2796                      (funcall message-send-mail-function)
2797                    (error
2798                     (throw 'message-sending-mail-failure err))))))
2799              nil)
2800            (condition-case err
2801                (progn
2802                  (funcall message-send-mail-function)
2803                  nil)
2804              (error err))))
2805     (when failure
2806       (if (eq 'error (car failure))
2807           (cadr failure)
2808         (prin1-to-string failure)))))
2809
2810 (defun message-send-mail-partially ()
2811   "Sendmail as message/partial."
2812   (let ((p (goto-char (point-min)))
2813         (tembuf (message-generate-new-buffer-clone-locals " message temp"))
2814         (curbuf (current-buffer))
2815         (id (message-make-message-id)) (n 1)
2816         plist total  header required-mail-headers)
2817     (while (not (eobp))
2818       (if (< (point-max) (+ p message-send-mail-partially-limit))
2819           (goto-char (point-max))
2820         (goto-char (+ p message-send-mail-partially-limit))
2821         (beginning-of-line)
2822         (if (<= (point) p) (forward-line 1))) ;; In case of bad message.
2823       (push p plist)
2824       (setq p (point)))
2825     (setq total (length plist))
2826     (push (point-max) plist)
2827     (setq plist (nreverse plist))
2828     (unwind-protect
2829         (save-excursion
2830           (setq p (pop plist))
2831           (while plist
2832             (set-buffer curbuf)
2833             (copy-to-buffer tembuf p (car plist))
2834             (set-buffer tembuf)
2835             (goto-char (point-min))
2836             (if header
2837                 (progn
2838                   (goto-char (point-min))
2839                   (narrow-to-region (point) (point))
2840                   (insert header))
2841               (message-goto-eoh)
2842               (setq header (buffer-substring (point-min) (point)))
2843               (goto-char (point-min))
2844               (narrow-to-region (point) (point))
2845               (insert header)
2846               (message-remove-header "Mime-Version")
2847               (message-remove-header "Content-Type")
2848               (message-remove-header "Content-Transfer-Encoding")
2849               (message-remove-header "Message-ID")
2850               (message-remove-header "Lines")
2851               (goto-char (point-max))
2852               (insert "Mime-Version: 1.0\n")
2853               (setq header (buffer-substring (point-min) (point-max))))
2854             (goto-char (point-max))
2855             (insert (format "Content-Type: message/partial; id=\"%s\"; number=%d; total=%d\n"
2856                             id n total))
2857             (let ((mail-header-separator ""))
2858               (when (memq 'Message-ID message-required-mail-headers)
2859                 (insert "Message-ID: " (message-make-message-id) "\n"))
2860               (when (memq 'Lines message-required-mail-headers)
2861                 (let ((mail-header-separator ""))
2862                   (insert "Lines: " (message-make-lines) "\n")))
2863               (message-goto-subject)
2864               (end-of-line)
2865               (insert (format " (%d/%d)" n total))
2866               (goto-char (point-max))
2867               (insert "\n")
2868               (widen)
2869               (mm-with-unibyte-current-buffer
2870                 (funcall message-send-mail-function)))
2871             (setq n (+ n 1))
2872             (setq p (pop plist))
2873             (erase-buffer)))
2874       (kill-buffer tembuf))))
2875
2876 (defun message-send-mail (&optional arg)
2877   (require 'mail-utils)
2878   (let* ((tembuf (message-generate-new-buffer-clone-locals " message temp"))
2879          (case-fold-search nil)
2880          (news (message-news-p))
2881          (message-this-is-mail t)
2882          failure)
2883     (save-restriction
2884       (message-narrow-to-headers)
2885       ;; Insert some headers.
2886       (let ((message-deletable-headers
2887              (if news nil message-deletable-headers)))
2888         (message-generate-headers message-required-mail-headers))
2889       ;; Let the user do all of the above.
2890       (run-hooks 'message-header-hook))
2891     (if (not (message-check-mail-syntax))
2892         (progn
2893           (message "")
2894           nil)
2895       (unwind-protect
2896           (save-excursion
2897             (set-buffer tembuf)
2898             (erase-buffer)
2899             (insert-buffer message-encoding-buffer)
2900             ;; Remove some headers.
2901             (save-restriction
2902               (message-narrow-to-headers)
2903 ;; We Semi-gnus people have no use for it.
2904 ;;            ;; We (re)generate the Lines header.
2905 ;;            (when (memq 'Lines message-required-mail-headers)
2906 ;;              (message-generate-headers '(Lines)))
2907               ;; Remove some headers.
2908               (message-remove-header message-ignored-mail-headers t))
2909             (goto-char (point-max))
2910             ;; require one newline at the end.
2911             (or (= (preceding-char) ?\n)
2912                 (insert ?\n))
2913             (when
2914                 (save-restriction
2915                   (message-narrow-to-headers)
2916                   (and news
2917                        (or (message-fetch-field "cc")
2918                            (message-fetch-field "to"))
2919                        (let ((ct (mime-read-Content-Type)))
2920                          (and (eq 'text (cdr (assq 'type ct)))
2921                               (eq 'plain (cdr (assq 'subtype ct)))))))
2922               (message-insert-courtesy-copy))
2923             (setq failure (message-maybe-split-and-send-mail)))
2924         (kill-buffer tembuf))
2925       (set-buffer message-edit-buffer)
2926       (if failure
2927           (progn
2928             (message "Couldn't send message via mail: %s" failure)
2929             nil)
2930         (push 'mail message-sent-message-via)))))
2931
2932 (defun message-send-mail-with-sendmail ()
2933   "Send off the prepared buffer with sendmail."
2934   (let ((errbuf (if message-interactive
2935                     (message-generate-new-buffer-clone-locals
2936                      " sendmail errors")
2937                   0))
2938         resend-to-addresses delimline)
2939     (let ((case-fold-search t))
2940       (save-restriction
2941         (message-narrow-to-headers)
2942         (setq resend-to-addresses (message-fetch-field "resent-to")))
2943       ;; Change header-delimiter to be what sendmail expects.
2944       (goto-char (point-min))
2945       (re-search-forward
2946        (concat "^" (regexp-quote mail-header-separator) "\n"))
2947       (replace-match "\n")
2948       (backward-char 1)
2949       (setq delimline (point-marker))
2950       (run-hooks 'message-send-mail-hook)
2951       ;; Insert an extra newline if we need it to work around
2952       ;; Sun's bug that swallows newlines.
2953       (goto-char (1+ delimline))
2954       (when (eval message-mailer-swallows-blank-line)
2955         (newline))
2956       (when message-interactive
2957         (save-excursion
2958           (set-buffer errbuf)
2959           (erase-buffer))))
2960     (let ((default-directory "/"))
2961       (as-binary-process
2962        (apply 'call-process-region
2963               (append (list (point-min) (point-max)
2964                             (if (boundp 'sendmail-program)
2965                                 sendmail-program
2966                               "/usr/lib/sendmail")
2967                             nil errbuf nil "-oi")
2968                       ;; Always specify who from,
2969                       ;; since some systems have broken sendmails.
2970                       ;; But some systems are more broken with -f, so
2971                       ;; we'll let users override this.
2972                       (if (null message-sendmail-f-is-evil)
2973                           (list "-f" (message-make-address)))
2974                       ;; These mean "report errors by mail"
2975                       ;; and "deliver in background".
2976                       (if (null message-interactive) '("-oem" "-odb"))
2977                       ;; Get the addresses from the message
2978                       ;; unless this is a resend.
2979                       ;; We must not do that for a resend
2980                       ;; because we would find the original addresses.
2981                       ;; For a resend, include the specific addresses.
2982                       (if resend-to-addresses
2983                           (list resend-to-addresses)
2984                         '("-t"))))))
2985     (when message-interactive
2986       (save-excursion
2987         (set-buffer errbuf)
2988         (goto-char (point-min))
2989         (while (re-search-forward "\n\n* *" nil t)
2990           (replace-match "; "))
2991         (if (not (zerop (buffer-size)))
2992             (error "Sending...failed to %s"
2993                    (buffer-substring (point-min) (point-max)))))
2994       (when (bufferp errbuf)
2995         (kill-buffer errbuf)))))
2996
2997 (defun message-send-mail-with-qmail ()
2998   "Pass the prepared message buffer to qmail-inject.
2999 Refer to the documentation for the variable `message-send-mail-function'
3000 to find out how to use this."
3001   ;; replace the header delimiter with a blank line
3002   (goto-char (point-min))
3003   (re-search-forward
3004    (concat "^" (regexp-quote mail-header-separator) "\n"))
3005   (replace-match "\n")
3006   (backward-char 1)
3007   (run-hooks 'message-send-mail-hook)
3008   ;; send the message
3009   (case
3010       (as-binary-process
3011        (apply
3012         'call-process-region 1 (point-max) message-qmail-inject-program
3013         nil nil nil
3014         ;; qmail-inject's default behaviour is to look for addresses on the
3015         ;; command line; if there're none, it scans the headers.
3016         ;; yes, it does The Right Thing w.r.t. Resent-To and it's kin.
3017         ;;
3018         ;; in general, ALL of qmail-inject's defaults are perfect for simply
3019         ;; reading a formatted (i. e., at least a To: or Resent-To header)
3020         ;; message from stdin.
3021         ;;
3022         ;; qmail also has the advantage of not having been raped by
3023         ;; various vendors, so we don't have to allow for that, either --
3024         ;; compare this with message-send-mail-with-sendmail and weep
3025         ;; for sendmail's lost innocence.
3026         ;;
3027         ;; all this is way cool coz it lets us keep the arguments entirely
3028         ;; free for -inject-arguments -- a big win for the user and for us
3029         ;; since we don't have to play that double-guessing game and the user
3030         ;; gets full control (no gestapo'ish -f's, for instance).  --sj
3031         message-qmail-inject-args))
3032     ;; qmail-inject doesn't say anything on it's stdout/stderr,
3033     ;; we have to look at the retval instead
3034     (0 nil)
3035     (1   (error "qmail-inject reported permanent failure"))
3036     (111 (error "qmail-inject reported transient failure"))
3037     ;; should never happen
3038     (t   (error "qmail-inject reported unknown failure"))))
3039
3040 (defun message-send-mail-with-mh ()
3041   "Send the prepared message buffer with mh."
3042   (let ((mh-previous-window-config nil)
3043         (name (mh-new-draft-name)))
3044     (setq buffer-file-name name)
3045     ;; MH wants to generate these headers itself.
3046     (when message-mh-deletable-headers
3047       (let ((headers message-mh-deletable-headers))
3048         (while headers
3049           (goto-char (point-min))
3050           (and (re-search-forward
3051                 (concat "^" (symbol-name (car headers)) ": *") nil t)
3052                (message-delete-line))
3053           (pop headers))))
3054     (run-hooks 'message-send-mail-hook)
3055     ;; Pass it on to mh.
3056     (mh-send-letter)))
3057
3058 (defun message-send-mail-with-smtp ()
3059   "Send off the prepared buffer with SMTP."
3060   (require 'smtp) ; XXX
3061   (let ((case-fold-search t)
3062         recipients)
3063     (save-restriction
3064       (message-narrow-to-headers)
3065       (setq recipients
3066             ;; XXX: Should be replaced by better one.
3067             (smtp-deduce-address-list (current-buffer)
3068                                       (point-min) (point-max)))
3069       ;; Remove BCC lines.
3070       (message-remove-header "bcc"))
3071     ;; replace the header delimiter with a blank line.
3072     (goto-char (point-min))
3073     (re-search-forward
3074      (concat "^" (regexp-quote mail-header-separator) "\n"))
3075     (replace-match "\n")
3076     (backward-char 1)
3077     (run-hooks 'message-send-mail-hook)
3078     (if recipients
3079         (let ((result (smtp-via-smtp user-mail-address
3080                                      recipients
3081                                      (current-buffer))))
3082           (unless (eq result t)
3083             (error "Sending failed; " result)))
3084       (error "Sending failed; no recipients"))))
3085
3086 (defsubst message-maybe-split-and-send-news (method)
3087   "Split a message if necessary, and send it via news.
3088 Returns nil if sending succeeded, returns t if sending failed.
3089 This sub function is for exclusive use of `message-send-news'."
3090   (let ((mime-edit-split-ignored-field-regexp
3091          mime-edit-split-ignored-field-regexp)
3092         (case-fold-search t))
3093     (while (string-match "Message-ID" mime-edit-split-ignored-field-regexp)
3094       (setq mime-edit-split-ignored-field-regexp
3095             (concat (substring mime-edit-split-ignored-field-regexp
3096                                0 (match-beginning 0))
3097                     "Hey_MIME-Edit,_there_is_an_inviolable_Message_ID"
3098                     "_so_don't_rape_it!"
3099                     (substring mime-edit-split-ignored-field-regexp
3100                                (match-end 0)))))
3101     (or
3102      (catch 'message-sending-news-failure
3103        (mime-edit-maybe-split-and-send
3104         (function
3105          (lambda ()
3106            (interactive)
3107            (save-restriction
3108              (std11-narrow-to-header mail-header-separator)
3109              (goto-char (point-min))
3110              (when (re-search-forward "^Message-ID:" nil t)
3111                (delete-region (match-end 0) (std11-field-end))
3112                (insert " " (message-make-message-id))))
3113            (unless (funcall message-send-news-function method)
3114              (throw 'message-sending-news-failure t)))))
3115        nil)
3116      (not (funcall message-send-news-function method)))))
3117
3118 (defun message-send-news (&optional arg)
3119   (let* ((tembuf (message-generate-new-buffer-clone-locals " *message temp*"))
3120          (case-fold-search nil)
3121          (method (if (message-functionp message-post-method)
3122                      (funcall message-post-method arg)
3123                    message-post-method))
3124          (group-name-charset (gnus-group-name-charset method ""))
3125          (message-syntax-checks
3126           (if arg
3127               (cons '(existing-newsgroups . disabled)
3128                     message-syntax-checks)
3129             message-syntax-checks))
3130          (message-this-is-news t)
3131          result)
3132     (save-restriction
3133       (message-narrow-to-headers)
3134       ;; Insert some headers.
3135       (message-generate-headers message-required-news-headers)
3136       ;; Let the user do all of the above.
3137       (run-hooks 'message-header-hook))
3138     (if group-name-charset
3139         (setq message-syntax-checks
3140               (cons '(valid-newsgroups . disabled)
3141                     message-syntax-checks)))
3142     (message-cleanup-headers)
3143     (if (not (message-check-news-syntax))
3144         nil
3145       (unwind-protect
3146           (save-excursion
3147             (set-buffer tembuf)
3148             (buffer-disable-undo)
3149             (erase-buffer)
3150             (insert-buffer message-encoding-buffer)
3151             ;; Remove some headers.
3152             (save-restriction
3153               (message-narrow-to-headers)
3154 ;; We Semi-gnus people have no use for it.
3155 ;;            ;; We (re)generate the Lines header.
3156 ;;            (when (memq 'Lines message-required-mail-headers)
3157 ;;              (message-generate-headers '(Lines)))
3158               ;; Remove some headers.
3159               (message-remove-header message-ignored-news-headers t))
3160             (goto-char (point-max))
3161             ;; require one newline at the end.
3162             (or (= (preceding-char) ?\n)
3163                 (insert ?\n))
3164             (setq result (message-maybe-split-and-send-news method)))
3165         (kill-buffer tembuf))
3166       (set-buffer message-edit-buffer)
3167       (if result
3168           (progn
3169             (message "Couldn't send message via news: %s"
3170                      (nnheader-get-report (car method)))
3171             nil)
3172         (push 'news message-sent-message-via)))))
3173
3174 ;; 1997-09-29 by MORIOKA Tomohiko
3175 (defun message-send-news-with-gnus (method)
3176   (let ((case-fold-search t))
3177     ;; Remove the delimiter.
3178     (goto-char (point-min))
3179     (re-search-forward
3180      (concat "^" (regexp-quote mail-header-separator) "\n"))
3181     (replace-match "\n")
3182     (backward-char 1)
3183     (run-hooks 'message-send-news-hook)
3184     (gnus-open-server method)
3185     (gnus-request-post method)
3186     ))
3187
3188 ;;;
3189 ;;; Header generation & syntax checking.
3190 ;;;
3191
3192 (defun message-check-element (type)
3193   "Returns non-nil if this type is not to be checked."
3194   (if (eq message-syntax-checks 'dont-check-for-anything-just-trust-me)
3195       t
3196     (let ((able (assq type message-syntax-checks)))
3197       (and (consp able)
3198            (eq (cdr able) 'disabled)))))
3199
3200 (defun message-check-news-syntax ()
3201   "Check the syntax of the message."
3202   (save-excursion
3203     (save-restriction
3204       (widen)
3205       (and
3206        ;; We narrow to the headers and check them first.
3207        (save-excursion
3208          (save-restriction
3209            (message-narrow-to-headers)
3210            (message-check-news-header-syntax)))
3211        ;; Check the body.
3212        (save-excursion
3213          (set-buffer message-edit-buffer)
3214          (message-check-news-body-syntax))))))
3215
3216 (defun message-check-news-header-syntax ()
3217   (and
3218    ;; Check Newsgroups header.
3219    (message-check 'newsgroups
3220      (let ((group (message-fetch-field "newsgroups")))
3221        (or
3222         (and group
3223              (not (string-match "\\`[ \t]*\\'" group)))
3224         (ignore
3225          (message
3226           "The newsgroups field is empty or missing.  Posting is denied.")))))
3227    ;; Check the Subject header.
3228    (message-check 'subject
3229      (let* ((case-fold-search t)
3230             (subject (message-fetch-field "subject")))
3231        (or
3232         (and subject
3233              (not (string-match "\\`[ \t]*\\'" subject)))
3234         (ignore
3235          (message
3236           "The subject field is empty or missing.  Posting is denied.")))))
3237    ;; Check for commands in Subject.
3238    (message-check 'subject-cmsg
3239      (if (string-match "^cmsg " (message-fetch-field "subject"))
3240          (y-or-n-p
3241           "The control code \"cmsg\" is in the subject.  Really post? ")
3242        t))
3243    ;; Check for multiple identical headers.
3244    (message-check 'multiple-headers
3245      (let (found)
3246        (while (and (not found)
3247                    (re-search-forward "^[^ \t:]+: " nil t))
3248          (save-excursion
3249            (or (re-search-forward
3250                 (concat "^"
3251                         (regexp-quote
3252                          (setq found
3253                                (buffer-substring
3254                                 (match-beginning 0) (- (match-end 0) 2))))
3255                         ":")
3256                 nil t)
3257                (setq found nil))))
3258        (if found
3259            (y-or-n-p (format "Multiple %s headers.  Really post? " found))
3260          t)))
3261    ;; Check for Version and Sendsys.
3262    (message-check 'sendsys
3263      (if (re-search-forward "^Sendsys:\\|^Version:" nil t)
3264          (y-or-n-p
3265           (format "The article contains a %s command.  Really post? "
3266                   (buffer-substring (match-beginning 0)
3267                                     (1- (match-end 0)))))
3268        t))
3269    ;; See whether we can shorten Followup-To.
3270    (message-check 'shorten-followup-to
3271      (let ((newsgroups (message-fetch-field "newsgroups"))
3272            (followup-to (message-fetch-field "followup-to"))
3273            to)
3274        (when (and newsgroups
3275                   (string-match "," newsgroups)
3276                   (not followup-to)
3277                   (not
3278                    (zerop
3279                     (length
3280                      (setq to (completing-read
3281                                "Followups to: (default all groups) "
3282                                (mapcar (lambda (g) (list g))
3283                                        (cons "poster"
3284                                              (message-tokenize-header
3285                                               newsgroups)))))))))
3286          (goto-char (point-min))
3287          (insert "Followup-To: " to "\n"))
3288        t))
3289    ;; Check "Shoot me".
3290    (message-check 'shoot
3291      (if (re-search-forward
3292           "Message-ID.*.i-did-not-set--mail-host-address--so-shoot-me" nil t)
3293          (y-or-n-p "You appear to have a misconfigured system.  Really post? ")
3294        t))
3295    ;; Check for Approved.
3296    (message-check 'approved
3297      (if (re-search-forward "^Approved:" nil t)
3298          (y-or-n-p "The article contains an Approved header.  Really post? ")
3299        t))
3300    ;; Check the Message-ID header.
3301    (message-check 'message-id
3302      (let* ((case-fold-search t)
3303             (message-id (message-fetch-field "message-id" t)))
3304        (or (not message-id)
3305            ;; Is there an @ in the ID?
3306            (and (string-match "@" message-id)
3307                 ;; Is there a dot in the ID?
3308                 (string-match "@[^.]*\\." message-id)
3309                 ;; Does the ID end with a dot?
3310                 (not (string-match "\\.>" message-id)))
3311            (y-or-n-p
3312             (format "The Message-ID looks strange: \"%s\".  Really post? "
3313                     message-id)))))
3314    ;; Check the Newsgroups & Followup-To headers.
3315    (message-check 'existing-newsgroups
3316      (let* ((case-fold-search t)
3317             (newsgroups (message-fetch-field "newsgroups"))
3318             (followup-to (message-fetch-field "followup-to"))
3319             (groups (message-tokenize-header
3320                      (if followup-to
3321                          (concat newsgroups "," followup-to)
3322                        newsgroups)))
3323             (hashtb (and (boundp 'gnus-active-hashtb)
3324                          gnus-active-hashtb))
3325             errors)
3326        (if (or (not hashtb)
3327                (not (boundp 'gnus-read-active-file))
3328                (not gnus-read-active-file)
3329                (eq gnus-read-active-file 'some))
3330            t
3331          (while groups
3332            (when (and (not (boundp (intern (car groups) hashtb)))
3333                       (not (equal (car groups) "poster")))
3334              (push (car groups) errors))
3335            (pop groups))
3336          (if (not errors)
3337              t
3338            (y-or-n-p
3339             (format
3340              "Really post to %s unknown group%s: %s "
3341              (if (= (length errors) 1) "this" "these")
3342              (if (= (length errors) 1) "" "s")
3343              (mapconcat 'identity errors ", ")))))))
3344    ;; Check the Newsgroups & Followup-To headers for syntax errors.
3345    (message-check 'valid-newsgroups
3346      (let ((case-fold-search t)
3347            (headers '("Newsgroups" "Followup-To"))
3348            header error)
3349        (while (and headers (not error))
3350          (when (setq header (mail-fetch-field (car headers)))
3351            (if (or
3352                 (not
3353                  (string-match
3354                   "\\`\\([-+_&.a-zA-Z0-9]+\\)?\\(,[-+_&.a-zA-Z0-9]+\\)*\\'"
3355                   header))
3356                 (memq
3357                  nil (mapcar
3358                       (lambda (g)
3359                         (not (string-match "\\.\\'\\|\\.\\." g)))
3360                       (message-tokenize-header header ","))))
3361                (setq error t)))
3362          (unless error
3363            (pop headers)))
3364        (if (not error)
3365            t
3366          (y-or-n-p
3367           (format "The %s header looks odd: \"%s\".  Really post? "
3368                   (car headers) header)))))
3369    (message-check 'repeated-newsgroups
3370      (let ((case-fold-search t)
3371            (headers '("Newsgroups" "Followup-To"))
3372            header error groups group)
3373        (while (and headers
3374                    (not error))
3375          (when (setq header (mail-fetch-field (pop headers)))
3376            (setq groups (message-tokenize-header header ","))
3377            (while (setq group (pop groups))
3378              (when (member group groups)
3379                (setq error group
3380                      groups nil)))))
3381        (if (not error)
3382            t
3383          (y-or-n-p
3384           (format "Group %s is repeated in headers.  Really post? " error)))))
3385    ;; Check the From header.
3386    (message-check 'from
3387      (let* ((case-fold-search t)
3388             (from (message-fetch-field "from"))
3389             ad)
3390        (cond
3391         ((not from)
3392          (message "There is no From line.  Posting is denied.")
3393          nil)
3394         ((or (not (string-match
3395                    "@[^\\.]*\\."
3396                    (setq ad (nth 1 (mail-extract-address-components
3397                                     from))))) ;larsi@ifi
3398              (string-match "\\.\\." ad) ;larsi@ifi..uio
3399              (string-match "@\\." ad)   ;larsi@.ifi.uio
3400              (string-match "\\.$" ad)   ;larsi@ifi.uio.
3401              (not (string-match "^[^@]+@[^@]+$" ad)) ;larsi.ifi.uio
3402              (string-match "(.*).*(.*)" from)) ;(lars) (lars)
3403          (message
3404           "Denied posting -- the From looks strange: \"%s\"." from)
3405          nil)
3406         (t t))))))
3407
3408 (defun message-check-news-body-syntax ()
3409   (and
3410    ;; Check for long lines.
3411    (message-check 'long-lines
3412      (goto-char (point-min))
3413      (re-search-forward
3414       (concat "^" (regexp-quote mail-header-separator) "$"))
3415      (while (and
3416              (progn
3417                (end-of-line)
3418                (< (current-column) 80))
3419              (zerop (forward-line 1))))
3420      (or (bolp)
3421          (eobp)
3422          (y-or-n-p
3423           "You have lines longer than 79 characters.  Really post? ")))
3424    ;; Check whether the article is empty.
3425    (message-check 'empty
3426      (goto-char (point-min))
3427      (re-search-forward
3428       (concat "^" (regexp-quote mail-header-separator) "$"))
3429      (forward-line 1)
3430      (let ((b (point)))
3431        (goto-char (point-max))
3432        (re-search-backward message-signature-separator nil t)
3433        (beginning-of-line)
3434        (or (re-search-backward "[^ \n\t]" b t)
3435            (y-or-n-p "Empty article.  Really post? "))))
3436    ;; Check for control characters.
3437    (message-check 'control-chars
3438      (if (re-search-forward "[\000-\007\013\015-\032\034-\037\200-\237]" nil t)
3439          (y-or-n-p
3440           "The article contains control characters.  Really post? ")
3441        t))
3442    ;; Check 8bit characters.
3443    (message-check '8bit
3444      (message-check-8bit))
3445    ;; Check excessive size.
3446    (message-check 'size
3447      (if (> (buffer-size) 60000)
3448          (y-or-n-p
3449           (format "The article is %d octets long.  Really post? "
3450                   (buffer-size)))
3451        t))
3452    ;; Check whether any new text has been added.
3453    (message-check 'new-text
3454      (or
3455       (not message-checksum)
3456       (not (eq (message-checksum) message-checksum))
3457       (y-or-n-p
3458        "It looks like no new text has been added.  Really post? ")))
3459    ;; Check the length of the signature.
3460    (message-check 'signature
3461      (goto-char (point-max))
3462      (if (> (count-lines (point) (point-max)) 5)
3463          (y-or-n-p
3464           (format
3465            "Your .sig is %d lines; it should be max 4.  Really post? "
3466            (1- (count-lines (point) (point-max)))))
3467        t))
3468    ;; Ensure that text follows last quoted portion.
3469    (message-check 'quoting-style
3470      (goto-char (point-max))
3471      (let ((no-problem t))
3472        (when (search-backward-regexp "^>[^\n]*\n>" nil t)
3473          (setq no-problem nil)
3474          (while (not (eobp))
3475            (when (and (not (eolp)) (looking-at "[^> \t]"))
3476              (setq no-problem t))
3477            (forward-line)))
3478        (if no-problem
3479            t
3480          (y-or-n-p "Your text should follow quoted text.  Really post? "))))))
3481
3482 (defun message-check-mail-syntax ()
3483   "Check the syntax of the message."
3484   (save-excursion
3485     (save-restriction
3486       (widen)
3487       (and
3488        ;; We narrow to the headers and check them first.
3489        (save-excursion
3490          (save-restriction
3491            (message-narrow-to-headers)
3492            (message-check-mail-header-syntax)))
3493        ;; Check the body.
3494        (save-excursion
3495          (set-buffer message-edit-buffer)
3496          (message-check-mail-body-syntax))))))
3497
3498 (defun message-check-mail-header-syntax ()
3499   t)
3500
3501 (defun message-check-mail-body-syntax ()
3502   (and
3503    ;; Check 8bit characters.
3504    (message-check '8bit
3505      (message-check-8bit)
3506      )))
3507
3508 (defun message-check-8bit ()
3509   "Check the article contains 8bit characters."
3510   (save-excursion
3511     (set-buffer message-encoding-buffer)
3512     (message-narrow-to-headers)
3513     (let* ((case-fold-search t)
3514            (field-value (message-fetch-field "content-transfer-encoding")))
3515       (if (and field-value
3516                (member (downcase field-value) message-8bit-encoding-list))
3517           t
3518         (widen)
3519         (set-buffer (get-buffer-create " message syntax"))
3520         (erase-buffer)
3521         (goto-char (point-min))
3522         (set-buffer-multibyte nil)
3523         (insert-buffer message-encoding-buffer)
3524         (goto-char (point-min))
3525         (if (re-search-forward "[^\x00-\x7f]" nil t)
3526             (y-or-n-p
3527              "The article contains 8bit characters.  Really post? ")
3528           t)))))
3529
3530 (defun message-checksum ()
3531   "Return a \"checksum\" for the current buffer."
3532   (let ((sum 0))
3533     (save-excursion
3534       (goto-char (point-min))
3535       (re-search-forward
3536        (concat "^" (regexp-quote mail-header-separator) "$"))
3537       (while (not (eobp))
3538         (when (not (looking-at "[ \t\n]"))
3539           (setq sum (logxor (ash sum 1) (if (natnump sum) 0 1)
3540                             (char-after))))
3541         (forward-char 1)))
3542     sum))
3543
3544 (defun message-do-fcc ()
3545   "Process Fcc headers in the current buffer."
3546   (let ((case-fold-search t)
3547         (coding-system-for-write 'raw-text)
3548         (output-coding-system 'raw-text)
3549         list file)
3550     (save-excursion
3551       (set-buffer (get-buffer-create " *message temp*"))
3552       (erase-buffer)
3553       (insert-buffer-substring message-encoding-buffer)
3554       (save-restriction
3555         (message-narrow-to-headers)
3556         (while (setq file (message-fetch-field "fcc"))
3557           (push file list)
3558           (message-remove-header "fcc" nil t)))
3559       (goto-char (point-min))
3560       (re-search-forward (concat "^" (regexp-quote mail-header-separator) "$"))
3561       (replace-match "" t t)
3562       ;; Process FCC operations.
3563       (while list
3564         (setq file (pop list))
3565         (if (string-match "^[ \t]*|[ \t]*\\(.*\\)[ \t]*$" file)
3566             ;; Pipe the article to the program in question.
3567             (call-process-region (point-min) (point-max) shell-file-name
3568                                  nil nil nil shell-command-switch
3569                                  (match-string 1 file))
3570           ;; Save the article.
3571           (setq file (expand-file-name file))
3572           (unless (file-exists-p (file-name-directory file))
3573             (make-directory (file-name-directory file) t))
3574           (if (and message-fcc-handler-function
3575                    (not (eq message-fcc-handler-function 'rmail-output)))
3576               (funcall message-fcc-handler-function file)
3577             (if (and (file-readable-p file) (mail-file-babyl-p file))
3578                 (rmail-output file 1 nil t)
3579               (let ((mail-use-rfc822 t))
3580                 (rmail-output file 1 t t))))))
3581       (kill-buffer (current-buffer)))))
3582
3583 (defun message-output (filename)
3584   "Append this article to Unix/babyl mail file.."
3585   (if (and (file-readable-p filename)
3586            (mail-file-babyl-p filename))
3587       (gnus-output-to-rmail filename t)
3588     (gnus-output-to-mail filename t)))
3589
3590 (defun message-cleanup-headers ()
3591   "Do various automatic cleanups of the headers."
3592   ;; Remove empty lines in the header.
3593   (save-restriction
3594     (message-narrow-to-headers)
3595     ;; Remove blank lines.
3596     (while (re-search-forward "^[ \t]*\n" nil t)
3597       (replace-match "" t t))
3598
3599     ;; Correct Newsgroups and Followup-To headers:  Change sequence of
3600     ;; spaces to comma and eliminate spaces around commas.  Eliminate
3601     ;; embedded line breaks.
3602     (goto-char (point-min))
3603     (while (re-search-forward "^\\(Newsgroups\\|Followup-To\\): +" nil t)
3604       (save-restriction
3605         (narrow-to-region
3606          (point)
3607          (if (re-search-forward "^[^ \t]" nil t)
3608              (match-beginning 0)
3609            (forward-line 1)
3610            (point)))
3611         (goto-char (point-min))
3612         (while (re-search-forward "\n[ \t]+" nil t)
3613           (replace-match " " t t))      ;No line breaks (too confusing)
3614         (goto-char (point-min))
3615         (while (re-search-forward "[ \t\n]*,[ \t\n]*\\|[ \t]+" nil t)
3616           (replace-match "," t t))
3617         (goto-char (point-min))
3618         ;; Remove trailing commas.
3619         (when (re-search-forward ",+$" nil t)
3620           (replace-match "" t t))))))
3621
3622 (defun message-make-date (&optional now)
3623   "Make a valid data header.
3624 If NOW, use that time instead."
3625   (let* ((now (or now (current-time)))
3626          (zone (nth 8 (decode-time now)))
3627          (sign "+"))
3628     (when (< zone 0)
3629       (setq sign "-")
3630       (setq zone (- zone)))
3631     (concat
3632      (format-time-string "%d" now)
3633      ;; The month name of the %b spec is locale-specific.  Pfff.
3634      (format " %s "
3635              (capitalize (car (rassoc (nth 4 (decode-time now))
3636                                       parse-time-months))))
3637      (format-time-string "%Y %H:%M:%S " now)
3638      ;; We do all of this because XEmacs doesn't have the %z spec.
3639      (format "%s%02d%02d" sign (/ zone 3600) (/ (% zone 3600) 60)))))
3640
3641 (defun message-make-followup-subject (subject)
3642   "Make a followup Subject."
3643   (cond
3644    ((and (eq message-use-subject-re 'guess)
3645          (string-match message-subject-encoded-re-regexp subject))
3646     subject)
3647    (message-use-subject-re
3648     (concat "Re: " (message-strip-subject-re subject)))
3649    (t subject)))
3650
3651 (defun message-make-message-id ()
3652   "Make a unique Message-ID."
3653   (concat "<" (message-unique-id)
3654           (let ((psubject (save-excursion (message-fetch-field "subject")))
3655                 (psupersedes
3656                  (save-excursion (message-fetch-field "supersedes"))))
3657             (if (or
3658                  (and message-reply-headers
3659                       (mail-header-references message-reply-headers)
3660                       (mail-header-subject message-reply-headers)
3661                       psubject
3662                       (mail-header-subject message-reply-headers)
3663                       (not (string=
3664                             (message-strip-subject-re
3665                              (mail-header-subject message-reply-headers))
3666                             (message-strip-subject-re psubject))))
3667                  (and psupersedes
3668                       (string-match "_-_@" psupersedes)))
3669                 "_-_" ""))
3670           "@" (message-make-fqdn) ">"))
3671
3672 (defvar message-unique-id-char nil)
3673
3674 ;; If you ever change this function, make sure the new version
3675 ;; cannot generate IDs that the old version could.
3676 ;; You might for example insert a "." somewhere (not next to another dot
3677 ;; or string boundary), or modify the "fsf" string.
3678 (defun message-unique-id ()
3679   ;; Don't use microseconds from (current-time), they may be unsupported.
3680   ;; Instead we use this randomly inited counter.
3681   (setq message-unique-id-char
3682         (% (1+ (or message-unique-id-char (logand (random t) (1- (lsh 1 20)))))
3683            ;; (current-time) returns 16-bit ints,
3684            ;; and 2^16*25 just fits into 4 digits i base 36.
3685            (* 25 25)))
3686   (let ((tm (current-time)))
3687     (concat
3688      (if (memq system-type '(ms-dos emx vax-vms))
3689          (let ((user (downcase (user-login-name))))
3690            (while (string-match "[^a-z0-9_]" user)
3691              (aset user (match-beginning 0) ?_))
3692            user)
3693        (message-number-base36 (user-uid) -1))
3694      (message-number-base36 (+ (car   tm)
3695                                (lsh (% message-unique-id-char 25) 16)) 4)
3696      (message-number-base36 (+ (nth 1 tm)
3697                                (lsh (/ message-unique-id-char 25) 16)) 4)
3698      ;; Append the newsreader name, because while the generated
3699      ;; ID is unique to this newsreader, other newsreaders might
3700      ;; otherwise generate the same ID via another algorithm.
3701      ".fsf")))
3702
3703 (defun message-number-base36 (num len)
3704   (if (if (< len 0)
3705           (<= num 0)
3706         (= len 0))
3707       ""
3708     (concat (message-number-base36 (/ num 36) (1- len))
3709             (char-to-string (aref "zyxwvutsrqponmlkjihgfedcba9876543210"
3710                                   (% num 36))))))
3711
3712 (defun message-make-organization ()
3713   "Make an Organization header."
3714   (let* ((organization
3715           (when message-user-organization
3716             (if (message-functionp message-user-organization)
3717                 (funcall message-user-organization)
3718               message-user-organization))))
3719     (save-excursion
3720       (message-set-work-buffer)
3721       (cond ((stringp organization)
3722              (insert organization))
3723             ((and (eq t organization)
3724                   message-user-organization-file
3725                   (file-exists-p message-user-organization-file))
3726              (insert-file-contents message-user-organization-file)))
3727       (goto-char (point-min))
3728       (while (re-search-forward "[\t\n]+" nil t)
3729         (replace-match "" t t))
3730       (unless (zerop (buffer-size))
3731         (buffer-string)))))
3732
3733 (defun message-make-lines ()
3734   "Count the number of lines and return numeric string."
3735   (save-excursion
3736     (save-restriction
3737       (widen)
3738       (goto-char (point-min))
3739       (re-search-forward
3740        (concat "^" (regexp-quote mail-header-separator) "$"))
3741       (forward-line 1)
3742       (int-to-string (count-lines (point) (point-max))))))
3743
3744 (defun message-make-in-reply-to ()
3745   "Return the In-Reply-To header for this message."
3746   (when message-reply-headers
3747     (let ((mid (mail-header-message-id message-reply-headers))
3748           (from (mail-header-from message-reply-headers))
3749           (date (mail-header-date message-reply-headers)))
3750       (when mid
3751         (concat mid
3752                 (when from
3753                   (let ((pair (std11-extract-address-components from)))
3754                     (concat "\n ("
3755                             (or (car pair) (cadr pair))
3756                             "'s message of \""
3757                             (if (or (not date) (string= date ""))
3758                                 "(unknown date)" date)
3759                             "\")"))))))))
3760
3761 (defun message-make-distribution ()
3762   "Make a Distribution header."
3763   (let ((orig-distribution (message-fetch-reply-field "distribution")))
3764     (cond ((message-functionp message-distribution-function)
3765            (funcall message-distribution-function))
3766           (t orig-distribution))))
3767
3768 (defun message-make-expires ()
3769   "Return an Expires header based on `message-expires'."
3770   (let ((current (current-time))
3771         (future (* 1.0 message-expires 60 60 24)))
3772     ;; Add the future to current.
3773     (setcar current (+ (car current) (round (/ future (expt 2 16)))))
3774     (setcar (cdr current) (+ (nth 1 current) (% (round future) (expt 2 16))))
3775     (message-make-date current)))
3776
3777 (defun message-make-path ()
3778   "Return uucp path."
3779   (let ((login-name (user-login-name)))
3780     (cond ((null message-user-path)
3781            (concat (system-name) "!" login-name))
3782           ((stringp message-user-path)
3783            ;; Support GENERICPATH.  Suggested by vixie@decwrl.dec.com.
3784            (concat message-user-path "!" login-name))
3785           (t login-name))))
3786
3787 (defun message-make-from ()
3788   "Make a From header."
3789   (let* ((style message-from-style)
3790          (login (message-make-address))
3791          (fullname
3792           (or (and (boundp 'user-full-name)
3793                    user-full-name)
3794               (user-full-name))))
3795     (when (string= fullname "&")
3796       (setq fullname (user-login-name)))
3797     (save-excursion
3798       (message-set-work-buffer)
3799       (cond
3800        ((or (null style)
3801             (equal fullname ""))
3802         (insert login))
3803        ((or (eq style 'angles)
3804             (and (not (eq style 'parens))
3805                  ;; Use angles if no quoting is needed, or if parens would
3806                  ;; need quoting too.
3807                  (or (not (string-match "[^- !#-'*+/-9=?A-Z^-~]" fullname))
3808                      (let ((tmp (concat fullname nil)))
3809                        (while (string-match "([^()]*)" tmp)
3810                          (aset tmp (match-beginning 0) ?-)
3811                          (aset tmp (1- (match-end 0)) ?-))
3812                        (string-match "[\\()]" tmp)))))
3813         (insert fullname)
3814         (goto-char (point-min))
3815         ;; Look for a character that cannot appear unquoted
3816         ;; according to RFC 822.
3817         (when (re-search-forward "[^- !#-'*+/-9=?A-Z^-~]" nil 1)
3818           ;; Quote fullname, escaping specials.
3819           (goto-char (point-min))
3820           (insert "\"")
3821           (while (re-search-forward "[\"\\]" nil 1)
3822             (replace-match "\\\\\\&" t))
3823           (insert "\""))
3824         (insert " <" login ">"))
3825        (t                               ; 'parens or default
3826         (insert login " (")
3827         (let ((fullname-start (point)))
3828           (insert fullname)
3829           (goto-char fullname-start)
3830           ;; RFC 822 says \ and nonmatching parentheses
3831           ;; must be escaped in comments.
3832           ;; Escape every instance of ()\ ...
3833           (while (re-search-forward "[()\\]" nil 1)
3834             (replace-match "\\\\\\&" t))
3835           ;; ... then undo escaping of matching parentheses,
3836           ;; including matching nested parentheses.
3837           (goto-char fullname-start)
3838           (while (re-search-forward
3839                   "\\(\\=\\|[^\\]\\(\\\\\\\\\\)*\\)\\\\(\\(\\([^\\]\\|\\\\\\\\\\)*\\)\\\\)"
3840                   nil 1)
3841             (replace-match "\\1(\\3)" t)
3842             (goto-char fullname-start)))
3843         (insert ")")))
3844       (buffer-string))))
3845
3846 (defun message-make-sender ()
3847   "Return the \"real\" user address.
3848 This function tries to ignore all user modifications, and
3849 give as trustworthy answer as possible."
3850   (concat (user-login-name) "@" (system-name)))
3851
3852 (defun message-make-address ()
3853   "Make the address of the user."
3854   (or (message-user-mail-address)
3855       (concat (user-login-name) "@" (message-make-domain))))
3856
3857 (defun message-user-mail-address ()
3858   "Return the pertinent part of `user-mail-address'."
3859   (when user-mail-address
3860     (if (string-match " " user-mail-address)
3861         (nth 1 (std11-extract-address-components user-mail-address))
3862       user-mail-address)))
3863
3864 (defun message-make-fqdn ()
3865   "Return user's fully qualified domain name."
3866   (let ((system-name (system-name))
3867         (user-mail (message-user-mail-address)))
3868     (cond
3869      ((string-match "[^.]\\.[^.]" system-name)
3870       ;; `system-name' returned the right result.
3871       system-name)
3872      ;; Try `mail-host-address'.
3873      ((and (boundp 'mail-host-address)
3874            (stringp mail-host-address)
3875            (string-match "\\." mail-host-address))
3876       mail-host-address)
3877      ;; We try `user-mail-address' as a backup.
3878      ((and user-mail
3879            (string-match "\\." user-mail)
3880            (string-match "@\\(.*\\)\\'" user-mail))
3881       (match-string 1 user-mail))
3882      ;; Default to this bogus thing.
3883      (t
3884       (concat system-name ".i-did-not-set--mail-host-address--so-shoot-me")))))
3885
3886 (defun message-make-host-name ()
3887   "Return the name of the host."
3888   (let ((fqdn (message-make-fqdn)))
3889     (string-match "^[^.]+\\." fqdn)
3890     (substring fqdn 0 (1- (match-end 0)))))
3891
3892 (defun message-make-domain ()
3893   "Return the domain name."
3894   (or mail-host-address
3895       (message-make-fqdn)))
3896
3897 ;; Dummy to avoid byte-compile warning.
3898 (defvar mule-version)
3899 (defvar emacs-beta-version)
3900 (defvar xemacs-codename)
3901 (defvar gnus-inviolable-extended-version)
3902
3903 (defun message-make-user-agent ()
3904   "Return user-agent info if the value `message-user-agent' is non-nil. If the
3905 \"User-Agent\" field has already exist, it's value will be added in the return
3906 string."
3907   (when message-user-agent
3908     (save-excursion
3909       (goto-char (point-min))
3910       (let ((case-fold-search t)
3911             user-agent start p end)
3912         (if (re-search-forward "^User-Agent:[\t ]*" nil t)
3913             (progn
3914               (setq start (match-beginning 0)
3915                     p (match-end 0)
3916                     end (std11-field-end)
3917                     user-agent (buffer-substring-no-properties p end))
3918               (delete-region start (1+ end))
3919               (concat message-user-agent " " user-agent))
3920           message-user-agent)))))
3921
3922 (defun message-generate-headers (headers)
3923   "Prepare article HEADERS.
3924 Headers already prepared in the buffer are not modified."
3925   (save-restriction
3926     (message-narrow-to-headers)
3927     (let* ((Date (message-make-date))
3928            (Message-ID (message-make-message-id))
3929            (Organization (message-make-organization))
3930            (From (message-make-from))
3931            (Path (message-make-path))
3932            (Subject nil)
3933            (Newsgroups nil)
3934            (In-Reply-To (message-make-in-reply-to))
3935            (To nil)
3936            (Distribution (message-make-distribution))
3937            (Lines (message-make-lines))
3938            (User-Agent (message-make-user-agent))
3939            (Expires (message-make-expires))
3940            (case-fold-search t)
3941            header value elem)
3942       ;; First we remove any old generated headers.
3943       (let ((headers message-deletable-headers))
3944         (unless (buffer-modified-p)
3945           (setq headers (delq 'Message-ID (copy-sequence headers))))
3946         (while headers
3947           (goto-char (point-min))
3948           (and (re-search-forward
3949                 (concat "^" (symbol-name (car headers)) ": *") nil t)
3950                (get-text-property (1+ (match-beginning 0)) 'message-deletable)
3951                (message-delete-line))
3952           (pop headers)))
3953       ;; Go through all the required headers and see if they are in the
3954       ;; articles already.  If they are not, or are empty, they are
3955       ;; inserted automatically - except for Subject, Newsgroups and
3956       ;; Distribution.
3957       (while headers
3958         (goto-char (point-min))
3959         (setq elem (pop headers))
3960         (if (consp elem)
3961             (if (eq (car elem) 'optional)
3962                 (setq header (cdr elem))
3963               (setq header (car elem)))
3964           (setq header elem))
3965         (when (or (not (re-search-forward
3966                         (concat "^"
3967                                 (regexp-quote
3968                                  (downcase
3969                                   (if (stringp header)
3970                                       header
3971                                     (symbol-name header))))
3972                                 ":")
3973                         nil t))
3974                   (progn
3975                     ;; The header was found.  We insert a space after the
3976                     ;; colon, if there is none.
3977                     (if (/= (char-after) ? ) (insert " ") (forward-char 1))
3978                     ;; Find out whether the header is empty...
3979                     (looking-at "[ \t]*\n[^ \t]")))
3980           ;; So we find out what value we should insert.
3981           (setq value
3982                 (cond
3983                  ((and (consp elem) (eq (car elem) 'optional))
3984                   ;; This is an optional header.  If the cdr of this
3985                   ;; is something that is nil, then we do not insert
3986                   ;; this header.
3987                   (setq header (cdr elem))
3988                   (or (and (fboundp (cdr elem)) (funcall (cdr elem)))
3989                       (and (boundp (cdr elem)) (symbol-value (cdr elem)))))
3990                  ((consp elem)
3991                   ;; The element is a cons.  Either the cdr is a
3992                   ;; string to be inserted verbatim, or it is a
3993                   ;; function, and we insert the value returned from
3994                   ;; this function.
3995                   (or (and (stringp (cdr elem)) (cdr elem))
3996                       (and (fboundp (cdr elem)) (funcall (cdr elem)))))
3997                  ((and (boundp header) (symbol-value header))
3998                   ;; The element is a symbol.  We insert the value
3999                   ;; of this symbol, if any.
4000                   (symbol-value header))
4001                  ((not (message-check-element header))
4002                   ;; We couldn't generate a value for this header,
4003                   ;; so we just ask the user.
4004                   (read-from-minibuffer
4005                    (format "Empty header for %s; enter value: " header)))))
4006           ;; Finally insert the header.
4007           (when (and value
4008                      (not (equal value "")))
4009             (save-excursion
4010               (if (bolp)
4011                   (progn
4012                     ;; This header didn't exist, so we insert it.
4013                     (goto-char (point-max))
4014                     (insert (if (stringp header) header (symbol-name header))
4015                             ": " value)
4016                     (unless (bolp)
4017                       (insert "\n"))
4018                     (forward-line -1))
4019                 ;; The value of this header was empty, so we clear
4020                 ;; totally and insert the new value.
4021                 (delete-region (point) (gnus-point-at-eol))
4022                 (insert value)
4023                 (when (bolp)
4024                   (delete-char -1)))
4025               ;; Add the deletable property to the headers that require it.
4026               (and (memq header message-deletable-headers)
4027                    (progn (beginning-of-line) (looking-at "[^:]+: "))
4028                    (add-text-properties
4029                     (point) (match-end 0)
4030                     '(message-deletable t face italic) (current-buffer)))))))
4031       ;; Insert new Sender if the From is strange.
4032       (let ((from (message-fetch-field "from"))
4033             (sender (message-fetch-field "sender"))
4034             (secure-sender (message-make-sender)))
4035         (when (and from
4036                    (not (message-check-element 'sender))
4037                    (not (string=
4038                          (downcase
4039                           (cadr (std11-extract-address-components from)))
4040                          (downcase secure-sender)))
4041                    (or (null sender)
4042                        (not
4043                         (string=
4044                          (downcase
4045                           (cadr (std11-extract-address-components sender)))
4046                          (downcase secure-sender)))))
4047           (goto-char (point-min))
4048           ;; Rename any old Sender headers to Original-Sender.
4049           (when (re-search-forward "^\\(Original-\\)*Sender:" nil t)
4050             (beginning-of-line)
4051             (insert "Original-")
4052             (beginning-of-line))
4053           (when (or (message-news-p)
4054                     (string-match "@.+\\.." secure-sender))
4055             (insert "Sender: " secure-sender "\n")))))))
4056
4057 (defun message-insert-courtesy-copy ()
4058   "Insert a courtesy message in mail copies of combined messages."
4059   (let (newsgroups)
4060     (save-excursion
4061       (save-restriction
4062         (message-narrow-to-headers)
4063         (when (setq newsgroups (message-fetch-field "newsgroups"))
4064           (goto-char (point-max))
4065           (insert "Posted-To: " newsgroups "\n")))
4066       (forward-line 1)
4067       (when message-courtesy-message
4068         (cond
4069          ((string-match "%s" message-courtesy-message)
4070           (insert (format message-courtesy-message newsgroups)))
4071          (t
4072           (insert message-courtesy-message)))))))
4073
4074 ;;;
4075 ;;; Setting up a message buffer
4076 ;;;
4077
4078 (defun message-fill-address (header value)
4079   (save-restriction
4080     (narrow-to-region (point) (point))
4081     (insert (capitalize (symbol-name header))
4082             ": "
4083             (if (consp value) (car value) value)
4084             "\n")
4085     (narrow-to-region (point-min) (1- (point-max)))
4086     (let (quoted last)
4087       (goto-char (point-min))
4088       (while (not (eobp))
4089         (skip-chars-forward "^,\"" (point-max))
4090         (if (or (eq (char-after) ?,)
4091                 (eobp))
4092             (when (not quoted)
4093               (if (and (> (current-column) 78)
4094                        last)
4095                   (save-excursion
4096                     (goto-char last)
4097                     (looking-at "[ \t]*")
4098                     (replace-match "\n " t t)))
4099               (setq last (1+ (point))))
4100           (setq quoted (not quoted)))
4101         (unless (eobp)
4102           (forward-char 1))))
4103     (goto-char (point-max))
4104     (widen)
4105     (forward-line 1)))
4106
4107 (defun message-fill-references (header value)
4108   (insert (capitalize (symbol-name header))
4109           ": "
4110           (std11-fill-msg-id-list-string
4111           (if (consp value) (car value) value))
4112           "\n"))
4113
4114 (defun message-fill-header (header value)
4115   (let ((begin (point))
4116         (fill-column 78)
4117         (fill-prefix " "))
4118     (insert (capitalize (symbol-name header))
4119             ": "
4120             (if (consp value) (car value) value)
4121             "\n")
4122     (save-restriction
4123       (narrow-to-region begin (point))
4124       (fill-region-as-paragraph begin (point))
4125       ;; Tapdance around looong Message-IDs.
4126       (forward-line -1)
4127       (when (looking-at "[ \t]*$")
4128         (message-delete-line))
4129       (goto-char begin)
4130       (re-search-forward ":" nil t)
4131       (when (looking-at "\n[ \t]+")
4132         (replace-match " " t t))
4133       (goto-char (point-max)))))
4134
4135 (defun message-shorten-1 (list cut surplus)
4136   ;; Cut SURPLUS elements out of LIST, beginning with CUTth one.
4137   (setcdr (nthcdr (- cut 2) list)
4138           (nthcdr (+ (- cut 2) surplus 1) list)))
4139
4140 (defun message-shorten-references (header references)
4141   "Trim REFERENCES to be less than 31 Message-ID long, and fold them.
4142 If folding is disallowed, also check that the REFERENCES are less
4143 than 988 characters long, and if they are not, trim them until they are."
4144   (let ((maxcount 31)
4145         (count 0)
4146         (cut 6)
4147         refs)
4148     (with-temp-buffer
4149       (insert references)
4150       (goto-char (point-min))
4151       ;; Cons a list of valid references.
4152       (while (re-search-forward "<[^>]+>" nil t)
4153         (push (match-string 0) refs))
4154       (setq refs (nreverse refs)
4155             count (length refs)))
4156
4157     ;; If the list has more than MAXCOUNT elements, trim it by
4158     ;; removing the CUTth element and the required number of
4159     ;; elements that follow.
4160     (when (> count maxcount)
4161       (let ((surplus (- count maxcount)))
4162         (message-shorten-1 refs cut surplus)
4163         (decf count surplus)))
4164
4165     ;; If folding is disallowed, make sure the total length (including
4166     ;; the spaces between) will be less than MAXSIZE characters.
4167     ;;
4168     ;; Only disallow folding for News messages. At this point the headers
4169     ;; have not been generated, thus we use message-this-is-news directly.
4170     (when (and message-this-is-news message-cater-to-broken-inn)
4171       (let ((maxsize 988)
4172             (totalsize (+ (apply #'+ (mapcar #'length refs))
4173                           (1- count)))
4174             (surplus 0)
4175             (ptr (nthcdr (1- cut) refs)))
4176         ;; Decide how many elements to cut off...
4177         (while (> totalsize maxsize)
4178           (decf totalsize (1+ (length (car ptr))))
4179           (incf surplus)
4180           (setq ptr (cdr ptr)))
4181         ;; ...and do it.
4182         (when (> surplus 0)
4183           (message-shorten-1 refs cut surplus))))
4184
4185     ;; Finally, collect the references back into a string and insert
4186     ;; it into the buffer.
4187     (let ((refstring (mapconcat #'identity refs " ")))
4188       (if (and message-this-is-news message-cater-to-broken-inn)
4189           (insert (capitalize (symbol-name header)) ": "
4190                   refstring "\n")
4191         (message-fill-header header refstring)))))
4192
4193 (defun message-position-point ()
4194   "Move point to where the user probably wants to find it."
4195   (message-narrow-to-headers)
4196   (cond
4197    ((re-search-forward "^[^:]+:[ \t]*$" nil t)
4198     (search-backward ":" )
4199     (widen)
4200     (forward-char 1)
4201     (if (eq (char-after) ? )
4202         (forward-char 1)
4203       (insert " ")))
4204    (t
4205     (goto-char (point-max))
4206     (widen)
4207     (forward-line 1)
4208     (unless (looking-at "$")
4209       (forward-line 2)))
4210    (sit-for 0)))
4211
4212 (defun message-buffer-name (type &optional to group)
4213   "Return a new (unique) buffer name based on TYPE and TO."
4214   (cond
4215    ;; Generate a new buffer name The Message Way.
4216    ((eq message-generate-new-buffers 'unique)
4217     (generate-new-buffer-name
4218      (concat "*" type
4219              (if to
4220                  (concat " to "
4221                          (or (car (std11-extract-address-components to))
4222                              to) "")
4223                "")
4224              (if (and group (not (string= group ""))) (concat " on " group) "")
4225              "*")))
4226    ;; Check whether `message-generate-new-buffers' is a function,
4227    ;; and if so, call it.
4228    ((message-functionp message-generate-new-buffers)
4229     (funcall message-generate-new-buffers type to group))
4230    ((eq message-generate-new-buffers 'unsent)
4231     (generate-new-buffer-name
4232      (concat "*unsent " type
4233              (if to
4234                  (concat " to "
4235                          (or (car (mail-extract-address-components to))
4236                              to) "")
4237                "")
4238              (if (and group (not (string= group ""))) (concat " on " group) "")
4239              "*")))
4240    ;; Use standard name.
4241    (t
4242     (format "*%s message*" type))))
4243
4244 (defmacro message-pop-to-buffer-1 (buffer)
4245   `(if pop-up-frames
4246        (let (special-display-buffer-names
4247              special-display-regexps
4248              same-window-buffer-names
4249              same-window-regexps)
4250          (pop-to-buffer ,buffer))
4251      (pop-to-buffer ,buffer)))
4252
4253 (defun message-pop-to-buffer (name)
4254   "Pop to buffer NAME, and warn if it already exists and is modified."
4255   (let ((buffer (get-buffer name))
4256         (pop-up-frames (and (or (static-if (featurep 'xemacs)
4257                                     (device-on-window-system-p)
4258                                   window-system)
4259                                 (>= emacs-major-version 20))
4260                             message-use-multi-frames)))
4261     (if (and buffer
4262              (buffer-name buffer))
4263         (progn
4264           (message-pop-to-buffer-1 buffer)
4265           (when (and (buffer-modified-p)
4266                      (not (y-or-n-p
4267                            "Message already being composed; erase? ")))
4268             (error "Message being composed")))
4269       (message-pop-to-buffer-1 name))
4270     (erase-buffer)
4271     (message-mode)
4272     (when pop-up-frames
4273       (set (make-local-variable 'message-original-frame) (selected-frame)))))
4274
4275 (defun message-do-send-housekeeping ()
4276   "Kill old message buffers."
4277   ;; We might have sent this buffer already.  Delete it from the
4278   ;; list of buffers.
4279   (setq message-buffer-list (delq (current-buffer) message-buffer-list))
4280   (while (and message-max-buffers
4281               message-buffer-list
4282               (>= (length message-buffer-list) message-max-buffers))
4283     ;; Kill the oldest buffer -- unless it has been changed.
4284     (let ((buffer (pop message-buffer-list)))
4285       (when (and (buffer-name buffer)
4286                  (not (buffer-modified-p buffer)))
4287         (kill-buffer buffer))))
4288   ;; Rename the buffer.
4289   (if message-send-rename-function
4290       (funcall message-send-rename-function)
4291     (when (string-match "\\`\\*\\(unsent \\)?" (buffer-name))
4292       (rename-buffer
4293        (concat "*sent " (substring (buffer-name) (match-end 0))) t)))
4294   ;; Push the current buffer onto the list.
4295   (when message-max-buffers
4296     (setq message-buffer-list
4297           (nconc message-buffer-list (list (current-buffer))))))
4298
4299 (defvar mc-modes-alist)
4300 (defun message-setup (headers &optional replybuffer actions)
4301   (when (and (boundp 'mc-modes-alist)
4302              (not (assq 'message-mode mc-modes-alist)))
4303     (push '(message-mode (encrypt . mc-encrypt-message)
4304                          (sign . mc-sign-message))
4305           mc-modes-alist))
4306   (when actions
4307     (setq message-send-actions actions))
4308   (setq message-reply-buffer
4309         (or (message-get-parameter 'reply-buffer)
4310             replybuffer))
4311   (goto-char (point-min))
4312   ;; Insert all the headers.
4313   (mail-header-format
4314    (let ((h headers)
4315          (alist message-header-format-alist))
4316      (while h
4317        (unless (assq (caar h) message-header-format-alist)
4318          (push (list (caar h)) alist))
4319        (pop h))
4320      alist)
4321    headers)
4322   (delete-region (point) (progn (forward-line -1) (point)))
4323   (when message-default-headers
4324     (insert message-default-headers)
4325     (or (bolp) (insert ?\n)))
4326   (put-text-property
4327    (point)
4328    (progn
4329      (insert mail-header-separator "\n")
4330      (1- (point)))
4331    'read-only nil)
4332   (forward-line -1)
4333   (when (message-news-p)
4334     (when message-default-news-headers
4335       (insert message-default-news-headers)
4336       (or (bolp) (insert ?\n)))
4337     (when message-generate-headers-first
4338       (message-generate-headers
4339        (delq 'Lines
4340              (delq 'Subject
4341                    (copy-sequence message-required-news-headers))))))
4342   (when (message-mail-p)
4343     (when message-default-mail-headers
4344       (insert message-default-mail-headers)
4345       (or (bolp) (insert ?\n)))
4346     (when message-generate-headers-first
4347       (message-generate-headers
4348        (delq 'Lines
4349              (delq 'Subject
4350                    (copy-sequence message-required-mail-headers))))))
4351   (run-hooks 'message-signature-setup-hook)
4352   (message-insert-signature)
4353   (save-restriction
4354     (message-narrow-to-headers)
4355     (run-hooks 'message-header-setup-hook))
4356   (set-buffer-modified-p nil)
4357   (setq buffer-undo-list nil)
4358   (run-hooks 'message-setup-hook)
4359   (message-position-point)
4360   (undo-boundary))
4361
4362 (defun message-set-auto-save-file-name ()
4363   "Associate the message buffer with a file in the drafts directory."
4364   (when message-auto-save-directory
4365     (if (gnus-alive-p)
4366         (setq message-draft-article
4367               (nndraft-request-associate-buffer "drafts"))
4368       (setq buffer-file-name (expand-file-name "*message*"
4369                                                message-auto-save-directory))
4370       (setq buffer-auto-save-file-name (make-auto-save-file-name)))
4371     (clear-visited-file-modtime)
4372     (static-if (boundp 'MULE)
4373         (set-file-coding-system message-draft-coding-system)
4374       (setq buffer-file-coding-system message-draft-coding-system))))
4375
4376 (defun message-disassociate-draft ()
4377   "Disassociate the message buffer from the drafts directory."
4378   (when message-draft-article
4379     (nndraft-request-expire-articles
4380      (list message-draft-article) "drafts" nil t)))
4381
4382 (defun message-insert-headers ()
4383   "Generate the headers for the article."
4384   (interactive)
4385   (save-excursion
4386     (save-restriction
4387       (message-narrow-to-headers)
4388       (when (message-news-p)
4389         (message-generate-headers
4390          (delq 'Lines
4391                (delq 'Subject
4392                      (copy-sequence message-required-news-headers)))))
4393       (when (message-mail-p)
4394         (message-generate-headers
4395          (delq 'Lines
4396                (delq 'Subject
4397                      (copy-sequence message-required-mail-headers))))))))
4398
4399 \f
4400
4401 ;;;
4402 ;;; Commands for interfacing with message
4403 ;;;
4404
4405 ;;;###autoload
4406 (defun message-mail (&optional to subject
4407                                other-headers continue switch-function
4408                                yank-action send-actions)
4409   "Start editing a mail message to be sent.
4410 OTHER-HEADERS is an alist of header/value pairs."
4411   (interactive)
4412   (let ((message-this-is-mail t))
4413     (message-pop-to-buffer (message-buffer-name "mail" to))
4414     (message-setup
4415      (nconc
4416       `((To . ,(or to "")) (Subject . ,(or subject "")))
4417       (when other-headers other-headers)))))
4418
4419 ;;;###autoload
4420 (defun message-news (&optional newsgroups subject)
4421   "Start editing a news article to be sent."
4422   (interactive)
4423   (let ((message-this-is-news t))
4424     (message-pop-to-buffer (message-buffer-name "news" nil newsgroups))
4425     (message-setup `((Newsgroups . ,(or newsgroups ""))
4426                      (Subject . ,(or subject ""))))))
4427
4428 (defun message-get-reply-headers (wide &optional to-address)
4429   (let (follow-to mct never-mct from to cc reply-to mft)
4430     ;; Find all relevant headers we need.
4431     (setq from (message-fetch-field "from")
4432           to (message-fetch-field "to")
4433           cc (message-fetch-field "cc")
4434           mct (when message-use-mail-copies-to
4435                 (message-fetch-field "mail-copies-to"))
4436           reply-to (when message-use-mail-reply-to
4437                      (or (message-fetch-field "mail-reply-to")
4438                          (message-fetch-field "reply-to")))
4439           mft (when (and (not to-address)
4440                          (not reply-to)
4441                          message-use-mail-followup-to)
4442                 (message-fetch-field "mail-followup-to")))
4443
4444     ;; Handle special values of Mail-Copies-To.
4445     (when mct
4446       (cond
4447        ((and (or (equal (downcase mct) "never")
4448                  (equal (downcase mct) "nobody"))
4449              (or (not (eq message-use-mail-copies-to 'ask))
4450                  (message-y-or-n-p
4451                   (concat "Obey Mail-Copies-To: never? ") t "\
4452 You should normally obey the Mail-Copies-To: header.
4453
4454         `Mail-Copies-To: never'
4455 directs you not to send your response to the author.")))
4456         (setq never-mct t)
4457         (setq mct nil))
4458        ((and (or (equal (downcase mct) "always")
4459                  (equal (downcase mct) "poster"))
4460              (or (not (eq message-use-mail-copies-to 'ask))
4461                  (message-y-or-n-p
4462                   (concat "Obey Mail-Copies-To: always? ") t "\
4463 You should normally obey the Mail-Copies-To: header.
4464
4465         `Mail-Copies-To: always'
4466 sends a copy of your response to the author.")))
4467         (setq mct (or reply-to from)))
4468        ((and (eq message-use-mail-copies-to 'ask)
4469              (not
4470               (message-y-or-n-p
4471                (concat "Obey Mail-Copies-To: " mct " ? ") t "\
4472 You should normally obey the Mail-Copies-To: header.
4473
4474         `Mail-Copies-To: " mct "'
4475 sends a copy of your response to " (if (string-match "," mct)
4476                                        "the specified addresses"
4477                                      "that address") ".")))
4478         (setq mct nil))))
4479
4480     ;; Handle Mail-Followup-To.
4481     (when (and mft
4482                (eq message-use-mail-followup-to 'ask)
4483                (not (message-y-or-n-p
4484                      (concat "Obey Mail-Followup-To: " mft "? ") t "\
4485 You should normally obey the Mail-Followup-To: header.
4486
4487         `Mail-Followup-To: " mft "'
4488 directs your response to " (if (string-match "," mft)
4489                                "the specified addresses"
4490                              "that address only") ".
4491
4492 A typical situation where Mail-Followup-To is used is when the author thinks
4493 that further discussion should take place only in "
4494                              (if (string-match "," mft)
4495                                  "the specified mailing lists"
4496                                "that mailing list") ".")))
4497       (setq mft nil))
4498
4499     (if (or (not wide)
4500             to-address)
4501         (progn
4502           (setq follow-to (list (cons 'To (or to-address reply-to mft from))))
4503           (when (and wide mct)
4504             (push (cons 'Cc mct) follow-to)))
4505       (let (ccalist)
4506         (save-excursion
4507           (message-set-work-buffer)
4508           (unless never-mct
4509             (insert (or reply-to from "")))
4510           (insert (if mft (concat (if (bolp) "" ", ") mft "") ""))
4511           (insert (if to (concat (if (bolp) "" ", ") to "") ""))
4512           (insert (if mct (concat (if (bolp) "" ", ") mct) ""))
4513           (insert (if cc (concat (if (bolp) "" ", ") cc) ""))
4514           (goto-char (point-min))
4515           (while (re-search-forward "[ \t]+" nil t)
4516             (replace-match " " t t))
4517           ;; Remove addresses that match `rmail-dont-reply-to-names'.
4518           (let ((rmail-dont-reply-to-names message-dont-reply-to-names))
4519             (insert (prog1 (rmail-dont-reply-to (buffer-string))
4520                       (erase-buffer))))
4521           (goto-char (point-min))
4522           ;; Perhaps "Mail-Copies-To: never" removed the only address?
4523           (when (eobp)
4524             (insert (or reply-to from "")))
4525           (setq ccalist
4526                 (mapcar
4527                  (lambda (addr)
4528                    (cons (mail-strip-quoted-names addr) addr))
4529                  (message-tokenize-header (buffer-string))))
4530           (let ((s ccalist))
4531             (while s
4532               (setq ccalist (delq (assoc (car (pop s)) s) ccalist)))))
4533         (setq follow-to (list (cons 'To (cdr (pop ccalist)))))
4534         (when ccalist
4535           (let ((ccs (cons 'Cc (mapconcat
4536                                 (lambda (addr) (cdr addr)) ccalist ", "))))
4537             (when (string-match "^ +" (cdr ccs))
4538               (setcdr ccs (substring (cdr ccs) (match-end 0))))
4539             (push ccs follow-to)))))
4540     follow-to))
4541
4542 ;;;###autoload
4543 (defun message-reply (&optional to-address wide)
4544   "Start editing a reply to the article in the current buffer."
4545   (interactive)
4546   (require 'gnus-sum)                   ; for gnus-list-identifiers
4547   (let ((cur (current-buffer))
4548         from subject date
4549         references message-id follow-to
4550         (inhibit-point-motion-hooks t)
4551         (message-this-is-mail t)
4552         gnus-warning in-reply-to)
4553     (save-restriction
4554       (message-narrow-to-head)
4555       ;; Allow customizations to have their say.
4556       (if (not wide)
4557           ;; This is a regular reply.
4558           (if (message-functionp message-reply-to-function)
4559               (setq follow-to (funcall message-reply-to-function)))
4560         ;; This is a followup.
4561         (if (message-functionp message-wide-reply-to-function)
4562             (save-excursion
4563               (setq follow-to
4564                     (funcall message-wide-reply-to-function)))))
4565       (setq message-id (message-fetch-field "message-id" t)
4566             references (message-fetch-field "references")
4567             date (message-fetch-field "date")
4568             from (message-fetch-field "from")
4569             subject (or (message-fetch-field "subject") "none"))
4570       (if gnus-list-identifiers
4571           (setq subject (message-strip-list-identifiers subject)))
4572       (setq subject (message-make-followup-subject subject))
4573
4574       (when (and (setq gnus-warning (message-fetch-field "gnus-warning"))
4575                  (string-match "<[^>]+>" gnus-warning))
4576         (setq message-id (match-string 0 gnus-warning)))
4577
4578       (unless follow-to
4579         (setq follow-to (message-get-reply-headers wide to-address)))
4580
4581       ;; Get the references from "In-Reply-To" field if there were
4582       ;; no references and "In-Reply-To" field looks promising.
4583       (unless references
4584         (when (and (setq in-reply-to (message-fetch-field "in-reply-to"))
4585                    (string-match "<[^>]+>" in-reply-to))
4586           (setq references (match-string 0 in-reply-to)))))
4587
4588     (message-pop-to-buffer
4589      (message-buffer-name
4590       (if wide "wide reply" "reply") from
4591       (if wide to-address nil)))
4592
4593     (setq message-reply-headers
4594           (make-full-mail-header-from-decoded-header
4595            0 subject from date message-id references 0 0 ""))
4596
4597     (message-setup
4598      `((Subject . ,subject)
4599        ,@follow-to
4600        ,@(if (or references message-id)
4601              `((References . ,(concat (or references "") (and references " ")
4602                                       (or message-id ""))))
4603            nil))
4604      cur)))
4605
4606 ;;;###autoload
4607 (defun message-wide-reply (&optional to-address)
4608   "Make a \"wide\" reply to the message in the current buffer."
4609   (interactive)
4610   (message-reply to-address t))
4611
4612 ;;;###autoload
4613 (defun message-followup (&optional to-newsgroups)
4614   "Follow up to the message in the current buffer.
4615 If TO-NEWSGROUPS, use that as the new Newsgroups line."
4616   (interactive)
4617   (let ((cur (current-buffer))
4618         from subject date mct
4619         references message-id follow-to
4620         (inhibit-point-motion-hooks t)
4621         (message-this-is-news t)
4622         followup-to distribution newsgroups gnus-warning posted-to mft mrt)
4623     (save-restriction
4624       (message-narrow-to-head)
4625       (when (message-functionp message-followup-to-function)
4626         (setq follow-to
4627               (funcall message-followup-to-function)))
4628       (setq from (message-fetch-field "from")
4629             date (message-fetch-field "date" t)
4630             subject (or (message-fetch-field "subject") "none")
4631             references (message-fetch-field "references")
4632             message-id (message-fetch-field "message-id" t)
4633             followup-to (when message-use-followup-to
4634                           (message-fetch-field "followup-to"))
4635             distribution (message-fetch-field "distribution")
4636             newsgroups (message-fetch-field "newsgroups")
4637             posted-to (message-fetch-field "posted-to")
4638             mct (when message-use-mail-copies-to
4639                   (message-fetch-field "mail-copies-to"))
4640             mft (when message-use-mail-followup-to
4641                   (message-fetch-field "mail-followup-to"))
4642             mrt (when message-use-mail-reply-to
4643                   (or (message-fetch-field "mail-reply-to")
4644                       (message-fetch-field "reply-to")))
4645             gnus-warning (message-fetch-field "gnus-warning"))
4646       (when (and gnus-warning (string-match "<[^>]+>" gnus-warning))
4647         (setq message-id (match-string 0 gnus-warning)))
4648       ;; Remove bogus distribution.
4649       (when (and (stringp distribution)
4650                  (let ((case-fold-search t))
4651                    (string-match "world" distribution)))
4652         (setq distribution nil))
4653       (if gnus-list-identifiers
4654           (setq subject (message-strip-list-identifiers subject)))
4655       (setq subject (message-make-followup-subject subject))
4656       (widen))
4657
4658     ;; Handle special values of Mail-Copies-To.
4659     (when mct
4660       (cond
4661        ((and (or (equal (downcase mct) "never")
4662                  (equal (downcase mct) "nobody"))
4663              (or (not (eq message-use-mail-copies-to 'ask))
4664                  (message-y-or-n-p
4665                   (concat "Obey Mail-Copies-To: never? ") t "\
4666 You should normally obey the Mail-Copies-To: header.
4667
4668         `Mail-Copies-To: never'
4669 directs you not to send your response to the author.")))
4670         (setq mct nil))
4671        ((and (or (equal (downcase mct) "always")
4672                  (equal (downcase mct) "poster"))
4673              (or (not (eq message-use-mail-copies-to 'ask))
4674                  (message-y-or-n-p
4675                   (concat "Obey Mail-Copies-To: always? ") t "\
4676 You should normally obey the Mail-Copies-To: header.
4677
4678         `Mail-Copies-To: always'
4679 sends a copy of your response to the author.")))
4680         (setq mct (or mrt from)))
4681        ((and (eq message-use-mail-copies-to 'ask)
4682              (not
4683               (message-y-or-n-p
4684                (concat "Obey Mail-Copies-To: " mct " ? ") t "\
4685 You should normally obey the Mail-Copies-To: header.
4686
4687         `Mail-Copies-To: " mct "'
4688 sends a copy of your response to " (if (string-match "," mct)
4689                                        "the specified addresses"
4690                                      "that address") ".")))
4691         (setq mct nil))
4692        ))
4693
4694     (unless follow-to
4695       (cond
4696        (to-newsgroups (setq follow-to (list (cons 'Newsgroups to-newsgroups))))
4697        ;; Handle Followup-To.
4698        (followup-to
4699         (cond
4700          ((equal (downcase followup-to) "poster")
4701           (if (or (eq message-use-followup-to 'use)
4702                   (message-y-or-n-p "Obey Followup-To: poster? " t "\
4703 You should normally obey the Followup-To: header.
4704
4705         `Followup-To: poster'
4706 sends your response via e-mail instead of news.
4707
4708 A typical situation where `Followup-To: poster' is used is when the author
4709 does not read the newsgroup, so he wouldn't see any replies sent to it."))
4710               (setq message-this-is-news nil
4711                     distribution nil
4712                     follow-to (list (cons 'To (or mrt from ""))))
4713             (setq follow-to (list (cons 'Newsgroups newsgroups)))))
4714          (t
4715           (if (or (equal followup-to newsgroups)
4716                   (not (eq message-use-followup-to 'ask))
4717                   (message-y-or-n-p
4718                    (concat "Obey Followup-To: " followup-to "? ") t "\
4719 You should normally obey the Followup-To: header.
4720
4721         `Followup-To: " followup-to "'
4722 directs your response to " (if (string-match "," followup-to)
4723                                "the specified newsgroups"
4724                              "that newsgroup only") ".
4725
4726 If a message is posted to several newsgroups, Followup-To is often
4727 used to direct the following discussion to one newsgroup only,
4728 because discussions that are spread over several newsgroup tend to
4729 be fragmented and very difficult to follow.
4730
4731 Also, some source/announcement newsgroups are not indented for discussion;
4732 responses here are directed to other newsgroups."))
4733               (setq follow-to (list (cons 'Newsgroups followup-to)))
4734             (setq follow-to (list (cons 'Newsgroups newsgroups)))))))
4735        ;; Handle Mail-Followup-To, followup via e-mail.
4736        ((and mft
4737              (or (not (eq message-use-mail-followup-to 'ask))
4738                  (message-y-or-n-p
4739                   (concat "Obey Mail-Followup-To: " mft "? ") t "\
4740 You should normally obey the Mail-Followup-To: header.
4741
4742         `Mail-Followup-To: " mft "'
4743 directs your response to " (if (string-match "," mft)
4744                                "the specified addresses"
4745                              "that address only") " instead of news.
4746
4747 A typical situation where Mail-Followup-To is used is when the author thinks
4748 that further discussion should take place only in "
4749                              (if (string-match "," mft)
4750                                  "the specified mailing lists"
4751                                "that mailing list") ".")))
4752         (setq message-this-is-news nil
4753               distribution nil
4754               follow-to (list (cons 'To mft))))
4755        (posted-to (setq follow-to (list (cons 'Newsgroups posted-to))))
4756        (t
4757         (setq follow-to (list (cons 'Newsgroups newsgroups))))))
4758
4759     (message-pop-to-buffer (message-buffer-name "followup" from newsgroups))
4760
4761     (setq message-reply-headers
4762           (make-full-mail-header-from-decoded-header
4763            0 subject from date message-id references 0 0 ""))
4764
4765     (message-setup
4766      `((Subject . ,subject)
4767        ,@follow-to
4768        ,@(and mct (list (cons 'Cc mct)))
4769        ,@(and distribution (list (cons 'Distribution distribution)))
4770        ,@(if (or references message-id)
4771              `((References . ,(concat (or references "") (and references " ")
4772                                       (or message-id ""))))))
4773      cur)))
4774
4775 ;;;###autoload
4776 (defun message-cancel-news (&optional arg)
4777   "Cancel an article you posted.
4778 If ARG, allow editing of the cancellation message."
4779   (interactive "P")
4780   (unless (message-news-p)
4781     (error "This is not a news article; canceling is impossible"))
4782   (when (yes-or-no-p "Do you really want to cancel this article? ")
4783     (let (from newsgroups message-id distribution buf sender)
4784       (save-excursion
4785         ;; Get header info from original article.
4786         (save-restriction
4787           (message-narrow-to-head)
4788           (setq from (message-fetch-field "from")
4789                 sender (message-fetch-field "sender")
4790                 newsgroups (message-fetch-field "newsgroups")
4791                 message-id (message-fetch-field "message-id" t)
4792                 distribution (message-fetch-field "distribution")))
4793         ;; Make sure that this article was written by the user.
4794         (unless (or (and sender
4795                          (string-equal
4796                           (downcase sender)
4797                           (downcase (message-make-sender))))
4798                     (string-equal
4799                      (downcase (cadr (std11-extract-address-components
4800                                       from)))
4801                      (downcase (cadr (std11-extract-address-components
4802                                       (message-make-from))))))
4803           (error "This article is not yours"))
4804         ;; Make control message.
4805         (if arg
4806             (message-news)
4807           (setq buf (set-buffer (get-buffer-create " *message cancel*"))))
4808         (erase-buffer)
4809         (insert "Newsgroups: " newsgroups "\n"
4810                "From: " from "\n"
4811                 "Subject: cmsg cancel " message-id "\n"
4812                 "Control: cancel " message-id "\n"
4813                 (if distribution
4814                     (concat "Distribution: " distribution "\n")
4815                   "")
4816                 mail-header-separator "\n"
4817                 message-cancel-message)
4818         (run-hooks 'message-cancel-hook)
4819         (message "Canceling your article...")
4820         (unless arg
4821           (if (let ((message-syntax-checks
4822                      'dont-check-for-anything-just-trust-me)
4823                     (message-encoding-buffer (current-buffer))
4824                     (message-edit-buffer (current-buffer)))
4825                 (message-send-news))
4826               (message "Canceling your article...done"))
4827           (kill-buffer buf))))))
4828
4829 (defun message-supersede-setup-for-mime-edit ()
4830   (set (make-local-variable 'message-setup-hook) nil)
4831   (mime-edit-again))
4832
4833 ;;;###autoload
4834 (defun message-supersede ()
4835   "Start composing a message to supersede the current message.
4836 This is done simply by taking the old article and adding a Supersedes
4837 header line with the old Message-ID."
4838   (interactive)
4839   (let ((cur (current-buffer))
4840         (sender (message-fetch-field "sender"))
4841         (from (message-fetch-field "from")))
4842     ;; Check whether the user owns the article that is to be superseded.
4843     (unless (or (and sender
4844                      (string-equal
4845                       (downcase sender)
4846                       (downcase (message-make-sender))))
4847                 (string-equal
4848                  (downcase (cadr (std11-extract-address-components from)))
4849                  (downcase (cadr (std11-extract-address-components
4850                                   (message-make-from))))))
4851       (error "This article is not yours"))
4852     ;; Get a normal message buffer.
4853     (message-pop-to-buffer (message-buffer-name "supersede"))
4854     (insert-buffer-substring cur)
4855     (message-narrow-to-head)
4856     ;; Remove unwanted headers.
4857     (when message-ignored-supersedes-headers
4858       (message-remove-header message-ignored-supersedes-headers t))
4859     (goto-char (point-min))
4860     (if (not (re-search-forward "^Message-ID: " nil t))
4861         (error "No Message-ID in this article")
4862       (replace-match "Supersedes: " t t))
4863     (goto-char (point-max))
4864     (insert mail-header-separator)
4865     (widen)
4866     (when message-supersede-setup-function
4867       (funcall message-supersede-setup-function))
4868     (run-hooks 'message-supersede-setup-hook)
4869     (goto-char (point-min))
4870     (search-forward (concat "\n" mail-header-separator "\n") nil t)))
4871
4872 ;;;###autoload
4873 (defun message-recover ()
4874   "Reread contents of current buffer from its last auto-save file."
4875   (interactive)
4876   (let ((file-name (make-auto-save-file-name)))
4877     (cond ((save-window-excursion
4878              (if (not (eq system-type 'vax-vms))
4879                  (with-output-to-temp-buffer "*Directory*"
4880                    (with-current-buffer standard-output
4881                      (fundamental-mode)) ; for Emacs 20.4+
4882                    (buffer-disable-undo standard-output)
4883                    (let ((default-directory "/"))
4884                      (call-process
4885                       "ls" nil standard-output nil "-l" file-name))))
4886              (yes-or-no-p (format "Recover auto save file %s? " file-name)))
4887            (let ((buffer-read-only nil))
4888              (erase-buffer)
4889              (insert-file-contents file-name nil)))
4890           (t (error "message-recover cancelled")))))
4891
4892 ;;; Washing Subject:
4893
4894 (defun message-wash-subject (subject)
4895   "Remove junk like \"Re:\", \"(fwd)\", etc. that was added to the subject by previous forwarders, replyers, etc."
4896   (with-temp-buffer
4897     (insert-string subject)
4898     (goto-char (point-min))
4899     ;; strip Re/Fwd stuff off the beginning
4900     (while (re-search-forward
4901             "\\([Rr][Ee]:\\|[Ff][Ww][Dd]\\(\\[[0-9]*\\]\\)?:\\|[Ff][Ww]:\\)" nil t)
4902       (replace-match ""))
4903
4904     ;; and gnus-style forwards [foo@bar.com] subject
4905     (goto-char (point-min))
4906     (while (re-search-forward "\\[[^ \t]*\\(@\\|\\.\\)[^ \t]*\\]" nil t)
4907       (replace-match ""))
4908
4909     ;; and off the end
4910     (goto-char (point-max))
4911     (while (re-search-backward "([Ff][Ww][Dd])" nil t)
4912       (replace-match ""))
4913
4914     ;; and finally, any whitespace that was left-over
4915     (goto-char (point-min))
4916     (while (re-search-forward "^[ \t]+" nil t)
4917       (replace-match ""))
4918     (goto-char (point-max))
4919     (while (re-search-backward "[ \t]+$" nil t)
4920       (replace-match ""))
4921
4922     (buffer-string)))
4923
4924 ;;; Forwarding messages.
4925
4926 (defun message-forward-subject-author-subject (subject)
4927   "Generate a subject for a forwarded message.
4928 The form is: [Source] Subject, where if the original message was mail,
4929 Source is the sender, and if the original message was news, Source is
4930 the list of newsgroups is was posted to."
4931   (concat "["
4932           (or (message-fetch-field
4933                (if (message-news-p) "newsgroups" "from"))
4934               "(nowhere)")
4935           "] " subject))
4936
4937 (defun message-forward-subject-fwd (subject)
4938   "Generate a subject for a forwarded message.
4939 The form is: Fwd: Subject, where Subject is the original subject of
4940 the message."
4941   (concat "Fwd: " subject))
4942
4943 (defun message-make-forward-subject ()
4944   "Return a Subject header suitable for the message in the current buffer."
4945   (save-excursion
4946     (save-restriction
4947       (message-narrow-to-head)
4948       (let ((funcs message-make-forward-subject-function)
4949             (subject (message-fetch-field "Subject")))
4950         (setq subject
4951               (if subject
4952                   (if message-wash-forwarded-subjects
4953                       (message-wash-subject
4954                        (nnheader-decode-subject subject))
4955                     (nnheader-decode-subject subject))
4956                 "(none)"))
4957         ;; Make sure funcs is a list.
4958         (and funcs
4959              (not (listp funcs))
4960              (setq funcs (list funcs)))
4961         ;; Apply funcs in order, passing subject generated by previous
4962         ;; func to the next one.
4963         (while funcs
4964           (when (message-functionp (car funcs))
4965             (setq subject (funcall (car funcs) subject)))
4966           (setq funcs (cdr funcs)))
4967         subject))))
4968
4969 ;;;###autoload
4970 (defun message-forward (&optional news)
4971   "Forward the current message via mail.
4972 Optional NEWS will use news to forward instead of mail."
4973   (interactive "P")
4974   (let ((cur (current-buffer))
4975         (subject (message-make-forward-subject))
4976         art-beg)
4977     (if news
4978         (message-news nil subject)
4979       (message-mail nil subject))
4980     ;; Put point where we want it before inserting the forwarded
4981     ;; message.
4982     (if message-forward-before-signature
4983         (message-goto-body)
4984       (goto-char (point-max)))
4985     ;; Make sure we're at the start of the line.
4986     (unless (bolp)
4987       (insert "\n"))
4988     ;; Narrow to the area we are to insert.
4989     (narrow-to-region (point) (point))
4990     ;; Insert the separators and the forwarded buffer.
4991     (insert message-forward-start-separator)
4992     (setq art-beg (point))
4993     (insert-buffer-substring cur)
4994     (goto-char (point-max))
4995     (insert message-forward-end-separator)
4996     (set-text-properties (point-min) (point-max) nil)
4997     ;; Remove all unwanted headers.
4998     (goto-char art-beg)
4999     (narrow-to-region (point) (if (search-forward "\n\n" nil t)
5000                                   (1- (point))
5001                                 (point)))
5002     (goto-char (point-min))
5003     (message-remove-header message-included-forward-headers t nil t)
5004     (widen)
5005     (message-position-point)))
5006
5007 ;;;###autoload
5008 (defun message-resend (address)
5009   "Resend the current article to ADDRESS."
5010   (interactive
5011    (list (message-read-from-minibuffer "Resend message to: ")))
5012   (message "Resending message to %s..." address)
5013   (save-excursion
5014     (let ((cur (current-buffer))
5015           beg)
5016       ;; We first set up a normal mail buffer.
5017       (set-buffer (get-buffer-create " *message resend*"))
5018       (erase-buffer)
5019       ;; avoid to turn-on-mime-edit
5020       (let (message-setup-hook)
5021         (message-setup `((To . ,address)))
5022         )
5023       ;; Insert our usual headers.
5024       (message-generate-headers '(From Date To))
5025       (message-narrow-to-headers)
5026       ;; Rename them all to "Resent-*".
5027       (while (re-search-forward "^[A-Za-z]" nil t)
5028         (forward-char -1)
5029         (insert "Resent-"))
5030       (widen)
5031       (forward-line)
5032       (delete-region (point) (point-max))
5033       (setq beg (point))
5034       ;; Insert the message to be resent.
5035       (insert-buffer-substring cur)
5036       (goto-char (point-min))
5037       (search-forward "\n\n")
5038       (forward-char -1)
5039       (save-restriction
5040         (narrow-to-region beg (point))
5041         (message-remove-header message-ignored-resent-headers t)
5042         (goto-char (point-max)))
5043       (insert mail-header-separator)
5044       ;; Rename all old ("Also-")Resent headers.
5045       (while (re-search-backward "^\\(Also-\\)*Resent-" beg t)
5046         (beginning-of-line)
5047         (insert "Also-"))
5048       ;; Quote any "From " lines at the beginning.
5049       (goto-char beg)
5050       (when (looking-at "From ")
5051         (replace-match "X-From-Line: "))
5052       ;; Send it.
5053       (let ((message-encoding-buffer (current-buffer))
5054             (message-edit-buffer (current-buffer)))
5055         (let (message-required-mail-headers)
5056           (message-send-mail)))
5057       (kill-buffer (current-buffer)))
5058     (message "Resending message to %s...done" address)))
5059
5060 (defun message-bounce-setup-for-mime-edit ()
5061   (set (make-local-variable 'message-setup-hook) nil)
5062   (mime-edit-again))
5063
5064 ;;;###autoload
5065 (defun message-bounce ()
5066   "Re-mail the current message.
5067 This only makes sense if the current message is a bounce message that
5068 contains some mail you have written which has been bounced back to
5069 you."
5070   (interactive)
5071   (let ((cur (current-buffer))
5072         boundary)
5073     (message-pop-to-buffer (message-buffer-name "bounce"))
5074     (insert-buffer-substring cur)
5075     (undo-boundary)
5076     (message-narrow-to-head)
5077     (if (and (message-fetch-field "MIME-Version")
5078              (setq boundary (message-fetch-field "Content-Type")))
5079         (if (string-match "boundary=\"\\([^\"]+\\)\"" boundary)
5080             (setq boundary (concat (match-string 1 boundary) " *\n"
5081                                    "Content-Type: message/rfc822"))
5082           (setq boundary nil)))
5083     (widen)
5084     (goto-char (point-min))
5085     (search-forward "\n\n" nil t)
5086     (or (and boundary
5087              (re-search-forward boundary nil t)
5088              (forward-line 2))
5089         (and (re-search-forward message-unsent-separator nil t)
5090              (forward-line 1))
5091         (re-search-forward "^Return-Path:.*\n" nil t))
5092     ;; We remove everything before the bounced mail.
5093     (delete-region
5094      (point-min)
5095      (if (re-search-forward "^[^ \n\t]+:" nil t)
5096          (match-beginning 0)
5097        (point)))
5098     (save-restriction
5099       (message-narrow-to-head)
5100       (message-remove-header message-ignored-bounced-headers t)
5101       (goto-char (point-max))
5102       (insert mail-header-separator))
5103     (when message-bounce-setup-function
5104       (funcall message-bounce-setup-function))
5105     (run-hooks 'message-bounce-setup-hook)
5106     (message-position-point)))
5107
5108 ;;;
5109 ;;; Interactive entry points for new message buffers.
5110 ;;;
5111
5112 ;;;###autoload
5113 (defun message-mail-other-window (&optional to subject)
5114   "Like `message-mail' command, but display mail buffer in another window."
5115   (interactive)
5116   (let ((pop-up-windows t)
5117         (special-display-buffer-names nil)
5118         (special-display-regexps nil)
5119         (same-window-buffer-names nil)
5120         (same-window-regexps nil))
5121     (message-pop-to-buffer (message-buffer-name "mail" to)))
5122   (let ((message-this-is-mail t))
5123     (message-setup `((To . ,(or to "")) (Subject . ,(or subject ""))))))
5124
5125 ;;;###autoload
5126 (defun message-mail-other-frame (&optional to subject)
5127   "Like `message-mail' command, but display mail buffer in another frame."
5128   (interactive)
5129   (let ((pop-up-frames t)
5130         (special-display-buffer-names nil)
5131         (special-display-regexps nil)
5132         (same-window-buffer-names nil)
5133         (same-window-regexps nil))
5134     (message-pop-to-buffer (message-buffer-name "mail" to)))
5135   (let ((message-this-is-mail t))
5136     (message-setup `((To . ,(or to "")) (Subject . ,(or subject ""))))))
5137
5138 ;;;###autoload
5139 (defun message-news-other-window (&optional newsgroups subject)
5140   "Start editing a news article to be sent."
5141   (interactive)
5142   (let ((pop-up-windows t)
5143         (special-display-buffer-names nil)
5144         (special-display-regexps nil)
5145         (same-window-buffer-names nil)
5146         (same-window-regexps nil))
5147     (message-pop-to-buffer (message-buffer-name "news" nil newsgroups)))
5148   (let ((message-this-is-news t))
5149     (message-setup `((Newsgroups . ,(or newsgroups ""))
5150                      (Subject . ,(or subject ""))))))
5151
5152 ;;;###autoload
5153 (defun message-news-other-frame (&optional newsgroups subject)
5154   "Start editing a news article to be sent."
5155   (interactive)
5156   (let ((pop-up-frames t)
5157         (special-display-buffer-names nil)
5158         (special-display-regexps nil)
5159         (same-window-buffer-names nil)
5160         (same-window-regexps nil))
5161     (message-pop-to-buffer (message-buffer-name "news" nil newsgroups)))
5162   (let ((message-this-is-news t))
5163     (message-setup `((Newsgroups . ,(or newsgroups ""))
5164                      (Subject . ,(or subject ""))))))
5165
5166 ;;; underline.el
5167
5168 ;; This code should be moved to underline.el (from which it is stolen).
5169
5170 ;;;###autoload
5171 (defun bold-region (start end)
5172   "Bold all nonblank characters in the region.
5173 Works by overstriking characters.
5174 Called from program, takes two arguments START and END
5175 which specify the range to operate on."
5176   (interactive "r")
5177   (save-excursion
5178     (let ((end1 (make-marker)))
5179       (move-marker end1 (max start end))
5180       (goto-char (min start end))
5181       (while (< (point) end1)
5182         (or (looking-at "[_\^@- ]")
5183             (insert (char-after) "\b"))
5184         (forward-char 1)))))
5185
5186 ;;;###autoload
5187 (defun unbold-region (start end)
5188   "Remove all boldness (overstruck characters) in the region.
5189 Called from program, takes two arguments START and END
5190 which specify the range to operate on."
5191   (interactive "r")
5192   (save-excursion
5193     (let ((end1 (make-marker)))
5194       (move-marker end1 (max start end))
5195       (goto-char (min start end))
5196       (while (re-search-forward "\b" end1 t)
5197         (if (eq (char-after) (char-after (- (point) 2)))
5198             (delete-char -2))))))
5199
5200 (defalias 'message-exchange-point-and-mark 'exchange-point-and-mark)
5201
5202 ;; Support for toolbar
5203 (when (string-match "XEmacs\\|Lucid" emacs-version)
5204   (require 'messagexmas))
5205
5206 ;;; Group name completion.
5207
5208 (defvar message-newgroups-header-regexp
5209   "^\\(Newsgroups\\|Followup-To\\|Posted-To\\|Gcc\\):"
5210   "Regexp that match headers that lists groups.")
5211
5212 (defun message-tab ()
5213   "Expand group names in Newsgroups and Followup-To headers.
5214 Do a `tab-to-tab-stop' if not in those headers."
5215   (interactive)
5216   (if (let ((mail-abbrev-mode-regexp message-newgroups-header-regexp))
5217         (mail-abbrev-in-expansion-header-p))
5218       (message-expand-group)
5219     (tab-to-tab-stop)))
5220
5221 (defvar gnus-active-hashtb)
5222 (defun message-expand-group ()
5223   "Expand the group name under point."
5224   (let* ((b (save-excursion
5225               (save-restriction
5226                 (narrow-to-region
5227                  (save-excursion
5228                    (beginning-of-line)
5229                    (skip-chars-forward "^:")
5230                    (1+ (point)))
5231                  (point))
5232                 (skip-chars-backward "^, \t\n") (point))))
5233          (completion-ignore-case t)
5234          (string (buffer-substring b (progn (skip-chars-forward "^,\t\n ")
5235                                             (point))))
5236          (hashtb (and (boundp 'gnus-active-hashtb) gnus-active-hashtb))
5237          (completions (all-completions string hashtb))
5238          comp)
5239     (delete-region b (point))
5240     (cond
5241      ((= (length completions) 1)
5242       (if (string= (car completions) string)
5243           (progn
5244             (insert string)
5245             (message "Only matching group"))
5246         (insert (car completions))))
5247      ((and (setq comp (try-completion string hashtb))
5248            (not (string= comp string)))
5249       (insert comp))
5250      (t
5251       (insert string)
5252       (if (not comp)
5253           (message "No matching groups")
5254         (save-selected-window
5255           (pop-to-buffer "*Completions*")
5256           (buffer-disable-undo)
5257           (let ((buffer-read-only nil))
5258             (erase-buffer)
5259             (let ((standard-output (current-buffer)))
5260               (display-completion-list (sort completions 'string<)))
5261             (goto-char (point-min))
5262             (delete-region (point) (progn (forward-line 3) (point))))))))))
5263
5264 ;;; Help stuff.
5265
5266 (defun message-talkative-question (ask question show &rest text)
5267   "Call FUNCTION with argument QUESTION; optionally display TEXT... args.
5268 If SHOW is non-nil, the arguments TEXT... are displayed in a temp buffer.
5269 The following arguments may contain lists of values."
5270   (if (and show
5271            (setq text (message-flatten-list text)))
5272       (save-window-excursion
5273         (save-excursion
5274           (with-output-to-temp-buffer " *MESSAGE information message*"
5275             (set-buffer " *MESSAGE information message*")
5276             (fundamental-mode)          ; for Emacs 20.4+
5277             (mapcar 'princ text)
5278             (goto-char (point-min))))
5279         (funcall ask question))
5280     (funcall ask question)))
5281
5282 (defun message-flatten-list (list)
5283   "Return a new, flat list that contains all elements of LIST.
5284
5285 \(message-flatten-list '(1 (2 3 (4 5 (6))) 7))
5286 => (1 2 3 4 5 6 7)"
5287   (cond ((consp list)
5288          (apply 'append (mapcar 'message-flatten-list list)))
5289         (list
5290          (list list))))
5291
5292 (defun message-generate-new-buffer-clone-locals (name &optional varstr)
5293   "Create and return a buffer with a name based on NAME using generate-new-buffer.
5294 Then clone the local variables and values from the old buffer to the
5295 new one, cloning only the locals having a substring matching the
5296 regexp varstr."
5297   (let ((oldbuf (current-buffer)))
5298     (save-excursion
5299       (set-buffer (generate-new-buffer name))
5300       (message-clone-locals oldbuf varstr)
5301       (current-buffer))))
5302
5303 (defun message-clone-locals (buffer &optional varstr)
5304   "Clone the local variables from BUFFER to the current buffer."
5305   (let ((locals (save-excursion
5306                   (set-buffer buffer)
5307                   (buffer-local-variables)))
5308         (regexp
5309          "^\\(gnus\\|nn\\|message\\|user-\\(mail-address\\|full-name\\)\\)"))
5310     (mapcar
5311      (lambda (local)
5312        (when (and (consp local)
5313                   (car local)
5314                   (string-match regexp (symbol-name (car local)))
5315                   (or (null varstr)
5316                       (string-match varstr (symbol-name (car local)))))
5317          (ignore-errors
5318            (set (make-local-variable (car local))
5319                 (cdr local)))))
5320      locals)))
5321
5322
5323 ;;; @ for MIME Edit mode
5324 ;;;
5325
5326 (defun message-maybe-encode ()
5327   (when message-mime-mode
5328     ;; Inherit the buffer local variable `mime-edit-pgp-processing'.
5329     (let ((pgp-processing (with-current-buffer message-edit-buffer
5330                             mime-edit-pgp-processing)))
5331       (setq mime-edit-pgp-processing pgp-processing))
5332     (run-hooks 'mime-edit-translate-hook)
5333     (if (catch 'mime-edit-error
5334           (save-excursion
5335             (mime-edit-pgp-enclose-buffer)
5336             (mime-edit-translate-body)
5337             ))
5338         (error "Translation error!")
5339       )
5340     (end-of-invisible)
5341     (run-hooks 'mime-edit-exit-hook)
5342     ))
5343
5344 (defun message-mime-insert-article (&optional full-headers)
5345   (interactive "P")
5346   (let ((message-cite-function 'mime-edit-inserted-message-filter)
5347         (message-reply-buffer
5348          (message-get-parameter-with-eval 'original-buffer))
5349         (start (point)))
5350     (message-yank-original nil)
5351     (save-excursion
5352       (narrow-to-region (goto-char start)
5353                         (if (search-forward "\n\n" nil t)
5354                             (1- (point))
5355                           (point-max)))
5356       (goto-char (point-min))
5357       (let ((message-included-forward-headers
5358              (if full-headers "" message-included-forward-headers)))
5359         (message-remove-header message-included-forward-headers t nil t))
5360       (widen))))
5361
5362 (set-alist 'mime-edit-message-inserter-alist
5363            'message-mode (function message-mime-insert-article))
5364
5365 ;;; Miscellaneous functions
5366
5367 ;; stolen (and renamed) from nnheader.el
5368 (static-if (fboundp 'subst-char-in-string)
5369     (defsubst message-replace-chars-in-string (string from to)
5370       (subst-char-in-string from to string))
5371   (defun message-replace-chars-in-string (string from to)
5372     "Replace characters in STRING from FROM to TO."
5373     (let ((string (substring string 0)) ;Copy string.
5374           (len (length string))
5375           (idx 0))
5376       ;; Replace all occurrences of FROM with TO.
5377       (while (< idx len)
5378         (when (= (aref string idx) from)
5379           (aset string idx to))
5380         (setq idx (1+ idx)))
5381       string)))
5382
5383 ;;;
5384 ;;; MIME functions
5385 ;;;
5386
5387 (defvar message-inhibit-body-encoding t)
5388
5389 (defun message-encode-message-body ()
5390   (unless message-inhibit-body-encoding
5391     (let ((mail-parse-charset (or mail-parse-charset
5392                                   message-default-charset))
5393           (case-fold-search t)
5394           lines content-type-p)
5395       (message-goto-body)
5396       (save-restriction
5397         (narrow-to-region (point) (point-max))
5398         (let ((new (mml-generate-mime)))
5399           (when new
5400             (delete-region (point-min) (point-max))
5401             (insert new)
5402             (goto-char (point-min))
5403             (if (eq (aref new 0) ?\n)
5404                 (delete-char 1)
5405               (search-forward "\n\n")
5406               (setq lines (buffer-substring (point-min) (1- (point))))
5407               (delete-region (point-min) (point))))))
5408       (save-restriction
5409         (message-narrow-to-headers-or-head)
5410         (message-remove-header "Mime-Version")
5411         (goto-char (point-max))
5412         (insert "MIME-Version: 1.0\n")
5413         (when lines
5414           (insert lines))
5415         (setq content-type-p
5416               (re-search-backward "^Content-Type:" nil t)))
5417       (save-restriction
5418         (message-narrow-to-headers-or-head)
5419         (message-remove-first-header "Content-Type")
5420         (message-remove-first-header "Content-Transfer-Encoding"))
5421       ;; We always make sure that the message has a Content-Type header.
5422       ;; This is because some broken MTAs and MUAs get awfully confused
5423       ;; when confronted with a message with a MIME-Version header and
5424       ;; without a Content-Type header.  For instance, Solaris'
5425       ;; /usr/bin/mail.
5426       (unless content-type-p
5427         (goto-char (point-min))
5428         (re-search-forward "^MIME-Version:")
5429         (forward-line 1)
5430         (insert "Content-Type: text/plain; charset=us-ascii\n")))))
5431
5432 (defun message-read-from-minibuffer (prompt)
5433   "Read from the minibuffer while providing abbrev expansion."
5434   (if (fboundp 'mail-abbrevs-setup)
5435       (let ((mail-abbrev-mode-regexp "")
5436             (minibuffer-setup-hook 'mail-abbrevs-setup))
5437         (read-from-minibuffer prompt))
5438     (let ((minibuffer-setup-hook 'mail-abbrev-minibuffer-setup-hook))
5439       (read-string prompt))))
5440
5441 (defun message-save-drafts ()
5442   "Postponing the message."
5443   (interactive)
5444   (message "Saving %s..." buffer-file-name)
5445   (let ((reply-headers message-reply-headers)
5446         (msg (buffer-substring-no-properties (point-min) (point-max))))
5447     (with-temp-file buffer-file-name
5448       (insert msg)
5449       (setq message-reply-headers reply-headers)
5450       (message-generate-headers '((optional . In-Reply-To)))
5451       (mime-edit-translate-buffer))
5452     (set-buffer-modified-p nil))
5453   (message "Saving %s...done" buffer-file-name))
5454
5455 (provide 'message)
5456
5457 (run-hooks 'message-load-hook)
5458
5459 ;; Local Variables:
5460 ;; coding: iso-8859-1
5461 ;; End:
5462
5463 ;;; message.el ends here