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