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