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