Sync with `t-gnus-6_14'.
[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 redirected-followup signature
198 approved sender empty empty-headers message-id from subject
199 shorten-followup-to existing-newsgroups buffer-file-name unchanged
200 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 (and news
2914                        (or (message-fetch-field "cc")
2915                            (message-fetch-field "to")))
2916               (message-insert-courtesy-copy))
2917             (setq failure (message-maybe-split-and-send-mail)))
2918         (kill-buffer tembuf))
2919       (set-buffer message-edit-buffer)
2920       (if failure
2921           (progn
2922             (message "Couldn't send message via mail: %s" failure)
2923             nil)
2924         (push 'mail message-sent-message-via)))))
2925
2926 (defun message-send-mail-with-sendmail ()
2927   "Send off the prepared buffer with sendmail."
2928   (let ((errbuf (if message-interactive
2929                     (message-generate-new-buffer-clone-locals
2930                      " sendmail errors")
2931                   0))
2932         resend-to-addresses delimline)
2933     (let ((case-fold-search t))
2934       (save-restriction
2935         (message-narrow-to-headers)
2936         (setq resend-to-addresses (message-fetch-field "resent-to")))
2937       ;; Change header-delimiter to be what sendmail expects.
2938       (goto-char (point-min))
2939       (re-search-forward
2940        (concat "^" (regexp-quote mail-header-separator) "\n"))
2941       (replace-match "\n")
2942       (backward-char 1)
2943       (setq delimline (point-marker))
2944       (run-hooks 'message-send-mail-hook)
2945       ;; Insert an extra newline if we need it to work around
2946       ;; Sun's bug that swallows newlines.
2947       (goto-char (1+ delimline))
2948       (when (eval message-mailer-swallows-blank-line)
2949         (newline))
2950       (when message-interactive
2951         (save-excursion
2952           (set-buffer errbuf)
2953           (erase-buffer))))
2954     (let ((default-directory "/"))
2955       (as-binary-process
2956        (apply 'call-process-region
2957               (append (list (point-min) (point-max)
2958                             (if (boundp 'sendmail-program)
2959                                 sendmail-program
2960                               "/usr/lib/sendmail")
2961                             nil errbuf nil "-oi")
2962                       ;; Always specify who from,
2963                       ;; since some systems have broken sendmails.
2964                       ;; But some systems are more broken with -f, so
2965                       ;; we'll let users override this.
2966                       (if (null message-sendmail-f-is-evil)
2967                           (list "-f" (message-make-address)))
2968                       ;; These mean "report errors by mail"
2969                       ;; and "deliver in background".
2970                       (if (null message-interactive) '("-oem" "-odb"))
2971                       ;; Get the addresses from the message
2972                       ;; unless this is a resend.
2973                       ;; We must not do that for a resend
2974                       ;; because we would find the original addresses.
2975                       ;; For a resend, include the specific addresses.
2976                       (if resend-to-addresses
2977                           (list resend-to-addresses)
2978                         '("-t"))))))
2979     (when message-interactive
2980       (save-excursion
2981         (set-buffer errbuf)
2982         (goto-char (point-min))
2983         (while (re-search-forward "\n\n* *" nil t)
2984           (replace-match "; "))
2985         (if (not (zerop (buffer-size)))
2986             (error "Sending...failed to %s"
2987                    (buffer-substring (point-min) (point-max)))))
2988       (when (bufferp errbuf)
2989         (kill-buffer errbuf)))))
2990
2991 (defun message-send-mail-with-qmail ()
2992   "Pass the prepared message buffer to qmail-inject.
2993 Refer to the documentation for the variable `message-send-mail-function'
2994 to find out how to use this."
2995   ;; replace the header delimiter with a blank line
2996   (goto-char (point-min))
2997   (re-search-forward
2998    (concat "^" (regexp-quote mail-header-separator) "\n"))
2999   (replace-match "\n")
3000   (backward-char 1)
3001   (run-hooks 'message-send-mail-hook)
3002   ;; send the message
3003   (case
3004       (as-binary-process
3005        (apply
3006         'call-process-region 1 (point-max) message-qmail-inject-program
3007         nil nil nil
3008         ;; qmail-inject's default behaviour is to look for addresses on the
3009         ;; command line; if there're none, it scans the headers.
3010         ;; yes, it does The Right Thing w.r.t. Resent-To and it's kin.
3011         ;;
3012         ;; in general, ALL of qmail-inject's defaults are perfect for simply
3013         ;; reading a formatted (i. e., at least a To: or Resent-To header)
3014         ;; message from stdin.
3015         ;;
3016         ;; qmail also has the advantage of not having been raped by
3017         ;; various vendors, so we don't have to allow for that, either --
3018         ;; compare this with message-send-mail-with-sendmail and weep
3019         ;; for sendmail's lost innocence.
3020         ;;
3021         ;; all this is way cool coz it lets us keep the arguments entirely
3022         ;; free for -inject-arguments -- a big win for the user and for us
3023         ;; since we don't have to play that double-guessing game and the user
3024         ;; gets full control (no gestapo'ish -f's, for instance).  --sj
3025         message-qmail-inject-args))
3026     ;; qmail-inject doesn't say anything on it's stdout/stderr,
3027     ;; we have to look at the retval instead
3028     (0 nil)
3029     (1   (error "qmail-inject reported permanent failure"))
3030     (111 (error "qmail-inject reported transient failure"))
3031     ;; should never happen
3032     (t   (error "qmail-inject reported unknown failure"))))
3033
3034 (defun message-send-mail-with-mh ()
3035   "Send the prepared message buffer with mh."
3036   (let ((mh-previous-window-config nil)
3037         (name (mh-new-draft-name)))
3038     (setq buffer-file-name name)
3039     ;; MH wants to generate these headers itself.
3040     (when message-mh-deletable-headers
3041       (let ((headers message-mh-deletable-headers))
3042         (while headers
3043           (goto-char (point-min))
3044           (and (re-search-forward
3045                 (concat "^" (symbol-name (car headers)) ": *") nil t)
3046                (message-delete-line))
3047           (pop headers))))
3048     (run-hooks 'message-send-mail-hook)
3049     ;; Pass it on to mh.
3050     (mh-send-letter)))
3051
3052 (defun message-send-mail-with-smtp ()
3053   "Send off the prepared buffer with SMTP."
3054   (require 'smtp) ; XXX
3055   (let ((case-fold-search t)
3056         recipients)
3057     (save-restriction
3058       (message-narrow-to-headers)
3059       (setq recipients
3060             ;; XXX: Should be replaced by better one.
3061             (smtp-deduce-address-list (current-buffer)
3062                                       (point-min) (point-max)))
3063       ;; Remove BCC lines.
3064       (message-remove-header "bcc"))
3065     ;; replace the header delimiter with a blank line.
3066     (goto-char (point-min))
3067     (re-search-forward
3068      (concat "^" (regexp-quote mail-header-separator) "\n"))
3069     (replace-match "\n")
3070     (backward-char 1)
3071     (run-hooks 'message-send-mail-hook)
3072     (if recipients
3073         (let ((result (smtp-via-smtp user-mail-address
3074                                      recipients
3075                                      (current-buffer))))
3076           (unless (eq result t)
3077             (error "Sending failed; " result)))
3078       (error "Sending failed; no recipients"))))
3079
3080 (defsubst message-maybe-split-and-send-news (method)
3081   "Split a message if necessary, and send it via news.
3082 Returns nil if sending succeeded, returns t if sending failed.
3083 This sub function is for exclusive use of `message-send-news'."
3084   (let ((mime-edit-split-ignored-field-regexp
3085          mime-edit-split-ignored-field-regexp)
3086         (case-fold-search t))
3087     (while (string-match "Message-ID" mime-edit-split-ignored-field-regexp)
3088       (setq mime-edit-split-ignored-field-regexp
3089             (concat (substring mime-edit-split-ignored-field-regexp
3090                                0 (match-beginning 0))
3091                     "Hey_MIME-Edit,_there_is_an_inviolable_Message_ID"
3092                     "_so_don't_rape_it!"
3093                     (substring mime-edit-split-ignored-field-regexp
3094                                (match-end 0)))))
3095     (or
3096      (catch 'message-sending-news-failure
3097        (mime-edit-maybe-split-and-send
3098         (function
3099          (lambda ()
3100            (interactive)
3101            (save-restriction
3102              (std11-narrow-to-header mail-header-separator)
3103              (goto-char (point-min))
3104              (when (re-search-forward "^Message-ID:" nil t)
3105                (delete-region (match-end 0) (std11-field-end))
3106                (insert " " (message-make-message-id))))
3107            (unless (funcall message-send-news-function method)
3108              (throw 'message-sending-news-failure t)))))
3109        nil)
3110      (not (funcall message-send-news-function method)))))
3111
3112 (defun message-send-news (&optional arg)
3113   (let* ((tembuf (message-generate-new-buffer-clone-locals " *message temp*"))
3114          (case-fold-search nil)
3115          (method (if (message-functionp message-post-method)
3116                      (funcall message-post-method arg)
3117                    message-post-method))
3118          (group-name-charset (gnus-group-name-charset method ""))
3119          (message-syntax-checks
3120           (if arg
3121               (cons '(existing-newsgroups . disabled)
3122                     message-syntax-checks)
3123             message-syntax-checks))
3124          (message-this-is-news t)
3125          result)
3126     (save-restriction
3127       (message-narrow-to-headers)
3128       ;; Insert some headers.
3129       (message-generate-headers message-required-news-headers)
3130       ;; Let the user do all of the above.
3131       (run-hooks 'message-header-hook))
3132     (if group-name-charset
3133         (setq message-syntax-checks
3134               (cons '(valid-newsgroups . disabled)
3135                     message-syntax-checks)))
3136     (message-cleanup-headers)
3137     (if (not (message-check-news-syntax))
3138         nil
3139       (unwind-protect
3140           (save-excursion
3141             (set-buffer tembuf)
3142             (buffer-disable-undo)
3143             (erase-buffer)
3144             (insert-buffer message-encoding-buffer)
3145             ;; Remove some headers.
3146             (save-restriction
3147               (message-narrow-to-headers)
3148 ;; We Semi-gnus people have no use for it.
3149 ;;            ;; We (re)generate the Lines header.
3150 ;;            (when (memq 'Lines message-required-mail-headers)
3151 ;;              (message-generate-headers '(Lines)))
3152               ;; Remove some headers.
3153               (message-remove-header message-ignored-news-headers t))
3154             (goto-char (point-max))
3155             ;; require one newline at the end.
3156             (or (= (preceding-char) ?\n)
3157                 (insert ?\n))
3158             (setq result (message-maybe-split-and-send-news method)))
3159         (kill-buffer tembuf))
3160       (set-buffer message-edit-buffer)
3161       (if result
3162           (progn
3163             (message "Couldn't send message via news: %s"
3164                      (nnheader-get-report (car method)))
3165             nil)
3166         (push 'news message-sent-message-via)))))
3167
3168 ;; 1997-09-29 by MORIOKA Tomohiko
3169 (defun message-send-news-with-gnus (method)
3170   (let ((case-fold-search t))
3171     ;; Remove the delimiter.
3172     (goto-char (point-min))
3173     (re-search-forward
3174      (concat "^" (regexp-quote mail-header-separator) "\n"))
3175     (replace-match "\n")
3176     (backward-char 1)
3177     (run-hooks 'message-send-news-hook)
3178     (gnus-open-server method)
3179     (gnus-request-post method)
3180     ))
3181
3182 ;;;
3183 ;;; Header generation & syntax checking.
3184 ;;;
3185
3186 (defun message-check-element (type)
3187   "Returns non-nil if this type is not to be checked."
3188   (if (eq message-syntax-checks 'dont-check-for-anything-just-trust-me)
3189       t
3190     (let ((able (assq type message-syntax-checks)))
3191       (and (consp able)
3192            (eq (cdr able) 'disabled)))))
3193
3194 (defun message-check-news-syntax ()
3195   "Check the syntax of the message."
3196   (save-excursion
3197     (save-restriction
3198       (widen)
3199       (and
3200        ;; We narrow to the headers and check them first.
3201        (save-excursion
3202          (save-restriction
3203            (message-narrow-to-headers)
3204            (message-check-news-header-syntax)))
3205        ;; Check the body.
3206        (save-excursion
3207          (set-buffer message-edit-buffer)
3208          (message-check-news-body-syntax))))))
3209
3210 (defun message-check-news-header-syntax ()
3211   (and
3212    ;; Check Newsgroups header.
3213    (message-check 'newsgroups
3214      (let ((group (message-fetch-field "newsgroups")))
3215        (or
3216         (and group
3217              (not (string-match "\\`[ \t]*\\'" group)))
3218         (ignore
3219          (message
3220           "The newsgroups field is empty or missing.  Posting is denied.")))))
3221    ;; Check the Subject header.
3222    (message-check 'subject
3223      (let* ((case-fold-search t)
3224             (subject (message-fetch-field "subject")))
3225        (or
3226         (and subject
3227              (not (string-match "\\`[ \t]*\\'" subject)))
3228         (ignore
3229          (message
3230           "The subject field is empty or missing.  Posting is denied.")))))
3231    ;; Check for commands in Subject.
3232    (message-check 'subject-cmsg
3233      (if (string-match "^cmsg " (message-fetch-field "subject"))
3234          (y-or-n-p
3235           "The control code \"cmsg\" is in the subject.  Really post? ")
3236        t))
3237    ;; Check for multiple identical headers.
3238    (message-check 'multiple-headers
3239      (let (found)
3240        (while (and (not found)
3241                    (re-search-forward "^[^ \t:]+: " nil t))
3242          (save-excursion
3243            (or (re-search-forward
3244                 (concat "^"
3245                         (regexp-quote
3246                          (setq found
3247                                (buffer-substring
3248                                 (match-beginning 0) (- (match-end 0) 2))))
3249                         ":")
3250                 nil t)
3251                (setq found nil))))
3252        (if found
3253            (y-or-n-p (format "Multiple %s headers.  Really post? " found))
3254          t)))
3255    ;; Check for Version and Sendsys.
3256    (message-check 'sendsys
3257      (if (re-search-forward "^Sendsys:\\|^Version:" nil t)
3258          (y-or-n-p
3259           (format "The article contains a %s command.  Really post? "
3260                   (buffer-substring (match-beginning 0)
3261                                     (1- (match-end 0)))))
3262        t))
3263    ;; See whether we can shorten Followup-To.
3264    (message-check 'shorten-followup-to
3265      (let ((newsgroups (message-fetch-field "newsgroups"))
3266            (followup-to (message-fetch-field "followup-to"))
3267            to)
3268        (when (and newsgroups
3269                   (string-match "," newsgroups)
3270                   (not followup-to)
3271                   (not
3272                    (zerop
3273                     (length
3274                      (setq to (completing-read
3275                                "Followups to: (default all groups) "
3276                                (mapcar (lambda (g) (list g))
3277                                        (cons "poster"
3278                                              (message-tokenize-header
3279                                               newsgroups)))))))))
3280          (goto-char (point-min))
3281          (insert "Followup-To: " to "\n"))
3282        t))
3283    ;; Check "Shoot me".
3284    (message-check 'shoot
3285      (if (re-search-forward
3286           "Message-ID.*.i-did-not-set--mail-host-address--so-shoot-me" nil t)
3287          (y-or-n-p "You appear to have a misconfigured system.  Really post? ")
3288        t))
3289    ;; Check for Approved.
3290    (message-check 'approved
3291      (if (re-search-forward "^Approved:" nil t)
3292          (y-or-n-p "The article contains an Approved header.  Really post? ")
3293        t))
3294    ;; Check the Message-ID header.
3295    (message-check 'message-id
3296      (let* ((case-fold-search t)
3297             (message-id (message-fetch-field "message-id" t)))
3298        (or (not message-id)
3299            ;; Is there an @ in the ID?
3300            (and (string-match "@" message-id)
3301                 ;; Is there a dot in the ID?
3302                 (string-match "@[^.]*\\." message-id)
3303                 ;; Does the ID end with a dot?
3304                 (not (string-match "\\.>" message-id)))
3305            (y-or-n-p
3306             (format "The Message-ID looks strange: \"%s\".  Really post? "
3307                     message-id)))))
3308    ;; Check the Newsgroups & Followup-To headers.
3309    (message-check 'existing-newsgroups
3310      (let* ((case-fold-search t)
3311             (newsgroups (message-fetch-field "newsgroups"))
3312             (followup-to (message-fetch-field "followup-to"))
3313             (groups (message-tokenize-header
3314                      (if followup-to
3315                          (concat newsgroups "," followup-to)
3316                        newsgroups)))
3317             (hashtb (and (boundp 'gnus-active-hashtb)
3318                          gnus-active-hashtb))
3319             errors)
3320        (if (or (not hashtb)
3321                (not (boundp 'gnus-read-active-file))
3322                (not gnus-read-active-file)
3323                (eq gnus-read-active-file 'some))
3324            t
3325          (while groups
3326            (when (and (not (boundp (intern (car groups) hashtb)))
3327                       (not (equal (car groups) "poster")))
3328              (push (car groups) errors))
3329            (pop groups))
3330          (if (not errors)
3331              t
3332            (y-or-n-p
3333             (format
3334              "Really post to %s unknown group%s: %s "
3335              (if (= (length errors) 1) "this" "these")
3336              (if (= (length errors) 1) "" "s")
3337              (mapconcat 'identity errors ", ")))))))
3338    ;; Check the Newsgroups & Followup-To headers for syntax errors.
3339    (message-check 'valid-newsgroups
3340      (let ((case-fold-search t)
3341            (headers '("Newsgroups" "Followup-To"))
3342            header error)
3343        (while (and headers (not error))
3344          (when (setq header (mail-fetch-field (car headers)))
3345            (if (or
3346                 (not
3347                  (string-match
3348                   "\\`\\([-+_&.a-zA-Z0-9]+\\)?\\(,[-+_&.a-zA-Z0-9]+\\)*\\'"
3349                   header))
3350                 (memq
3351                  nil (mapcar
3352                       (lambda (g)
3353                         (not (string-match "\\.\\'\\|\\.\\." g)))
3354                       (message-tokenize-header header ","))))
3355                (setq error t)))
3356          (unless error
3357            (pop headers)))
3358        (if (not error)
3359            t
3360          (y-or-n-p
3361           (format "The %s header looks odd: \"%s\".  Really post? "
3362                   (car headers) header)))))
3363    (message-check 'repeated-newsgroups
3364      (let ((case-fold-search t)
3365            (headers '("Newsgroups" "Followup-To"))
3366            header error groups group)
3367        (while (and headers
3368                    (not error))
3369          (when (setq header (mail-fetch-field (pop headers)))
3370            (setq groups (message-tokenize-header header ","))
3371            (while (setq group (pop groups))
3372              (when (member group groups)
3373                (setq error group
3374                      groups nil)))))
3375        (if (not error)
3376            t
3377          (y-or-n-p
3378           (format "Group %s is repeated in headers.  Really post? " error)))))
3379    ;; Check the From header.
3380    (message-check 'from
3381      (let* ((case-fold-search t)
3382             (from (message-fetch-field "from"))
3383             ad)
3384        (cond
3385         ((not from)
3386          (message "There is no From line.  Posting is denied.")
3387          nil)
3388         ((or (not (string-match
3389                    "@[^\\.]*\\."
3390                    (setq ad (nth 1 (mail-extract-address-components
3391                                     from))))) ;larsi@ifi
3392              (string-match "\\.\\." ad) ;larsi@ifi..uio
3393              (string-match "@\\." ad)   ;larsi@.ifi.uio
3394              (string-match "\\.$" ad)   ;larsi@ifi.uio.
3395              (not (string-match "^[^@]+@[^@]+$" ad)) ;larsi.ifi.uio
3396              (string-match "(.*).*(.*)" from)) ;(lars) (lars)
3397          (message
3398           "Denied posting -- the From looks strange: \"%s\"." from)
3399          nil)
3400         (t t))))))
3401
3402 (defun message-check-news-body-syntax ()
3403   (and
3404    ;; Check for long lines.
3405    (message-check 'long-lines
3406      (goto-char (point-min))
3407      (re-search-forward
3408       (concat "^" (regexp-quote mail-header-separator) "$"))
3409      (while (and
3410              (progn
3411                (end-of-line)
3412                (< (current-column) 80))
3413              (zerop (forward-line 1))))
3414      (or (bolp)
3415          (eobp)
3416          (y-or-n-p
3417           "You have lines longer than 79 characters.  Really post? ")))
3418    ;; Check whether the article is empty.
3419    (message-check 'empty
3420      (goto-char (point-min))
3421      (re-search-forward
3422       (concat "^" (regexp-quote mail-header-separator) "$"))
3423      (forward-line 1)
3424      (let ((b (point)))
3425        (goto-char (point-max))
3426        (re-search-backward message-signature-separator nil t)
3427        (beginning-of-line)
3428        (or (re-search-backward "[^ \n\t]" b t)
3429            (y-or-n-p "Empty article.  Really post? "))))
3430    ;; Check for control characters.
3431    (message-check 'control-chars
3432      (if (re-search-forward "[\000-\007\013\015-\032\034-\037\200-\237]" nil t)
3433          (y-or-n-p
3434           "The article contains control characters.  Really post? ")
3435        t))
3436    ;; Check 8bit characters.
3437    (message-check '8bit
3438      (message-check-8bit))
3439    ;; Check excessive size.
3440    (message-check 'size
3441      (if (> (buffer-size) 60000)
3442          (y-or-n-p
3443           (format "The article is %d octets long.  Really post? "
3444                   (buffer-size)))
3445        t))
3446    ;; Check whether any new text has been added.
3447    (message-check 'new-text
3448      (or
3449       (not message-checksum)
3450       (not (eq (message-checksum) message-checksum))
3451       (y-or-n-p
3452        "It looks like no new text has been added.  Really post? ")))
3453    ;; Check the length of the signature.
3454    (message-check 'signature
3455      (goto-char (point-max))
3456      (if (> (count-lines (point) (point-max)) 5)
3457          (y-or-n-p
3458           (format
3459            "Your .sig is %d lines; it should be max 4.  Really post? "
3460            (1- (count-lines (point) (point-max)))))
3461        t))))
3462
3463 (defun message-check-mail-syntax ()
3464   "Check the syntax of the message."
3465   (save-excursion
3466     (save-restriction
3467       (widen)
3468       (and
3469        ;; We narrow to the headers and check them first.
3470        (save-excursion
3471          (save-restriction
3472            (message-narrow-to-headers)
3473            (message-check-mail-header-syntax)))
3474        ;; Check the body.
3475        (save-excursion
3476          (set-buffer message-edit-buffer)
3477          (message-check-mail-body-syntax))))))
3478
3479 (defun message-check-mail-header-syntax ()
3480   t)
3481
3482 (defun message-check-mail-body-syntax ()
3483   (and
3484    ;; Check 8bit characters.
3485    (message-check '8bit
3486      (message-check-8bit)
3487      )))
3488
3489 (defun message-check-8bit ()
3490   "Check the article contains 8bit characters."
3491   (save-excursion
3492     (set-buffer message-encoding-buffer)
3493     (message-narrow-to-headers)
3494     (let* ((case-fold-search t)
3495            (field-value (message-fetch-field "content-transfer-encoding")))
3496       (if (and field-value
3497                (member (downcase field-value) message-8bit-encoding-list))
3498           t
3499         (widen)
3500         (set-buffer (get-buffer-create " message syntax"))
3501         (erase-buffer)
3502         (goto-char (point-min))
3503         (set-buffer-multibyte nil)
3504         (insert-buffer message-encoding-buffer)
3505         (goto-char (point-min))
3506         (if (re-search-forward "[^\x00-\x7f]" nil t)
3507             (y-or-n-p
3508              "The article contains 8bit characters.  Really post? ")
3509           t)))))
3510
3511 (defun message-checksum ()
3512   "Return a \"checksum\" for the current buffer."
3513   (let ((sum 0))
3514     (save-excursion
3515       (goto-char (point-min))
3516       (re-search-forward
3517        (concat "^" (regexp-quote mail-header-separator) "$"))
3518       (while (not (eobp))
3519         (when (not (looking-at "[ \t\n]"))
3520           (setq sum (logxor (ash sum 1) (if (natnump sum) 0 1)
3521                             (char-after))))
3522         (forward-char 1)))
3523     sum))
3524
3525 (defun message-do-fcc ()
3526   "Process Fcc headers in the current buffer."
3527   (let ((case-fold-search t)
3528         (coding-system-for-write 'raw-text)
3529         (output-coding-system 'raw-text)
3530         list file)
3531     (save-excursion
3532       (set-buffer (get-buffer-create " *message temp*"))
3533       (erase-buffer)
3534       (insert-buffer-substring message-encoding-buffer)
3535       (save-restriction
3536         (message-narrow-to-headers)
3537         (while (setq file (message-fetch-field "fcc"))
3538           (push file list)
3539           (message-remove-header "fcc" nil t)))
3540       (goto-char (point-min))
3541       (re-search-forward (concat "^" (regexp-quote mail-header-separator) "$"))
3542       (replace-match "" t t)
3543       ;; Process FCC operations.
3544       (while list
3545         (setq file (pop list))
3546         (if (string-match "^[ \t]*|[ \t]*\\(.*\\)[ \t]*$" file)
3547             ;; Pipe the article to the program in question.
3548             (call-process-region (point-min) (point-max) shell-file-name
3549                                  nil nil nil shell-command-switch
3550                                  (match-string 1 file))
3551           ;; Save the article.
3552           (setq file (expand-file-name file))
3553           (unless (file-exists-p (file-name-directory file))
3554             (make-directory (file-name-directory file) t))
3555           (if (and message-fcc-handler-function
3556                    (not (eq message-fcc-handler-function 'rmail-output)))
3557               (funcall message-fcc-handler-function file)
3558             (if (and (file-readable-p file) (mail-file-babyl-p file))
3559                 (rmail-output file 1 nil t)
3560               (let ((mail-use-rfc822 t))
3561                 (rmail-output file 1 t t))))))
3562       (kill-buffer (current-buffer)))))
3563
3564 (defun message-output (filename)
3565   "Append this article to Unix/babyl mail file.."
3566   (if (and (file-readable-p filename)
3567            (mail-file-babyl-p filename))
3568       (gnus-output-to-rmail filename t)
3569     (gnus-output-to-mail filename t)))
3570
3571 (defun message-cleanup-headers ()
3572   "Do various automatic cleanups of the headers."
3573   ;; Remove empty lines in the header.
3574   (save-restriction
3575     (message-narrow-to-headers)
3576     ;; Remove blank lines.
3577     (while (re-search-forward "^[ \t]*\n" nil t)
3578       (replace-match "" t t))
3579
3580     ;; Correct Newsgroups and Followup-To headers:  Change sequence of
3581     ;; spaces to comma and eliminate spaces around commas.  Eliminate
3582     ;; embedded line breaks.
3583     (goto-char (point-min))
3584     (while (re-search-forward "^\\(Newsgroups\\|Followup-To\\): +" nil t)
3585       (save-restriction
3586         (narrow-to-region
3587          (point)
3588          (if (re-search-forward "^[^ \t]" nil t)
3589              (match-beginning 0)
3590            (forward-line 1)
3591            (point)))
3592         (goto-char (point-min))
3593         (while (re-search-forward "\n[ \t]+" nil t)
3594           (replace-match " " t t))      ;No line breaks (too confusing)
3595         (goto-char (point-min))
3596         (while (re-search-forward "[ \t\n]*,[ \t\n]*\\|[ \t]+" nil t)
3597           (replace-match "," t t))
3598         (goto-char (point-min))
3599         ;; Remove trailing commas.
3600         (when (re-search-forward ",+$" nil t)
3601           (replace-match "" t t))))))
3602
3603 (defun message-make-date (&optional now)
3604   "Make a valid data header.
3605 If NOW, use that time instead."
3606   (let* ((now (or now (current-time)))
3607          (zone (nth 8 (decode-time now)))
3608          (sign "+"))
3609     (when (< zone 0)
3610       (setq sign "-")
3611       (setq zone (- zone)))
3612     (concat
3613      (format-time-string "%d" now)
3614      ;; The month name of the %b spec is locale-specific.  Pfff.
3615      (format " %s "
3616              (capitalize (car (rassoc (nth 4 (decode-time now))
3617                                       parse-time-months))))
3618      (format-time-string "%Y %H:%M:%S " now)
3619      ;; We do all of this because XEmacs doesn't have the %z spec.
3620      (format "%s%02d%02d" sign (/ zone 3600) (/ (% zone 3600) 60)))))
3621
3622 (defun message-make-followup-subject (subject)
3623   "Make a followup Subject."
3624   (cond
3625    ((and (eq message-use-subject-re 'guess)
3626          (string-match message-subject-encoded-re-regexp subject))
3627     subject)
3628    (message-use-subject-re
3629     (concat "Re: " (message-strip-subject-re subject)))
3630    (t subject)))
3631
3632 (defun message-make-message-id ()
3633   "Make a unique Message-ID."
3634   (concat "<" (message-unique-id)
3635           (let ((psubject (save-excursion (message-fetch-field "subject")))
3636                 (psupersedes
3637                  (save-excursion (message-fetch-field "supersedes"))))
3638             (if (or
3639                  (and message-reply-headers
3640                       (mail-header-references message-reply-headers)
3641                       (mail-header-subject message-reply-headers)
3642                       psubject
3643                       (mail-header-subject message-reply-headers)
3644                       (not (string=
3645                             (message-strip-subject-re
3646                              (mail-header-subject message-reply-headers))
3647                             (message-strip-subject-re psubject))))
3648                  (and psupersedes
3649                       (string-match "_-_@" psupersedes)))
3650                 "_-_" ""))
3651           "@" (message-make-fqdn) ">"))
3652
3653 (defvar message-unique-id-char nil)
3654
3655 ;; If you ever change this function, make sure the new version
3656 ;; cannot generate IDs that the old version could.
3657 ;; You might for example insert a "." somewhere (not next to another dot
3658 ;; or string boundary), or modify the "fsf" string.
3659 (defun message-unique-id ()
3660   ;; Don't use microseconds from (current-time), they may be unsupported.
3661   ;; Instead we use this randomly inited counter.
3662   (setq message-unique-id-char
3663         (% (1+ (or message-unique-id-char (logand (random t) (1- (lsh 1 20)))))
3664            ;; (current-time) returns 16-bit ints,
3665            ;; and 2^16*25 just fits into 4 digits i base 36.
3666            (* 25 25)))
3667   (let ((tm (current-time)))
3668     (concat
3669      (if (memq system-type '(ms-dos emx vax-vms))
3670          (let ((user (downcase (user-login-name))))
3671            (while (string-match "[^a-z0-9_]" user)
3672              (aset user (match-beginning 0) ?_))
3673            user)
3674        (message-number-base36 (user-uid) -1))
3675      (message-number-base36 (+ (car   tm)
3676                                (lsh (% message-unique-id-char 25) 16)) 4)
3677      (message-number-base36 (+ (nth 1 tm)
3678                                (lsh (/ message-unique-id-char 25) 16)) 4)
3679      ;; Append the newsreader name, because while the generated
3680      ;; ID is unique to this newsreader, other newsreaders might
3681      ;; otherwise generate the same ID via another algorithm.
3682      ".fsf")))
3683
3684 (defun message-number-base36 (num len)
3685   (if (if (< len 0)
3686           (<= num 0)
3687         (= len 0))
3688       ""
3689     (concat (message-number-base36 (/ num 36) (1- len))
3690             (char-to-string (aref "zyxwvutsrqponmlkjihgfedcba9876543210"
3691                                   (% num 36))))))
3692
3693 (defun message-make-organization ()
3694   "Make an Organization header."
3695   (let* ((organization
3696           (when message-user-organization
3697             (if (message-functionp message-user-organization)
3698                 (funcall message-user-organization)
3699               message-user-organization))))
3700     (save-excursion
3701       (message-set-work-buffer)
3702       (cond ((stringp organization)
3703              (insert organization))
3704             ((and (eq t organization)
3705                   message-user-organization-file
3706                   (file-exists-p message-user-organization-file))
3707              (insert-file-contents message-user-organization-file)))
3708       (goto-char (point-min))
3709       (while (re-search-forward "[\t\n]+" nil t)
3710         (replace-match "" t t))
3711       (unless (zerop (buffer-size))
3712         (buffer-string)))))
3713
3714 (defun message-make-lines ()
3715   "Count the number of lines and return numeric string."
3716   (save-excursion
3717     (save-restriction
3718       (widen)
3719       (goto-char (point-min))
3720       (re-search-forward
3721        (concat "^" (regexp-quote mail-header-separator) "$"))
3722       (forward-line 1)
3723       (int-to-string (count-lines (point) (point-max))))))
3724
3725 (defun message-make-in-reply-to ()
3726   "Return the In-Reply-To header for this message."
3727   (when message-reply-headers
3728     (let ((mid (mail-header-message-id message-reply-headers))
3729           (from (mail-header-from message-reply-headers))
3730           (date (mail-header-date message-reply-headers)))
3731       (when mid
3732         (concat mid
3733                 (when from
3734                   (let ((pair (std11-extract-address-components from)))
3735                     (concat "\n ("
3736                             (or (car pair) (cadr pair))
3737                             "'s message of \""
3738                             (if (or (not date) (string= date ""))
3739                                 "(unknown date)" date)
3740                             "\")"))))))))
3741
3742 (defun message-make-distribution ()
3743   "Make a Distribution header."
3744   (let ((orig-distribution (message-fetch-reply-field "distribution")))
3745     (cond ((message-functionp message-distribution-function)
3746            (funcall message-distribution-function))
3747           (t orig-distribution))))
3748
3749 (defun message-make-expires ()
3750   "Return an Expires header based on `message-expires'."
3751   (let ((current (current-time))
3752         (future (* 1.0 message-expires 60 60 24)))
3753     ;; Add the future to current.
3754     (setcar current (+ (car current) (round (/ future (expt 2 16)))))
3755     (setcar (cdr current) (+ (nth 1 current) (% (round future) (expt 2 16))))
3756     (message-make-date current)))
3757
3758 (defun message-make-path ()
3759   "Return uucp path."
3760   (let ((login-name (user-login-name)))
3761     (cond ((null message-user-path)
3762            (concat (system-name) "!" login-name))
3763           ((stringp message-user-path)
3764            ;; Support GENERICPATH.  Suggested by vixie@decwrl.dec.com.
3765            (concat message-user-path "!" login-name))
3766           (t login-name))))
3767
3768 (defun message-make-from ()
3769   "Make a From header."
3770   (let* ((style message-from-style)
3771          (login (message-make-address))
3772          (fullname
3773           (or (and (boundp 'user-full-name)
3774                    user-full-name)
3775               (user-full-name))))
3776     (when (string= fullname "&")
3777       (setq fullname (user-login-name)))
3778     (save-excursion
3779       (message-set-work-buffer)
3780       (cond
3781        ((or (null style)
3782             (equal fullname ""))
3783         (insert login))
3784        ((or (eq style 'angles)
3785             (and (not (eq style 'parens))
3786                  ;; Use angles if no quoting is needed, or if parens would
3787                  ;; need quoting too.
3788                  (or (not (string-match "[^- !#-'*+/-9=?A-Z^-~]" fullname))
3789                      (let ((tmp (concat fullname nil)))
3790                        (while (string-match "([^()]*)" tmp)
3791                          (aset tmp (match-beginning 0) ?-)
3792                          (aset tmp (1- (match-end 0)) ?-))
3793                        (string-match "[\\()]" tmp)))))
3794         (insert fullname)
3795         (goto-char (point-min))
3796         ;; Look for a character that cannot appear unquoted
3797         ;; according to RFC 822.
3798         (when (re-search-forward "[^- !#-'*+/-9=?A-Z^-~]" nil 1)
3799           ;; Quote fullname, escaping specials.
3800           (goto-char (point-min))
3801           (insert "\"")
3802           (while (re-search-forward "[\"\\]" nil 1)
3803             (replace-match "\\\\\\&" t))
3804           (insert "\""))
3805         (insert " <" login ">"))
3806        (t                               ; 'parens or default
3807         (insert login " (")
3808         (let ((fullname-start (point)))
3809           (insert fullname)
3810           (goto-char fullname-start)
3811           ;; RFC 822 says \ and nonmatching parentheses
3812           ;; must be escaped in comments.
3813           ;; Escape every instance of ()\ ...
3814           (while (re-search-forward "[()\\]" nil 1)
3815             (replace-match "\\\\\\&" t))
3816           ;; ... then undo escaping of matching parentheses,
3817           ;; including matching nested parentheses.
3818           (goto-char fullname-start)
3819           (while (re-search-forward
3820                   "\\(\\=\\|[^\\]\\(\\\\\\\\\\)*\\)\\\\(\\(\\([^\\]\\|\\\\\\\\\\)*\\)\\\\)"
3821                   nil 1)
3822             (replace-match "\\1(\\3)" t)
3823             (goto-char fullname-start)))
3824         (insert ")")))
3825       (buffer-string))))
3826
3827 (defun message-make-sender ()
3828   "Return the \"real\" user address.
3829 This function tries to ignore all user modifications, and
3830 give as trustworthy answer as possible."
3831   (concat (user-login-name) "@" (system-name)))
3832
3833 (defun message-make-address ()
3834   "Make the address of the user."
3835   (or (message-user-mail-address)
3836       (concat (user-login-name) "@" (message-make-domain))))
3837
3838 (defun message-user-mail-address ()
3839   "Return the pertinent part of `user-mail-address'."
3840   (when user-mail-address
3841     (if (string-match " " user-mail-address)
3842         (nth 1 (std11-extract-address-components user-mail-address))
3843       user-mail-address)))
3844
3845 (defun message-make-fqdn ()
3846   "Return user's fully qualified domain name."
3847   (let ((system-name (system-name))
3848         (user-mail (message-user-mail-address)))
3849     (cond
3850      ((string-match "[^.]\\.[^.]" system-name)
3851       ;; `system-name' returned the right result.
3852       system-name)
3853      ;; Try `mail-host-address'.
3854      ((and (boundp 'mail-host-address)
3855            (stringp mail-host-address)
3856            (string-match "\\." mail-host-address))
3857       mail-host-address)
3858      ;; We try `user-mail-address' as a backup.
3859      ((and user-mail
3860            (string-match "\\." user-mail)
3861            (string-match "@\\(.*\\)\\'" user-mail))
3862       (match-string 1 user-mail))
3863      ;; Default to this bogus thing.
3864      (t
3865       (concat system-name ".i-did-not-set--mail-host-address--so-shoot-me")))))
3866
3867 (defun message-make-host-name ()
3868   "Return the name of the host."
3869   (let ((fqdn (message-make-fqdn)))
3870     (string-match "^[^.]+\\." fqdn)
3871     (substring fqdn 0 (1- (match-end 0)))))
3872
3873 (defun message-make-domain ()
3874   "Return the domain name."
3875   (or mail-host-address
3876       (message-make-fqdn)))
3877
3878 ;; Dummy to avoid byte-compile warning.
3879 (defvar mule-version)
3880 (defvar emacs-beta-version)
3881 (defvar xemacs-codename)
3882 (defvar gnus-inviolable-extended-version)
3883
3884 (defun message-make-user-agent ()
3885   "Return user-agent info if the value `message-user-agent' is non-nil. If the
3886 \"User-Agent\" field has already exist, it's value will be added in the return
3887 string."
3888   (when message-user-agent
3889     (save-excursion
3890       (goto-char (point-min))
3891       (let ((case-fold-search t)
3892             user-agent start p end)
3893         (if (re-search-forward "^User-Agent:[\t ]*" nil t)
3894             (progn
3895               (setq start (match-beginning 0)
3896                     p (match-end 0)
3897                     end (std11-field-end)
3898                     user-agent (buffer-substring-no-properties p end))
3899               (delete-region start (1+ end))
3900               (concat message-user-agent " " user-agent))
3901           message-user-agent)))))
3902
3903 (defun message-generate-headers (headers)
3904   "Prepare article HEADERS.
3905 Headers already prepared in the buffer are not modified."
3906   (save-restriction
3907     (message-narrow-to-headers)
3908     (let* ((Date (message-make-date))
3909            (Message-ID (message-make-message-id))
3910            (Organization (message-make-organization))
3911            (From (message-make-from))
3912            (Path (message-make-path))
3913            (Subject nil)
3914            (Newsgroups nil)
3915            (In-Reply-To (message-make-in-reply-to))
3916            (To nil)
3917            (Distribution (message-make-distribution))
3918            (Lines (message-make-lines))
3919            (User-Agent (message-make-user-agent))
3920            (Expires (message-make-expires))
3921            (case-fold-search t)
3922            header value elem)
3923       ;; First we remove any old generated headers.
3924       (let ((headers message-deletable-headers))
3925         (unless (buffer-modified-p)
3926           (setq headers (delq 'Message-ID (copy-sequence headers))))
3927         (while headers
3928           (goto-char (point-min))
3929           (and (re-search-forward
3930                 (concat "^" (symbol-name (car headers)) ": *") nil t)
3931                (get-text-property (1+ (match-beginning 0)) 'message-deletable)
3932                (message-delete-line))
3933           (pop headers)))
3934       ;; Go through all the required headers and see if they are in the
3935       ;; articles already.  If they are not, or are empty, they are
3936       ;; inserted automatically - except for Subject, Newsgroups and
3937       ;; Distribution.
3938       (while headers
3939         (goto-char (point-min))
3940         (setq elem (pop headers))
3941         (if (consp elem)
3942             (if (eq (car elem) 'optional)
3943                 (setq header (cdr elem))
3944               (setq header (car elem)))
3945           (setq header elem))
3946         (when (or (not (re-search-forward
3947                         (concat "^"
3948                                 (regexp-quote
3949                                  (downcase
3950                                   (if (stringp header)
3951                                       header
3952                                     (symbol-name header))))
3953                                 ":")
3954                         nil t))
3955                   (progn
3956                     ;; The header was found.  We insert a space after the
3957                     ;; colon, if there is none.
3958                     (if (/= (char-after) ? ) (insert " ") (forward-char 1))
3959                     ;; Find out whether the header is empty...
3960                     (looking-at "[ \t]*\n[^ \t]")))
3961           ;; So we find out what value we should insert.
3962           (setq value
3963                 (cond
3964                  ((and (consp elem) (eq (car elem) 'optional))
3965                   ;; This is an optional header.  If the cdr of this
3966                   ;; is something that is nil, then we do not insert
3967                   ;; this header.
3968                   (setq header (cdr elem))
3969                   (or (and (fboundp (cdr elem)) (funcall (cdr elem)))
3970                       (and (boundp (cdr elem)) (symbol-value (cdr elem)))))
3971                  ((consp elem)
3972                   ;; The element is a cons.  Either the cdr is a
3973                   ;; string to be inserted verbatim, or it is a
3974                   ;; function, and we insert the value returned from
3975                   ;; this function.
3976                   (or (and (stringp (cdr elem)) (cdr elem))
3977                       (and (fboundp (cdr elem)) (funcall (cdr elem)))))
3978                  ((and (boundp header) (symbol-value header))
3979                   ;; The element is a symbol.  We insert the value
3980                   ;; of this symbol, if any.
3981                   (symbol-value header))
3982                  ((not (message-check-element header))
3983                   ;; We couldn't generate a value for this header,
3984                   ;; so we just ask the user.
3985                   (read-from-minibuffer
3986                    (format "Empty header for %s; enter value: " header)))))
3987           ;; Finally insert the header.
3988           (when (and value
3989                      (not (equal value "")))
3990             (save-excursion
3991               (if (bolp)
3992                   (progn
3993                     ;; This header didn't exist, so we insert it.
3994                     (goto-char (point-max))
3995                     (insert (if (stringp header) header (symbol-name header))
3996                             ": " value)
3997                     (unless (bolp)
3998                       (insert "\n"))
3999                     (forward-line -1))
4000                 ;; The value of this header was empty, so we clear
4001                 ;; totally and insert the new value.
4002                 (delete-region (point) (gnus-point-at-eol))
4003                 (insert value)
4004                 (when (bolp)
4005                   (delete-char -1)))
4006               ;; Add the deletable property to the headers that require it.
4007               (and (memq header message-deletable-headers)
4008                    (progn (beginning-of-line) (looking-at "[^:]+: "))
4009                    (add-text-properties
4010                     (point) (match-end 0)
4011                     '(message-deletable t face italic) (current-buffer)))))))
4012       ;; Insert new Sender if the From is strange.
4013       (let ((from (message-fetch-field "from"))
4014             (sender (message-fetch-field "sender"))
4015             (secure-sender (message-make-sender)))
4016         (when (and from
4017                    (not (message-check-element 'sender))
4018                    (not (string=
4019                          (downcase
4020                           (cadr (std11-extract-address-components from)))
4021                          (downcase secure-sender)))
4022                    (or (null sender)
4023                        (not
4024                         (string=
4025                          (downcase
4026                           (cadr (std11-extract-address-components sender)))
4027                          (downcase secure-sender)))))
4028           (goto-char (point-min))
4029           ;; Rename any old Sender headers to Original-Sender.
4030           (when (re-search-forward "^\\(Original-\\)*Sender:" nil t)
4031             (beginning-of-line)
4032             (insert "Original-")
4033             (beginning-of-line))
4034           (when (or (message-news-p)
4035                     (string-match "@.+\\.." secure-sender))
4036             (insert "Sender: " secure-sender "\n")))))))
4037
4038 (defun message-insert-courtesy-copy ()
4039   "Insert a courtesy message in mail copies of combined messages."
4040   (let (newsgroups)
4041     (save-excursion
4042       (save-restriction
4043         (message-narrow-to-headers)
4044         (when (setq newsgroups (message-fetch-field "newsgroups"))
4045           (goto-char (point-max))
4046           (insert "Posted-To: " newsgroups "\n")))
4047       (forward-line 1)
4048       (when message-courtesy-message
4049         (cond
4050          ((string-match "%s" message-courtesy-message)
4051           (insert (format message-courtesy-message newsgroups)))
4052          (t
4053           (insert message-courtesy-message)))))))
4054
4055 ;;;
4056 ;;; Setting up a message buffer
4057 ;;;
4058
4059 (defun message-fill-address (header value)
4060   (save-restriction
4061     (narrow-to-region (point) (point))
4062     (insert (capitalize (symbol-name header))
4063             ": "
4064             (if (consp value) (car value) value)
4065             "\n")
4066     (narrow-to-region (point-min) (1- (point-max)))
4067     (let (quoted last)
4068       (goto-char (point-min))
4069       (while (not (eobp))
4070         (skip-chars-forward "^,\"" (point-max))
4071         (if (or (eq (char-after) ?,)
4072                 (eobp))
4073             (when (not quoted)
4074               (if (and (> (current-column) 78)
4075                        last)
4076                   (save-excursion
4077                     (goto-char last)
4078                     (looking-at "[ \t]*")
4079                     (replace-match "\n " t t)))
4080               (setq last (1+ (point))))
4081           (setq quoted (not quoted)))
4082         (unless (eobp)
4083           (forward-char 1))))
4084     (goto-char (point-max))
4085     (widen)
4086     (forward-line 1)))
4087
4088 (defun message-fill-references (header value)
4089   (insert (capitalize (symbol-name header))
4090           ": "
4091           (std11-fill-msg-id-list-string
4092           (if (consp value) (car value) value))
4093           "\n"))
4094
4095 (defun message-fill-header (header value)
4096   (let ((begin (point))
4097         (fill-column 78)
4098         (fill-prefix " "))
4099     (insert (capitalize (symbol-name header))
4100             ": "
4101             (if (consp value) (car value) value)
4102             "\n")
4103     (save-restriction
4104       (narrow-to-region begin (point))
4105       (fill-region-as-paragraph begin (point))
4106       ;; Tapdance around looong Message-IDs.
4107       (forward-line -1)
4108       (when (looking-at "[ \t]*$")
4109         (message-delete-line))
4110       (goto-char begin)
4111       (re-search-forward ":" nil t)
4112       (when (looking-at "\n[ \t]+")
4113         (replace-match " " t t))
4114       (goto-char (point-max)))))
4115
4116 (defun message-shorten-1 (list cut surplus)
4117   ;; Cut SURPLUS elements out of LIST, beginning with CUTth one.
4118   (setcdr (nthcdr (- cut 2) list)
4119           (nthcdr (+ (- cut 2) surplus 1) list)))
4120
4121 (defun message-shorten-references (header references)
4122   "Trim REFERENCES to be less than 31 Message-ID long, and fold them.
4123 If folding is disallowed, also check that the REFERENCES are less
4124 than 988 characters long, and if they are not, trim them until they are."
4125   (let ((maxcount 31)
4126         (count 0)
4127         (cut 6)
4128         refs)
4129     (with-temp-buffer
4130       (insert references)
4131       (goto-char (point-min))
4132       ;; Cons a list of valid references.
4133       (while (re-search-forward "<[^>]+>" nil t)
4134         (push (match-string 0) refs))
4135       (setq refs (nreverse refs)
4136             count (length refs)))
4137
4138     ;; If the list has more than MAXCOUNT elements, trim it by
4139     ;; removing the CUTth element and the required number of
4140     ;; elements that follow.
4141     (when (> count maxcount)
4142       (let ((surplus (- count maxcount)))
4143         (message-shorten-1 refs cut surplus)
4144         (decf count surplus)))
4145
4146     ;; If folding is disallowed, make sure the total length (including
4147     ;; the spaces between) will be less than MAXSIZE characters.
4148     ;;
4149     ;; Only disallow folding for News messages. At this point the headers
4150     ;; have not been generated, thus we use message-this-is-news directly.
4151     (when (and message-this-is-news message-cater-to-broken-inn)
4152       (let ((maxsize 988)
4153             (totalsize (+ (apply #'+ (mapcar #'length refs))
4154                           (1- count)))
4155             (surplus 0)
4156             (ptr (nthcdr (1- cut) refs)))
4157         ;; Decide how many elements to cut off...
4158         (while (> totalsize maxsize)
4159           (decf totalsize (1+ (length (car ptr))))
4160           (incf surplus)
4161           (setq ptr (cdr ptr)))
4162         ;; ...and do it.
4163         (when (> surplus 0)
4164           (message-shorten-1 refs cut surplus))))
4165
4166     ;; Finally, collect the references back into a string and insert
4167     ;; it into the buffer.
4168     (let ((refstring (mapconcat #'identity refs " ")))
4169       (if (and message-this-is-news message-cater-to-broken-inn)
4170           (insert (capitalize (symbol-name header)) ": "
4171                   refstring "\n")
4172         (message-fill-header header refstring)))))
4173
4174 (defun message-position-point ()
4175   "Move point to where the user probably wants to find it."
4176   (message-narrow-to-headers)
4177   (cond
4178    ((re-search-forward "^[^:]+:[ \t]*$" nil t)
4179     (search-backward ":" )
4180     (widen)
4181     (forward-char 1)
4182     (if (eq (char-after) ? )
4183         (forward-char 1)
4184       (insert " ")))
4185    (t
4186     (goto-char (point-max))
4187     (widen)
4188     (forward-line 1)
4189     (unless (looking-at "$")
4190       (forward-line 2)))
4191    (sit-for 0)))
4192
4193 (defun message-buffer-name (type &optional to group)
4194   "Return a new (unique) buffer name based on TYPE and TO."
4195   (cond
4196    ;; Generate a new buffer name The Message Way.
4197    ((eq message-generate-new-buffers 'unique)
4198     (generate-new-buffer-name
4199      (concat "*" type
4200              (if to
4201                  (concat " to "
4202                          (or (car (std11-extract-address-components to))
4203                              to) "")
4204                "")
4205              (if (and group (not (string= group ""))) (concat " on " group) "")
4206              "*")))
4207    ;; Check whether `message-generate-new-buffers' is a function,
4208    ;; and if so, call it.
4209    ((message-functionp message-generate-new-buffers)
4210     (funcall message-generate-new-buffers type to group))
4211    ((eq message-generate-new-buffers 'unsent)
4212     (generate-new-buffer-name
4213      (concat "*unsent " type
4214              (if to
4215                  (concat " to "
4216                          (or (car (mail-extract-address-components to))
4217                              to) "")
4218                "")
4219              (if (and group (not (string= group ""))) (concat " on " group) "")
4220              "*")))
4221    ;; Use standard name.
4222    (t
4223     (format "*%s message*" type))))
4224
4225 (defmacro message-pop-to-buffer-1 (buffer)
4226   `(if pop-up-frames
4227        (let (special-display-buffer-names
4228              special-display-regexps
4229              same-window-buffer-names
4230              same-window-regexps)
4231          (pop-to-buffer ,buffer))
4232      (pop-to-buffer ,buffer)))
4233
4234 (defun message-pop-to-buffer (name)
4235   "Pop to buffer NAME, and warn if it already exists and is modified."
4236   (let ((buffer (get-buffer name))
4237         (pop-up-frames (and (or (static-if (featurep 'xemacs)
4238                                     (device-on-window-system-p)
4239                                   window-system)
4240                                 (>= emacs-major-version 20))
4241                             message-use-multi-frames)))
4242     (if (and buffer
4243              (buffer-name buffer))
4244         (progn
4245           (message-pop-to-buffer-1 buffer)
4246           (when (and (buffer-modified-p)
4247                      (not (y-or-n-p
4248                            "Message already being composed; erase? ")))
4249             (error "Message being composed")))
4250       (message-pop-to-buffer-1 name))
4251     (erase-buffer)
4252     (message-mode)
4253     (when pop-up-frames
4254       (set (make-local-variable 'message-original-frame) (selected-frame)))))
4255
4256 (defun message-do-send-housekeeping ()
4257   "Kill old message buffers."
4258   ;; We might have sent this buffer already.  Delete it from the
4259   ;; list of buffers.
4260   (setq message-buffer-list (delq (current-buffer) message-buffer-list))
4261   (while (and message-max-buffers
4262               message-buffer-list
4263               (>= (length message-buffer-list) message-max-buffers))
4264     ;; Kill the oldest buffer -- unless it has been changed.
4265     (let ((buffer (pop message-buffer-list)))
4266       (when (and (buffer-name buffer)
4267                  (not (buffer-modified-p buffer)))
4268         (kill-buffer buffer))))
4269   ;; Rename the buffer.
4270   (if message-send-rename-function
4271       (funcall message-send-rename-function)
4272     (when (string-match "\\`\\*\\(unsent \\)?" (buffer-name))
4273       (rename-buffer
4274        (concat "*sent " (substring (buffer-name) (match-end 0))) t)))
4275   ;; Push the current buffer onto the list.
4276   (when message-max-buffers
4277     (setq message-buffer-list
4278           (nconc message-buffer-list (list (current-buffer))))))
4279
4280 (defvar mc-modes-alist)
4281 (defun message-setup (headers &optional replybuffer actions)
4282   (when (and (boundp 'mc-modes-alist)
4283              (not (assq 'message-mode mc-modes-alist)))
4284     (push '(message-mode (encrypt . mc-encrypt-message)
4285                          (sign . mc-sign-message))
4286           mc-modes-alist))
4287   (when actions
4288     (setq message-send-actions actions))
4289   (setq message-reply-buffer
4290         (or (message-get-parameter 'reply-buffer)
4291             replybuffer))
4292   (goto-char (point-min))
4293   ;; Insert all the headers.
4294   (mail-header-format
4295    (let ((h headers)
4296          (alist message-header-format-alist))
4297      (while h
4298        (unless (assq (caar h) message-header-format-alist)
4299          (push (list (caar h)) alist))
4300        (pop h))
4301      alist)
4302    headers)
4303   (delete-region (point) (progn (forward-line -1) (point)))
4304   (when message-default-headers
4305     (insert message-default-headers)
4306     (or (bolp) (insert ?\n)))
4307   (put-text-property
4308    (point)
4309    (progn
4310      (insert mail-header-separator "\n")
4311      (1- (point)))
4312    'read-only nil)
4313   (forward-line -1)
4314   (when (message-news-p)
4315     (when message-default-news-headers
4316       (insert message-default-news-headers)
4317       (or (bolp) (insert ?\n)))
4318     (when message-generate-headers-first
4319       (message-generate-headers
4320        (delq 'Lines
4321              (delq 'Subject
4322                    (copy-sequence message-required-news-headers))))))
4323   (when (message-mail-p)
4324     (when message-default-mail-headers
4325       (insert message-default-mail-headers)
4326       (or (bolp) (insert ?\n)))
4327     (when message-generate-headers-first
4328       (message-generate-headers
4329        (delq 'Lines
4330              (delq 'Subject
4331                    (copy-sequence message-required-mail-headers))))))
4332   (run-hooks 'message-signature-setup-hook)
4333   (message-insert-signature)
4334   (save-restriction
4335     (message-narrow-to-headers)
4336     (run-hooks 'message-header-setup-hook))
4337   (set-buffer-modified-p nil)
4338   (setq buffer-undo-list nil)
4339   (run-hooks 'message-setup-hook)
4340   (message-position-point)
4341   (undo-boundary))
4342
4343 (defun message-set-auto-save-file-name ()
4344   "Associate the message buffer with a file in the drafts directory."
4345   (when message-auto-save-directory
4346     (if (gnus-alive-p)
4347         (setq message-draft-article
4348               (nndraft-request-associate-buffer "drafts"))
4349       (setq buffer-file-name (expand-file-name "*message*"
4350                                                message-auto-save-directory))
4351       (setq buffer-auto-save-file-name (make-auto-save-file-name)))
4352     (clear-visited-file-modtime)
4353     (static-if (boundp 'MULE)
4354         (set-file-coding-system message-draft-coding-system)
4355       (setq buffer-file-coding-system message-draft-coding-system))))
4356
4357 (defun message-disassociate-draft ()
4358   "Disassociate the message buffer from the drafts directory."
4359   (when message-draft-article
4360     (nndraft-request-expire-articles
4361      (list message-draft-article) "drafts" nil t)))
4362
4363 (defun message-insert-headers ()
4364   "Generate the headers for the article."
4365   (interactive)
4366   (save-excursion
4367     (save-restriction
4368       (message-narrow-to-headers)
4369       (when (message-news-p)
4370         (message-generate-headers
4371          (delq 'Lines
4372                (delq 'Subject
4373                      (copy-sequence message-required-news-headers)))))
4374       (when (message-mail-p)
4375         (message-generate-headers
4376          (delq 'Lines
4377                (delq 'Subject
4378                      (copy-sequence message-required-mail-headers))))))))
4379
4380 \f
4381
4382 ;;;
4383 ;;; Commands for interfacing with message
4384 ;;;
4385
4386 ;;;###autoload
4387 (defun message-mail (&optional to subject
4388                                other-headers continue switch-function
4389                                yank-action send-actions)
4390   "Start editing a mail message to be sent.
4391 OTHER-HEADERS is an alist of header/value pairs."
4392   (interactive)
4393   (let ((message-this-is-mail t))
4394     (message-pop-to-buffer (message-buffer-name "mail" to))
4395     (message-setup
4396      (nconc
4397       `((To . ,(or to "")) (Subject . ,(or subject "")))
4398       (when other-headers other-headers)))))
4399
4400 ;;;###autoload
4401 (defun message-news (&optional newsgroups subject)
4402   "Start editing a news article to be sent."
4403   (interactive)
4404   (let ((message-this-is-news t))
4405     (message-pop-to-buffer (message-buffer-name "news" nil newsgroups))
4406     (message-setup `((Newsgroups . ,(or newsgroups ""))
4407                      (Subject . ,(or subject ""))))))
4408
4409 (defun message-get-reply-headers (wide &optional to-address)
4410   (let (follow-to mct never-mct from to cc reply-to mft)
4411     ;; Find all relevant headers we need.
4412     (setq from (message-fetch-field "from")
4413           to (message-fetch-field "to")
4414           cc (message-fetch-field "cc")
4415           mct (when message-use-mail-copies-to
4416                 (message-fetch-field "mail-copies-to"))
4417           reply-to (when message-use-mail-reply-to
4418                      (or (message-fetch-field "mail-reply-to")
4419                          (message-fetch-field "reply-to")))
4420           mft (when (and (not to-address)
4421                          (not reply-to)
4422                          message-use-mail-followup-to)
4423                 (message-fetch-field "mail-followup-to")))
4424
4425     ;; Handle special values of Mail-Copies-To.
4426     (when mct
4427       (cond
4428        ((and (or (equal (downcase mct) "never")
4429                  (equal (downcase mct) "nobody"))
4430              (or (not (eq message-use-mail-copies-to 'ask))
4431                  (message-y-or-n-p
4432                   (concat "Obey Mail-Copies-To: never? ") t "\
4433 You should normally obey the Mail-Copies-To: header.
4434
4435         `Mail-Copies-To: never'
4436 directs you not to send your response to the author.")))
4437         (setq never-mct t)
4438         (setq mct nil))
4439        ((and (or (equal (downcase mct) "always")
4440                  (equal (downcase mct) "poster"))
4441              (or (not (eq message-use-mail-copies-to 'ask))
4442                  (message-y-or-n-p
4443                   (concat "Obey Mail-Copies-To: always? ") t "\
4444 You should normally obey the Mail-Copies-To: header.
4445
4446         `Mail-Copies-To: always'
4447 sends a copy of your response to the author.")))
4448         (setq mct (or reply-to from)))
4449        ((and (eq message-use-mail-copies-to 'ask)
4450              (not
4451               (message-y-or-n-p
4452                (concat "Obey Mail-Copies-To: " mct " ? ") t "\
4453 You should normally obey the Mail-Copies-To: header.
4454
4455         `Mail-Copies-To: " mct "'
4456 sends a copy of your response to " (if (string-match "," mct)
4457                                        "the specified addresses"
4458                                      "that address") ".")))
4459         (setq mct nil))))
4460
4461     ;; Handle Mail-Followup-To.
4462     (when (and mft
4463                (eq message-use-mail-followup-to 'ask)
4464                (not (message-y-or-n-p
4465                      (concat "Obey Mail-Followup-To: " mft "? ") t "\
4466 You should normally obey the Mail-Followup-To: header.
4467
4468         `Mail-Followup-To: " mft "'
4469 directs your response to " (if (string-match "," mft)
4470                                "the specified addresses"
4471                              "that address only") ".
4472
4473 A typical situation where Mail-Followup-To is used is when the author thinks
4474 that further discussion should take place only in "
4475                              (if (string-match "," mft)
4476                                  "the specified mailing lists"
4477                                "that mailing list") ".")))
4478       (setq mft nil))
4479
4480     (if (or (not wide)
4481             to-address)
4482         (progn
4483           (setq follow-to (list (cons 'To (or to-address reply-to mft from))))
4484           (when (and wide mct)
4485             (push (cons 'Cc mct) follow-to)))
4486       (let (ccalist)
4487         (save-excursion
4488           (message-set-work-buffer)
4489           (unless never-mct
4490             (insert (or reply-to from "")))
4491           (insert (if mft (concat (if (bolp) "" ", ") mft "") ""))
4492           (insert (if to (concat (if (bolp) "" ", ") to "") ""))
4493           (insert (if mct (concat (if (bolp) "" ", ") mct) ""))
4494           (insert (if cc (concat (if (bolp) "" ", ") cc) ""))
4495           (goto-char (point-min))
4496           (while (re-search-forward "[ \t]+" nil t)
4497             (replace-match " " t t))
4498           ;; Remove addresses that match `rmail-dont-reply-to-names'.
4499           (let ((rmail-dont-reply-to-names message-dont-reply-to-names))
4500             (insert (prog1 (rmail-dont-reply-to (buffer-string))
4501                       (erase-buffer))))
4502           (goto-char (point-min))
4503           ;; Perhaps "Mail-Copies-To: never" removed the only address?
4504           (when (eobp)
4505             (insert (or reply-to from "")))
4506           (setq ccalist
4507                 (mapcar
4508                  (lambda (addr)
4509                    (cons (mail-strip-quoted-names addr) addr))
4510                  (message-tokenize-header (buffer-string))))
4511           (let ((s ccalist))
4512             (while s
4513               (setq ccalist (delq (assoc (car (pop s)) s) ccalist)))))
4514         (setq follow-to (list (cons 'To (cdr (pop ccalist)))))
4515         (when ccalist
4516           (let ((ccs (cons 'Cc (mapconcat
4517                                 (lambda (addr) (cdr addr)) ccalist ", "))))
4518             (when (string-match "^ +" (cdr ccs))
4519               (setcdr ccs (substring (cdr ccs) (match-end 0))))
4520             (push ccs follow-to)))))
4521     follow-to))
4522
4523 ;;;###autoload
4524 (defun message-reply (&optional to-address wide)
4525   "Start editing a reply to the article in the current buffer."
4526   (interactive)
4527   (require 'gnus-sum)                   ; for gnus-list-identifiers
4528   (let ((cur (current-buffer))
4529         from subject date
4530         references message-id follow-to
4531         (inhibit-point-motion-hooks t)
4532         (message-this-is-mail t)
4533         gnus-warning in-reply-to)
4534     (save-restriction
4535       (message-narrow-to-head)
4536       ;; Allow customizations to have their say.
4537       (if (not wide)
4538           ;; This is a regular reply.
4539           (if (message-functionp message-reply-to-function)
4540               (setq follow-to (funcall message-reply-to-function)))
4541         ;; This is a followup.
4542         (if (message-functionp message-wide-reply-to-function)
4543             (save-excursion
4544               (setq follow-to
4545                     (funcall message-wide-reply-to-function)))))
4546       (setq message-id (message-fetch-field "message-id" t)
4547             references (message-fetch-field "references")
4548             date (message-fetch-field "date")
4549             from (message-fetch-field "from")
4550             subject (or (message-fetch-field "subject") "none"))
4551       (if gnus-list-identifiers
4552           (setq subject (message-strip-list-identifiers subject)))
4553       (setq subject (message-make-followup-subject subject))
4554
4555       (when (and (setq gnus-warning (message-fetch-field "gnus-warning"))
4556                  (string-match "<[^>]+>" gnus-warning))
4557         (setq message-id (match-string 0 gnus-warning)))
4558
4559       (unless follow-to
4560         (setq follow-to (message-get-reply-headers wide to-address)))
4561
4562       ;; Get the references from "In-Reply-To" field if there were
4563       ;; no references and "In-Reply-To" field looks promising.
4564       (unless references
4565         (when (and (setq in-reply-to (message-fetch-field "in-reply-to"))
4566                    (string-match "<[^>]+>" in-reply-to))
4567           (setq references (match-string 0 in-reply-to)))))
4568
4569     (message-pop-to-buffer
4570      (message-buffer-name
4571       (if wide "wide reply" "reply") from
4572       (if wide to-address nil)))
4573
4574     (setq message-reply-headers
4575           (make-full-mail-header-from-decoded-header
4576            0 subject from date message-id references 0 0 ""))
4577
4578     (message-setup
4579      `((Subject . ,subject)
4580        ,@follow-to
4581        ,@(if (or references message-id)
4582              `((References . ,(concat (or references "") (and references " ")
4583                                       (or message-id ""))))
4584            nil))
4585      cur)))
4586
4587 ;;;###autoload
4588 (defun message-wide-reply (&optional to-address)
4589   "Make a \"wide\" reply to the message in the current buffer."
4590   (interactive)
4591   (message-reply to-address t))
4592
4593 ;;;###autoload
4594 (defun message-followup (&optional to-newsgroups)
4595   "Follow up to the message in the current buffer.
4596 If TO-NEWSGROUPS, use that as the new Newsgroups line."
4597   (interactive)
4598   (let ((cur (current-buffer))
4599         from subject date mct
4600         references message-id follow-to
4601         (inhibit-point-motion-hooks t)
4602         (message-this-is-news t)
4603         followup-to distribution newsgroups gnus-warning posted-to mft mrt)
4604     (save-restriction
4605       (message-narrow-to-head)
4606       (when (message-functionp message-followup-to-function)
4607         (setq follow-to
4608               (funcall message-followup-to-function)))
4609       (setq from (message-fetch-field "from")
4610             date (message-fetch-field "date" t)
4611             subject (or (message-fetch-field "subject") "none")
4612             references (message-fetch-field "references")
4613             message-id (message-fetch-field "message-id" t)
4614             followup-to (when message-use-followup-to
4615                           (message-fetch-field "followup-to"))
4616             distribution (message-fetch-field "distribution")
4617             newsgroups (message-fetch-field "newsgroups")
4618             posted-to (message-fetch-field "posted-to")
4619             mct (when message-use-mail-copies-to
4620                   (message-fetch-field "mail-copies-to"))
4621             mft (when message-use-mail-followup-to
4622                   (message-fetch-field "mail-followup-to"))
4623             mrt (when message-use-mail-reply-to
4624                   (or (message-fetch-field "mail-reply-to")
4625                       (message-fetch-field "reply-to")))
4626             gnus-warning (message-fetch-field "gnus-warning"))
4627       (when (and gnus-warning (string-match "<[^>]+>" gnus-warning))
4628         (setq message-id (match-string 0 gnus-warning)))
4629       ;; Remove bogus distribution.
4630       (when (and (stringp distribution)
4631                  (let ((case-fold-search t))
4632                    (string-match "world" distribution)))
4633         (setq distribution nil))
4634       (if gnus-list-identifiers
4635           (setq subject (message-strip-list-identifiers subject)))
4636       (setq subject (message-make-followup-subject subject))
4637       (widen))
4638
4639     ;; Handle special values of Mail-Copies-To.
4640     (when mct
4641       (cond
4642        ((and (or (equal (downcase mct) "never")
4643                  (equal (downcase mct) "nobody"))
4644              (or (not (eq message-use-mail-copies-to 'ask))
4645                  (message-y-or-n-p
4646                   (concat "Obey Mail-Copies-To: never? ") t "\
4647 You should normally obey the Mail-Copies-To: header.
4648
4649         `Mail-Copies-To: never'
4650 directs you not to send your response to the author.")))
4651         (setq mct nil))
4652        ((and (or (equal (downcase mct) "always")
4653                  (equal (downcase mct) "poster"))
4654              (or (not (eq message-use-mail-copies-to 'ask))
4655                  (message-y-or-n-p
4656                   (concat "Obey Mail-Copies-To: always? ") t "\
4657 You should normally obey the Mail-Copies-To: header.
4658
4659         `Mail-Copies-To: always'
4660 sends a copy of your response to the author.")))
4661         (setq mct (or mrt from)))
4662        ((and (eq message-use-mail-copies-to 'ask)
4663              (not
4664               (message-y-or-n-p
4665                (concat "Obey Mail-Copies-To: " mct " ? ") t "\
4666 You should normally obey the Mail-Copies-To: header.
4667
4668         `Mail-Copies-To: " mct "'
4669 sends a copy of your response to " (if (string-match "," mct)
4670                                        "the specified addresses"
4671                                      "that address") ".")))
4672         (setq mct nil))
4673        ))
4674
4675     (unless follow-to
4676       (cond
4677        (to-newsgroups (setq follow-to (list (cons 'Newsgroups to-newsgroups))))
4678        ;; Handle Followup-To.
4679        (followup-to
4680         (cond
4681          ((equal (downcase followup-to) "poster")
4682           (if (or (eq message-use-followup-to 'use)
4683                   (message-y-or-n-p "Obey Followup-To: poster? " t "\
4684 You should normally obey the Followup-To: header.
4685
4686         `Followup-To: poster'
4687 sends your response via e-mail instead of news.
4688
4689 A typical situation where `Followup-To: poster' is used is when the author
4690 does not read the newsgroup, so he wouldn't see any replies sent to it."))
4691               (setq message-this-is-news nil
4692                     distribution nil
4693                     follow-to (list (cons 'To (or mrt from ""))))
4694             (setq follow-to (list (cons 'Newsgroups newsgroups)))))
4695          (t
4696           (if (or (equal followup-to newsgroups)
4697                   (not (eq message-use-followup-to 'ask))
4698                   (message-y-or-n-p
4699                    (concat "Obey Followup-To: " followup-to "? ") t "\
4700 You should normally obey the Followup-To: header.
4701
4702         `Followup-To: " followup-to "'
4703 directs your response to " (if (string-match "," followup-to)
4704                                "the specified newsgroups"
4705                              "that newsgroup only") ".
4706
4707 If a message is posted to several newsgroups, Followup-To is often
4708 used to direct the following discussion to one newsgroup only,
4709 because discussions that are spread over several newsgroup tend to
4710 be fragmented and very difficult to follow.
4711
4712 Also, some source/announcement newsgroups are not indented for discussion;
4713 responses here are directed to other newsgroups."))
4714               (setq follow-to (list (cons 'Newsgroups followup-to)))
4715             (setq follow-to (list (cons 'Newsgroups newsgroups)))))))
4716        ;; Handle Mail-Followup-To, followup via e-mail.
4717        ((and mft
4718              (or (not (eq message-use-mail-followup-to 'ask))
4719                  (message-y-or-n-p
4720                   (concat "Obey Mail-Followup-To: " mft "? ") t "\
4721 You should normally obey the Mail-Followup-To: header.
4722
4723         `Mail-Followup-To: " mft "'
4724 directs your response to " (if (string-match "," mft)
4725                                "the specified addresses"
4726                              "that address only") " instead of news.
4727
4728 A typical situation where Mail-Followup-To is used is when the author thinks
4729 that further discussion should take place only in "
4730                              (if (string-match "," mft)
4731                                  "the specified mailing lists"
4732                                "that mailing list") ".")))
4733         (setq message-this-is-news nil
4734               distribution nil
4735               follow-to (list (cons 'To mft))))
4736        (posted-to (setq follow-to (list (cons 'Newsgroups posted-to))))
4737        (t
4738         (setq follow-to (list (cons 'Newsgroups newsgroups))))))
4739
4740     (message-pop-to-buffer (message-buffer-name "followup" from newsgroups))
4741
4742     (setq message-reply-headers
4743           (make-full-mail-header-from-decoded-header
4744            0 subject from date message-id references 0 0 ""))
4745
4746     (message-setup
4747      `((Subject . ,subject)
4748        ,@follow-to
4749        ,@(and mct (list (cons 'Cc mct)))
4750        ,@(and distribution (list (cons 'Distribution distribution)))
4751        ,@(if (or references message-id)
4752              `((References . ,(concat (or references "") (and references " ")
4753                                       (or message-id ""))))))
4754      cur)))
4755
4756 ;;;###autoload
4757 (defun message-cancel-news (&optional arg)
4758   "Cancel an article you posted.
4759 If ARG, allow editing of the cancellation message."
4760   (interactive "P")
4761   (unless (message-news-p)
4762     (error "This is not a news article; canceling is impossible"))
4763   (when (yes-or-no-p "Do you really want to cancel this article? ")
4764     (let (from newsgroups message-id distribution buf sender)
4765       (save-excursion
4766         ;; Get header info from original article.
4767         (save-restriction
4768           (message-narrow-to-head)
4769           (setq from (message-fetch-field "from")
4770                 sender (message-fetch-field "sender")
4771                 newsgroups (message-fetch-field "newsgroups")
4772                 message-id (message-fetch-field "message-id" t)
4773                 distribution (message-fetch-field "distribution")))
4774         ;; Make sure that this article was written by the user.
4775         (unless (or (and sender
4776                          (string-equal
4777                           (downcase sender)
4778                           (downcase (message-make-sender))))
4779                     (string-equal
4780                      (downcase (cadr (std11-extract-address-components
4781                                       from)))
4782                      (downcase (cadr (std11-extract-address-components
4783                                       (message-make-from))))))
4784           (error "This article is not yours"))
4785         ;; Make control message.
4786         (if arg
4787             (message-news)
4788           (setq buf (set-buffer (get-buffer-create " *message cancel*"))))
4789         (erase-buffer)
4790         (insert "Newsgroups: " newsgroups "\n"
4791                 "From: " (message-make-from) "\n"
4792                 "Subject: cmsg cancel " message-id "\n"
4793                 "Control: cancel " message-id "\n"
4794                 (if distribution
4795                     (concat "Distribution: " distribution "\n")
4796                   "")
4797                 mail-header-separator "\n"
4798                 message-cancel-message)
4799         (run-hooks 'message-cancel-hook)
4800         (message "Canceling your article...")
4801         (unless arg
4802           (if (let ((message-syntax-checks
4803                      'dont-check-for-anything-just-trust-me)
4804                     (message-encoding-buffer (current-buffer))
4805                     (message-edit-buffer (current-buffer)))
4806                 (message-send-news))
4807               (message "Canceling your article...done"))
4808           (kill-buffer buf))))))
4809
4810 (defun message-supersede-setup-for-mime-edit ()
4811   (set (make-local-variable 'message-setup-hook) nil)
4812   (mime-edit-again))
4813
4814 ;;;###autoload
4815 (defun message-supersede ()
4816   "Start composing a message to supersede the current message.
4817 This is done simply by taking the old article and adding a Supersedes
4818 header line with the old Message-ID."
4819   (interactive)
4820   (let ((cur (current-buffer))
4821         (sender (message-fetch-field "sender"))
4822         (from (message-fetch-field "from")))
4823     ;; Check whether the user owns the article that is to be superseded.
4824     (unless (or (and sender
4825                      (string-equal
4826                       (downcase sender)
4827                       (downcase (message-make-sender))))
4828                 (string-equal
4829                  (downcase (cadr (std11-extract-address-components from)))
4830                  (downcase (cadr (std11-extract-address-components
4831                                   (message-make-from))))))
4832       (error "This article is not yours"))
4833     ;; Get a normal message buffer.
4834     (message-pop-to-buffer (message-buffer-name "supersede"))
4835     (insert-buffer-substring cur)
4836     (message-narrow-to-head)
4837     ;; Remove unwanted headers.
4838     (when message-ignored-supersedes-headers
4839       (message-remove-header message-ignored-supersedes-headers t))
4840     (goto-char (point-min))
4841     (if (not (re-search-forward "^Message-ID: " nil t))
4842         (error "No Message-ID in this article")
4843       (replace-match "Supersedes: " t t))
4844     (goto-char (point-max))
4845     (insert mail-header-separator)
4846     (widen)
4847     (when message-supersede-setup-function
4848       (funcall message-supersede-setup-function))
4849     (run-hooks 'message-supersede-setup-hook)
4850     (goto-char (point-min))
4851     (search-forward (concat "\n" mail-header-separator "\n") nil t)))
4852
4853 ;;;###autoload
4854 (defun message-recover ()
4855   "Reread contents of current buffer from its last auto-save file."
4856   (interactive)
4857   (let ((file-name (make-auto-save-file-name)))
4858     (cond ((save-window-excursion
4859              (if (not (eq system-type 'vax-vms))
4860                  (with-output-to-temp-buffer "*Directory*"
4861                    (with-current-buffer standard-output
4862                      (fundamental-mode)) ; for Emacs 20.4+
4863                    (buffer-disable-undo standard-output)
4864                    (let ((default-directory "/"))
4865                      (call-process
4866                       "ls" nil standard-output nil "-l" file-name))))
4867              (yes-or-no-p (format "Recover auto save file %s? " file-name)))
4868            (let ((buffer-read-only nil))
4869              (erase-buffer)
4870              (insert-file-contents file-name nil)))
4871           (t (error "message-recover cancelled")))))
4872
4873 ;;; Washing Subject:
4874
4875 (defun message-wash-subject (subject)
4876   "Remove junk like \"Re:\", \"(fwd)\", etc. that was added to the subject by previous forwarders, replyers, etc."
4877   (with-temp-buffer
4878     (insert-string subject)
4879     (goto-char (point-min))
4880     ;; strip Re/Fwd stuff off the beginning
4881     (while (re-search-forward
4882             "\\([Rr][Ee]:\\|[Ff][Ww][Dd]\\(\\[[0-9]*\\]\\)?:\\|[Ff][Ww]:\\)" nil t)
4883       (replace-match ""))
4884
4885     ;; and gnus-style forwards [foo@bar.com] subject
4886     (goto-char (point-min))
4887     (while (re-search-forward "\\[[^ \t]*\\(@\\|\\.\\)[^ \t]*\\]" nil t)
4888       (replace-match ""))
4889
4890     ;; and off the end
4891     (goto-char (point-max))
4892     (while (re-search-backward "([Ff][Ww][Dd])" nil t)
4893       (replace-match ""))
4894
4895     ;; and finally, any whitespace that was left-over
4896     (goto-char (point-min))
4897     (while (re-search-forward "^[ \t]+" nil t)
4898       (replace-match ""))
4899     (goto-char (point-max))
4900     (while (re-search-backward "[ \t]+$" nil t)
4901       (replace-match ""))
4902
4903     (buffer-string)))
4904
4905 ;;; Forwarding messages.
4906
4907 (defun message-forward-subject-author-subject (subject)
4908   "Generate a subject for a forwarded message.
4909 The form is: [Source] Subject, where if the original message was mail,
4910 Source is the sender, and if the original message was news, Source is
4911 the list of newsgroups is was posted to."
4912   (concat "["
4913           (or (message-fetch-field
4914                (if (message-news-p) "newsgroups" "from"))
4915               "(nowhere)")
4916           "] " subject))
4917
4918 (defun message-forward-subject-fwd (subject)
4919   "Generate a subject for a forwarded message.
4920 The form is: Fwd: Subject, where Subject is the original subject of
4921 the message."
4922   (concat "Fwd: " subject))
4923
4924 (defun message-make-forward-subject ()
4925   "Return a Subject header suitable for the message in the current buffer."
4926   (save-excursion
4927     (save-restriction
4928       (message-narrow-to-head)
4929       (let ((funcs message-make-forward-subject-function)
4930             (subject (message-fetch-field "Subject")))
4931         (setq subject
4932               (if subject
4933                   (if message-wash-forwarded-subjects
4934                       (message-wash-subject
4935                        (nnheader-decode-subject subject))
4936                     (nnheader-decode-subject subject))
4937                 "(none)"))
4938         ;; Make sure funcs is a list.
4939         (and funcs
4940              (not (listp funcs))
4941              (setq funcs (list funcs)))
4942         ;; Apply funcs in order, passing subject generated by previous
4943         ;; func to the next one.
4944         (while funcs
4945           (when (message-functionp (car funcs))
4946             (setq subject (funcall (car funcs) subject)))
4947           (setq funcs (cdr funcs)))
4948         subject))))
4949
4950 ;;;###autoload
4951 (defun message-forward (&optional news)
4952   "Forward the current message via mail.
4953 Optional NEWS will use news to forward instead of mail."
4954   (interactive "P")
4955   (let ((cur (current-buffer))
4956         (subject (message-make-forward-subject))
4957         art-beg)
4958     (if news
4959         (message-news nil subject)
4960       (message-mail nil subject))
4961     ;; Put point where we want it before inserting the forwarded
4962     ;; message.
4963     (if message-forward-before-signature
4964         (message-goto-body)
4965       (goto-char (point-max)))
4966     ;; Make sure we're at the start of the line.
4967     (unless (bolp)
4968       (insert "\n"))
4969     ;; Narrow to the area we are to insert.
4970     (narrow-to-region (point) (point))
4971     ;; Insert the separators and the forwarded buffer.
4972     (insert message-forward-start-separator)
4973     (setq art-beg (point))
4974     (insert-buffer-substring cur)
4975     (goto-char (point-max))
4976     (insert message-forward-end-separator)
4977     (set-text-properties (point-min) (point-max) nil)
4978     ;; Remove all unwanted headers.
4979     (goto-char art-beg)
4980     (narrow-to-region (point) (if (search-forward "\n\n" nil t)
4981                                   (1- (point))
4982                                 (point)))
4983     (goto-char (point-min))
4984     (message-remove-header message-included-forward-headers t nil t)
4985     (widen)
4986     (message-position-point)))
4987
4988 ;;;###autoload
4989 (defun message-resend (address)
4990   "Resend the current article to ADDRESS."
4991   (interactive
4992    (list (message-read-from-minibuffer "Resend message to: ")))
4993   (message "Resending message to %s..." address)
4994   (save-excursion
4995     (let ((cur (current-buffer))
4996           beg)
4997       ;; We first set up a normal mail buffer.
4998       (set-buffer (get-buffer-create " *message resend*"))
4999       (erase-buffer)
5000       ;; avoid to turn-on-mime-edit
5001       (let (message-setup-hook)
5002         (message-setup `((To . ,address)))
5003         )
5004       ;; Insert our usual headers.
5005       (message-generate-headers '(From Date To))
5006       (message-narrow-to-headers)
5007       ;; Rename them all to "Resent-*".
5008       (while (re-search-forward "^[A-Za-z]" nil t)
5009         (forward-char -1)
5010         (insert "Resent-"))
5011       (widen)
5012       (forward-line)
5013       (delete-region (point) (point-max))
5014       (setq beg (point))
5015       ;; Insert the message to be resent.
5016       (insert-buffer-substring cur)
5017       (goto-char (point-min))
5018       (search-forward "\n\n")
5019       (forward-char -1)
5020       (save-restriction
5021         (narrow-to-region beg (point))
5022         (message-remove-header message-ignored-resent-headers t)
5023         (goto-char (point-max)))
5024       (insert mail-header-separator)
5025       ;; Rename all old ("Also-")Resent headers.
5026       (while (re-search-backward "^\\(Also-\\)*Resent-" beg t)
5027         (beginning-of-line)
5028         (insert "Also-"))
5029       ;; Quote any "From " lines at the beginning.
5030       (goto-char beg)
5031       (when (looking-at "From ")
5032         (replace-match "X-From-Line: "))
5033       ;; Send it.
5034       (let ((message-encoding-buffer (current-buffer))
5035             (message-edit-buffer (current-buffer)))
5036         (let (message-required-mail-headers)
5037           (message-send-mail)))
5038       (kill-buffer (current-buffer)))
5039     (message "Resending message to %s...done" address)))
5040
5041 (defun message-bounce-setup-for-mime-edit ()
5042   (set (make-local-variable 'message-setup-hook) nil)
5043   (mime-edit-again))
5044
5045 ;;;###autoload
5046 (defun message-bounce ()
5047   "Re-mail the current message.
5048 This only makes sense if the current message is a bounce message that
5049 contains some mail you have written which has been bounced back to
5050 you."
5051   (interactive)
5052   (let ((cur (current-buffer))
5053         boundary)
5054     (message-pop-to-buffer (message-buffer-name "bounce"))
5055     (insert-buffer-substring cur)
5056     (undo-boundary)
5057     (message-narrow-to-head)
5058     (if (and (message-fetch-field "MIME-Version")
5059              (setq boundary (message-fetch-field "Content-Type")))
5060         (if (string-match "boundary=\"\\([^\"]+\\)\"" boundary)
5061             (setq boundary (concat (match-string 1 boundary) " *\n"
5062                                    "Content-Type: message/rfc822"))
5063           (setq boundary nil)))
5064     (widen)
5065     (goto-char (point-min))
5066     (search-forward "\n\n" nil t)
5067     (or (and boundary
5068              (re-search-forward boundary nil t)
5069              (forward-line 2))
5070         (and (re-search-forward message-unsent-separator nil t)
5071              (forward-line 1))
5072         (re-search-forward "^Return-Path:.*\n" nil t))
5073     ;; We remove everything before the bounced mail.
5074     (delete-region
5075      (point-min)
5076      (if (re-search-forward "^[^ \n\t]+:" nil t)
5077          (match-beginning 0)
5078        (point)))
5079     (save-restriction
5080       (message-narrow-to-head)
5081       (message-remove-header message-ignored-bounced-headers t)
5082       (goto-char (point-max))
5083       (insert mail-header-separator))
5084     (when message-bounce-setup-function
5085       (funcall message-bounce-setup-function))
5086     (run-hooks 'message-bounce-setup-hook)
5087     (message-position-point)))
5088
5089 ;;;
5090 ;;; Interactive entry points for new message buffers.
5091 ;;;
5092
5093 ;;;###autoload
5094 (defun message-mail-other-window (&optional to subject)
5095   "Like `message-mail' command, but display mail buffer in another window."
5096   (interactive)
5097   (let ((pop-up-windows t)
5098         (special-display-buffer-names nil)
5099         (special-display-regexps nil)
5100         (same-window-buffer-names nil)
5101         (same-window-regexps nil))
5102     (message-pop-to-buffer (message-buffer-name "mail" to)))
5103   (let ((message-this-is-mail t))
5104     (message-setup `((To . ,(or to "")) (Subject . ,(or subject ""))))))
5105
5106 ;;;###autoload
5107 (defun message-mail-other-frame (&optional to subject)
5108   "Like `message-mail' command, but display mail buffer in another frame."
5109   (interactive)
5110   (let ((pop-up-frames t)
5111         (special-display-buffer-names nil)
5112         (special-display-regexps nil)
5113         (same-window-buffer-names nil)
5114         (same-window-regexps nil))
5115     (message-pop-to-buffer (message-buffer-name "mail" to)))
5116   (let ((message-this-is-mail t))
5117     (message-setup `((To . ,(or to "")) (Subject . ,(or subject ""))))))
5118
5119 ;;;###autoload
5120 (defun message-news-other-window (&optional newsgroups subject)
5121   "Start editing a news article to be sent."
5122   (interactive)
5123   (let ((pop-up-windows t)
5124         (special-display-buffer-names nil)
5125         (special-display-regexps nil)
5126         (same-window-buffer-names nil)
5127         (same-window-regexps nil))
5128     (message-pop-to-buffer (message-buffer-name "news" nil newsgroups)))
5129   (let ((message-this-is-news t))
5130     (message-setup `((Newsgroups . ,(or newsgroups ""))
5131                      (Subject . ,(or subject ""))))))
5132
5133 ;;;###autoload
5134 (defun message-news-other-frame (&optional newsgroups subject)
5135   "Start editing a news article to be sent."
5136   (interactive)
5137   (let ((pop-up-frames t)
5138         (special-display-buffer-names nil)
5139         (special-display-regexps nil)
5140         (same-window-buffer-names nil)
5141         (same-window-regexps nil))
5142     (message-pop-to-buffer (message-buffer-name "news" nil newsgroups)))
5143   (let ((message-this-is-news t))
5144     (message-setup `((Newsgroups . ,(or newsgroups ""))
5145                      (Subject . ,(or subject ""))))))
5146
5147 ;;; underline.el
5148
5149 ;; This code should be moved to underline.el (from which it is stolen).
5150
5151 ;;;###autoload
5152 (defun bold-region (start end)
5153   "Bold all nonblank characters in the region.
5154 Works by overstriking characters.
5155 Called from program, takes two arguments START and END
5156 which specify the range to operate on."
5157   (interactive "r")
5158   (save-excursion
5159     (let ((end1 (make-marker)))
5160       (move-marker end1 (max start end))
5161       (goto-char (min start end))
5162       (while (< (point) end1)
5163         (or (looking-at "[_\^@- ]")
5164             (insert (char-after) "\b"))
5165         (forward-char 1)))))
5166
5167 ;;;###autoload
5168 (defun unbold-region (start end)
5169   "Remove all boldness (overstruck characters) in the region.
5170 Called from program, takes two arguments START and END
5171 which specify the range to operate on."
5172   (interactive "r")
5173   (save-excursion
5174     (let ((end1 (make-marker)))
5175       (move-marker end1 (max start end))
5176       (goto-char (min start end))
5177       (while (re-search-forward "\b" end1 t)
5178         (if (eq (char-after) (char-after (- (point) 2)))
5179             (delete-char -2))))))
5180
5181 (defalias 'message-exchange-point-and-mark 'exchange-point-and-mark)
5182
5183 ;; Support for toolbar
5184 (when (string-match "XEmacs\\|Lucid" emacs-version)
5185   (require 'messagexmas))
5186
5187 ;;; Group name completion.
5188
5189 (defvar message-newgroups-header-regexp
5190   "^\\(Newsgroups\\|Followup-To\\|Posted-To\\|Gcc\\):"
5191   "Regexp that match headers that lists groups.")
5192
5193 (defun message-tab ()
5194   "Expand group names in Newsgroups and Followup-To headers.
5195 Do a `tab-to-tab-stop' if not in those headers."
5196   (interactive)
5197   (if (let ((mail-abbrev-mode-regexp message-newgroups-header-regexp))
5198         (mail-abbrev-in-expansion-header-p))
5199       (message-expand-group)
5200     (tab-to-tab-stop)))
5201
5202 (defvar gnus-active-hashtb)
5203 (defun message-expand-group ()
5204   "Expand the group name under point."
5205   (let* ((b (save-excursion
5206               (save-restriction
5207                 (narrow-to-region
5208                  (save-excursion
5209                    (beginning-of-line)
5210                    (skip-chars-forward "^:")
5211                    (1+ (point)))
5212                  (point))
5213                 (skip-chars-backward "^, \t\n") (point))))
5214          (completion-ignore-case t)
5215          (string (buffer-substring b (progn (skip-chars-forward "^,\t\n ")
5216                                             (point))))
5217          (hashtb (and (boundp 'gnus-active-hashtb) gnus-active-hashtb))
5218          (completions (all-completions string hashtb))
5219          comp)
5220     (delete-region b (point))
5221     (cond
5222      ((= (length completions) 1)
5223       (if (string= (car completions) string)
5224           (progn
5225             (insert string)
5226             (message "Only matching group"))
5227         (insert (car completions))))
5228      ((and (setq comp (try-completion string hashtb))
5229            (not (string= comp string)))
5230       (insert comp))
5231      (t
5232       (insert string)
5233       (if (not comp)
5234           (message "No matching groups")
5235         (save-selected-window
5236           (pop-to-buffer "*Completions*")
5237           (buffer-disable-undo)
5238           (let ((buffer-read-only nil))
5239             (erase-buffer)
5240             (let ((standard-output (current-buffer)))
5241               (display-completion-list (sort completions 'string<)))
5242             (goto-char (point-min))
5243             (delete-region (point) (progn (forward-line 3) (point))))))))))
5244
5245 ;;; Help stuff.
5246
5247 (defun message-talkative-question (ask question show &rest text)
5248   "Call FUNCTION with argument QUESTION; optionally display TEXT... args.
5249 If SHOW is non-nil, the arguments TEXT... are displayed in a temp buffer.
5250 The following arguments may contain lists of values."
5251   (if (and show
5252            (setq text (message-flatten-list text)))
5253       (save-window-excursion
5254         (save-excursion
5255           (with-output-to-temp-buffer " *MESSAGE information message*"
5256             (set-buffer " *MESSAGE information message*")
5257             (fundamental-mode)          ; for Emacs 20.4+
5258             (mapcar 'princ text)
5259             (goto-char (point-min))))
5260         (funcall ask question))
5261     (funcall ask question)))
5262
5263 (defun message-flatten-list (list)
5264   "Return a new, flat list that contains all elements of LIST.
5265
5266 \(message-flatten-list '(1 (2 3 (4 5 (6))) 7))
5267 => (1 2 3 4 5 6 7)"
5268   (cond ((consp list)
5269          (apply 'append (mapcar 'message-flatten-list list)))
5270         (list
5271          (list list))))
5272
5273 (defun message-generate-new-buffer-clone-locals (name &optional varstr)
5274   "Create and return a buffer with a name based on NAME using generate-new-buffer.
5275 Then clone the local variables and values from the old buffer to the
5276 new one, cloning only the locals having a substring matching the
5277 regexp varstr."
5278   (let ((oldbuf (current-buffer)))
5279     (save-excursion
5280       (set-buffer (generate-new-buffer name))
5281       (message-clone-locals oldbuf varstr)
5282       (current-buffer))))
5283
5284 (defun message-clone-locals (buffer &optional varstr)
5285   "Clone the local variables from BUFFER to the current buffer."
5286   (let ((locals (save-excursion
5287                   (set-buffer buffer)
5288                   (buffer-local-variables)))
5289         (regexp
5290          "^\\(gnus\\|nn\\|message\\|user-\\(mail-address\\|full-name\\)\\)"))
5291     (mapcar
5292      (lambda (local)
5293        (when (and (consp local)
5294                   (car local)
5295                   (string-match regexp (symbol-name (car local)))
5296                   (or (null varstr)
5297                       (string-match varstr (symbol-name (car local)))))
5298          (ignore-errors
5299            (set (make-local-variable (car local))
5300                 (cdr local)))))
5301      locals)))
5302
5303
5304 ;;; @ for MIME Edit mode
5305 ;;;
5306
5307 (defun message-maybe-encode ()
5308   (when message-mime-mode
5309     ;; Inherit the buffer local variable `mime-edit-pgp-processing'.
5310     (let ((pgp-processing (with-current-buffer message-edit-buffer
5311                             mime-edit-pgp-processing)))
5312       (setq mime-edit-pgp-processing pgp-processing))
5313     (run-hooks 'mime-edit-translate-hook)
5314     (if (catch 'mime-edit-error
5315           (save-excursion
5316             (mime-edit-pgp-enclose-buffer)
5317             (mime-edit-translate-body)
5318             ))
5319         (error "Translation error!")
5320       )
5321     (end-of-invisible)
5322     (run-hooks 'mime-edit-exit-hook)
5323     ))
5324
5325 (defun message-mime-insert-article (&optional full-headers)
5326   (interactive "P")
5327   (let ((message-cite-function 'mime-edit-inserted-message-filter)
5328         (message-reply-buffer
5329          (message-get-parameter-with-eval 'original-buffer))
5330         (start (point)))
5331     (message-yank-original nil)
5332     (save-excursion
5333       (narrow-to-region (goto-char start)
5334                         (if (search-forward "\n\n" nil t)
5335                             (1- (point))
5336                           (point-max)))
5337       (goto-char (point-min))
5338       (let ((message-included-forward-headers
5339              (if full-headers "" message-included-forward-headers)))
5340         (message-remove-header message-included-forward-headers t nil t))
5341       (widen))))
5342
5343 (set-alist 'mime-edit-message-inserter-alist
5344            'message-mode (function message-mime-insert-article))
5345
5346 ;;; Miscellaneous functions
5347
5348 ;; stolen (and renamed) from nnheader.el
5349 (if (fboundp 'subst-char-in-string)
5350     (defsubst message-replace-chars-in-string (string from to)
5351       (subst-char-in-string from to string))
5352   (defun message-replace-chars-in-string (string from to)
5353     "Replace characters in STRING from FROM to TO."
5354     (let ((string (substring string 0)) ;Copy string.
5355           (len (length string))
5356           (idx 0))
5357       ;; Replace all occurrences of FROM with TO.
5358       (while (< idx len)
5359         (when (= (aref string idx) from)
5360           (aset string idx to))
5361         (setq idx (1+ idx)))
5362       string)))
5363
5364 ;;;
5365 ;;; MIME functions
5366 ;;;
5367
5368 (defvar message-inhibit-body-encoding t)
5369
5370 (defun message-encode-message-body ()
5371   (unless message-inhibit-body-encoding
5372     (let ((mail-parse-charset (or mail-parse-charset
5373                                   message-default-charset))
5374           (case-fold-search t)
5375           lines content-type-p)
5376       (message-goto-body)
5377       (save-restriction
5378         (narrow-to-region (point) (point-max))
5379         (let ((new (mml-generate-mime)))
5380           (when new
5381             (delete-region (point-min) (point-max))
5382             (insert new)
5383             (goto-char (point-min))
5384             (if (eq (aref new 0) ?\n)
5385                 (delete-char 1)
5386               (search-forward "\n\n")
5387               (setq lines (buffer-substring (point-min) (1- (point))))
5388               (delete-region (point-min) (point))))))
5389       (save-restriction
5390         (message-narrow-to-headers-or-head)
5391         (message-remove-header "Mime-Version")
5392         (goto-char (point-max))
5393         (insert "MIME-Version: 1.0\n")
5394         (when lines
5395           (insert lines))
5396         (setq content-type-p
5397               (re-search-backward "^Content-Type:" nil t)))
5398       (save-restriction
5399         (message-narrow-to-headers-or-head)
5400         (message-remove-first-header "Content-Type")
5401         (message-remove-first-header "Content-Transfer-Encoding"))
5402       ;; We always make sure that the message has a Content-Type header.
5403       ;; This is because some broken MTAs and MUAs get awfully confused
5404       ;; when confronted with a message with a MIME-Version header and
5405       ;; without a Content-Type header.  For instance, Solaris'
5406       ;; /usr/bin/mail.
5407       (unless content-type-p
5408         (goto-char (point-min))
5409         (re-search-forward "^MIME-Version:")
5410         (forward-line 1)
5411         (insert "Content-Type: text/plain; charset=us-ascii\n")))))
5412
5413 (defun message-read-from-minibuffer (prompt)
5414   "Read from the minibuffer while providing abbrev expansion."
5415   (if (fboundp 'mail-abbrevs-setup)
5416       (let ((mail-abbrev-mode-regexp "")
5417             (minibuffer-setup-hook 'mail-abbrevs-setup))
5418         (read-from-minibuffer prompt))
5419     (let ((minibuffer-setup-hook 'mail-abbrev-minibuffer-setup-hook))
5420       (read-string prompt))))
5421
5422 (defun message-save-drafts ()
5423   "Postponing the message."
5424   (interactive)
5425   (message "Saving %s..." buffer-file-name)
5426   (let ((reply-headers message-reply-headers)
5427         (msg (buffer-substring-no-properties (point-min) (point-max))))
5428     (with-temp-file buffer-file-name
5429       (insert msg)
5430       (setq message-reply-headers reply-headers)
5431       (message-generate-headers '((optional . In-Reply-To)))
5432       (mime-edit-translate-buffer))
5433     (set-buffer-modified-p nil))
5434   (message "Saving %s...done" buffer-file-name))
5435
5436 (provide 'message)
5437
5438 (run-hooks 'message-load-hook)
5439
5440 ;; Local Variables:
5441 ;; coding: iso-8859-1
5442 ;; End:
5443
5444 ;;; message.el ends here