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