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