Commit | Line | Data |
---|---|---|
23f87bed | 1 | ;;; message.el --- composing mail and news messages |
e84b4b86 | 2 | |
ba318903 | 3 | ;; Copyright (C) 1996-2014 Free Software Foundation, Inc. |
eec82323 | 4 | |
6748645f | 5 | ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org> |
eec82323 LMI |
6 | ;; Keywords: mail, news |
7 | ||
8 | ;; This file is part of GNU Emacs. | |
9 | ||
5e809f55 | 10 | ;; GNU Emacs is free software: you can redistribute it and/or modify |
eec82323 | 11 | ;; it under the terms of the GNU General Public License as published by |
5e809f55 GM |
12 | ;; the Free Software Foundation, either version 3 of the License, or |
13 | ;; (at your option) any later version. | |
eec82323 LMI |
14 | |
15 | ;; GNU Emacs is distributed in the hope that it will be useful, | |
16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
5e809f55 | 17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
eec82323 LMI |
18 | ;; GNU General Public License for more details. |
19 | ||
20 | ;; You should have received a copy of the GNU General Public License | |
5e809f55 | 21 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. |
eec82323 LMI |
22 | |
23 | ;;; Commentary: | |
24 | ||
25 | ;; This mode provides mail-sending facilities from within Emacs. It | |
26 | ;; consists mainly of large chunks of code from the sendmail.el, | |
27 | ;; gnus-msg.el and rnewspost.el files. | |
28 | ||
29 | ;;; Code: | |
30 | ||
f0b7f5a8 | 31 | ;; For Emacs <22.2 and XEmacs. |
b43906b6 GM |
32 | (eval-and-compile |
33 | (unless (fboundp 'declare-function) (defmacro declare-function (&rest r)))) | |
16409b0b | 34 | (eval-when-compile |
9efa445f | 35 | (require 'cl)) |
b43906b6 | 36 | |
eec82323 | 37 | (require 'mailheader) |
18c06a99 | 38 | (require 'gmm-utils) |
aa8f8277 GM |
39 | (require 'mail-utils) |
40 | ;; Only for the trivial macros mail-header-from, mail-header-date | |
41 | ;; mail-header-references, mail-header-subject, mail-header-id | |
42 | (eval-when-compile (require 'nnheader)) | |
23f87bed MB |
43 | ;; This is apparently necessary even though things are autoloaded. |
44 | ;; Because we dynamically bind mail-abbrev-mode-regexp, we'd better | |
45 | ;; require mailabbrev here. | |
16409b0b | 46 | (if (featurep 'xemacs) |
23f87bed MB |
47 | (require 'mail-abbrevs) |
48 | (require 'mailabbrev)) | |
16409b0b GM |
49 | (require 'mail-parse) |
50 | (require 'mml) | |
23f87bed | 51 | (require 'rfc822) |
a123622d | 52 | (require 'format-spec) |
01c52d31 | 53 | |
2ea6167b RS |
54 | (autoload 'mailclient-send-it "mailclient") ;; Emacs 22 or contrib/ |
55 | ||
9efa445f DN |
56 | (defvar gnus-message-group-art) |
57 | (defvar gnus-list-identifiers) ; gnus-sum is required where necessary | |
58 | (defvar rmail-enable-mime-composing) | |
eec82323 LMI |
59 | |
60 | (defgroup message '((user-mail-address custom-variable) | |
61 | (user-full-name custom-variable)) | |
62 | "Mail and news message composing." | |
63 | :link '(custom-manual "(message)Top") | |
64 | :group 'mail | |
65 | :group 'news) | |
66 | ||
67 | (put 'user-mail-address 'custom-type 'string) | |
68 | (put 'user-full-name 'custom-type 'string) | |
69 | ||
70 | (defgroup message-various nil | |
339ccc6a | 71 | "Various Message Variables." |
eec82323 LMI |
72 | :link '(custom-manual "(message)Various Message Variables") |
73 | :group 'message) | |
74 | ||
75 | (defgroup message-buffers nil | |
339ccc6a | 76 | "Message Buffers." |
eec82323 LMI |
77 | :link '(custom-manual "(message)Message Buffers") |
78 | :group 'message) | |
79 | ||
80 | (defgroup message-sending nil | |
339ccc6a | 81 | "Message Sending." |
eec82323 LMI |
82 | :link '(custom-manual "(message)Sending Variables") |
83 | :group 'message) | |
84 | ||
85 | (defgroup message-interface nil | |
339ccc6a | 86 | "Message Interface." |
eec82323 LMI |
87 | :link '(custom-manual "(message)Interface") |
88 | :group 'message) | |
89 | ||
90 | (defgroup message-forwarding nil | |
339ccc6a | 91 | "Message Forwarding." |
eec82323 LMI |
92 | :link '(custom-manual "(message)Forwarding") |
93 | :group 'message-interface) | |
94 | ||
95 | (defgroup message-insertion nil | |
339ccc6a | 96 | "Message Insertion." |
eec82323 LMI |
97 | :link '(custom-manual "(message)Insertion") |
98 | :group 'message) | |
99 | ||
100 | (defgroup message-headers nil | |
339ccc6a | 101 | "Message Headers." |
eec82323 LMI |
102 | :link '(custom-manual "(message)Message Headers") |
103 | :group 'message) | |
104 | ||
105 | (defgroup message-news nil | |
339ccc6a | 106 | "Composing News Messages." |
eec82323 LMI |
107 | :group 'message) |
108 | ||
109 | (defgroup message-mail nil | |
339ccc6a | 110 | "Composing Mail Messages." |
eec82323 LMI |
111 | :group 'message) |
112 | ||
113 | (defgroup message-faces nil | |
114 | "Faces used for message composing." | |
115 | :group 'message | |
116 | :group 'faces) | |
117 | ||
118 | (defcustom message-directory "~/Mail/" | |
119 | "*Directory from which all other mail file variables are derived." | |
120 | :group 'message-various | |
121 | :type 'directory) | |
122 | ||
123 | (defcustom message-max-buffers 10 | |
124 | "*How many buffers to keep before starting to kill them off." | |
125 | :group 'message-buffers | |
126 | :type 'integer) | |
127 | ||
128 | (defcustom message-send-rename-function nil | |
129 | "Function called to rename the buffer after sending it." | |
130 | :group 'message-buffers | |
7d829636 | 131 | :type '(choice function (const nil))) |
eec82323 LMI |
132 | |
133 | (defcustom message-fcc-handler-function 'message-output | |
134 | "*A function called to save outgoing articles. | |
135 | This function will be called with the name of the file to store the | |
136 | article in. The default function is `message-output' which saves in Unix | |
137 | mailbox format." | |
138 | :type '(radio (function-item message-output) | |
139 | (function :tag "Other")) | |
140 | :group 'message-sending) | |
141 | ||
23f87bed MB |
142 | (defcustom message-fcc-externalize-attachments nil |
143 | "If non-nil, attachments are included as external parts in Fcc copies." | |
bf247b6e | 144 | :version "22.1" |
23f87bed MB |
145 | :type 'boolean |
146 | :group 'message-sending) | |
147 | ||
eec82323 | 148 | (defcustom message-courtesy-message |
f129a4df | 149 | "The following message is a courtesy copy of an article\nthat has been posted to %s as well.\n\n" |
eec82323 LMI |
150 | "*This is inserted at the start of a mailed copy of a posted message. |
151 | If the string contains the format spec \"%s\", the Newsgroups | |
152 | the article has been posted to will be inserted there. | |
153 | If this variable is nil, no such courtesy message will be added." | |
154 | :group 'message-sending | |
ad136a7c | 155 | :type '(radio string (const nil))) |
eec82323 | 156 | |
23f87bed MB |
157 | (defcustom message-ignored-bounced-headers |
158 | "^\\(Received\\|Return-Path\\|Delivered-To\\):" | |
eec82323 LMI |
159 | "*Regexp that matches headers to be removed in resent bounced mail." |
160 | :group 'message-interface | |
161 | :type 'regexp) | |
162 | ||
106c1842 | 163 | (defcustom message-from-style mail-from-style |
ed797193 | 164 | "Specifies how \"From\" headers look. |
eec82323 | 165 | |
7d829636 | 166 | If nil, they contain just the return address like: |
eec82323 LMI |
167 | king@grassland.com |
168 | If `parens', they look like: | |
169 | king@grassland.com (Elvis Parsley) | |
170 | If `angles', they look like: | |
171 | Elvis Parsley <king@grassland.com> | |
172 | ||
173 | Otherwise, most addresses look like `angles', but they look like | |
174 | `parens' if `angles' would need quoting and `parens' would not." | |
937e60c8 | 175 | :version "23.2" |
eec82323 LMI |
176 | :type '(choice (const :tag "simple" nil) |
177 | (const parens) | |
178 | (const angles) | |
179 | (const default)) | |
180 | :group 'message-headers) | |
181 | ||
23f87bed MB |
182 | (defcustom message-insert-canlock t |
183 | "Whether to insert a Cancel-Lock header in news postings." | |
bf247b6e | 184 | :version "22.1" |
23f87bed MB |
185 | :group 'message-headers |
186 | :type 'boolean) | |
187 | ||
188 | (defcustom message-syntax-checks | |
189 | (if message-insert-canlock '((sender . disabled)) nil) | |
16409b0b | 190 | ;; Guess this one shouldn't be easy to customize... |
6748645f | 191 | "*Controls what syntax checks should not be performed on outgoing posts. |
eec82323 LMI |
192 | To disable checking of long signatures, for instance, add |
193 | `(signature . disabled)' to this list. | |
194 | ||
195 | Don't touch this variable unless you really know what you're doing. | |
196 | ||
437ce4be MB |
197 | Checks include `approved', `bogus-recipient', `continuation-headers', |
198 | `control-chars', `empty', `existing-newsgroups', `from', `illegible-text', | |
f362b760 MB |
199 | `invisible-text', `long-header-lines', `long-lines', `message-id', |
200 | `multiple-headers', `new-text', `newsgroups', `quoting-style', | |
5cfd0f2e | 201 | `repeated-newsgroups', `reply-to', `sender', `sendsys', `shoot', |
f362b760 MB |
202 | `shorten-followup-to', `signature', `size', `subject', `subject-cmsg' |
203 | and `valid-newsgroups'." | |
16409b0b | 204 | :group 'message-news |
7d829636 | 205 | :type '(repeat sexp)) ; Fixme: improve this |
eec82323 | 206 | |
23f87bed MB |
207 | (defcustom message-required-headers '((optional . References) |
208 | From) | |
209 | "*Headers to be generated or prompted for when sending a message. | |
210 | Also see `message-required-news-headers' and | |
211 | `message-required-mail-headers'." | |
bf247b6e | 212 | :version "22.1" |
23f87bed MB |
213 | :group 'message-news |
214 | :group 'message-headers | |
215 | :link '(custom-manual "(message)Message Headers") | |
216 | :type '(repeat sexp)) | |
217 | ||
01c52d31 | 218 | (defcustom message-draft-headers '(References From Date) |
23f87bed | 219 | "*Headers to be generated when saving a draft message." |
bf247b6e | 220 | :version "22.1" |
23f87bed MB |
221 | :group 'message-news |
222 | :group 'message-headers | |
223 | :link '(custom-manual "(message)Message Headers") | |
224 | :type '(repeat sexp)) | |
225 | ||
eec82323 LMI |
226 | (defcustom message-required-news-headers |
227 | '(From Newsgroups Subject Date Message-ID | |
23f87bed | 228 | (optional . Organization) |
16409b0b | 229 | (optional . User-Agent)) |
6748645f | 230 | "*Headers to be generated or prompted for when posting an article. |
eec82323 LMI |
231 | RFC977 and RFC1036 require From, Date, Newsgroups, Subject, |
232 | Message-ID. Organization, Lines, In-Reply-To, Expires, and | |
815b81c8 | 233 | User-Agent are optional. If you don't want message to insert some |
eec82323 LMI |
234 | header, remove it from this list." |
235 | :group 'message-news | |
236 | :group 'message-headers | |
23f87bed | 237 | :link '(custom-manual "(message)Message Headers") |
eec82323 LMI |
238 | :type '(repeat sexp)) |
239 | ||
240 | (defcustom message-required-mail-headers | |
23f87bed | 241 | '(From Subject Date (optional . In-Reply-To) Message-ID |
16409b0b | 242 | (optional . User-Agent)) |
6748645f | 243 | "*Headers to be generated or prompted for when mailing a message. |
23f87bed MB |
244 | It is recommended that From, Date, To, Subject and Message-ID be |
245 | included. Organization and User-Agent are optional." | |
eec82323 LMI |
246 | :group 'message-mail |
247 | :group 'message-headers | |
23f87bed | 248 | :link '(custom-manual "(message)Message Headers") |
eec82323 LMI |
249 | :type '(repeat sexp)) |
250 | ||
2cdd366f KY |
251 | (defcustom message-prune-recipient-rules nil |
252 | "Rules for how to prune the list of recipients when doing wide replies. | |
253 | This is a list of regexps and regexp matches." | |
b0e30310 | 254 | :version "24.1" |
2cdd366f KY |
255 | :group 'message-mail |
256 | :group 'message-headers | |
257 | :link '(custom-manual "(message)Wide Reply") | |
258 | :type '(repeat regexp)) | |
259 | ||
eec82323 LMI |
260 | (defcustom message-deletable-headers '(Message-ID Date Lines) |
261 | "Headers to be deleted if they already exist and were generated by message previously." | |
262 | :group 'message-headers | |
23f87bed | 263 | :link '(custom-manual "(message)Message Headers") |
eec82323 LMI |
264 | :type 'sexp) |
265 | ||
266 | (defcustom message-ignored-news-headers | |
89cccc2f | 267 | "^NNTP-Posting-Host:\\|^Xref:\\|^[BGF]cc:\\|^Resent-Fcc:\\|^X-Draft-From:\\|^X-Gnus-Agent-Meta-Information:\\|^X-Message-SMTP-Method:\\|^X-Gnus-Delayed:" |
eec82323 LMI |
268 | "*Regexp of headers to be removed unconditionally before posting." |
269 | :group 'message-news | |
270 | :group 'message-headers | |
23f87bed | 271 | :link '(custom-manual "(message)Message Headers") |
8903a9c8 MB |
272 | :type '(repeat :value-to-internal (lambda (widget value) |
273 | (custom-split-regexp-maybe value)) | |
274 | :match (lambda (widget value) | |
275 | (or (stringp value) | |
276 | (widget-editable-list-match widget value))) | |
277 | regexp)) | |
eec82323 | 278 | |
23f87bed | 279 | (defcustom message-ignored-mail-headers |
674c5ccf | 280 | "^\\([GF]cc\\|Resent-Fcc\\|Xref\\|X-Draft-From\\|X-Gnus-Agent-Meta-Information\\):" |
eec82323 LMI |
281 | "*Regexp of headers to be removed unconditionally before mailing." |
282 | :group 'message-mail | |
283 | :group 'message-headers | |
23f87bed | 284 | :link '(custom-manual "(message)Mail Headers") |
eec82323 LMI |
285 | :type 'regexp) |
286 | ||
6b958814 | 287 | (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:\\|^Approved:\\|^Injection-Date:\\|^Injection-Info:" |
eec82323 LMI |
288 | "*Header lines matching this regexp will be deleted before posting. |
289 | It's best to delete old Path and Date headers before posting to avoid | |
290 | any confusion." | |
291 | :group 'message-interface | |
23f87bed | 292 | :link '(custom-manual "(message)Superseding") |
8903a9c8 MB |
293 | :type '(repeat :value-to-internal (lambda (widget value) |
294 | (custom-split-regexp-maybe value)) | |
295 | :match (lambda (widget value) | |
296 | (or (stringp value) | |
297 | (widget-editable-list-match widget value))) | |
298 | regexp)) | |
eec82323 | 299 | |
23f87bed MB |
300 | (defcustom message-subject-re-regexp |
301 | "^[ \t]*\\([Rr][Ee]\\(\\[[0-9]*\\]\\)*:[ \t]*\\)*[ \t]*" | |
6748645f LMI |
302 | "*Regexp matching \"Re: \" in the subject line." |
303 | :group 'message-various | |
23f87bed | 304 | :link '(custom-manual "(message)Message Headers") |
6748645f LMI |
305 | :type 'regexp) |
306 | ||
23f87bed MB |
307 | ;;; Start of variables adopted from `message-utils.el'. |
308 | ||
a8336650 | 309 | (defcustom message-subject-trailing-was-query t |
23f87bed MB |
310 | "*What to do with trailing \"(was: <old subject>)\" in subject lines. |
311 | If nil, leave the subject unchanged. If it is the symbol `ask', query | |
312 | the user what do do. In this case, the subject is matched against | |
313 | `message-subject-trailing-was-ask-regexp'. If | |
314 | `message-subject-trailing-was-query' is t, always strip the trailing | |
315 | old subject. In this case, `message-subject-trailing-was-regexp' is | |
316 | used." | |
a8336650 | 317 | :version "24.1" |
23f87bed MB |
318 | :type '(choice (const :tag "never" nil) |
319 | (const :tag "always strip" t) | |
01c52d31 | 320 | (const ask)) |
23f87bed MB |
321 | :link '(custom-manual "(message)Message Headers") |
322 | :group 'message-various) | |
323 | ||
324 | (defcustom message-subject-trailing-was-ask-regexp | |
be3c11b3 | 325 | "[ \t]*\\([[(]+[Ww][Aa][Ss]:?[ \t]*.*[])]+\\)" |
23f87bed MB |
326 | "*Regexp matching \"(was: <old subject>)\" in the subject line. |
327 | ||
328 | The function `message-strip-subject-trailing-was' uses this regexp if | |
329 | `message-subject-trailing-was-query' is set to the symbol `ask'. If | |
330 | the variable is t instead of `ask', use | |
331 | `message-subject-trailing-was-regexp' instead. | |
332 | ||
333 | It is okay to create some false positives here, as the user is asked." | |
be3c11b3 | 334 | :version "22.1" |
23f87bed MB |
335 | :group 'message-various |
336 | :link '(custom-manual "(message)Message Headers") | |
337 | :type 'regexp) | |
338 | ||
339 | (defcustom message-subject-trailing-was-regexp | |
be3c11b3 | 340 | "[ \t]*\\((*[Ww][Aa][Ss]:[ \t]*.*)\\)" |
23f87bed MB |
341 | "*Regexp matching \"(was: <old subject>)\" in the subject line. |
342 | ||
343 | If `message-subject-trailing-was-query' is set to t, the subject is | |
344 | matched against `message-subject-trailing-was-regexp' in | |
345 | `message-strip-subject-trailing-was'. You should use a regexp creating very | |
346 | few false positives here." | |
be3c11b3 | 347 | :version "22.1" |
23f87bed MB |
348 | :group 'message-various |
349 | :link '(custom-manual "(message)Message Headers") | |
350 | :type 'regexp) | |
351 | ||
23f87bed MB |
352 | ;;; marking inserted text |
353 | ||
23f87bed | 354 | (defcustom message-mark-insert-begin |
f129a4df | 355 | "--8<---------------cut here---------------start------------->8---\n" |
23f87bed | 356 | "How to mark the beginning of some inserted text." |
bf247b6e | 357 | :version "22.1" |
23f87bed MB |
358 | :type 'string |
359 | :link '(custom-manual "(message)Insertion Variables") | |
360 | :group 'message-various) | |
361 | ||
23f87bed | 362 | (defcustom message-mark-insert-end |
f129a4df | 363 | "--8<---------------cut here---------------end--------------->8---\n" |
23f87bed | 364 | "How to mark the end of some inserted text." |
bf247b6e | 365 | :version "22.1" |
23f87bed MB |
366 | :type 'string |
367 | :link '(custom-manual "(message)Insertion Variables") | |
368 | :group 'message-various) | |
369 | ||
f129a4df | 370 | (defcustom message-archive-header "X-No-Archive: Yes\n" |
23f87bed MB |
371 | "Header to insert when you don't want your article to be archived. |
372 | Archives \(such as groups.google.com\) respect this header." | |
bf247b6e | 373 | :version "22.1" |
23f87bed MB |
374 | :type 'string |
375 | :link '(custom-manual "(message)Header Commands") | |
376 | :group 'message-various) | |
377 | ||
23f87bed MB |
378 | (defcustom message-archive-note |
379 | "X-No-Archive: Yes - save http://groups.google.com/" | |
380 | "Note to insert why you wouldn't want this posting archived. | |
381 | If nil, don't insert any text in the body." | |
bf247b6e | 382 | :version "22.1" |
ad136a7c | 383 | :type '(radio string (const nil)) |
23f87bed MB |
384 | :link '(custom-manual "(message)Header Commands") |
385 | :group 'message-various) | |
386 | ||
387 | ;;; Crossposts and Followups | |
388 | ;; inspired by JoH-followup-to by Jochem Huhman <joh at gmx.de> | |
389 | ;; new suggestions by R. Weikusat <rw at another.de> | |
390 | ||
391 | (defvar message-cross-post-old-target nil | |
392 | "Old target for cross-posts or follow-ups.") | |
393 | (make-variable-buffer-local 'message-cross-post-old-target) | |
394 | ||
23f87bed MB |
395 | (defcustom message-cross-post-default t |
396 | "When non-nil `message-cross-post-followup-to' will perform a crosspost. | |
397 | If nil, `message-cross-post-followup-to' will only do a followup. Note that | |
398 | you can explicitly override this setting by calling | |
399 | `message-cross-post-followup-to' with a prefix." | |
bf247b6e | 400 | :version "22.1" |
23f87bed MB |
401 | :type 'boolean |
402 | :group 'message-various) | |
403 | ||
5f5475ac | 404 | (defcustom message-cross-post-note "Crosspost & Followup-To: " |
23f87bed | 405 | "Note to insert before signature to notify of cross-post and follow-up." |
bf247b6e | 406 | :version "22.1" |
23f87bed MB |
407 | :type 'string |
408 | :group 'message-various) | |
409 | ||
5f5475ac | 410 | (defcustom message-followup-to-note "Followup-To: " |
23f87bed | 411 | "Note to insert before signature to notify of follow-up only." |
bf247b6e | 412 | :version "22.1" |
23f87bed MB |
413 | :type 'string |
414 | :group 'message-various) | |
415 | ||
5f5475ac | 416 | (defcustom message-cross-post-note-function 'message-cross-post-insert-note |
23f87bed MB |
417 | "Function to use to insert note about Crosspost or Followup-To. |
418 | The function will be called with four arguments. The function should not only | |
419 | insert a note, but also ensure old notes are deleted. See the documentation | |
420 | for `message-cross-post-insert-note'." | |
bf247b6e | 421 | :version "22.1" |
23f87bed MB |
422 | :type 'function |
423 | :group 'message-various) | |
424 | ||
425 | ;;; End of variables adopted from `message-utils.el'. | |
426 | ||
9b3ebcb6 MB |
427 | (defcustom message-signature-separator "^-- $" |
428 | "Regexp matching the signature separator. | |
429 | This variable is used to strip off the signature from quoted text | |
430 | when `message-cite-function' is | |
431 | `message-cite-original-without-signature'. Most useful values | |
432 | are \"^-- $\" (strict) and \"^-- *$\" (loose; allow missing | |
433 | whitespace)." | |
434 | :type '(choice (const :tag "strict" "^-- $") | |
435 | (const :tag "loose" "^-- *$") | |
436 | regexp) | |
d55fe5bb | 437 | :version "22.3" ;; Gnus 5.10.12 (changed default) |
23f87bed | 438 | :link '(custom-manual "(message)Various Message Variables") |
eec82323 LMI |
439 | :group 'message-various) |
440 | ||
f129a4df | 441 | (defcustom message-elide-ellipsis "\n[...]\n\n" |
a123622d G |
442 | "*The string which is inserted for elided text. |
443 | This is a format-spec string, and you can use %l to say how many | |
444 | lines were removed, and %c to say how many characters were | |
445 | removed." | |
6748645f | 446 | :type 'string |
23f87bed | 447 | :link '(custom-manual "(message)Various Commands") |
6748645f | 448 | :group 'message-various) |
eec82323 | 449 | |
937e60c8 | 450 | (defcustom message-interactive mail-interactive |
eec82323 | 451 | "Non-nil means when sending a message wait for and display errors. |
2b8f62e9 | 452 | A value of nil means let mailer mail back a message to report errors." |
937e60c8 | 453 | :version "23.2" |
eec82323 LMI |
454 | :group 'message-sending |
455 | :group 'message-mail | |
23f87bed | 456 | :link '(custom-manual "(message)Sending Variables") |
eec82323 LMI |
457 | :type 'boolean) |
458 | ||
16dd13df | 459 | (defcustom message-confirm-send nil |
eef5ade7 | 460 | "When non-nil, ask for confirmation when sending a message." |
16dd13df MB |
461 | :group 'message-sending |
462 | :group 'message-mail | |
4520e527 | 463 | :version "23.1" ;; No Gnus |
16dd13df MB |
464 | :link '(custom-manual "(message)Sending Variables") |
465 | :type 'boolean) | |
466 | ||
1d297c49 | 467 | (defcustom message-generate-new-buffers 'unsent |
815b81c8 MB |
468 | "*Say whether to create a new message buffer to compose a message. |
469 | Valid values include: | |
470 | ||
471 | nil | |
472 | Generate the buffer name in the Message way (e.g., *mail*, *news*, | |
473 | *mail to whom*, *news on group*, etc.) and continue editing in the | |
474 | existing buffer of that name. If there is no such buffer, it will | |
475 | be newly created. | |
476 | ||
477 | `unique' or t | |
478 | Create the new buffer with the name generated in the Message way. | |
479 | ||
480 | `unsent' | |
481 | Similar to `unique' but the buffer name begins with \"*unsent \". | |
482 | ||
483 | `standard' | |
484 | Similar to nil but the buffer name is simpler like *mail message*. | |
485 | ||
486 | function | |
487 | If this is a function, call that function with three parameters: | |
488 | The type, the To address and the group name (any of these may be nil). | |
489 | The function should return the new buffer name." | |
1d297c49 | 490 | :version "24.1" |
eec82323 | 491 | :group 'message-buffers |
23f87bed | 492 | :link '(custom-manual "(message)Message Buffers") |
815b81c8 MB |
493 | :type '(choice (const nil) |
494 | (sexp :tag "unique" :format "unique\n" :value unique | |
495 | :match (lambda (widget value) (memq value '(unique t)))) | |
496 | (const unsent) | |
497 | (const standard) | |
498 | (function :format "\n %{%t%}: %v"))) | |
eec82323 LMI |
499 | |
500 | (defcustom message-kill-buffer-on-exit nil | |
501 | "*Non-nil means that the message buffer will be killed after sending a message." | |
502 | :group 'message-buffers | |
23f87bed | 503 | :link '(custom-manual "(message)Message Buffers") |
eec82323 LMI |
504 | :type 'boolean) |
505 | ||
01c52d31 MB |
506 | (defcustom message-kill-buffer-query t |
507 | "*Non-nil means that killing a modified message buffer has to be confirmed. | |
508 | This is used by `message-kill-buffer'." | |
330f707b | 509 | :version "23.1" ;; No Gnus |
01c52d31 MB |
510 | :group 'message-buffers |
511 | :type 'boolean) | |
512 | ||
eec82323 | 513 | (defcustom message-user-organization |
ed797193 G |
514 | (or (getenv "ORGANIZATION") t) |
515 | "String to be used as an Organization header. | |
eec82323 LMI |
516 | If t, use `message-user-organization-file'." |
517 | :group 'message-headers | |
518 | :type '(choice string | |
519 | (const :tag "consult file" t))) | |
520 | ||
01c52d31 MB |
521 | (defcustom message-user-organization-file |
522 | (let (orgfile) | |
523 | (dolist (f (list "/etc/organization" | |
524 | "/etc/news/organization" | |
525 | "/usr/lib/news/organization")) | |
526 | (when (file-readable-p f) | |
527 | (setq orgfile f))) | |
528 | orgfile) | |
eec82323 | 529 | "*Local news organization file." |
a931698a | 530 | :type '(choice (const nil) file) |
23f87bed | 531 | :link '(custom-manual "(message)News Headers") |
eec82323 LMI |
532 | :group 'message-headers) |
533 | ||
6748645f | 534 | (defcustom message-make-forward-subject-function |
23f87bed | 535 | #'message-forward-subject-name-subject |
7d829636 | 536 | "*List of functions called to generate subject headers for forwarded messages. |
6748645f LMI |
537 | The subject generated by the previous function is passed into each |
538 | successive function. | |
539 | ||
540 | The provided functions are: | |
541 | ||
23f87bed MB |
542 | * `message-forward-subject-author-subject' Source of article (author or |
543 | newsgroup), in brackets followed by the subject | |
544 | * `message-forward-subject-name-subject' Source of article (name of author | |
545 | or newsgroup), in brackets followed by the subject | |
546 | * `message-forward-subject-fwd' Subject of article with 'Fwd:' prepended | |
6748645f | 547 | to it." |
16409b0b | 548 | :group 'message-forwarding |
23f87bed | 549 | :link '(custom-manual "(message)Forwarding") |
16409b0b | 550 | :type '(radio (function-item message-forward-subject-author-subject) |
7d829636 | 551 | (function-item message-forward-subject-fwd) |
23f87bed | 552 | (function-item message-forward-subject-name-subject) |
7d829636 | 553 | (repeat :tag "List of functions" function))) |
16409b0b GM |
554 | |
555 | (defcustom message-forward-as-mime t | |
23f87bed MB |
556 | "*Non-nil means forward messages as an inline/rfc822 MIME section. |
557 | Otherwise, directly inline the old message in the forwarded message." | |
2d447df6 | 558 | :version "21.1" |
16409b0b | 559 | :group 'message-forwarding |
23f87bed | 560 | :link '(custom-manual "(message)Forwarding") |
16409b0b GM |
561 | :type 'boolean) |
562 | ||
23f87bed MB |
563 | (defcustom message-forward-show-mml 'best |
564 | "*Non-nil means show forwarded messages as MML (decoded from MIME). | |
565 | Otherwise, forwarded messages are unchanged. | |
566 | Can also be the symbol `best' to indicate that MML should be | |
567 | used, except when it is a bad idea to use MML. One example where | |
568 | it is a bad idea is when forwarding a signed or encrypted | |
569 | message, because converting MIME to MML would invalidate the | |
570 | digital signature." | |
88818fbe | 571 | :version "21.1" |
16409b0b | 572 | :group 'message-forwarding |
23f87bed MB |
573 | :type '(choice (const :tag "use MML" t) |
574 | (const :tag "don't use MML " nil) | |
575 | (const :tag "use MML when appropriate" best))) | |
16409b0b GM |
576 | |
577 | (defcustom message-forward-before-signature t | |
23f87bed | 578 | "*Non-nil means put forwarded message before signature, else after." |
16409b0b GM |
579 | :group 'message-forwarding |
580 | :type 'boolean) | |
6748645f LMI |
581 | |
582 | (defcustom message-wash-forwarded-subjects nil | |
23f87bed MB |
583 | "*Non-nil means try to remove as much cruft as possible from the subject. |
584 | Done before generating the new subject of a forward." | |
6748645f | 585 | :group 'message-forwarding |
23f87bed | 586 | :link '(custom-manual "(message)Forwarding") |
6748645f LMI |
587 | :type 'boolean) |
588 | ||
dd58a686 SM |
589 | (defcustom message-ignored-resent-headers |
590 | ;; `Delivered-To' needs to be removed because some mailers use it to | |
591 | ;; detect loops, so if you resend a message to an address that ultimately | |
592 | ;; comes back to you (e.g. a mailing-list to which you subscribe, in which | |
593 | ;; case you may be removed from the list on the grounds that mail to you | |
594 | ;; bounced with a "mailing loop" error). | |
a71e2379 G |
595 | "^Return-receipt\\|^X-Gnus\\|^Gnus-Warning:\\|^>?From \\|^Delivered-To:\ |
596 | \\|^X-Content-Length:\\|^X-UIDL:" | |
eec82323 | 597 | "*All headers that match this regexp will be deleted when resending a message." |
a71e2379 | 598 | :version "24.4" |
eec82323 | 599 | :group 'message-interface |
23f87bed | 600 | :link '(custom-manual "(message)Resending") |
8903a9c8 MB |
601 | :type '(repeat :value-to-internal (lambda (widget value) |
602 | (custom-split-regexp-maybe value)) | |
603 | :match (lambda (widget value) | |
604 | (or (stringp value) | |
605 | (widget-editable-list-match widget value))) | |
606 | regexp)) | |
eec82323 | 607 | |
16409b0b GM |
608 | (defcustom message-forward-ignored-headers "^Content-Transfer-Encoding:\\|^X-Gnus" |
609 | "*All headers that match this regexp will be deleted when forwarding a message." | |
2d447df6 | 610 | :version "21.1" |
16409b0b | 611 | :group 'message-forwarding |
8903a9c8 MB |
612 | :type '(repeat :value-to-internal (lambda (widget value) |
613 | (custom-split-regexp-maybe value)) | |
614 | :match (lambda (widget value) | |
615 | (or (stringp value) | |
616 | (widget-editable-list-match widget value))) | |
16409b0b GM |
617 | regexp)) |
618 | ||
eec82323 LMI |
619 | (defcustom message-ignored-cited-headers "." |
620 | "*Delete these headers from the messages you yank." | |
621 | :group 'message-insertion | |
23f87bed MB |
622 | :link '(custom-manual "(message)Insertion Variables") |
623 | :type 'regexp) | |
624 | ||
e0a185ae | 625 | (defcustom message-cite-prefix-regexp mail-citation-prefix-regexp |
23f87bed | 626 | "*Regexp matching the longest possible citation prefix on a line." |
2696d88f | 627 | :version "24.1" |
23f87bed MB |
628 | :group 'message-insertion |
629 | :link '(custom-manual "(message)Insertion Variables") | |
01c52d31 MB |
630 | :type 'regexp |
631 | :set (lambda (symbol value) | |
632 | (prog1 | |
633 | (custom-set-default symbol value) | |
634 | (if (boundp 'gnus-message-cite-prefix-regexp) | |
635 | (setq gnus-message-cite-prefix-regexp | |
636 | (concat "^\\(?:" value "\\)")))))) | |
eec82323 | 637 | |
f129a4df | 638 | (defcustom message-cancel-message "I am canceling my own article.\n" |
eec82323 LMI |
639 | "Message to be inserted in the cancel message." |
640 | :group 'message-interface | |
23f87bed | 641 | :link '(custom-manual "(message)Canceling News") |
eec82323 LMI |
642 | :type 'string) |
643 | ||
2ea6167b RS |
644 | (defun message-send-mail-function () |
645 | "Return suitable value for the variable `message-send-mail-function'." | |
b62a39c4 RS |
646 | (cond ((and (require 'sendmail) |
647 | (boundp 'sendmail-program) | |
648 | sendmail-program | |
9e0a8576 | 649 | (executable-find sendmail-program)) |
2ea6167b | 650 | 'message-send-mail-with-sendmail) |
9e0a8576 | 651 | ((and (locate-library "smtpmail") |
aa8f8277 | 652 | (boundp 'smtpmail-default-smtp-server) |
9e0a8576 RS |
653 | smtpmail-default-smtp-server) |
654 | 'message-smtpmail-send-it) | |
655 | ((locate-library "mailclient") | |
2ea6167b RS |
656 | 'message-send-mail-with-mailclient) |
657 | (t | |
aa8f8277 | 658 | (error "Don't know how to send mail. Please customize `message-send-mail-function'")))) |
2ea6167b | 659 | |
cc26d239 | 660 | (defun message-default-send-mail-function () |
ac1af635 GM |
661 | (cond ((eq send-mail-function 'smtpmail-send-it) 'message-smtpmail-send-it) |
662 | ((eq send-mail-function 'feedmail-send-it) 'feedmail-send-it) | |
3451795c | 663 | ((eq send-mail-function 'sendmail-query-once) 'sendmail-query-once) |
ac1af635 GM |
664 | ((eq send-mail-function 'mailclient-send-it) |
665 | 'message-send-mail-with-mailclient) | |
cc26d239 LI |
666 | (t (message-send-mail-function)))) |
667 | ||
668 | ;; Useful to set in site-init.el | |
669 | (defcustom message-send-mail-function (message-default-send-mail-function) | |
eec82323 LMI |
670 | "Function to call to send the current buffer as mail. |
671 | The headers should be delimited by a line whose contents match the | |
672 | variable `mail-header-separator'. | |
673 | ||
2ea6167b | 674 | Valid values include `message-send-mail-with-sendmail' |
7d829636 | 675 | `message-send-mail-with-mh', `message-send-mail-with-qmail', |
2ea6167b RS |
676 | `message-smtpmail-send-it', `smtpmail-send-it', |
677 | `feedmail-send-it' and `message-send-mail-with-mailclient'. The | |
8f7abae3 MB |
678 | default is system dependent and determined by the function |
679 | `message-send-mail-function'. | |
7d829636 DL |
680 | |
681 | See also `send-mail-function'." | |
eec82323 LMI |
682 | :type '(radio (function-item message-send-mail-with-sendmail) |
683 | (function-item message-send-mail-with-mh) | |
684 | (function-item message-send-mail-with-qmail) | |
23f87bed | 685 | (function-item message-smtpmail-send-it) |
6748645f | 686 | (function-item smtpmail-send-it) |
7d829636 | 687 | (function-item feedmail-send-it) |
2ea6167b RS |
688 | (function-item message-send-mail-with-mailclient |
689 | :tag "Use Mailclient package") | |
690 | (function :tag "Other")) | |
eec82323 | 691 | :group 'message-sending |
ac1af635 | 692 | :version "23.2" |
2ea6167b | 693 | :initialize 'custom-initialize-default |
23f87bed | 694 | :link '(custom-manual "(message)Mail Variables") |
eec82323 LMI |
695 | :group 'message-mail) |
696 | ||
697 | (defcustom message-send-news-function 'message-send-news | |
698 | "Function to call to send the current buffer as news. | |
699 | The headers should be delimited by a line whose contents match the | |
700 | variable `mail-header-separator'." | |
701 | :group 'message-sending | |
702 | :group 'message-news | |
23f87bed | 703 | :link '(custom-manual "(message)News Variables") |
eec82323 LMI |
704 | :type 'function) |
705 | ||
706 | (defcustom message-reply-to-function nil | |
7d829636 | 707 | "If non-nil, function that should return a list of headers. |
eec82323 LMI |
708 | This function should pick out addresses from the To, Cc, and From headers |
709 | and respond with new To and Cc headers." | |
710 | :group 'message-interface | |
23f87bed | 711 | :link '(custom-manual "(message)Reply") |
7d829636 | 712 | :type '(choice function (const nil))) |
eec82323 LMI |
713 | |
714 | (defcustom message-wide-reply-to-function nil | |
7d829636 | 715 | "If non-nil, function that should return a list of headers. |
eec82323 LMI |
716 | This function should pick out addresses from the To, Cc, and From headers |
717 | and respond with new To and Cc headers." | |
718 | :group 'message-interface | |
23f87bed | 719 | :link '(custom-manual "(message)Wide Reply") |
7d829636 | 720 | :type '(choice function (const nil))) |
eec82323 LMI |
721 | |
722 | (defcustom message-followup-to-function nil | |
7d829636 | 723 | "If non-nil, function that should return a list of headers. |
eec82323 LMI |
724 | This function should pick out addresses from the To, Cc, and From headers |
725 | and respond with new To and Cc headers." | |
726 | :group 'message-interface | |
23f87bed | 727 | :link '(custom-manual "(message)Followup") |
7d829636 | 728 | :type '(choice function (const nil))) |
eec82323 | 729 | |
01c52d31 MB |
730 | (defcustom message-extra-wide-headers nil |
731 | "If non-nil, a list of additional address headers. | |
732 | These are used when composing a wide reply." | |
733 | :group 'message-sending | |
734 | :type '(repeat string)) | |
735 | ||
eec82323 LMI |
736 | (defcustom message-use-followup-to 'ask |
737 | "*Specifies what to do with Followup-To header. | |
738 | If nil, always ignore the header. If it is t, use its value, but | |
739 | query before using the \"poster\" value. If it is the symbol `ask', | |
740 | always query the user whether to use the value. If it is the symbol | |
741 | `use', always use the value." | |
742 | :group 'message-interface | |
23f87bed | 743 | :link '(custom-manual "(message)Followup") |
eec82323 | 744 | :type '(choice (const :tag "ignore" nil) |
23f87bed | 745 | (const :tag "use & query" t) |
eec82323 LMI |
746 | (const use) |
747 | (const ask))) | |
748 | ||
23f87bed MB |
749 | (defcustom message-use-mail-followup-to 'use |
750 | "*Specifies what to do with Mail-Followup-To header. | |
751 | If nil, always ignore the header. If it is the symbol `ask', always | |
752 | query the user whether to use the value. If it is the symbol `use', | |
753 | always use the value." | |
bf247b6e | 754 | :version "22.1" |
23f87bed MB |
755 | :group 'message-interface |
756 | :link '(custom-manual "(message)Mailing Lists") | |
757 | :type '(choice (const :tag "ignore" nil) | |
758 | (const use) | |
759 | (const ask))) | |
760 | ||
761 | (defcustom message-subscribed-address-functions nil | |
762 | "*Specifies functions for determining list subscription. | |
763 | If nil, do not attempt to determine list subscription with functions. | |
764 | If non-nil, this variable contains a list of functions which return | |
765 | regular expressions to match lists. These functions can be used in | |
766 | conjunction with `message-subscribed-regexps' and | |
767 | `message-subscribed-addresses'." | |
bf247b6e | 768 | :version "22.1" |
23f87bed MB |
769 | :group 'message-interface |
770 | :link '(custom-manual "(message)Mailing Lists") | |
771 | :type '(repeat sexp)) | |
772 | ||
773 | (defcustom message-subscribed-address-file nil | |
774 | "*A file containing addresses the user is subscribed to. | |
775 | If nil, do not look at any files to determine list subscriptions. If | |
776 | non-nil, each line of this file should be a mailing list address." | |
bf247b6e | 777 | :version "22.1" |
23f87bed MB |
778 | :group 'message-interface |
779 | :link '(custom-manual "(message)Mailing Lists") | |
ad136a7c | 780 | :type '(radio file (const nil))) |
23f87bed MB |
781 | |
782 | (defcustom message-subscribed-addresses nil | |
783 | "*Specifies a list of addresses the user is subscribed to. | |
784 | If nil, do not use any predefined list subscriptions. This list of | |
785 | addresses can be used in conjunction with | |
786 | `message-subscribed-address-functions' and `message-subscribed-regexps'." | |
bf247b6e | 787 | :version "22.1" |
23f87bed MB |
788 | :group 'message-interface |
789 | :link '(custom-manual "(message)Mailing Lists") | |
790 | :type '(repeat string)) | |
791 | ||
792 | (defcustom message-subscribed-regexps nil | |
793 | "*Specifies a list of addresses the user is subscribed to. | |
794 | If nil, do not use any predefined list subscriptions. This list of | |
795 | regular expressions can be used in conjunction with | |
796 | `message-subscribed-address-functions' and `message-subscribed-addresses'." | |
bf247b6e | 797 | :version "22.1" |
23f87bed MB |
798 | :group 'message-interface |
799 | :link '(custom-manual "(message)Mailing Lists") | |
800 | :type '(repeat regexp)) | |
801 | ||
802 | (defcustom message-allow-no-recipients 'ask | |
803 | "Specifies what to do when there are no recipients other than Gcc/Fcc. | |
804 | If it is the symbol `always', the posting is allowed. If it is the | |
805 | symbol `never', the posting is not allowed. If it is the symbol | |
806 | `ask', you are prompted." | |
bf247b6e | 807 | :version "22.1" |
23f87bed MB |
808 | :group 'message-interface |
809 | :link '(custom-manual "(message)Message Headers") | |
810 | :type '(choice (const always) | |
811 | (const never) | |
812 | (const ask))) | |
813 | ||
eec82323 | 814 | (defcustom message-sendmail-f-is-evil nil |
7d829636 | 815 | "*Non-nil means don't add \"-f username\" to the sendmail command line. |
16409b0b | 816 | Doing so would be even more evil than leaving it out." |
eec82323 | 817 | :group 'message-sending |
23f87bed | 818 | :link '(custom-manual "(message)Mail Variables") |
eec82323 LMI |
819 | :type 'boolean) |
820 | ||
4332f3ec | 821 | (defcustom message-sendmail-envelope-from |
e0a185ae | 822 | ;; `mail-envelope-from' is unavailable unless sendmail.el is loaded. |
937e60c8 | 823 | (if (boundp 'mail-envelope-from) mail-envelope-from) |
23f87bed MB |
824 | "*Envelope-from when sending mail with sendmail. |
825 | If this is nil, use `user-mail-address'. If it is the symbol | |
826 | `header', use the From: header of the message." | |
937e60c8 | 827 | :version "23.2" |
23f87bed MB |
828 | :type '(choice (string :tag "From name") |
829 | (const :tag "Use From: header from message" header) | |
830 | (const :tag "Use `user-mail-address'" nil)) | |
831 | :link '(custom-manual "(message)Mail Variables") | |
832 | :group 'message-sending) | |
833 | ||
01c52d31 MB |
834 | (defcustom message-sendmail-extra-arguments nil |
835 | "Additional arguments to `sendmail-program'." | |
836 | ;; E.g. '("-a" "account") for msmtp | |
330f707b | 837 | :version "23.1" ;; No Gnus |
01c52d31 MB |
838 | :type '(repeat string) |
839 | ;; :link '(custom-manual "(message)Mail Variables") | |
840 | :group 'message-sending) | |
841 | ||
eec82323 LMI |
842 | ;; qmail-related stuff |
843 | (defcustom message-qmail-inject-program "/var/qmail/bin/qmail-inject" | |
844 | "Location of the qmail-inject program." | |
845 | :group 'message-sending | |
23f87bed | 846 | :link '(custom-manual "(message)Mail Variables") |
eec82323 LMI |
847 | :type 'file) |
848 | ||
849 | (defcustom message-qmail-inject-args nil | |
850 | "Arguments passed to qmail-inject programs. | |
2b8f62e9 JB |
851 | This should be a list of strings, one string for each argument. |
852 | It may also be a function. | |
eec82323 LMI |
853 | |
854 | For e.g., if you wish to set the envelope sender address so that bounces | |
855 | go to the right place or to deal with listserv's usage of that address, you | |
856 | might set this variable to '(\"-f\" \"you@some.where\")." | |
857 | :group 'message-sending | |
23f87bed MB |
858 | :link '(custom-manual "(message)Mail Variables") |
859 | :type '(choice (function) | |
860 | (repeat string))) | |
eec82323 | 861 | |
9efa445f DN |
862 | (defvar gnus-post-method) |
863 | (defvar gnus-select-method) | |
eec82323 LMI |
864 | (defcustom message-post-method |
865 | (cond ((and (boundp 'gnus-post-method) | |
6748645f | 866 | (listp gnus-post-method) |
eec82323 LMI |
867 | gnus-post-method) |
868 | gnus-post-method) | |
869 | ((boundp 'gnus-select-method) | |
870 | gnus-select-method) | |
871 | (t '(nnspool ""))) | |
6748645f LMI |
872 | "*Method used to post news. |
873 | Note that when posting from inside Gnus, for instance, this | |
874 | variable isn't used." | |
eec82323 LMI |
875 | :group 'message-news |
876 | :group 'message-sending | |
877 | ;; This should be the `gnus-select-method' widget, but that might | |
878 | ;; create a dependence to `gnus.el'. | |
879 | :type 'sexp) | |
880 | ||
0d1c2cc8 | 881 | (defcustom message-generate-headers-first nil |
23f87bed | 882 | "Which headers should be generated before starting to compose a message. |
f5d01350 | 883 | If t, generate all required headers. This can also be a list of headers to |
23f87bed MB |
884 | generate. The variables `message-required-news-headers' and |
885 | `message-required-mail-headers' specify which headers to generate. | |
886 | ||
887 | Note that the variable `message-deletable-headers' specifies headers which | |
888 | are to be deleted and then re-generated before sending, so this variable | |
889 | will not have a visible effect for those headers." | |
eec82323 | 890 | :group 'message-headers |
23f87bed MB |
891 | :link '(custom-manual "(message)Message Headers") |
892 | :type '(choice (const :tag "None" nil) | |
01c52d31 MB |
893 | (const :tag "All" t) |
894 | (repeat (sexp :tag "Header")))) | |
895 | ||
896 | (defcustom message-fill-column 72 | |
897 | "Column beyond which automatic line-wrapping should happen. | |
898 | Local value for message buffers. If non-nil, also turn on | |
899 | auto-fill in message buffers." | |
900 | :group 'message-various | |
901 | ;; :link '(custom-manual "(message)Message Headers") | |
902 | :type '(choice (const :tag "Don't turn on auto fill" nil) | |
903 | (integer))) | |
eec82323 | 904 | |
4e14547f CY |
905 | (defcustom message-setup-hook nil |
906 | "Normal hook, run each time a new outgoing message is initialized. | |
907 | The function `message-setup' runs this hook." | |
908 | :group 'message-various | |
909 | :link '(custom-manual "(message)Various Message Variables") | |
910 | :type 'hook) | |
eec82323 | 911 | |
16409b0b | 912 | (defcustom message-cancel-hook nil |
c80e3b4a | 913 | "Hook run when canceling articles." |
16409b0b | 914 | :group 'message-various |
23f87bed | 915 | :link '(custom-manual "(message)Various Message Variables") |
16409b0b GM |
916 | :type 'hook) |
917 | ||
eec82323 LMI |
918 | (defcustom message-signature-setup-hook nil |
919 | "Normal hook, run each time a new outgoing message is initialized. | |
920 | It is run after the headers have been inserted and before | |
921 | the signature is inserted." | |
922 | :group 'message-various | |
23f87bed | 923 | :link '(custom-manual "(message)Various Message Variables") |
eec82323 LMI |
924 | :type 'hook) |
925 | ||
926 | (defcustom message-mode-hook nil | |
927 | "Hook run in message mode buffers." | |
928 | :group 'message-various | |
929 | :type 'hook) | |
930 | ||
931 | (defcustom message-header-hook nil | |
932 | "Hook run in a message mode buffer narrowed to the headers." | |
933 | :group 'message-various | |
934 | :type 'hook) | |
935 | ||
936 | (defcustom message-header-setup-hook nil | |
6748645f | 937 | "Hook called narrowed to the headers when setting up a message buffer." |
eec82323 | 938 | :group 'message-various |
23f87bed | 939 | :link '(custom-manual "(message)Various Message Variables") |
eec82323 LMI |
940 | :type 'hook) |
941 | ||
23f87bed MB |
942 | (defcustom message-minibuffer-local-map |
943 | (let ((map (make-sparse-keymap 'message-minibuffer-local-map))) | |
944 | (set-keymap-parent map minibuffer-local-map) | |
945 | map) | |
e2642250 | 946 | "Keymap for `message-read-from-minibuffer'." |
9c5a5c77 GM |
947 | ;; FIXME improve type. |
948 | :type '(restricted-sexp :match-alternatives (symbolp keymapp)) | |
d0859c9a MB |
949 | :version "22.1" |
950 | :group 'message-various) | |
23f87bed | 951 | |
eec82323 | 952 | (defcustom message-citation-line-function 'message-insert-citation-line |
23f87bed MB |
953 | "*Function called to insert the \"Whomever writes:\" line. |
954 | ||
01c52d31 | 955 | Predefined functions include `message-insert-citation-line' and |
4d8a28ec | 956 | `message-insert-formatted-citation-line' (see the variable |
01c52d31 MB |
957 | `message-citation-line-format'). |
958 | ||
23f87bed MB |
959 | Note that Gnus provides a feature where the reader can click on |
960 | `writes:' to hide the cited text. If you change this line too much, | |
961 | people who read your message will have to change their Gnus | |
962 | configuration. See the variable `gnus-cite-attribution-suffix'." | |
01c52d31 MB |
963 | :type '(choice |
964 | (function-item :tag "plain" message-insert-citation-line) | |
4d8a28ec | 965 | (function-item :tag "formatted" message-insert-formatted-citation-line) |
01c52d31 | 966 | (function :tag "Other")) |
23f87bed | 967 | :link '(custom-manual "(message)Insertion Variables") |
eec82323 LMI |
968 | :group 'message-insertion) |
969 | ||
01c52d31 MB |
970 | (defcustom message-citation-line-format "On %a, %b %d %Y, %N wrote:\n" |
971 | "Format of the \"Whomever writes:\" line. | |
972 | ||
973 | The string is formatted using `format-spec'. The following | |
974 | constructs are replaced: | |
975 | ||
976 | %f The full From, e.g. \"John Doe <john.doe@example.invalid>\". | |
977 | %n The mail address, e.g. \"john.doe@example.invalid\". | |
978 | %N The real name if present, e.g.: \"John Doe\", else fall | |
979 | back to the mail address. | |
980 | %F The first name if present, e.g.: \"John\". | |
981 | %L The last name if present, e.g.: \"Doe\". | |
982 | ||
983 | All other format specifiers are passed to `format-time-string' | |
984 | which is called using the date from the article your replying to. | |
985 | Extracting the first (%F) and last name (%L) is done | |
986 | heuristically, so you should always check it yourself. | |
987 | ||
988 | Please also read the note in the documentation of | |
989 | `message-citation-line-function'." | |
990 | :type '(choice (const :tag "Plain" "%f writes:") | |
991 | (const :tag "Include date" "On %a, %b %d %Y, %n wrote:") | |
992 | string) | |
993 | :link '(custom-manual "(message)Insertion Variables") | |
330f707b | 994 | :version "23.1" ;; No Gnus |
01c52d31 MB |
995 | :group 'message-insertion) |
996 | ||
e0a185ae | 997 | (defcustom message-yank-prefix mail-yank-prefix |
23f87bed MB |
998 | "*Prefix inserted on the lines of yanked messages. |
999 | Fix `message-cite-prefix-regexp' if it is set to an abnormal value. | |
01c52d31 | 1000 | See also `message-yank-cited-prefix' and `message-yank-empty-prefix'." |
937e60c8 | 1001 | :version "23.2" |
23f87bed MB |
1002 | :type 'string |
1003 | :link '(custom-manual "(message)Insertion Variables") | |
1004 | :group 'message-insertion) | |
1005 | ||
1006 | (defcustom message-yank-cited-prefix ">" | |
01c52d31 | 1007 | "*Prefix inserted on cited lines of yanked messages. |
23f87bed | 1008 | Fix `message-cite-prefix-regexp' if it is set to an abnormal value. |
01c52d31 MB |
1009 | See also `message-yank-prefix' and `message-yank-empty-prefix'." |
1010 | :version "22.1" | |
1011 | :type 'string | |
1012 | :link '(custom-manual "(message)Insertion Variables") | |
1013 | :group 'message-insertion) | |
1014 | ||
1015 | (defcustom message-yank-empty-prefix ">" | |
1016 | "*Prefix inserted on empty lines of yanked messages. | |
1017 | See also `message-yank-prefix' and `message-yank-cited-prefix'." | |
bf247b6e | 1018 | :version "22.1" |
eec82323 | 1019 | :type 'string |
23f87bed | 1020 | :link '(custom-manual "(message)Insertion Variables") |
eec82323 LMI |
1021 | :group 'message-insertion) |
1022 | ||
e0a185ae | 1023 | (defcustom message-indentation-spaces mail-indentation-spaces |
eec82323 LMI |
1024 | "*Number of spaces to insert at the beginning of each cited line. |
1025 | Used by `message-yank-original' via `message-yank-cite'." | |
937e60c8 | 1026 | :version "23.2" |
eec82323 | 1027 | :group 'message-insertion |
23f87bed | 1028 | :link '(custom-manual "(message)Insertion Variables") |
eec82323 LMI |
1029 | :type 'integer) |
1030 | ||
9b3ebcb6 | 1031 | (defcustom message-cite-function 'message-cite-original-without-signature |
4a55f847 RS |
1032 | "*Function for citing an original message. |
1033 | Predefined functions include `message-cite-original' and | |
1034 | `message-cite-original-without-signature'. | |
01c52d31 | 1035 | Note that these functions use `mail-citation-hook' if that is non-nil." |
eec82323 | 1036 | :type '(radio (function-item message-cite-original) |
16409b0b | 1037 | (function-item message-cite-original-without-signature) |
eec82323 LMI |
1038 | (function-item sc-cite-original) |
1039 | (function :tag "Other")) | |
23f87bed | 1040 | :link '(custom-manual "(message)Insertion Variables") |
d55fe5bb | 1041 | :version "22.3" ;; Gnus 5.10.12 (changed default) |
eec82323 LMI |
1042 | :group 'message-insertion) |
1043 | ||
eec82323 LMI |
1044 | (defcustom message-indent-citation-function 'message-indent-citation |
1045 | "*Function for modifying a citation just inserted in the mail buffer. | |
1046 | This can also be a list of functions. Each function can find the | |
1047 | citation between (point) and (mark t). And each function should leave | |
1048 | point and mark around the citation text as modified." | |
1049 | :type 'function | |
23f87bed | 1050 | :link '(custom-manual "(message)Insertion Variables") |
eec82323 LMI |
1051 | :group 'message-insertion) |
1052 | ||
937e60c8 | 1053 | (defcustom message-signature mail-signature |
eec82323 LMI |
1054 | "*String to be inserted at the end of the message buffer. |
1055 | If t, the `message-signature-file' file will be inserted instead. | |
1056 | If a function, the result from the function will be used instead. | |
1057 | If a form, the result from the form will be used instead." | |
937e60c8 | 1058 | :version "23.2" |
eec82323 | 1059 | :type 'sexp |
23f87bed | 1060 | :link '(custom-manual "(message)Insertion Variables") |
eec82323 LMI |
1061 | :group 'message-insertion) |
1062 | ||
e0a185ae | 1063 | (defcustom message-signature-file mail-signature-file |
5449c317 DL |
1064 | "*Name of file containing the text inserted at end of message buffer. |
1065 | Ignored if the named file doesn't exist. | |
01c52d31 MB |
1066 | If nil, don't insert a signature. |
1067 | If a path is specified, the value of `message-signature-directory' is ignored, | |
1068 | even if set." | |
937e60c8 | 1069 | :version "23.2" |
5449c317 | 1070 | :type '(choice file (const :tags "None" nil)) |
23f87bed MB |
1071 | :link '(custom-manual "(message)Insertion Variables") |
1072 | :group 'message-insertion) | |
1073 | ||
01c52d31 MB |
1074 | (defcustom message-signature-directory nil |
1075 | "*Name of directory containing signature files. | |
1076 | Comes in handy if you have many such files, handled via posting styles for | |
1077 | instance. | |
1078 | If nil, `message-signature-file' is expected to specify the directory if | |
1079 | needed." | |
1080 | :type '(choice string (const :tags "None" nil)) | |
1081 | :link '(custom-manual "(message)Insertion Variables") | |
1082 | :group 'message-insertion) | |
1083 | ||
23f87bed MB |
1084 | (defcustom message-signature-insert-empty-line t |
1085 | "*If non-nil, insert an empty line before the signature separator." | |
bf247b6e | 1086 | :version "22.1" |
23f87bed MB |
1087 | :type 'boolean |
1088 | :link '(custom-manual "(message)Insertion Variables") | |
eec82323 LMI |
1089 | :group 'message-insertion) |
1090 | ||
706b5974 G |
1091 | (defcustom message-cite-reply-position 'traditional |
1092 | "*Where the reply should be positioned. | |
1093 | If `traditional', reply inline. | |
1094 | If `above', reply above quoted text. | |
1095 | If `below', reply below quoted text. | |
1096 | ||
1097 | Note: Many newsgroups frown upon nontraditional reply styles. You | |
1098 | probably want to set this variable only for specific groups, | |
1099 | e.g. using `gnus-posting-styles': | |
1100 | ||
b5244046 | 1101 | (eval (set (make-local-variable 'message-cite-reply-position) 'above))" |
2bed3f04 | 1102 | :version "24.1" |
a931698a GM |
1103 | :type '(choice (const :tag "Reply inline" traditional) |
1104 | (const :tag "Reply above" above) | |
1105 | (const :tag "Reply below" below)) | |
706b5974 G |
1106 | :group 'message-insertion) |
1107 | ||
1108 | (defcustom message-cite-style nil | |
1109 | "*The overall style to be used when yanking cited text. | |
1110 | Value is either `nil' (no variable overrides) or a let-style list | |
1111 | of pairs (VARIABLE VALUE) that will be bound in | |
1112 | `message-yank-original' to do the quoting. | |
1113 | ||
1114 | Presets to impersonate popular mail agents are found in the | |
1115 | message-cite-style-* variables. This variable is intended for | |
1116 | use in `gnus-posting-styles', such as: | |
1117 | ||
1118 | ((posting-from-work-p) (eval (set (make-local-variable 'message-cite-style) message-cite-style-outlook)))" | |
1119 | :version "24.1" | |
1120 | :group 'message-insertion | |
1121 | :type '(choice (const :tag "Do not override variables" :value nil) | |
1122 | (const :tag "MS Outlook" :value message-cite-style-outlook) | |
1123 | (const :tag "Mozilla Thunderbird" :value message-cite-style-thunderbird) | |
1124 | (const :tag "Gmail" :value message-cite-style-gmail) | |
1125 | (variable :tag "User-specified"))) | |
1126 | ||
1127 | (defconst message-cite-style-outlook | |
1128 | '((message-cite-function 'message-cite-original) | |
1129 | (message-citation-line-function 'message-insert-formatted-citation-line) | |
1130 | (message-cite-reply-position 'above) | |
1131 | (message-yank-prefix "") | |
1132 | (message-yank-cited-prefix "") | |
1133 | (message-yank-empty-prefix "") | |
1134 | (message-citation-line-format "\n\n-----------------------\nOn %a, %b %d %Y, %N wrote:\n")) | |
1135 | "Message citation style used by MS Outlook. Use with message-cite-style.") | |
1136 | ||
1137 | (defconst message-cite-style-thunderbird | |
1138 | '((message-cite-function 'message-cite-original) | |
1139 | (message-citation-line-function 'message-insert-formatted-citation-line) | |
1140 | (message-cite-reply-position 'above) | |
1141 | (message-yank-prefix "> ") | |
1142 | (message-yank-cited-prefix ">") | |
1143 | (message-yank-empty-prefix ">") | |
1144 | (message-citation-line-format "On %D %R %p, %N wrote:")) | |
1145 | "Message citation style used by Mozilla Thunderbird. Use with message-cite-style.") | |
1146 | ||
1147 | (defconst message-cite-style-gmail | |
1148 | '((message-cite-function 'message-cite-original) | |
1149 | (message-citation-line-function 'message-insert-formatted-citation-line) | |
1150 | (message-cite-reply-position 'above) | |
1151 | (message-yank-prefix " ") | |
1152 | (message-yank-cited-prefix " ") | |
1153 | (message-yank-empty-prefix " ") | |
1154 | (message-citation-line-format "On %e %B %Y %R, %f wrote:\n")) | |
1155 | "Message citation style used by Gmail. Use with message-cite-style.") | |
1156 | ||
eec82323 LMI |
1157 | (defcustom message-distribution-function nil |
1158 | "*Function called to return a Distribution header." | |
1159 | :group 'message-news | |
1160 | :group 'message-headers | |
23f87bed | 1161 | :link '(custom-manual "(message)News Headers") |
7d829636 | 1162 | :type '(choice function (const nil))) |
eec82323 LMI |
1163 | |
1164 | (defcustom message-expires 14 | |
1165 | "Number of days before your article expires." | |
1166 | :group 'message-news | |
1167 | :group 'message-headers | |
1168 | :link '(custom-manual "(message)News Headers") | |
1169 | :type 'integer) | |
1170 | ||
1171 | (defcustom message-user-path nil | |
1172 | "If nil, use the NNTP server name in the Path header. | |
1173 | If stringp, use this; if non-nil, use no host name (user name only)." | |
1174 | :group 'message-news | |
1175 | :group 'message-headers | |
1176 | :link '(custom-manual "(message)News Headers") | |
1177 | :type '(choice (const :tag "nntp" nil) | |
1178 | (string :tag "name") | |
1179 | (sexp :tag "none" :format "%t" t))) | |
1180 | ||
b1ea3797 CY |
1181 | ;; This can be the name of a buffer, or a cons cell (FUNCTION . ARGS) |
1182 | ;; for yanking the original buffer. | |
eec82323 | 1183 | (defvar message-reply-buffer nil) |
23f87bed MB |
1184 | (defvar message-reply-headers nil |
1185 | "The headers of the current replied article. | |
1186 | It is a vector of the following headers: | |
1187 | \[number subject from date id references chars lines xref extra].") | |
eec82323 LMI |
1188 | (defvar message-newsreader nil) |
1189 | (defvar message-mailer nil) | |
1190 | (defvar message-sent-message-via nil) | |
1191 | (defvar message-checksum nil) | |
1192 | (defvar message-send-actions nil | |
1193 | "A list of actions to be performed upon successful sending of a message.") | |
25ca2e61 | 1194 | (defvar message-return-action nil |
9173deec | 1195 | "Action to return to the caller after sending or postponing a message.") |
eec82323 LMI |
1196 | (defvar message-exit-actions nil |
1197 | "A list of actions to be performed upon exiting after sending a message.") | |
1198 | (defvar message-kill-actions nil | |
1199 | "A list of actions to be performed before killing a message buffer.") | |
1200 | (defvar message-postpone-actions nil | |
1201 | "A list of actions to be performed after postponing a message.") | |
1202 | ||
6748645f LMI |
1203 | (define-widget 'message-header-lines 'text |
1204 | "All header lines must be LFD terminated." | |
3536d0c1 | 1205 | :format "%{%t%}:%n%v" |
6748645f LMI |
1206 | :valid-regexp "^\\'" |
1207 | :error "All header lines must be newline terminated") | |
1208 | ||
481134fd | 1209 | (defcustom message-default-headers "" |
b5c575e6 G |
1210 | "Header lines to be inserted in outgoing messages. |
1211 | This can be set to a string containing or a function returning | |
1212 | header lines to be inserted before you edit the message, so you | |
1213 | can edit or delete these lines. If set to a function, it is | |
1214 | called and its result is inserted." | |
937e60c8 | 1215 | :version "23.2" |
eec82323 | 1216 | :group 'message-headers |
23f87bed | 1217 | :link '(custom-manual "(message)Message Headers") |
b5c575e6 G |
1218 | :type '(choice |
1219 | (message-header-lines :tag "String") | |
1220 | (function :tag "Function"))) | |
eec82323 | 1221 | |
dc4d6273 GM |
1222 | (defcustom message-default-mail-headers |
1223 | ;; Ease the transition from mail-mode to message-mode. See bugs#4431, 5555. | |
1224 | (concat (if (and (boundp 'mail-default-reply-to) | |
1225 | (stringp mail-default-reply-to)) | |
481134fd | 1226 | (format "Reply-to: %s\n" mail-default-reply-to)) |
dc4d6273 GM |
1227 | (if (and (boundp 'mail-self-blind) |
1228 | mail-self-blind) | |
481134fd | 1229 | (format "BCC: %s\n" user-mail-address)) |
dc4d6273 GM |
1230 | (if (and (boundp 'mail-archive-file-name) |
1231 | (stringp mail-archive-file-name)) | |
481134fd CY |
1232 | (format "FCC: %s\n" mail-archive-file-name)) |
1233 | ;; Use the value of `mail-default-headers' if available. | |
5b5dafd2 KY |
1234 | ;; Note: as for XEmacs 21.4 and 21.5, it is unavailable |
1235 | ;; unless sendmail.el is loaded. | |
481134fd CY |
1236 | (if (boundp 'mail-default-headers) |
1237 | mail-default-headers)) | |
eec82323 | 1238 | "*A string of header lines to be inserted in outgoing mails." |
dc4d6273 | 1239 | :version "23.2" |
eec82323 LMI |
1240 | :group 'message-headers |
1241 | :group 'message-mail | |
23f87bed | 1242 | :link '(custom-manual "(message)Mail Headers") |
6748645f | 1243 | :type 'message-header-lines) |
eec82323 LMI |
1244 | |
1245 | (defcustom message-default-news-headers "" | |
16409b0b | 1246 | "*A string of header lines to be inserted in outgoing news articles." |
eec82323 LMI |
1247 | :group 'message-headers |
1248 | :group 'message-news | |
23f87bed | 1249 | :link '(custom-manual "(message)News Headers") |
6748645f | 1250 | :type 'message-header-lines) |
eec82323 LMI |
1251 | |
1252 | ;; Note: could use /usr/ucb/mail instead of sendmail; | |
1253 | ;; options -t, and -v if not interactive. | |
1254 | (defcustom message-mailer-swallows-blank-line | |
1255 | (if (and (string-match "sparc-sun-sunos\\(\\'\\|[^5]\\)" | |
1256 | system-configuration) | |
1257 | (file-readable-p "/etc/sendmail.cf") | |
8dabbfd6 SM |
1258 | (with-temp-buffer |
1259 | (insert-file-contents "/etc/sendmail.cf") | |
1260 | (goto-char (point-min)) | |
1261 | (let ((case-fold-search nil)) | |
1262 | (re-search-forward "^OR\\>" nil t)))) | |
eec82323 LMI |
1263 | ;; According to RFC822, "The field-name must be composed of printable |
1264 | ;; ASCII characters (i. e., characters that have decimal values between | |
1265 | ;; 33 and 126, except colon)", i. e., any chars except ctl chars, | |
1266 | ;; space, or colon. | |
1267 | '(looking-at "[ \t]\\|[][!\"#$%&'()*+,-./0-9;<=>?@A-Z\\\\^_`a-z{|}~]+:")) | |
6748645f | 1268 | "*Set this non-nil if the system's mailer runs the header and body together. |
eec82323 LMI |
1269 | \(This problem exists on Sunos 4 when sendmail is run in remote mode.) |
1270 | The value should be an expression to test whether the problem will | |
1271 | actually occur." | |
1272 | :group 'message-sending | |
23f87bed | 1273 | :link '(custom-manual "(message)Mail Variables") |
eec82323 LMI |
1274 | :type 'sexp) |
1275 | ||
961a48db | 1276 | ;;;###autoload |
16409b0b GM |
1277 | (define-mail-user-agent 'message-user-agent |
1278 | 'message-mail 'message-send-and-exit | |
1279 | 'message-kill-buffer 'message-send-hook) | |
eec82323 LMI |
1280 | |
1281 | (defvar message-mh-deletable-headers '(Message-ID Date Lines Sender) | |
1282 | "If non-nil, delete the deletable headers before feeding to mh.") | |
1283 | ||
a8151ef7 LMI |
1284 | (defvar message-send-method-alist |
1285 | '((news message-news-p message-send-via-news) | |
1286 | (mail message-mail-p message-send-via-mail)) | |
1287 | "Alist of ways to send outgoing messages. | |
1288 | Each element has the form | |
1289 | ||
1290 | \(TYPE PREDICATE FUNCTION) | |
1291 | ||
1292 | where TYPE is a symbol that names the method; PREDICATE is a function | |
1293 | called without any parameters to determine whether the message is | |
1294 | a message of type TYPE; and FUNCTION is a function to be called if | |
1295 | PREDICATE returns non-nil. FUNCTION is called with one parameter -- | |
1296 | the prefix.") | |
1297 | ||
c541eb9a | 1298 | (defcustom message-mail-alias-type 'abbrev |
a8151ef7 | 1299 | "*What alias expansion type to use in Message buffers. |
01c52d31 MB |
1300 | The default is `abbrev', which uses mailabbrev. `ecomplete' uses |
1301 | an electric completion mode. nil switches mail aliases off. | |
1302 | This can also be a list of values." | |
c541eb9a DL |
1303 | :group 'message |
1304 | :link '(custom-manual "(message)Mail Aliases") | |
1305 | :type '(choice (const :tag "Use Mailabbrev" abbrev) | |
01c52d31 | 1306 | (const :tag "Use ecomplete" ecomplete) |
c541eb9a | 1307 | (const :tag "No expansion" nil))) |
a8151ef7 | 1308 | |
01c52d31 MB |
1309 | (defcustom message-self-insert-commands '(self-insert-command) |
1310 | "List of `self-insert-command's used to trigger ecomplete. | |
1311 | When one of those commands is invoked to enter a character in To or Cc | |
1312 | header, ecomplete will suggest the candidates of recipients (see also | |
1313 | `message-mail-alias-type'). If you use some tool to enter non-ASCII | |
1314 | text and it replaces `self-insert-command' with the other command, e.g. | |
1315 | `egg-self-insert-command', you may want to add it to this list." | |
1316 | :group 'message-various | |
1317 | :type '(repeat function)) | |
1318 | ||
6748645f | 1319 | (defcustom message-auto-save-directory |
4dcb0d7a | 1320 | (if (file-writable-p message-directory) |
64a465b2 LMI |
1321 | (file-name-as-directory (expand-file-name "drafts" message-directory)) |
1322 | "~/") | |
6748645f LMI |
1323 | "*Directory where Message auto-saves buffers if Gnus isn't running. |
1324 | If nil, Message won't auto-save." | |
1325 | :group 'message-buffers | |
23f87bed | 1326 | :link '(custom-manual "(message)Various Message Variables") |
7d829636 | 1327 | :type '(choice directory (const :tag "Don't auto-save" nil))) |
6748645f | 1328 | |
7d829636 | 1329 | (defcustom message-default-charset |
83595c0c DL |
1330 | (and (not (mm-multibyte-p)) 'iso-8859-1) |
1331 | "Default charset used in non-MULE Emacsen. | |
1332 | If nil, you might be asked to input the charset." | |
2d447df6 | 1333 | :version "21.1" |
16409b0b | 1334 | :group 'message |
23f87bed | 1335 | :link '(custom-manual "(message)Various Message Variables") |
16409b0b GM |
1336 | :type 'symbol) |
1337 | ||
7d829636 | 1338 | (defcustom message-dont-reply-to-names |
0fb40182 | 1339 | (and (boundp 'mail-dont-reply-to-names) mail-dont-reply-to-names) |
01c52d31 | 1340 | "*Addresses to prune when doing wide replies. |
82816ba1 | 1341 | This can be a regexp or a list of regexps. Also, a value of nil means |
01c52d31 | 1342 | exclude your own user name only." |
2a1e2476 | 1343 | :version "24.3" |
16409b0b | 1344 | :group 'message |
23f87bed | 1345 | :link '(custom-manual "(message)Wide Reply") |
16409b0b | 1346 | :type '(choice (const :tag "Yourself" nil) |
01c52d31 MB |
1347 | regexp |
1348 | (repeat :tag "Regexp List" regexp))) | |
1349 | ||
1350 | (defsubst message-dont-reply-to-names () | |
1351 | (gmm-regexp-concat message-dont-reply-to-names)) | |
16409b0b | 1352 | |
23f87bed MB |
1353 | (defvar message-shoot-gnksa-feet nil |
1354 | "*A list of GNKSA feet you are allowed to shoot. | |
1355 | Gnus gives you all the opportunity you could possibly want for | |
1356 | shooting yourself in the foot. Also, Gnus allows you to shoot the | |
1357 | feet of Good Net-Keeping Seal of Approval. The following are foot | |
1358 | candidates: | |
1359 | `empty-article' Allow you to post an empty article; | |
1360 | `quoted-text-only' Allow you to post quoted text only; | |
1361 | `multiple-copies' Allow you to post multiple copies; | |
1362 | `cancel-messages' Allow you to cancel or supersede messages from | |
4dcb0d7a LMI |
1363 | your other email addresses; |
1364 | `canlock-verify' Allow you to cancel messages without verifying canlock.") | |
23f87bed MB |
1365 | |
1366 | (defsubst message-gnksa-enable-p (feature) | |
1367 | (or (not (listp message-shoot-gnksa-feet)) | |
1368 | (memq feature message-shoot-gnksa-feet))) | |
1369 | ||
01c52d31 MB |
1370 | (defcustom message-hidden-headers '("^References:" "^Face:" "^X-Face:" |
1371 | "^X-Draft-From:") | |
23f87bed MB |
1372 | "Regexp of headers to be hidden when composing new messages. |
1373 | This can also be a list of regexps to match headers. Or a list | |
1374 | starting with `not' and followed by regexps." | |
bf247b6e | 1375 | :version "22.1" |
23f87bed MB |
1376 | :group 'message |
1377 | :link '(custom-manual "(message)Message Headers") | |
01c52d31 MB |
1378 | :type '(choice |
1379 | :format "%{%t%}: %[Value Type%] %v" | |
1380 | (regexp :menu-tag "regexp" :format "regexp\n%t: %v") | |
1381 | (repeat :menu-tag "(regexp ...)" :format "(regexp ...)\n%v%i" | |
1382 | (regexp :format "%t: %v")) | |
1383 | (cons :menu-tag "(not regexp ...)" :format "(not regexp ...)\n%v" | |
1384 | (const not) | |
1385 | (repeat :format "%v%i" | |
1386 | (regexp :format "%t: %v"))))) | |
1387 | ||
1388 | (defcustom message-cite-articles-with-x-no-archive t | |
1389 | "If non-nil, cite text from articles that has X-No-Archive set." | |
1390 | :group 'message | |
1391 | :type 'boolean) | |
23f87bed | 1392 | |
eec82323 LMI |
1393 | ;;; Internal variables. |
1394 | ;;; Well, not really internal. | |
1395 | ||
1396 | (defvar message-mode-syntax-table | |
1397 | (let ((table (copy-syntax-table text-mode-syntax-table))) | |
1398 | (modify-syntax-entry ?% ". " table) | |
16409b0b GM |
1399 | (modify-syntax-entry ?> ". " table) |
1400 | (modify-syntax-entry ?< ". " table) | |
eec82323 LMI |
1401 | table) |
1402 | "Syntax table used while in Message mode.") | |
1403 | ||
0f49874b | 1404 | (defface message-header-to |
eec82323 LMI |
1405 | '((((class color) |
1406 | (background dark)) | |
01c52d31 | 1407 | (:foreground "DarkOliveGreen1" :bold t)) |
eec82323 LMI |
1408 | (((class color) |
1409 | (background light)) | |
23f87bed | 1410 | (:foreground "MidnightBlue" :bold t)) |
eec82323 | 1411 | (t |
23f87bed | 1412 | (:bold t :italic t))) |
eec82323 LMI |
1413 | "Face used for displaying From headers." |
1414 | :group 'message-faces) | |
0f49874b MB |
1415 | ;; backward-compatibility alias |
1416 | (put 'message-header-to-face 'face-alias 'message-header-to) | |
3d493bef | 1417 | (put 'message-header-to-face 'obsolete-face "22.1") |
eec82323 | 1418 | |
0f49874b | 1419 | (defface message-header-cc |
eec82323 LMI |
1420 | '((((class color) |
1421 | (background dark)) | |
01c52d31 | 1422 | (:foreground "chartreuse1" :bold t)) |
eec82323 LMI |
1423 | (((class color) |
1424 | (background light)) | |
1425 | (:foreground "MidnightBlue")) | |
1426 | (t | |
23f87bed | 1427 | (:bold t))) |
eec82323 LMI |
1428 | "Face used for displaying Cc headers." |
1429 | :group 'message-faces) | |
0f49874b MB |
1430 | ;; backward-compatibility alias |
1431 | (put 'message-header-cc-face 'face-alias 'message-header-cc) | |
3d493bef | 1432 | (put 'message-header-cc-face 'obsolete-face "22.1") |
eec82323 | 1433 | |
0f49874b | 1434 | (defface message-header-subject |
eec82323 LMI |
1435 | '((((class color) |
1436 | (background dark)) | |
01c52d31 | 1437 | (:foreground "OliveDrab1")) |
eec82323 LMI |
1438 | (((class color) |
1439 | (background light)) | |
23f87bed | 1440 | (:foreground "navy blue" :bold t)) |
eec82323 | 1441 | (t |
23f87bed | 1442 | (:bold t))) |
eec82323 LMI |
1443 | "Face used for displaying subject headers." |
1444 | :group 'message-faces) | |
0f49874b MB |
1445 | ;; backward-compatibility alias |
1446 | (put 'message-header-subject-face 'face-alias 'message-header-subject) | |
3d493bef | 1447 | (put 'message-header-subject-face 'obsolete-face "22.1") |
eec82323 | 1448 | |
0f49874b | 1449 | (defface message-header-newsgroups |
eec82323 LMI |
1450 | '((((class color) |
1451 | (background dark)) | |
23f87bed | 1452 | (:foreground "yellow" :bold t :italic t)) |
eec82323 LMI |
1453 | (((class color) |
1454 | (background light)) | |
23f87bed | 1455 | (:foreground "blue4" :bold t :italic t)) |
eec82323 | 1456 | (t |
23f87bed | 1457 | (:bold t :italic t))) |
eec82323 LMI |
1458 | "Face used for displaying newsgroups headers." |
1459 | :group 'message-faces) | |
0f49874b MB |
1460 | ;; backward-compatibility alias |
1461 | (put 'message-header-newsgroups-face 'face-alias 'message-header-newsgroups) | |
3d493bef | 1462 | (put 'message-header-newsgroups-face 'obsolete-face "22.1") |
eec82323 | 1463 | |
0f49874b | 1464 | (defface message-header-other |
eec82323 LMI |
1465 | '((((class color) |
1466 | (background dark)) | |
01c52d31 | 1467 | (:foreground "VioletRed1")) |
eec82323 LMI |
1468 | (((class color) |
1469 | (background light)) | |
1470 | (:foreground "steel blue")) | |
1471 | (t | |
23f87bed | 1472 | (:bold t :italic t))) |
eec82323 LMI |
1473 | "Face used for displaying newsgroups headers." |
1474 | :group 'message-faces) | |
0f49874b MB |
1475 | ;; backward-compatibility alias |
1476 | (put 'message-header-other-face 'face-alias 'message-header-other) | |
3d493bef | 1477 | (put 'message-header-other-face 'obsolete-face "22.1") |
eec82323 | 1478 | |
0f49874b | 1479 | (defface message-header-name |
eec82323 LMI |
1480 | '((((class color) |
1481 | (background dark)) | |
01c52d31 | 1482 | (:foreground "green")) |
eec82323 LMI |
1483 | (((class color) |
1484 | (background light)) | |
1485 | (:foreground "cornflower blue")) | |
1486 | (t | |
23f87bed | 1487 | (:bold t))) |
eec82323 LMI |
1488 | "Face used for displaying header names." |
1489 | :group 'message-faces) | |
0f49874b MB |
1490 | ;; backward-compatibility alias |
1491 | (put 'message-header-name-face 'face-alias 'message-header-name) | |
3d493bef | 1492 | (put 'message-header-name-face 'obsolete-face "22.1") |
eec82323 | 1493 | |
0f49874b | 1494 | (defface message-header-xheader |
eec82323 LMI |
1495 | '((((class color) |
1496 | (background dark)) | |
01c52d31 | 1497 | (:foreground "DeepSkyBlue1")) |
eec82323 LMI |
1498 | (((class color) |
1499 | (background light)) | |
1500 | (:foreground "blue")) | |
1501 | (t | |
23f87bed | 1502 | (:bold t))) |
eec82323 LMI |
1503 | "Face used for displaying X-Header headers." |
1504 | :group 'message-faces) | |
0f49874b MB |
1505 | ;; backward-compatibility alias |
1506 | (put 'message-header-xheader-face 'face-alias 'message-header-xheader) | |
3d493bef | 1507 | (put 'message-header-xheader-face 'obsolete-face "22.1") |
eec82323 | 1508 | |
0f49874b | 1509 | (defface message-separator |
eec82323 LMI |
1510 | '((((class color) |
1511 | (background dark)) | |
01c52d31 | 1512 | (:foreground "LightSkyBlue1")) |
eec82323 LMI |
1513 | (((class color) |
1514 | (background light)) | |
1515 | (:foreground "brown")) | |
1516 | (t | |
23f87bed | 1517 | (:bold t))) |
eec82323 LMI |
1518 | "Face used for displaying the separator." |
1519 | :group 'message-faces) | |
0f49874b MB |
1520 | ;; backward-compatibility alias |
1521 | (put 'message-separator-face 'face-alias 'message-separator) | |
3d493bef | 1522 | (put 'message-separator-face 'obsolete-face "22.1") |
eec82323 | 1523 | |
0f49874b | 1524 | (defface message-cited-text |
eec82323 LMI |
1525 | '((((class color) |
1526 | (background dark)) | |
01c52d31 | 1527 | (:foreground "LightPink1")) |
eec82323 LMI |
1528 | (((class color) |
1529 | (background light)) | |
1530 | (:foreground "red")) | |
1531 | (t | |
23f87bed | 1532 | (:bold t))) |
eec82323 LMI |
1533 | "Face used for displaying cited text names." |
1534 | :group 'message-faces) | |
0f49874b MB |
1535 | ;; backward-compatibility alias |
1536 | (put 'message-cited-text-face 'face-alias 'message-cited-text) | |
3d493bef | 1537 | (put 'message-cited-text-face 'obsolete-face "22.1") |
eec82323 | 1538 | |
0f49874b | 1539 | (defface message-mml |
16409b0b GM |
1540 | '((((class color) |
1541 | (background dark)) | |
01c52d31 | 1542 | (:foreground "MediumSpringGreen")) |
16409b0b GM |
1543 | (((class color) |
1544 | (background light)) | |
1545 | (:foreground "ForestGreen")) | |
1546 | (t | |
23f87bed | 1547 | (:bold t))) |
16409b0b GM |
1548 | "Face used for displaying MML." |
1549 | :group 'message-faces) | |
0f49874b MB |
1550 | ;; backward-compatibility alias |
1551 | (put 'message-mml-face 'face-alias 'message-mml) | |
3d493bef | 1552 | (put 'message-mml-face 'obsolete-face "22.1") |
16409b0b | 1553 | |
23f87bed MB |
1554 | (defun message-font-lock-make-header-matcher (regexp) |
1555 | (let ((form | |
1556 | `(lambda (limit) | |
1557 | (let ((start (point))) | |
1558 | (save-restriction | |
1559 | (widen) | |
1560 | (goto-char (point-min)) | |
1561 | (if (re-search-forward | |
1562 | (concat "^" (regexp-quote mail-header-separator) "$") | |
1563 | nil t) | |
1564 | (setq limit (min limit (match-beginning 0)))) | |
1565 | (goto-char start)) | |
1566 | (and (< start limit) | |
1567 | (re-search-forward ,regexp limit t)))))) | |
1568 | (if (featurep 'bytecomp) | |
1569 | (byte-compile form) | |
1570 | form))) | |
1571 | ||
eec82323 | 1572 | (defvar message-font-lock-keywords |
23f87bed MB |
1573 | (let ((content "[ \t]*\\(.+\\(\n[ \t].*\\)*\\)\n?")) |
1574 | `((,(message-font-lock-make-header-matcher | |
1575 | (concat "^\\([Tt]o:\\)" content)) | |
0f49874b MB |
1576 | (1 'message-header-name) |
1577 | (2 'message-header-to nil t)) | |
23f87bed MB |
1578 | (,(message-font-lock-make-header-matcher |
1579 | (concat "^\\(^[GBF]?[Cc][Cc]:\\|^[Rr]eply-[Tt]o:\\)" content)) | |
0f49874b MB |
1580 | (1 'message-header-name) |
1581 | (2 'message-header-cc nil t)) | |
23f87bed MB |
1582 | (,(message-font-lock-make-header-matcher |
1583 | (concat "^\\([Ss]ubject:\\)" content)) | |
0f49874b MB |
1584 | (1 'message-header-name) |
1585 | (2 'message-header-subject nil t)) | |
23f87bed MB |
1586 | (,(message-font-lock-make-header-matcher |
1587 | (concat "^\\([Nn]ewsgroups:\\|Followup-[Tt]o:\\)" content)) | |
0f49874b MB |
1588 | (1 'message-header-name) |
1589 | (2 'message-header-newsgroups nil t)) | |
23f87bed | 1590 | (,(message-font-lock-make-header-matcher |
01c52d31 | 1591 | (concat "^\\(X-[A-Za-z0-9-]+:\\|In-Reply-To:\\)" content)) |
0f49874b | 1592 | (1 'message-header-name) |
01c52d31 | 1593 | (2 'message-header-xheader)) |
23f87bed | 1594 | (,(message-font-lock-make-header-matcher |
01c52d31 | 1595 | (concat "^\\([A-Z][^: \n\t]+:\\)" content)) |
0f49874b | 1596 | (1 'message-header-name) |
01c52d31 | 1597 | (2 'message-header-other nil t)) |
6748645f LMI |
1598 | ,@(if (and mail-header-separator |
1599 | (not (equal mail-header-separator ""))) | |
1600 | `((,(concat "^\\(" (regexp-quote mail-header-separator) "\\)$") | |
0f49874b | 1601 | 1 'message-separator)) |
6748645f | 1602 | nil) |
23f87bed MB |
1603 | ((lambda (limit) |
1604 | (re-search-forward (concat "^\\(" | |
1605 | message-cite-prefix-regexp | |
1606 | "\\).*") | |
1607 | limit t)) | |
0f49874b | 1608 | (0 'message-cited-text)) |
23f87bed | 1609 | ("<#/?\\(multipart\\|part\\|external\\|mml\\|secure\\)[^>]*>" |
0f49874b | 1610 | (0 'message-mml)))) |
eec82323 LMI |
1611 | "Additional expressions to highlight in Message mode.") |
1612 | ||
23f87bed | 1613 | |
6748645f LMI |
1614 | ;; XEmacs does it like this. For Emacs, we have to set the |
1615 | ;; `font-lock-defaults' buffer-local variable. | |
1616 | (put 'message-mode 'font-lock-defaults '(message-font-lock-keywords t)) | |
1617 | ||
eec82323 | 1618 | (defvar message-face-alist |
01c52d31 | 1619 | '((bold . message-bold-region) |
eec82323 LMI |
1620 | (underline . underline-region) |
1621 | (default . (lambda (b e) | |
01c52d31 | 1622 | (message-unbold-region b e) |
eec82323 LMI |
1623 | (ununderline-region b e)))) |
1624 | "Alist of mail and news faces for facemenu. | |
8f688cb0 | 1625 | The cdr of each entry is a function for applying the face to a region.") |
eec82323 | 1626 | |
4e14547f CY |
1627 | (defcustom message-send-hook nil |
1628 | "Hook run before sending messages. | |
1629 | This hook is run quite early when sending." | |
1630 | :group 'message-various | |
1631 | :options '(ispell-message) | |
1632 | :link '(custom-manual "(message)Various Message Variables") | |
1633 | :type 'hook) | |
eec82323 LMI |
1634 | |
1635 | (defcustom message-send-mail-hook nil | |
23f87bed MB |
1636 | "Hook run before sending mail messages. |
1637 | This hook is run very late -- just before the message is sent as | |
1638 | mail." | |
eec82323 | 1639 | :group 'message-various |
23f87bed | 1640 | :link '(custom-manual "(message)Various Message Variables") |
eec82323 LMI |
1641 | :type 'hook) |
1642 | ||
1643 | (defcustom message-send-news-hook nil | |
23f87bed MB |
1644 | "Hook run before sending news messages. |
1645 | This hook is run very late -- just before the message is sent as | |
1646 | news." | |
eec82323 | 1647 | :group 'message-various |
23f87bed | 1648 | :link '(custom-manual "(message)Various Message Variables") |
eec82323 LMI |
1649 | :type 'hook) |
1650 | ||
1651 | (defcustom message-sent-hook nil | |
1652 | "Hook run after sending messages." | |
1653 | :group 'message-various | |
1654 | :type 'hook) | |
1655 | ||
6748645f LMI |
1656 | (defvar message-send-coding-system 'binary |
1657 | "Coding system to encode outgoing mail.") | |
1658 | ||
16409b0b GM |
1659 | (defvar message-draft-coding-system |
1660 | mm-auto-save-coding-system | |
23f87bed MB |
1661 | "*Coding system to compose mail. |
1662 | If you'd like to make it possible to share draft files between XEmacs | |
1663 | and Emacs, you may use `iso-2022-7bit' for this value at your own risk. | |
1664 | Note that the coding-system `iso-2022-7bit' isn't suitable to all data.") | |
16409b0b | 1665 | |
8bff7c00 | 1666 | (defcustom message-send-mail-partially-limit nil |
16409b0b | 1667 | "The limitation of messages sent as message/partial. |
7d829636 DL |
1668 | The lower bound of message size in characters, beyond which the message |
1669 | should be sent in several parts. If it is nil, the size is unlimited." | |
1a10d421 | 1670 | :version "24.1" |
16409b0b | 1671 | :group 'message-buffers |
23f87bed | 1672 | :link '(custom-manual "(message)Mail Variables") |
16409b0b GM |
1673 | :type '(choice (const :tag "unlimited" nil) |
1674 | (integer 1000000))) | |
1675 | ||
83595c0c | 1676 | (defcustom message-alternative-emails nil |
46e8fe3d MB |
1677 | "*Regexp matching alternative email addresses. |
1678 | The first address in the To, Cc or From headers of the original | |
1679 | article matching this variable is used as the From field of | |
1680 | outgoing messages. | |
1681 | ||
1682 | This variable has precedence over posting styles and anything that runs | |
1683 | off `message-setup-hook'." | |
83595c0c | 1684 | :group 'message-headers |
23f87bed | 1685 | :link '(custom-manual "(message)Message Headers") |
83595c0c DL |
1686 | :type '(choice (const :tag "Always use primary" nil) |
1687 | regexp)) | |
1688 | ||
23f87bed MB |
1689 | (defcustom message-hierarchical-addresses nil |
1690 | "A list of hierarchical mail address definitions. | |
1691 | ||
1692 | Inside each entry, the first address is the \"top\" address, and | |
1693 | subsequent addresses are subaddresses; this is used to indicate that | |
1694 | mail sent to the first address will automatically be delivered to the | |
1695 | subaddresses. So if the first address appears in the recipient list | |
1696 | for a message, the subaddresses will be removed (if present) before | |
1697 | the mail is sent. All addresses in this structure should be | |
1698 | downcased." | |
bf247b6e | 1699 | :version "22.1" |
23f87bed MB |
1700 | :group 'message-headers |
1701 | :type '(repeat (repeat string))) | |
1702 | ||
88818fbe SZ |
1703 | (defcustom message-mail-user-agent nil |
1704 | "Like `mail-user-agent'. | |
7d829636 | 1705 | Except if it is nil, use Gnus native MUA; if it is t, use |
88818fbe | 1706 | `mail-user-agent'." |
bf247b6e | 1707 | :version "22.1" |
88818fbe SZ |
1708 | :type '(radio (const :tag "Gnus native" |
1709 | :format "%t\n" | |
1710 | nil) | |
1711 | (const :tag "`mail-user-agent'" | |
1712 | :format "%t\n" | |
1713 | t) | |
1714 | (function-item :tag "Default Emacs mail" | |
1715 | :format "%t\n" | |
1716 | sendmail-user-agent) | |
1717 | (function-item :tag "Emacs interface to MH" | |
1718 | :format "%t\n" | |
1719 | mh-e-user-agent) | |
1720 | (function :tag "Other")) | |
1721 | :version "21.1" | |
1722 | :group 'message) | |
1723 | ||
23f87bed MB |
1724 | (defcustom message-wide-reply-confirm-recipients nil |
1725 | "Whether to confirm a wide reply to multiple email recipients. | |
1726 | If this variable is nil, don't ask whether to reply to all recipients. | |
1727 | If this variable is non-nil, pose the question \"Reply to all | |
1728 | recipients?\" before a wide reply to multiple recipients. If the user | |
1729 | answers yes, reply to all recipients as usual. If the user answers | |
1730 | no, only reply back to the author." | |
bf247b6e | 1731 | :version "22.1" |
23f87bed MB |
1732 | :group 'message-headers |
1733 | :link '(custom-manual "(message)Wide Reply") | |
1734 | :type 'boolean) | |
1735 | ||
1736 | (defcustom message-user-fqdn nil | |
bcc7dd61 | 1737 | "*Domain part of Message-Ids." |
bf247b6e | 1738 | :version "22.1" |
23f87bed MB |
1739 | :group 'message-headers |
1740 | :link '(custom-manual "(message)News Headers") | |
1741 | :type '(radio (const :format "%v " nil) | |
ad136a7c | 1742 | (string :format "FQDN: %v"))) |
23f87bed MB |
1743 | |
1744 | (defcustom message-use-idna (and (condition-case nil (require 'idna) | |
1745 | (file-error)) | |
1746 | (mm-coding-system-p 'utf-8) | |
1747 | (executable-find idna-program) | |
c38e0c97 | 1748 | (string= (idna-to-ascii "räksmörgås") |
a33704bb MB |
1749 | "xn--rksmrgs-5wao1o") |
1750 | t) | |
1751 | "Whether to encode non-ASCII in domain names into ASCII according to IDNA. | |
1752 | GNU Libidn, and in particular the elisp package \"idna.el\" and | |
1753 | the external program \"idn\", must be installed for this | |
1754 | functionality to work." | |
bf247b6e | 1755 | :version "22.1" |
23f87bed MB |
1756 | :group 'message-headers |
1757 | :link '(custom-manual "(message)IDNA") | |
1758 | :type '(choice (const :tag "Ask" ask) | |
1759 | (const :tag "Never" nil) | |
1760 | (const :tag "Always" t))) | |
1761 | ||
5d97d032 | 1762 | (defcustom message-generate-hashcash (if (executable-find "hashcash") 'opportunistic) |
01c52d31 | 1763 | "*Whether to generate X-Hashcash: headers. |
82816ba1 | 1764 | If t, always generate hashcash headers. If `opportunistic', |
01c52d31 MB |
1765 | only generate hashcash headers if it can be done without the user |
1766 | waiting (i.e., only asynchronously). | |
1767 | ||
1768 | You must have the \"hashcash\" binary installed, see `hashcash-path'." | |
5d97d032 | 1769 | :version "24.1" |
01c52d31 MB |
1770 | :group 'message-headers |
1771 | :link '(custom-manual "(message)Mail Headers") | |
1772 | :type '(choice (const :tag "Always" t) | |
1773 | (const :tag "Never" nil) | |
1774 | (const :tag "Opportunistic" opportunistic))) | |
1775 | ||
eec82323 LMI |
1776 | ;;; Internal variables. |
1777 | ||
83595c0c | 1778 | (defvar message-sending-message "Sending...") |
eec82323 LMI |
1779 | (defvar message-buffer-list nil) |
1780 | (defvar message-this-is-news nil) | |
1781 | (defvar message-this-is-mail nil) | |
6748645f | 1782 | (defvar message-draft-article nil) |
16409b0b GM |
1783 | (defvar message-mime-part nil) |
1784 | (defvar message-posting-charset nil) | |
23f87bed | 1785 | (defvar message-inserted-headers nil) |
6b8382e4 | 1786 | (defvar message-inhibit-ecomplete nil) |
eec82323 LMI |
1787 | |
1788 | ;; Byte-compiler warning | |
9efa445f DN |
1789 | (defvar gnus-active-hashtb) |
1790 | (defvar gnus-read-active-file) | |
eec82323 LMI |
1791 | |
1792 | ;;; Regexp matching the delimiter of messages in UNIX mail format | |
05d70628 RS |
1793 | ;;; (UNIX From lines), minus the initial ^. It should be a copy |
1794 | ;;; of rmail.el's rmail-unix-mail-delimiter. | |
eec82323 LMI |
1795 | (defvar message-unix-mail-delimiter |
1796 | (let ((time-zone-regexp | |
1797 | (concat "\\([A-Z]?[A-Z]?[A-Z][A-Z]\\( DST\\)?" | |
1798 | "\\|[-+]?[0-9][0-9][0-9][0-9]" | |
1799 | "\\|" | |
1800 | "\\) *"))) | |
1801 | (concat | |
1802 | "From " | |
1803 | ||
05d70628 RS |
1804 | ;; Many things can happen to an RFC 822 mailbox before it is put into |
1805 | ;; a `From' line. The leading phrase can be stripped, e.g. | |
1806 | ;; `Joe <@w.x:joe@y.z>' -> `<@w.x:joe@y.z>'. The <> can be stripped, e.g. | |
1807 | ;; `<@x.y:joe@y.z>' -> `@x.y:joe@y.z'. Everything starting with a CRLF | |
1808 | ;; can be removed, e.g. | |
1809 | ;; From: joe@y.z (Joe K | |
1810 | ;; User) | |
23f87bed | 1811 | ;; can yield `From joe@y.z (Joe K Fri Mar 22 08:11:15 1996', and |
05d70628 RS |
1812 | ;; From: Joe User |
1813 | ;; <joe@y.z> | |
1814 | ;; can yield `From Joe User Fri Mar 22 08:11:15 1996'. | |
1815 | ;; The mailbox can be removed or be replaced by white space, e.g. | |
1816 | ;; From: "Joe User"{space}{tab} | |
1817 | ;; <joe@y.z> | |
1818 | ;; can yield `From {space}{tab} Fri Mar 22 08:11:15 1996', | |
1819 | ;; where {space} and {tab} represent the Ascii space and tab characters. | |
1820 | ;; We want to match the results of any of these manglings. | |
1821 | ;; The following regexp rejects names whose first characters are | |
1822 | ;; obviously bogus, but after that anything goes. | |
23f87bed | 1823 | "\\([^\0-\b\n-\r\^?].*\\)?" |
eec82323 LMI |
1824 | |
1825 | ;; The time the message was sent. | |
16409b0b GM |
1826 | "\\([^\0-\r \^?]+\\) +" ; day of the week |
1827 | "\\([^\0-\r \^?]+\\) +" ; month | |
1828 | "\\([0-3]?[0-9]\\) +" ; day of month | |
1829 | "\\([0-2][0-9]:[0-5][0-9]\\(:[0-6][0-9]\\)?\\) *" ; time of day | |
eec82323 LMI |
1830 | |
1831 | ;; Perhaps a time zone, specified by an abbreviation, or by a | |
1832 | ;; numeric offset. | |
1833 | time-zone-regexp | |
1834 | ||
1835 | ;; The year. | |
05d70628 | 1836 | " \\([0-9][0-9]+\\) *" |
eec82323 LMI |
1837 | |
1838 | ;; On some systems the time zone can appear after the year, too. | |
1839 | time-zone-regexp | |
1840 | ||
1841 | ;; Old uucp cruft. | |
1842 | "\\(remote from .*\\)?" | |
1843 | ||
05d70628 | 1844 | "\n")) |
6748645f | 1845 | "Regexp matching the delimiter of messages in UNIX mail format.") |
eec82323 LMI |
1846 | |
1847 | (defvar message-unsent-separator | |
1848 | (concat "^ *---+ +Unsent message follows +---+ *$\\|" | |
1849 | "^ *---+ +Returned message +---+ *$\\|" | |
1850 | "^Start of returned message$\\|" | |
1851 | "^ *---+ +Original message +---+ *$\\|" | |
1852 | "^ *--+ +begin message +--+ *$\\|" | |
1853 | "^ *---+ +Original message follows +---+ *$\\|" | |
16409b0b | 1854 | "^ *---+ +Undelivered message follows +---+ *$\\|" |
52bec650 | 1855 | "^------ This is a copy of the message, including all the headers. ------ *$\\|" |
eec82323 LMI |
1856 | "^|? *---+ +Message text follows: +---+ *|?$") |
1857 | "A regexp that matches the separator before the text of a failed message.") | |
1858 | ||
01c52d31 MB |
1859 | (defvar message-field-fillers |
1860 | '((To message-fill-field-address) | |
1861 | (Cc message-fill-field-address) | |
1862 | (From message-fill-field-address)) | |
1863 | "Alist of header names/filler functions.") | |
1864 | ||
eec82323 | 1865 | (defvar message-header-format-alist |
01c52d31 MB |
1866 | `((From) |
1867 | (Newsgroups) | |
1868 | (To) | |
1869 | (Cc) | |
eec82323 LMI |
1870 | (Subject) |
1871 | (In-Reply-To) | |
1872 | (Fcc) | |
1873 | (Bcc) | |
1874 | (Date) | |
1875 | (Organization) | |
1876 | (Distribution) | |
1877 | (Lines) | |
1878 | (Expires) | |
1879 | (Message-ID) | |
6748645f | 1880 | (References . message-shorten-references) |
16409b0b | 1881 | (User-Agent)) |
eec82323 LMI |
1882 | "Alist used for formatting headers.") |
1883 | ||
23f87bed MB |
1884 | (defvar message-options nil |
1885 | "Some saved answers when sending message.") | |
8879add8 DU |
1886 | ;; FIXME: On XEmacs this causes problems since let-binding like: |
1887 | ;; (let ((message-options message-options)) ...) | |
1888 | ;; as in `message-send' and `mml-preview' loses to buffer-local | |
1889 | ;; variable initialization. | |
1890 | (unless (featurep 'xemacs) | |
1891 | (make-variable-buffer-local 'message-options)) | |
23f87bed MB |
1892 | |
1893 | (defvar message-send-mail-real-function nil | |
1894 | "Internal send mail function.") | |
1895 | ||
70f6144d | 1896 | (defvar message-bogus-system-names "\\`localhost\\.\\|\\.local\\'" |
23f87bed MB |
1897 | "The regexp of bogus system names.") |
1898 | ||
1899 | (defcustom message-valid-fqdn-regexp | |
1900 | (concat "[a-z0-9][-.a-z0-9]+\\." ;; [hostname.subdomain.]domain. | |
1901 | ;; valid TLDs: | |
0565caeb | 1902 | "\\([a-z][a-z]\\|" ;; two letter country TDLs |
bacede64 | 1903 | "aero\\|arpa\\|asia\\|bitnet\\|biz\\|bofh\\|" |
0565caeb MB |
1904 | "cat\\|com\\|coop\\|edu\\|gov\\|" |
1905 | "info\\|int\\|jobs\\|" | |
1906 | "mil\\|mobi\\|museum\\|name\\|net\\|" | |
bacede64 | 1907 | "org\\|pro\\|tel\\|travel\\|uucp\\)") |
0565caeb MB |
1908 | ;; http://en.wikipedia.org/wiki/List_of_Internet_top-level_domains |
1909 | ;; http://en.wikipedia.org/wiki/GTLD | |
bacede64 | 1910 | ;; `approved, but not yet in operation': .xxx |
0565caeb | 1911 | ;; "dead" nato bitnet uucp |
23f87bed MB |
1912 | "Regular expression that matches a valid FQDN." |
1913 | ;; see also: gnus-button-valid-fqdn-regexp | |
bf247b6e | 1914 | :version "22.1" |
23f87bed MB |
1915 | :group 'message-headers |
1916 | :type 'regexp) | |
1917 | ||
8abf1b22 GM |
1918 | (autoload 'gnus-alive-p "gnus-util") |
1919 | (autoload 'gnus-delay-article "gnus-delay") | |
1920 | (autoload 'gnus-extract-address-components "gnus-util") | |
1921 | (autoload 'gnus-find-method-for-group "gnus") | |
1922 | (autoload 'gnus-group-decoded-name "gnus-group") | |
1923 | (autoload 'gnus-group-name-charset "gnus-group") | |
1924 | (autoload 'gnus-group-name-decode "gnus-group") | |
1925 | (autoload 'gnus-groups-from-server "gnus") | |
1926 | (autoload 'gnus-make-local-hook "gnus-util") | |
1927 | (autoload 'gnus-open-server "gnus-int") | |
1928 | (autoload 'gnus-output-to-mail "gnus-util") | |
1929 | (autoload 'gnus-output-to-rmail "gnus-util") | |
1930 | (autoload 'gnus-request-post "gnus-int") | |
1931 | (autoload 'gnus-select-frame-set-input-focus "gnus-util") | |
1932 | (autoload 'gnus-server-string "gnus") | |
1933 | (autoload 'idna-to-ascii "idna") | |
1934 | (autoload 'message-setup-toolbar "messagexmas") | |
1935 | (autoload 'mh-new-draft-name "mh-comp") | |
1936 | (autoload 'mh-send-letter "mh-comp") | |
1937 | (autoload 'nndraft-request-associate-buffer "nndraft") | |
1938 | (autoload 'nndraft-request-expire-articles "nndraft") | |
1939 | (autoload 'nnvirtual-find-group-art "nnvirtual") | |
8abf1b22 | 1940 | (autoload 'rmail-msg-is-pruned "rmail") |
8abf1b22 | 1941 | (autoload 'rmail-output "rmailout") |
eec82323 | 1942 | |
69c3b839 JD |
1943 | ;; Emacs < 24.1 do not have mail-dont-reply-to |
1944 | (unless (fboundp 'mail-dont-reply-to) | |
1945 | (defalias 'mail-dont-reply-to 'rmail-dont-reply-to)) | |
1946 | ||
eec82323 LMI |
1947 | \f |
1948 | ||
1949 | ;;; | |
1950 | ;;; Utility functions. | |
1951 | ;;; | |
1952 | ||
1953 | (defmacro message-y-or-n-p (question show &rest text) | |
7d829636 | 1954 | "Ask QUESTION, displaying remaining args in a temporary buffer if SHOW." |
eec82323 LMI |
1955 | `(message-talkative-question 'y-or-n-p ,question ,show ,@text)) |
1956 | ||
eec82323 | 1957 | (defmacro message-delete-line (&optional n) |
7d829636 | 1958 | "Delete the current line (and the next N lines)." |
eec82323 LMI |
1959 | `(delete-region (progn (beginning-of-line) (point)) |
1960 | (progn (forward-line ,(or n 1)) (point)))) | |
1961 | ||
23f87bed MB |
1962 | (defun message-mark-active-p () |
1963 | "Non-nil means the mark and region are currently active in this buffer." | |
1964 | mark-active) | |
1965 | ||
16409b0b | 1966 | (defun message-unquote-tokens (elems) |
7d829636 | 1967 | "Remove double quotes (\") from strings in list ELEMS." |
16409b0b | 1968 | (mapcar (lambda (item) |
23f87bed MB |
1969 | (while (string-match "^\\(.*\\)\"\\(.*\\)$" item) |
1970 | (setq item (concat (match-string 1 item) | |
1971 | (match-string 2 item)))) | |
1972 | item) | |
1973 | elems)) | |
16409b0b | 1974 | |
eec82323 LMI |
1975 | (defun message-tokenize-header (header &optional separator) |
1976 | "Split HEADER into a list of header elements. | |
16409b0b GM |
1977 | SEPARATOR is a string of characters to be used as separators. \",\" |
1978 | is used by default." | |
eec82323 LMI |
1979 | (if (not header) |
1980 | nil | |
1981 | (let ((regexp (format "[%s]+" (or separator ","))) | |
eec82323 | 1982 | (first t) |
ebbeed62 | 1983 | beg quoted elems paren) |
23f87bed MB |
1984 | (with-temp-buffer |
1985 | (mm-enable-multibyte) | |
ebbeed62 | 1986 | (setq beg (point-min)) |
eec82323 LMI |
1987 | (insert header) |
1988 | (goto-char (point-min)) | |
1989 | (while (not (eobp)) | |
1990 | (if first | |
1991 | (setq first nil) | |
1992 | (forward-char 1)) | |
1993 | (cond ((and (> (point) beg) | |
1994 | (or (eobp) | |
1995 | (and (looking-at regexp) | |
1996 | (not quoted) | |
1997 | (not paren)))) | |
1998 | (push (buffer-substring beg (point)) elems) | |
1999 | (setq beg (match-end 0))) | |
16409b0b | 2000 | ((eq (char-after) ?\") |
eec82323 | 2001 | (setq quoted (not quoted))) |
16409b0b | 2002 | ((and (eq (char-after) ?\() |
eec82323 LMI |
2003 | (not quoted)) |
2004 | (setq paren t)) | |
16409b0b | 2005 | ((and (eq (char-after) ?\)) |
eec82323 LMI |
2006 | (not quoted)) |
2007 | (setq paren nil)))) | |
23f87bed | 2008 | (nreverse elems))))) |
eec82323 | 2009 | |
aa8f8277 GM |
2010 | (autoload 'nnheader-insert-file-contents "nnheader") |
2011 | ||
eec82323 LMI |
2012 | (defun message-mail-file-mbox-p (file) |
2013 | "Say whether FILE looks like a Unix mbox file." | |
2014 | (when (and (file-exists-p file) | |
2015 | (file-readable-p file) | |
2016 | (file-regular-p file)) | |
16409b0b | 2017 | (with-temp-buffer |
eec82323 LMI |
2018 | (nnheader-insert-file-contents file) |
2019 | (goto-char (point-min)) | |
2020 | (looking-at message-unix-mail-delimiter)))) | |
2021 | ||
2022 | (defun message-fetch-field (header &optional not-all) | |
23f87bed MB |
2023 | "The same as `mail-fetch-field', only remove all newlines. |
2024 | The buffer is expected to be narrowed to just the header of the message; | |
2025 | see `message-narrow-to-headers-or-head'." | |
6748645f LMI |
2026 | (let* ((inhibit-point-motion-hooks t) |
2027 | (value (mail-fetch-field header nil (not not-all)))) | |
eec82323 | 2028 | (when value |
16409b0b GM |
2029 | (while (string-match "\n[\t ]+" value) |
2030 | (setq value (replace-match " " t t value))) | |
16409b0b GM |
2031 | value))) |
2032 | ||
23f87bed MB |
2033 | (defun message-field-value (header &optional not-all) |
2034 | "The same as `message-fetch-field', only narrow to the headers first." | |
2035 | (save-excursion | |
2036 | (save-restriction | |
2037 | (message-narrow-to-headers-or-head) | |
2038 | (message-fetch-field header not-all)))) | |
2039 | ||
16409b0b GM |
2040 | (defun message-narrow-to-field () |
2041 | "Narrow the buffer to the header on the current line." | |
2042 | (beginning-of-line) | |
01c52d31 MB |
2043 | (while (looking-at "[ \t]") |
2044 | (forward-line -1)) | |
16409b0b GM |
2045 | (narrow-to-region |
2046 | (point) | |
2047 | (progn | |
2048 | (forward-line 1) | |
2049 | (if (re-search-forward "^[^ \n\t]" nil t) | |
01c52d31 | 2050 | (point-at-bol) |
16409b0b GM |
2051 | (point-max)))) |
2052 | (goto-char (point-min))) | |
eec82323 LMI |
2053 | |
2054 | (defun message-add-header (&rest headers) | |
2055 | "Add the HEADERS to the message header, skipping those already present." | |
2056 | (while headers | |
2057 | (let (hclean) | |
2058 | (unless (string-match "^\\([^:]+\\):[ \t]*[^ \t]" (car headers)) | |
2059 | (error "Invalid header `%s'" (car headers))) | |
2060 | (setq hclean (match-string 1 (car headers))) | |
16409b0b GM |
2061 | (save-restriction |
2062 | (message-narrow-to-headers) | |
2063 | (unless (re-search-forward (concat "^" (regexp-quote hclean) ":") nil t) | |
23f87bed MB |
2064 | (goto-char (point-max)) |
2065 | (if (string-match "\n$" (car headers)) | |
2066 | (insert (car headers)) | |
f129a4df | 2067 | (insert (car headers) ?\n))))) |
eec82323 LMI |
2068 | (setq headers (cdr headers)))) |
2069 | ||
23f87bed MB |
2070 | (defmacro message-with-reply-buffer (&rest forms) |
2071 | "Evaluate FORMS in the reply buffer, if it exists." | |
b1ea3797 | 2072 | `(when (and (bufferp message-reply-buffer) |
23f87bed | 2073 | (buffer-name message-reply-buffer)) |
82816ba1 | 2074 | (with-current-buffer message-reply-buffer |
23f87bed MB |
2075 | ,@forms))) |
2076 | ||
2077 | (put 'message-with-reply-buffer 'lisp-indent-function 0) | |
2078 | (put 'message-with-reply-buffer 'edebug-form-spec '(body)) | |
16409b0b | 2079 | |
eec82323 | 2080 | (defun message-fetch-reply-field (header) |
7d829636 | 2081 | "Fetch field HEADER from the message we're replying to." |
23f87bed MB |
2082 | (message-with-reply-buffer |
2083 | (save-restriction | |
2084 | (mail-narrow-to-head) | |
eec82323 LMI |
2085 | (message-fetch-field header)))) |
2086 | ||
16409b0b | 2087 | (defun message-strip-list-identifiers (subject) |
7d829636 | 2088 | "Remove list identifiers in `gnus-list-identifiers' from string SUBJECT." |
16409b0b GM |
2089 | (require 'gnus-sum) ; for gnus-list-identifiers |
2090 | (let ((regexp (if (stringp gnus-list-identifiers) | |
2091 | gnus-list-identifiers | |
2092 | (mapconcat 'identity gnus-list-identifiers " *\\|")))) | |
7d829636 | 2093 | (if (string-match (concat "\\(\\(\\(Re: +\\)?\\(" regexp |
23f87bed | 2094 | " *\\)\\)+\\(Re: +\\)?\\)") subject) |
16409b0b GM |
2095 | (concat (substring subject 0 (match-beginning 1)) |
2096 | (or (match-string 3 subject) | |
2097 | (match-string 5 subject)) | |
2098 | (substring subject | |
2099 | (match-end 1))) | |
2100 | subject))) | |
2101 | ||
eec82323 | 2102 | (defun message-strip-subject-re (subject) |
7d829636 | 2103 | "Remove \"Re:\" from subject lines in string SUBJECT." |
6748645f | 2104 | (if (string-match message-subject-re-regexp subject) |
eec82323 LMI |
2105 | (substring subject (match-end 0)) |
2106 | subject)) | |
2107 | ||
4573e0df MB |
2108 | (defcustom message-replacement-char "." |
2109 | "Replacement character used instead of unprintable or not decodable chars." | |
2110 | :group 'message-various | |
2111 | :version "22.1" ;; Gnus 5.10.9 | |
2112 | :type '(choice string | |
2113 | (const ".") | |
2114 | (const "?"))) | |
2115 | ||
2116 | ;; FIXME: We also should call `message-strip-subject-encoded-words' | |
2117 | ;; when forwarding. Probably in `message-make-forward-subject' and | |
2118 | ;; `message-forward-make-body'. | |
2119 | ||
2120 | (defun message-strip-subject-encoded-words (subject) | |
2121 | "Fix non-decodable words in SUBJECT." | |
2122 | ;; Cf. `gnus-simplify-subject-fully'. | |
2123 | (let* ((case-fold-search t) | |
2124 | (replacement-chars (format "[%s%s%s]" | |
2125 | message-replacement-char | |
2126 | message-replacement-char | |
2127 | message-replacement-char)) | |
2128 | (enc-word-re "=\\?\\([^?]+\\)\\?\\([QB]\\)\\?\\([^?]+\\)\\(\\?=\\)") | |
2129 | cs-string | |
2130 | (have-marker | |
2131 | (with-temp-buffer | |
2132 | (insert subject) | |
2133 | (goto-char (point-min)) | |
2134 | (when (re-search-forward enc-word-re nil t) | |
2135 | (setq cs-string (match-string 1))))) | |
2136 | cs-coding q-or-b word-beg word-end) | |
2137 | (if (or (not have-marker) ;; No encoded word found... | |
2138 | ;; ... or double encoding was correct: | |
2139 | (and (stringp cs-string) | |
2140 | (setq cs-string (downcase cs-string)) | |
2141 | (mm-coding-system-p (intern cs-string)) | |
2142 | (not (prog1 | |
2143 | (y-or-n-p | |
2144 | (format "\ | |
2145 | Decoded Subject \"%s\" | |
2146 | contains a valid encoded word. Decode again? " | |
2147 | subject)) | |
2148 | (setq cs-coding (intern cs-string)))))) | |
2149 | subject | |
2150 | (with-temp-buffer | |
2151 | (insert subject) | |
2152 | (goto-char (point-min)) | |
2153 | (while (re-search-forward enc-word-re nil t) | |
2154 | (setq cs-string (downcase (match-string 1)) | |
2155 | q-or-b (match-string 2) | |
2156 | word-beg (match-beginning 0) | |
2157 | word-end (match-end 0)) | |
2158 | (setq cs-coding | |
2159 | (if (mm-coding-system-p (intern cs-string)) | |
2160 | (setq cs-coding (intern cs-string)) | |
2161 | nil)) | |
2162 | ;; No double encoded subject? => bogus charset. | |
2163 | (unless cs-coding | |
2164 | (setq cs-coding | |
2165 | (mm-read-coding-system | |
2166 | (format "\ | |
2167 | Decoded Subject \"%s\" | |
2168 | contains an encoded word. The charset `%s' is unknown or invalid. | |
2169 | Hit RET to replace non-decodable characters with \"%s\" or enter replacement | |
2170 | charset: " | |
2171 | subject cs-string message-replacement-char))) | |
2172 | (if cs-coding | |
2173 | (replace-match (concat "=?" (symbol-name cs-coding) | |
2174 | "?\\2?\\3\\4\\5")) | |
2175 | (save-excursion | |
2176 | (goto-char word-beg) | |
2177 | (re-search-forward "=\\?\\([^?]+\\)\\?\\([QB]\\)\\?" word-end t) | |
2178 | (replace-match "") | |
2179 | ;; QP or base64 | |
2180 | (if (string-match "\\`Q\\'" q-or-b) | |
2181 | ;; QP | |
2182 | (progn | |
2183 | (message "Replacing non-decodable characters with \"%s\"." | |
2184 | message-replacement-char) | |
2185 | (while (re-search-forward "\\(=[A-F0-9][A-F0-9]\\)+" | |
2186 | word-end t) | |
2187 | (replace-match message-replacement-char))) | |
2188 | ;; base64 | |
2189 | (message "Replacing non-decodable characters with \"%s\"." | |
2190 | replacement-chars) | |
2191 | (re-search-forward "[^?]+" word-end t) | |
2192 | (replace-match replacement-chars)) | |
2193 | (re-search-forward "\\?=") | |
2194 | (replace-match ""))))) | |
2195 | (rfc2047-decode-region (point-min) (point-max)) | |
2196 | (buffer-string))))) | |
2197 | ||
23f87bed MB |
2198 | ;;; Start of functions adopted from `message-utils.el'. |
2199 | ||
2200 | (defun message-strip-subject-trailing-was (subject) | |
5f5475ac | 2201 | "Remove trailing \"(was: <old subject>)\" from SUBJECT lines. |
23f87bed MB |
2202 | Leading \"Re: \" is not stripped by this function. Use the function |
2203 | `message-strip-subject-re' for this." | |
2204 | (let* ((query message-subject-trailing-was-query) | |
2205 | (new) (found)) | |
2206 | (setq found | |
2207 | (string-match | |
2208 | (if (eq query 'ask) | |
2209 | message-subject-trailing-was-ask-regexp | |
2210 | message-subject-trailing-was-regexp) | |
2211 | subject)) | |
2212 | (if found | |
2213 | (setq new (substring subject 0 (match-beginning 0)))) | |
2214 | (if (or (not found) (eq query nil)) | |
2215 | subject | |
2216 | (if (eq query 'ask) | |
2217 | (if (message-y-or-n-p | |
2218 | "Strip `(was: <old subject>)' in subject? " t | |
2219 | (concat | |
2220 | "Strip `(was: <old subject>)' in subject " | |
2221 | "and use the new one instead?\n\n" | |
2222 | "Current subject is: \"" | |
2223 | subject "\"\n\n" | |
2224 | "New subject would be: \"" | |
2225 | new "\"\n\n" | |
2226 | "See the variable `message-subject-trailing-was-query' " | |
2227 | "to get rid of this query." | |
2228 | )) | |
2229 | new subject) | |
2230 | new)))) | |
2231 | ||
2232 | ;;; Suggested by Jonas Steverud @ www.dtek.chalmers.se/~d4jonas/ | |
2233 | ||
23f87bed MB |
2234 | (defun message-change-subject (new-subject) |
2235 | "Ask for NEW-SUBJECT header, append (was: <Old Subject>)." | |
23f87bed MB |
2236 | (interactive |
2237 | (list | |
2238 | (read-from-minibuffer "New subject: "))) | |
2239 | (cond ((and (not (or (null new-subject) ; new subject not empty | |
2240 | (zerop (string-width new-subject)) | |
2241 | (string-match "^[ \t]*$" new-subject)))) | |
2242 | (save-excursion | |
2243 | (let ((old-subject | |
2244 | (save-restriction | |
2245 | (message-narrow-to-headers) | |
2246 | (message-fetch-field "Subject")))) | |
2247 | (cond ((not old-subject) | |
2248 | (error "No current subject")) | |
2249 | ((not (string-match | |
2250 | (concat "^[ \t]*" | |
2251 | (regexp-quote new-subject) | |
2252 | " \t]*$") | |
2253 | old-subject)) ; yes, it really is a new subject | |
2254 | ;; delete eventual Re: prefix | |
2255 | (setq old-subject | |
2256 | (message-strip-subject-re old-subject)) | |
2257 | (message-goto-subject) | |
2258 | (message-delete-line) | |
2259 | (insert (concat "Subject: " | |
2260 | new-subject | |
2261 | " (was: " | |
f129a4df | 2262 | old-subject ")\n"))))))))) |
23f87bed | 2263 | |
01c52d31 | 2264 | (defun message-mark-inserted-region (beg end &optional verbatim) |
23f87bed | 2265 | "Mark some region in the current article with enclosing tags. |
01c52d31 MB |
2266 | See `message-mark-insert-begin' and `message-mark-insert-end'. |
2267 | If VERBATIM, use slrn style verbatim marks (\"#v+\" and \"#v-\")." | |
2268 | (interactive "r\nP") | |
23f87bed MB |
2269 | (save-excursion |
2270 | ;; add to the end of the region first, otherwise end would be invalid | |
2271 | (goto-char end) | |
01c52d31 | 2272 | (insert (if verbatim "#v-\n" message-mark-insert-end)) |
23f87bed | 2273 | (goto-char beg) |
01c52d31 | 2274 | (insert (if verbatim "#v+\n" message-mark-insert-begin)))) |
23f87bed | 2275 | |
01c52d31 | 2276 | (defun message-mark-insert-file (file &optional verbatim) |
23f87bed | 2277 | "Insert FILE at point, marking it with enclosing tags. |
01c52d31 MB |
2278 | See `message-mark-insert-begin' and `message-mark-insert-end'. |
2279 | If VERBATIM, use slrn style verbatim marks (\"#v+\" and \"#v-\")." | |
2280 | (interactive "fFile to insert: \nP") | |
23f87bed MB |
2281 | ;; reverse insertion to get correct result. |
2282 | (let ((p (point))) | |
01c52d31 | 2283 | (insert (if verbatim "#v-\n" message-mark-insert-end)) |
23f87bed MB |
2284 | (goto-char p) |
2285 | (insert-file-contents file) | |
2286 | (goto-char p) | |
01c52d31 | 2287 | (insert (if verbatim "#v+\n" message-mark-insert-begin)))) |
23f87bed | 2288 | |
23f87bed MB |
2289 | (defun message-add-archive-header () |
2290 | "Insert \"X-No-Archive: Yes\" in the header and a note in the body. | |
2291 | The note can be customized using `message-archive-note'. When called with a | |
2292 | prefix argument, ask for a text to insert. If you don't want the note in the | |
2293 | body, set `message-archive-note' to nil." | |
2294 | (interactive) | |
2295 | (if current-prefix-arg | |
2296 | (setq message-archive-note | |
2297 | (read-from-minibuffer "Reason for No-Archive: " | |
2298 | (cons message-archive-note 0)))) | |
2299 | (save-excursion | |
2300 | (if (message-goto-signature) | |
2301 | (re-search-backward message-signature-separator)) | |
2302 | (when message-archive-note | |
2303 | (insert message-archive-note) | |
2304 | (newline)) | |
2305 | (message-add-header message-archive-header) | |
2306 | (message-sort-headers))) | |
2307 | ||
23f87bed MB |
2308 | (defun message-cross-post-followup-to-header (target-group) |
2309 | "Mangles FollowUp-To and Newsgroups header to point to TARGET-GROUP. | |
2310 | With prefix-argument just set Follow-Up, don't cross-post." | |
2311 | (interactive | |
2312 | (list ; Completion based on Gnus | |
2313 | (completing-read "Followup To: " | |
2314 | (if (boundp 'gnus-newsrc-alist) | |
2315 | gnus-newsrc-alist) | |
2316 | nil nil '("poster" . 0) | |
2317 | (if (boundp 'gnus-group-history) | |
2318 | 'gnus-group-history)))) | |
2319 | (message-remove-header "Follow[Uu]p-[Tt]o" t) | |
2320 | (message-goto-newsgroups) | |
2321 | (beginning-of-line) | |
2322 | ;; if we already did a crosspost before, kill old target | |
2323 | (if (and message-cross-post-old-target | |
2324 | (re-search-forward | |
2325 | (regexp-quote (concat "," message-cross-post-old-target)) | |
2326 | nil t)) | |
2327 | (replace-match "")) | |
2328 | ;; unless (followup is to poster or user explicitly asked not | |
2329 | ;; to cross-post, or target-group is already in Newsgroups) | |
2330 | ;; add target-group to Newsgroups line. | |
2331 | (cond ((and (or | |
2332 | ;; def: cross-post, req:no | |
2333 | (and message-cross-post-default (not current-prefix-arg)) | |
2334 | ;; def: no-cross-post, req:yes | |
2335 | (and (not message-cross-post-default) current-prefix-arg)) | |
2336 | (not (string-match "poster" target-group)) | |
2337 | (not (string-match (regexp-quote target-group) | |
2338 | (message-fetch-field "Newsgroups")))) | |
2339 | (end-of-line) | |
2340 | (insert (concat "," target-group)))) | |
2341 | (end-of-line) ; ensure Followup: comes after Newsgroups: | |
2342 | ;; unless new followup would be identical to Newsgroups line | |
2343 | ;; make a new Followup-To line | |
2344 | (if (not (string-match (concat "^[ \t]*" | |
2345 | target-group | |
2346 | "[ \t]*$") | |
2347 | (message-fetch-field "Newsgroups"))) | |
f129a4df | 2348 | (insert (concat "\nFollowup-To: " target-group))) |
23f87bed MB |
2349 | (setq message-cross-post-old-target target-group)) |
2350 | ||
23f87bed MB |
2351 | (defun message-cross-post-insert-note (target-group cross-post in-old |
2352 | old-groups) | |
2353 | "Insert a in message body note about a set Followup or Crosspost. | |
2354 | If there have been previous notes, delete them. TARGET-GROUP specifies the | |
2355 | group to Followup-To. When CROSS-POST is t, insert note about | |
2356 | crossposting. IN-OLD specifies whether TARGET-GROUP is a member of | |
2357 | OLD-GROUPS. OLD-GROUPS lists the old-groups the posting would have | |
2358 | been made to before the user asked for a Crosspost." | |
2359 | ;; start scanning body for previous uses | |
2360 | (message-goto-signature) | |
2361 | (let ((head (re-search-backward | |
2362 | (concat "^" mail-header-separator) | |
2363 | nil t))) ; just search in body | |
2364 | (message-goto-signature) | |
2365 | (while (re-search-backward | |
2366 | (concat "^" (regexp-quote message-cross-post-note) ".*") | |
2367 | head t) | |
2368 | (message-delete-line)) | |
2369 | (message-goto-signature) | |
2370 | (while (re-search-backward | |
2371 | (concat "^" (regexp-quote message-followup-to-note) ".*") | |
2372 | head t) | |
2373 | (message-delete-line)) | |
2374 | ;; insert new note | |
2375 | (if (message-goto-signature) | |
2376 | (re-search-backward message-signature-separator)) | |
2377 | (if (or in-old | |
2378 | (not cross-post) | |
2379 | (string-match "^[ \t]*poster[ \t]*$" target-group)) | |
f129a4df CY |
2380 | (insert (concat message-followup-to-note target-group "\n")) |
2381 | (insert (concat message-cross-post-note target-group "\n"))))) | |
23f87bed | 2382 | |
23f87bed MB |
2383 | (defun message-cross-post-followup-to (target-group) |
2384 | "Crossposts message and set Followup-To to TARGET-GROUP. | |
2385 | With prefix-argument just set Follow-Up, don't cross-post." | |
2386 | (interactive | |
2387 | (list ; Completion based on Gnus | |
2388 | (completing-read "Followup To: " | |
2389 | (if (boundp 'gnus-newsrc-alist) | |
2390 | gnus-newsrc-alist) | |
2391 | nil nil '("poster" . 0) | |
2392 | (if (boundp 'gnus-group-history) | |
2393 | 'gnus-group-history)))) | |
2394 | (cond ((not (or (null target-group) ; new subject not empty | |
2395 | (zerop (string-width target-group)) | |
2396 | (string-match "^[ \t]*$" target-group))) | |
2397 | (save-excursion | |
2398 | (let* ((old-groups (message-fetch-field "Newsgroups")) | |
2399 | (in-old (string-match | |
2400 | (regexp-quote target-group) | |
2401 | (or old-groups "")))) | |
2402 | ;; check whether target exactly matches old Newsgroups | |
2403 | (cond ((not old-groups) | |
2404 | (error "No current newsgroup")) | |
2405 | ((or (not in-old) | |
2406 | (not (string-match | |
2407 | (concat "^[ \t]*" | |
2408 | (regexp-quote target-group) | |
2409 | "[ \t]*$") | |
2410 | old-groups))) | |
2411 | ;; yes, Newsgroups line must change | |
2412 | (message-cross-post-followup-to-header target-group) | |
2413 | ;; insert note whether we do cross-post or followup-to | |
2414 | (funcall message-cross-post-note-function | |
2415 | target-group | |
2416 | (if (or (and message-cross-post-default | |
2417 | (not current-prefix-arg)) | |
2418 | (and (not message-cross-post-default) | |
2419 | current-prefix-arg)) t) | |
2420 | in-old old-groups)))))))) | |
2421 | ||
2422 | ;;; Reduce To: to Cc: or Bcc: header | |
2423 | ||
23f87bed MB |
2424 | (defun message-reduce-to-to-cc () |
2425 | "Replace contents of To: header with contents of Cc: or Bcc: header." | |
2426 | (interactive) | |
2427 | (let ((cc-content | |
2428 | (save-restriction (message-narrow-to-headers) | |
2429 | (message-fetch-field "cc"))) | |
2430 | (bcc nil)) | |
2431 | (if (and (not cc-content) | |
2432 | (setq cc-content | |
2433 | (save-restriction | |
2434 | (message-narrow-to-headers) | |
2435 | (message-fetch-field "bcc")))) | |
2436 | (setq bcc t)) | |
2437 | (cond (cc-content | |
2438 | (save-excursion | |
2439 | (message-goto-to) | |
2440 | (message-delete-line) | |
f129a4df | 2441 | (insert (concat "To: " cc-content "\n")) |
23f87bed MB |
2442 | (save-restriction |
2443 | (message-narrow-to-headers) | |
2444 | (message-remove-header (if bcc | |
2445 | "bcc" | |
2446 | "cc")))))))) | |
2447 | ||
2448 | ;;; End of functions adopted from `message-utils.el'. | |
2449 | ||
eec82323 LMI |
2450 | (defun message-remove-header (header &optional is-regexp first reverse) |
2451 | "Remove HEADER in the narrowed buffer. | |
7d829636 | 2452 | If IS-REGEXP, HEADER is a regular expression. |
eec82323 LMI |
2453 | If FIRST, only remove the first instance of the header. |
2454 | Return the number of headers removed." | |
2455 | (goto-char (point-min)) | |
6748645f | 2456 | (let ((regexp (if is-regexp header (concat "^" (regexp-quote header) ":"))) |
eec82323 LMI |
2457 | (number 0) |
2458 | (case-fold-search t) | |
2459 | last) | |
2460 | (while (and (not (eobp)) | |
2461 | (not last)) | |
2462 | (if (if reverse | |
2463 | (not (looking-at regexp)) | |
2464 | (looking-at regexp)) | |
2465 | (progn | |
2466 | (incf number) | |
2467 | (when first | |
2468 | (setq last t)) | |
2469 | (delete-region | |
2470 | (point) | |
2471 | ;; There might be a continuation header, so we have to search | |
2472 | ;; until we find a new non-continuation line. | |
2473 | (progn | |
2474 | (forward-line 1) | |
2475 | (if (re-search-forward "^[^ \t]" nil t) | |
2476 | (goto-char (match-beginning 0)) | |
2477 | (point-max))))) | |
2478 | (forward-line 1) | |
2479 | (if (re-search-forward "^[^ \t]" nil t) | |
2480 | (goto-char (match-beginning 0)) | |
16409b0b | 2481 | (goto-char (point-max))))) |
eec82323 LMI |
2482 | number)) |
2483 | ||
16409b0b GM |
2484 | (defun message-remove-first-header (header) |
2485 | "Remove the first instance of HEADER if there is more than one." | |
2486 | (let ((count 0) | |
2487 | (regexp (concat "^" (regexp-quote header) ":"))) | |
2488 | (save-excursion | |
2489 | (goto-char (point-min)) | |
2490 | (while (re-search-forward regexp nil t) | |
2491 | (incf count))) | |
2492 | (while (> count 1) | |
2493 | (message-remove-header header nil t) | |
2494 | (decf count)))) | |
2495 | ||
eec82323 LMI |
2496 | (defun message-narrow-to-headers () |
2497 | "Narrow the buffer to the head of the message." | |
2498 | (widen) | |
2499 | (narrow-to-region | |
2500 | (goto-char (point-min)) | |
2501 | (if (re-search-forward | |
2502 | (concat "^" (regexp-quote mail-header-separator) "\n") nil t) | |
2503 | (match-beginning 0) | |
2504 | (point-max))) | |
2505 | (goto-char (point-min))) | |
2506 | ||
158d6e07 | 2507 | (defun message-narrow-to-head-1 () |
7d829636 | 2508 | "Like `message-narrow-to-head'. Don't widen." |
eec82323 LMI |
2509 | (narrow-to-region |
2510 | (goto-char (point-min)) | |
2511 | (if (search-forward "\n\n" nil 1) | |
2512 | (1- (point)) | |
2513 | (point-max))) | |
2514 | (goto-char (point-min))) | |
2515 | ||
4c36be58 | 2516 | ;; FIXME: clarify difference: message-narrow-to-head, |
e3e955fe | 2517 | ;; message-narrow-to-headers-or-head, message-narrow-to-headers |
158d6e07 SZ |
2518 | (defun message-narrow-to-head () |
2519 | "Narrow the buffer to the head of the message. | |
2520 | Point is left at the beginning of the narrowed-to region." | |
2521 | (widen) | |
2522 | (message-narrow-to-head-1)) | |
2523 | ||
16409b0b GM |
2524 | (defun message-narrow-to-headers-or-head () |
2525 | "Narrow the buffer to the head of the message." | |
2526 | (widen) | |
2527 | (narrow-to-region | |
2528 | (goto-char (point-min)) | |
d05b3cfd CY |
2529 | (if (re-search-forward (concat "\\(\n\\)\n\\|^\\(" |
2530 | (regexp-quote mail-header-separator) | |
2531 | "\n\\)") | |
2532 | nil t) | |
2533 | (or (match-end 1) (match-beginning 2)) | |
2534 | (point-max))) | |
16409b0b GM |
2535 | (goto-char (point-min))) |
2536 | ||
eec82323 LMI |
2537 | (defun message-news-p () |
2538 | "Say whether the current buffer contains a news message." | |
6748645f LMI |
2539 | (and (not message-this-is-mail) |
2540 | (or message-this-is-news | |
2541 | (save-excursion | |
2542 | (save-restriction | |
2543 | (message-narrow-to-headers) | |
2544 | (and (message-fetch-field "newsgroups") | |
2545 | (not (message-fetch-field "posted-to")))))))) | |
eec82323 LMI |
2546 | |
2547 | (defun message-mail-p () | |
2548 | "Say whether the current buffer contains a mail message." | |
6748645f LMI |
2549 | (and (not message-this-is-news) |
2550 | (or message-this-is-mail | |
2551 | (save-excursion | |
2552 | (save-restriction | |
2553 | (message-narrow-to-headers) | |
2554 | (or (message-fetch-field "to") | |
2555 | (message-fetch-field "cc") | |
2556 | (message-fetch-field "bcc"))))))) | |
eec82323 | 2557 | |
23f87bed MB |
2558 | (defun message-subscribed-p () |
2559 | "Say whether we need to insert a MFT header." | |
2560 | (or message-subscribed-regexps | |
2561 | message-subscribed-addresses | |
2562 | message-subscribed-address-file | |
2563 | message-subscribed-address-functions)) | |
2564 | ||
eec82323 LMI |
2565 | (defun message-next-header () |
2566 | "Go to the beginning of the next header." | |
2567 | (beginning-of-line) | |
2568 | (or (eobp) (forward-char 1)) | |
2569 | (not (if (re-search-forward "^[^ \t]" nil t) | |
2570 | (beginning-of-line) | |
2571 | (goto-char (point-max))))) | |
2572 | ||
2573 | (defun message-sort-headers-1 () | |
2574 | "Sort the buffer as headers using `message-rank' text props." | |
2575 | (goto-char (point-min)) | |
16409b0b | 2576 | (require 'sort) |
eec82323 LMI |
2577 | (sort-subr |
2578 | nil 'message-next-header | |
2579 | (lambda () | |
2580 | (message-next-header) | |
2581 | (unless (bobp) | |
2582 | (forward-char -1))) | |
2583 | (lambda () | |
2584 | (or (get-text-property (point) 'message-rank) | |
2585 | 10000)))) | |
2586 | ||
2587 | (defun message-sort-headers () | |
2588 | "Sort the headers of the current message according to `message-header-format-alist'." | |
2589 | (interactive) | |
2590 | (save-excursion | |
2591 | (save-restriction | |
2592 | (let ((max (1+ (length message-header-format-alist))) | |
2593 | rank) | |
2594 | (message-narrow-to-headers) | |
2595 | (while (re-search-forward "^[^ \n]+:" nil t) | |
2596 | (put-text-property | |
2597 | (match-beginning 0) (1+ (match-beginning 0)) | |
2598 | 'message-rank | |
2599 | (if (setq rank (length (memq (assq (intern (buffer-substring | |
2600 | (match-beginning 0) | |
2601 | (1- (match-end 0)))) | |
2602 | message-header-format-alist) | |
2603 | message-header-format-alist))) | |
2604 | (- max rank) | |
2605 | (1+ max))))) | |
2606 | (message-sort-headers-1)))) | |
2607 | ||
01c52d31 MB |
2608 | (defun message-kill-address () |
2609 | "Kill the address under point." | |
2610 | (interactive) | |
2611 | (let ((start (point))) | |
2612 | (message-skip-to-next-address) | |
0000d0d5 | 2613 | (kill-region start (if (bolp) (1- (point)) (point))))) |
01c52d31 MB |
2614 | |
2615 | ||
ea7d768a | 2616 | (autoload 'Info-goto-node "info") |
a0e08467 | 2617 | (defvar mml2015-use) |
ea7d768a | 2618 | |
c4288669 MB |
2619 | (defun message-info (&optional arg) |
2620 | "Display the Message manual. | |
2621 | ||
9b3ebcb6 MB |
2622 | Prefixed with one \\[universal-argument], display the Emacs MIME |
2623 | manual. With two \\[universal-argument]'s, display the EasyPG or | |
2624 | PGG manual, depending on the value of `mml2015-use'." | |
c4288669 | 2625 | (interactive "p") |
4a9b5bf5 RS |
2626 | ;; Don't use `info' because support for `(filename)nodename' is not |
2627 | ;; available in XEmacs < 21.5.12. | |
9b3ebcb6 | 2628 | (Info-goto-node (format "(%s)Top" |
a0e08467 GM |
2629 | (cond ((eq arg 16) |
2630 | (require 'mml2015) | |
2631 | mml2015-use) | |
9b3ebcb6 MB |
2632 | ((eq arg 4) 'emacs-mime) |
2633 | ;; `booleanp' only available in Emacs 22+ | |
2634 | ((and (not (memq arg '(nil t))) | |
2635 | (symbolp arg)) | |
2636 | arg) | |
2637 | (t | |
2638 | 'message))))) | |
23f87bed | 2639 | |
eec82323 LMI |
2640 | \f |
2641 | ||
2642 | ;;; | |
2643 | ;;; Message mode | |
2644 | ;;; | |
2645 | ||
2646 | ;;; Set up keymap. | |
2647 | ||
2648 | (defvar message-mode-map nil) | |
2649 | ||
2650 | (unless message-mode-map | |
16409b0b GM |
2651 | (setq message-mode-map (make-keymap)) |
2652 | (set-keymap-parent message-mode-map text-mode-map) | |
eec82323 LMI |
2653 | (define-key message-mode-map "\C-c?" 'describe-mode) |
2654 | ||
2655 | (define-key message-mode-map "\C-c\C-f\C-t" 'message-goto-to) | |
23f87bed | 2656 | (define-key message-mode-map "\C-c\C-f\C-o" 'message-goto-from) |
eec82323 LMI |
2657 | (define-key message-mode-map "\C-c\C-f\C-b" 'message-goto-bcc) |
2658 | (define-key message-mode-map "\C-c\C-f\C-w" 'message-goto-fcc) | |
2659 | (define-key message-mode-map "\C-c\C-f\C-c" 'message-goto-cc) | |
2660 | (define-key message-mode-map "\C-c\C-f\C-s" 'message-goto-subject) | |
2661 | (define-key message-mode-map "\C-c\C-f\C-r" 'message-goto-reply-to) | |
2662 | (define-key message-mode-map "\C-c\C-f\C-n" 'message-goto-newsgroups) | |
2663 | (define-key message-mode-map "\C-c\C-f\C-d" 'message-goto-distribution) | |
2664 | (define-key message-mode-map "\C-c\C-f\C-f" 'message-goto-followup-to) | |
23f87bed | 2665 | (define-key message-mode-map "\C-c\C-f\C-m" 'message-goto-mail-followup-to) |
eec82323 LMI |
2666 | (define-key message-mode-map "\C-c\C-f\C-k" 'message-goto-keywords) |
2667 | (define-key message-mode-map "\C-c\C-f\C-u" 'message-goto-summary) | |
23f87bed MB |
2668 | (define-key message-mode-map "\C-c\C-f\C-i" |
2669 | 'message-insert-or-toggle-importance) | |
2670 | (define-key message-mode-map "\C-c\C-f\C-a" | |
2671 | 'message-generate-unsubscribed-mail-followup-to) | |
2672 | ||
2673 | ;; modify headers (and insert notes in body) | |
2674 | (define-key message-mode-map "\C-c\C-fs" 'message-change-subject) | |
2675 | ;; | |
2676 | (define-key message-mode-map "\C-c\C-fx" 'message-cross-post-followup-to) | |
2677 | ;; prefix+message-cross-post-followup-to = same w/o cross-post | |
2678 | (define-key message-mode-map "\C-c\C-ft" 'message-reduce-to-to-cc) | |
2679 | (define-key message-mode-map "\C-c\C-fa" 'message-add-archive-header) | |
2680 | ;; mark inserted text | |
2681 | (define-key message-mode-map "\C-c\M-m" 'message-mark-inserted-region) | |
2682 | (define-key message-mode-map "\C-c\M-f" 'message-mark-insert-file) | |
2683 | ||
eec82323 LMI |
2684 | (define-key message-mode-map "\C-c\C-b" 'message-goto-body) |
2685 | (define-key message-mode-map "\C-c\C-i" 'message-goto-signature) | |
2686 | ||
2687 | (define-key message-mode-map "\C-c\C-t" 'message-insert-to) | |
23f87bed | 2688 | (define-key message-mode-map "\C-c\C-fw" 'message-insert-wide-reply) |
eec82323 | 2689 | (define-key message-mode-map "\C-c\C-n" 'message-insert-newsgroups) |
23f87bed | 2690 | (define-key message-mode-map "\C-c\C-l" 'message-to-list-only) |
01c52d31 | 2691 | (define-key message-mode-map "\C-c\C-f\C-e" 'message-insert-expires) |
23f87bed MB |
2692 | |
2693 | (define-key message-mode-map "\C-c\C-u" 'message-insert-or-toggle-importance) | |
2694 | (define-key message-mode-map "\C-c\M-n" | |
2695 | 'message-insert-disposition-notification-to) | |
eec82323 LMI |
2696 | |
2697 | (define-key message-mode-map "\C-c\C-y" 'message-yank-original) | |
16409b0b | 2698 | (define-key message-mode-map "\C-c\M-\C-y" 'message-yank-buffer) |
eec82323 LMI |
2699 | (define-key message-mode-map "\C-c\C-q" 'message-fill-yanked-message) |
2700 | (define-key message-mode-map "\C-c\C-w" 'message-insert-signature) | |
16409b0b | 2701 | (define-key message-mode-map "\C-c\M-h" 'message-insert-headers) |
eec82323 LMI |
2702 | (define-key message-mode-map "\C-c\C-r" 'message-caesar-buffer-body) |
2703 | (define-key message-mode-map "\C-c\C-o" 'message-sort-headers) | |
2704 | (define-key message-mode-map "\C-c\M-r" 'message-rename-buffer) | |
2705 | ||
2706 | (define-key message-mode-map "\C-c\C-c" 'message-send-and-exit) | |
2707 | (define-key message-mode-map "\C-c\C-s" 'message-send) | |
2708 | (define-key message-mode-map "\C-c\C-k" 'message-kill-buffer) | |
2709 | (define-key message-mode-map "\C-c\C-d" 'message-dont-send) | |
23f87bed | 2710 | (define-key message-mode-map "\C-c\n" 'gnus-delay-article) |
eec82323 | 2711 | |
01c52d31 | 2712 | (define-key message-mode-map "\C-c\M-k" 'message-kill-address) |
eec82323 | 2713 | (define-key message-mode-map "\C-c\C-e" 'message-elide-region) |
6748645f LMI |
2714 | (define-key message-mode-map "\C-c\C-v" 'message-delete-not-region) |
2715 | (define-key message-mode-map "\C-c\C-z" 'message-kill-to-signature) | |
2716 | (define-key message-mode-map "\M-\r" 'message-newline-and-reformat) | |
10893bb6 | 2717 | (define-key message-mode-map [remap split-line] 'message-split-line) |
eec82323 | 2718 | |
16409b0b GM |
2719 | (define-key message-mode-map "\C-c\C-a" 'mml-attach-file) |
2720 | ||
23f87bed MB |
2721 | (define-key message-mode-map "\C-a" 'message-beginning-of-line) |
2722 | (define-key message-mode-map "\t" 'message-tab) | |
01c52d31 MB |
2723 | |
2724 | (define-key message-mode-map "\M-n" 'message-display-abbrev)) | |
eec82323 LMI |
2725 | |
2726 | (easy-menu-define | |
23f87bed MB |
2727 | message-mode-menu message-mode-map "Message Menu." |
2728 | `("Message" | |
2729 | ["Yank Original" message-yank-original message-reply-buffer] | |
2730 | ["Fill Yanked Message" message-fill-yanked-message t] | |
2731 | ["Insert Signature" message-insert-signature t] | |
2732 | ["Caesar (rot13) Message" message-caesar-buffer-body t] | |
2733 | ["Caesar (rot13) Region" message-caesar-region (message-mark-active-p)] | |
2734 | ["Elide Region" message-elide-region | |
2735 | :active (message-mark-active-p) | |
2736 | ,@(if (featurep 'xemacs) nil | |
2737 | '(:help "Replace text in region with an ellipsis"))] | |
2738 | ["Delete Outside Region" message-delete-not-region | |
2739 | :active (message-mark-active-p) | |
2740 | ,@(if (featurep 'xemacs) nil | |
2741 | '(:help "Delete all quoted text outside region"))] | |
2742 | ["Kill To Signature" message-kill-to-signature t] | |
2743 | ["Newline and Reformat" message-newline-and-reformat t] | |
2744 | ["Rename buffer" message-rename-buffer t] | |
2745 | ["Spellcheck" ispell-message | |
2746 | ,@(if (featurep 'xemacs) '(t) | |
2747 | '(:help "Spellcheck this message"))] | |
2748 | "----" | |
2749 | ["Insert Region Marked" message-mark-inserted-region | |
2750 | :active (message-mark-active-p) | |
2751 | ,@(if (featurep 'xemacs) nil | |
2752 | '(:help "Mark region with enclosing tags"))] | |
2753 | ["Insert File Marked..." message-mark-insert-file | |
2754 | ,@(if (featurep 'xemacs) '(t) | |
2755 | '(:help "Insert file at point marked with enclosing tags"))] | |
2756 | "----" | |
2757 | ["Send Message" message-send-and-exit | |
2758 | ,@(if (featurep 'xemacs) '(t) | |
2759 | '(:help "Send this message"))] | |
2760 | ["Postpone Message" message-dont-send | |
2761 | ,@(if (featurep 'xemacs) '(t) | |
2762 | '(:help "File this draft message and exit"))] | |
2763 | ["Send at Specific Time..." gnus-delay-article | |
2764 | ,@(if (featurep 'xemacs) '(t) | |
2765 | '(:help "Ask, then arrange to send message at that time"))] | |
2766 | ["Kill Message" message-kill-buffer | |
2767 | ,@(if (featurep 'xemacs) '(t) | |
c4288669 MB |
2768 | '(:help "Delete this message without sending"))] |
2769 | "----" | |
2770 | ["Message manual" message-info | |
2771 | ,@(if (featurep 'xemacs) '(t) | |
2772 | '(:help "Display the Message manual"))])) | |
eec82323 LMI |
2773 | |
2774 | (easy-menu-define | |
23f87bed MB |
2775 | message-mode-field-menu message-mode-map "" |
2776 | `("Field" | |
2777 | ["To" message-goto-to t] | |
2778 | ["From" message-goto-from t] | |
2779 | ["Subject" message-goto-subject t] | |
2780 | ["Change subject..." message-change-subject t] | |
2781 | ["Cc" message-goto-cc t] | |
2782 | ["Bcc" message-goto-bcc t] | |
2783 | ["Fcc" message-goto-fcc t] | |
2784 | ["Reply-To" message-goto-reply-to t] | |
2785 | ["Flag As Important" message-insert-importance-high | |
2786 | ,@(if (featurep 'xemacs) '(t) | |
2787 | '(:help "Mark this message as important"))] | |
2788 | ["Flag As Unimportant" message-insert-importance-low | |
2789 | ,@(if (featurep 'xemacs) '(t) | |
2790 | '(:help "Mark this message as unimportant"))] | |
2791 | ["Request Receipt" | |
2792 | message-insert-disposition-notification-to | |
2793 | ,@(if (featurep 'xemacs) '(t) | |
2794 | '(:help "Request a receipt notification"))] | |
2795 | "----" | |
2796 | ;; (typical) news stuff | |
2797 | ["Summary" message-goto-summary t] | |
2798 | ["Keywords" message-goto-keywords t] | |
2799 | ["Newsgroups" message-goto-newsgroups t] | |
2800 | ["Fetch Newsgroups" message-insert-newsgroups t] | |
2801 | ["Followup-To" message-goto-followup-to t] | |
2802 | ;; ["Followup-To (with note in body)" message-cross-post-followup-to t] | |
2803 | ["Crosspost / Followup-To..." message-cross-post-followup-to t] | |
2804 | ["Distribution" message-goto-distribution t] | |
01c52d31 MB |
2805 | ["Expires" message-insert-expires t ] |
2806 | ["X-No-Archive" message-add-archive-header t ] | |
23f87bed MB |
2807 | "----" |
2808 | ;; (typical) mailing-lists stuff | |
2809 | ["Fetch To" message-insert-to | |
2810 | ,@(if (featurep 'xemacs) '(t) | |
2811 | '(:help "Insert a To header that points to the author."))] | |
2812 | ["Fetch To and Cc" message-insert-wide-reply | |
2813 | ,@(if (featurep 'xemacs) '(t) | |
2814 | '(:help | |
2815 | "Insert To and Cc headers as if you were doing a wide reply."))] | |
2816 | "----" | |
2817 | ["Send to list only" message-to-list-only t] | |
2818 | ["Mail-Followup-To" message-goto-mail-followup-to t] | |
2819 | ["Unsubscribed list post" message-generate-unsubscribed-mail-followup-to | |
2820 | ,@(if (featurep 'xemacs) '(t) | |
2821 | '(:help "Insert a reasonable `Mail-Followup-To:' header."))] | |
2822 | ["Reduce To: to Cc:" message-reduce-to-to-cc t] | |
2823 | "----" | |
2824 | ["Sort Headers" message-sort-headers t] | |
2825 | ["Encode non-ASCII domain names" message-idna-to-ascii-rhs t] | |
01c52d31 MB |
2826 | ;; We hide `message-hidden-headers' by narrowing the buffer. |
2827 | ["Show Hidden Headers" widen t] | |
23f87bed MB |
2828 | ["Goto Body" message-goto-body t] |
2829 | ["Goto Signature" message-goto-signature t])) | |
2830 | ||
2831 | (defvar message-tool-bar-map nil) | |
eec82323 | 2832 | |
9efa445f DN |
2833 | (defvar facemenu-add-face-function) |
2834 | (defvar facemenu-remove-face-function) | |
eec82323 | 2835 | |
23f87bed MB |
2836 | ;;; Forbidden properties |
2837 | ;; | |
2838 | ;; We use `after-change-functions' to keep special text properties | |
dc4d6273 | 2839 | ;; that interfere with the normal function of message mode out of the |
23f87bed MB |
2840 | ;; buffer. |
2841 | ||
2842 | (defcustom message-strip-special-text-properties t | |
2843 | "Strip special properties from the message buffer. | |
2844 | ||
2845 | Emacs has a number of special text properties which can break message | |
2846 | composing in various ways. If this option is set, message will strip | |
2847 | these properties from the message composition buffer. However, some | |
2848 | packages requires these properties to be present in order to work. | |
2849 | If you use one of these packages, turn this option off, and hope the | |
2850 | message composition doesn't break too bad." | |
bf247b6e | 2851 | :version "22.1" |
23f87bed MB |
2852 | :group 'message-various |
2853 | :link '(custom-manual "(message)Various Message Variables") | |
2854 | :type 'boolean) | |
2855 | ||
2b7feab0 | 2856 | (defvar message-forbidden-properties |
23f87bed MB |
2857 | ;; No reason this should be clutter up customize. We make it a |
2858 | ;; property list (rather than a list of property symbols), to be | |
2859 | ;; directly useful for `remove-text-properties'. | |
2860 | '(field nil read-only nil invisible nil intangible nil | |
2861 | mouse-face nil modification-hooks nil insert-in-front-hooks nil | |
2862 | insert-behind-hooks nil point-entered nil point-left nil) | |
2863 | ;; Other special properties: | |
2864 | ;; category, face, display: probably doesn't do any harm. | |
2865 | ;; fontified: is used by font-lock. | |
2866 | ;; syntax-table, local-map: I dunno. | |
2867 | ;; We need to add XEmacs names to the list. | |
ad136a7c | 2868 | "Property list of with properties forbidden in message buffers. |
23f87bed MB |
2869 | The values of the properties are ignored, only the property names are used.") |
2870 | ||
2871 | (defun message-tamago-not-in-use-p (pos) | |
2872 | "Return t when tamago version 4 is not in use at the cursor position. | |
2873 | Tamago version 4 is a popular input method for writing Japanese text. | |
2874 | It uses the properties `intangible', `invisible', `modification-hooks' | |
2875 | and `read-only' when translating ascii or kana text to kanji text. | |
2876 | These properties are essential to work, so we should never strip them." | |
2877 | (not (and (boundp 'egg-modefull-mode) | |
2878 | (symbol-value 'egg-modefull-mode) | |
2879 | (or (memq (get-text-property pos 'intangible) | |
2880 | '(its-part-1 its-part-2)) | |
2881 | (get-text-property pos 'egg-end) | |
2882 | (get-text-property pos 'egg-lang) | |
2883 | (get-text-property pos 'egg-start))))) | |
2884 | ||
01c52d31 MB |
2885 | (defsubst message-mail-alias-type-p (type) |
2886 | (if (atom message-mail-alias-type) | |
2887 | (eq message-mail-alias-type type) | |
2888 | (memq type message-mail-alias-type))) | |
2889 | ||
23f87bed MB |
2890 | (defun message-strip-forbidden-properties (begin end &optional old-length) |
2891 | "Strip forbidden properties between BEGIN and END, ignoring the third arg. | |
2892 | This function is intended to be called from `after-change-functions'. | |
2893 | See also `message-forbidden-properties'." | |
01c52d31 MB |
2894 | (when (and (message-mail-alias-type-p 'ecomplete) |
2895 | (memq this-command message-self-insert-commands)) | |
2896 | (message-display-abbrev)) | |
23f87bed MB |
2897 | (when (and message-strip-special-text-properties |
2898 | (message-tamago-not-in-use-p begin)) | |
ad136a7c MB |
2899 | (let ((buffer-read-only nil) |
2900 | (inhibit-read-only t)) | |
01c52d31 | 2901 | (remove-text-properties begin end message-forbidden-properties)))) |
23f87bed | 2902 | |
ec7995fa KY |
2903 | (autoload 'ecomplete-setup "ecomplete") ;; for Emacs <23. |
2904 | ||
eec82323 | 2905 | ;;;###autoload |
23f87bed | 2906 | (define-derived-mode message-mode text-mode "Message" |
eec82323 | 2907 | "Major mode for editing mail and news to be sent. |
7d829636 DL |
2908 | Like Text Mode but with these additional commands:\\<message-mode-map> |
2909 | C-c C-s `message-send' (send the message) C-c C-c `message-send-and-exit' | |
2910 | C-c C-d Postpone sending the message C-c C-k Kill the message | |
eec82323 LMI |
2911 | C-c C-f move to a header field (and create it if there isn't): |
2912 | C-c C-f C-t move to To C-c C-f C-s move to Subject | |
2913 | C-c C-f C-c move to Cc C-c C-f C-b move to Bcc | |
2914 | C-c C-f C-w move to Fcc C-c C-f C-r move to Reply-To | |
2915 | C-c C-f C-u move to Summary C-c C-f C-n move to Newsgroups | |
2916 | C-c C-f C-k move to Keywords C-c C-f C-d move to Distribution | |
01c52d31 | 2917 | C-c C-f C-o move to From (\"Originator\") |
eec82323 | 2918 | C-c C-f C-f move to Followup-To |
23f87bed | 2919 | C-c C-f C-m move to Mail-Followup-To |
01c52d31 | 2920 | C-c C-f C-e move to Expires |
23f87bed MB |
2921 | C-c C-f C-i cycle through Importance values |
2922 | C-c C-f s change subject and append \"(was: <Old Subject>)\" | |
2923 | C-c C-f x crossposting with FollowUp-To header and note in body | |
2924 | C-c C-f t replace To: header with contents of Cc: or Bcc: | |
2925 | C-c C-f a Insert X-No-Archive: header and a note in the body | |
7d829636 | 2926 | C-c C-t `message-insert-to' (add a To header to a news followup) |
23f87bed | 2927 | C-c C-l `message-to-list-only' (removes all but list address in to/cc) |
7d829636 DL |
2928 | C-c C-n `message-insert-newsgroups' (add a Newsgroup header to a news reply) |
2929 | C-c C-b `message-goto-body' (move to beginning of message text). | |
2930 | C-c C-i `message-goto-signature' (move to the beginning of the signature). | |
2931 | C-c C-w `message-insert-signature' (insert `message-signature-file' file). | |
2932 | C-c C-y `message-yank-original' (insert current message, if any). | |
2933 | C-c C-q `message-fill-yanked-message' (fill what was yanked). | |
2934 | C-c C-e `message-elide-region' (elide the text between point and mark). | |
2935 | C-c C-v `message-delete-not-region' (remove the text outside the region). | |
2936 | C-c C-z `message-kill-to-signature' (kill the text up to the signature). | |
2937 | C-c C-r `message-caesar-buffer-body' (rot13 the message body). | |
2938 | C-c C-a `mml-attach-file' (attach a file as MIME). | |
23f87bed MB |
2939 | C-c C-u `message-insert-or-toggle-importance' (insert or cycle importance). |
2940 | C-c M-n `message-insert-disposition-notification-to' (request receipt). | |
2941 | C-c M-m `message-mark-inserted-region' (mark region with enclosing tags). | |
2942 | C-c M-f `message-mark-insert-file' (insert file marked with enclosing tags). | |
7d829636 | 2943 | M-RET `message-newline-and-reformat' (break the line and reformat)." |
16409b0b | 2944 | (set (make-local-variable 'message-reply-buffer) nil) |
23f87bed MB |
2945 | (set (make-local-variable 'message-inserted-headers) nil) |
2946 | (set (make-local-variable 'message-send-actions) nil) | |
25ca2e61 | 2947 | (set (make-local-variable 'message-return-action) nil) |
23f87bed MB |
2948 | (set (make-local-variable 'message-exit-actions) nil) |
2949 | (set (make-local-variable 'message-kill-actions) nil) | |
2950 | (set (make-local-variable 'message-postpone-actions) nil) | |
2951 | (set (make-local-variable 'message-draft-article) nil) | |
eec82323 | 2952 | (setq buffer-offer-save t) |
23f87bed MB |
2953 | (set (make-local-variable 'facemenu-add-face-function) |
2954 | (lambda (face end) | |
2955 | (let ((face-fun (cdr (assq face message-face-alist)))) | |
2956 | (if face-fun | |
2957 | (funcall face-fun (point) end) | |
2958 | (error "Face %s not configured for %s mode" face mode-name))) | |
2959 | "")) | |
2960 | (set (make-local-variable 'facemenu-remove-face-function) t) | |
2961 | (set (make-local-variable 'message-reply-headers) nil) | |
eec82323 LMI |
2962 | (make-local-variable 'message-newsreader) |
2963 | (make-local-variable 'message-mailer) | |
2964 | (make-local-variable 'message-post-method) | |
16409b0b GM |
2965 | (set (make-local-variable 'message-sent-message-via) nil) |
2966 | (set (make-local-variable 'message-checksum) nil) | |
2967 | (set (make-local-variable 'message-mime-part) 0) | |
f8c0f31e | 2968 | (message-setup-fill-variables) |
01c52d31 MB |
2969 | (when message-fill-column |
2970 | (setq fill-column message-fill-column) | |
2971 | (turn-on-auto-fill)) | |
980984d8 | 2972 | ;; Allow using comment commands to add/remove quoting. |
23f87bed | 2973 | ;; (set (make-local-variable 'comment-start) message-yank-prefix) |
528f876a SM |
2974 | (when message-yank-prefix |
2975 | (set (make-local-variable 'comment-start) message-yank-prefix) | |
2976 | (set (make-local-variable 'comment-start-skip) | |
2977 | (concat "^" (regexp-quote message-yank-prefix) "[ \t]*"))) | |
16409b0b GM |
2978 | (if (featurep 'xemacs) |
2979 | (message-setup-toolbar) | |
2980 | (set (make-local-variable 'font-lock-defaults) | |
c47a0dc8 | 2981 | '(message-font-lock-keywords t)) |
23f87bed | 2982 | (if (boundp 'tool-bar-map) |
18c06a99 | 2983 | (set (make-local-variable 'tool-bar-map) (message-make-tool-bar)))) |
eec82323 LMI |
2984 | (easy-menu-add message-mode-menu message-mode-map) |
2985 | (easy-menu-add message-mode-field-menu message-mode-map) | |
23f87bed MB |
2986 | (gnus-make-local-hook 'after-change-functions) |
2987 | ;; Mmmm... Forbidden properties... | |
2988 | (add-hook 'after-change-functions 'message-strip-forbidden-properties | |
2989 | nil 'local) | |
eec82323 | 2990 | ;; Allow mail alias things. |
01c52d31 MB |
2991 | (cond |
2992 | ((message-mail-alias-type-p 'abbrev) | |
a8151ef7 LMI |
2993 | (if (fboundp 'mail-abbrevs-setup) |
2994 | (mail-abbrevs-setup) | |
23f87bed MB |
2995 | (if (fboundp 'mail-aliases-setup) ; warning avoidance |
2996 | (mail-aliases-setup)))) | |
01c52d31 MB |
2997 | ((message-mail-alias-type-p 'ecomplete) |
2998 | (ecomplete-setup))) | |
674c5ccf | 2999 | (add-hook 'completion-at-point-functions 'message-completion-function nil t) |
3efe5554 SZ |
3000 | (unless buffer-file-name |
3001 | (message-set-auto-save-file-name)) | |
23f87bed MB |
3002 | (unless (buffer-base-buffer) |
3003 | ;; Don't enable multibyte on an indirect buffer. Maybe enabling | |
3004 | ;; multibyte is not necessary at all. -- zsh | |
3005 | (mm-enable-multibyte)) | |
3006 | (set (make-local-variable 'indent-tabs-mode) nil) ;No tabs for indentation. | |
3007 | (mml-mode)) | |
eec82323 | 3008 | |
f8c0f31e DL |
3009 | (defun message-setup-fill-variables () |
3010 | "Setup message fill variables." | |
23f87bed MB |
3011 | (set (make-local-variable 'fill-paragraph-function) |
3012 | 'message-fill-paragraph) | |
f8c0f31e DL |
3013 | (make-local-variable 'paragraph-separate) |
3014 | (make-local-variable 'paragraph-start) | |
3015 | (make-local-variable 'adaptive-fill-regexp) | |
3016 | (unless (boundp 'adaptive-fill-first-line-regexp) | |
3017 | (setq adaptive-fill-first-line-regexp nil)) | |
3018 | (make-local-variable 'adaptive-fill-first-line-regexp) | |
f8c0f31e | 3019 | (let ((quote-prefix-regexp |
23f87bed MB |
3020 | ;; User should change message-cite-prefix-regexp if |
3021 | ;; message-yank-prefix is set to an abnormal value. | |
3022 | (concat "\\(" message-cite-prefix-regexp "\\)[ \t]*"))) | |
f8c0f31e | 3023 | (setq paragraph-start |
23f87bed MB |
3024 | (concat |
3025 | (regexp-quote mail-header-separator) "$\\|" | |
3026 | "[ \t]*$\\|" ; blank lines | |
3027 | "-- $\\|" ; signature delimiter | |
3028 | "---+$\\|" ; delimiters for forwarded messages | |
3029 | page-delimiter "$\\|" ; spoiler warnings | |
3030 | ".*wrote:$\\|" ; attribution lines | |
3031 | quote-prefix-regexp "$\\|" ; empty lines in quoted text | |
3032 | ; mml tags | |
3033 | "<#!*/?\\(multipart\\|part\\|external\\|mml\\|secure\\)")) | |
f8c0f31e DL |
3034 | (setq paragraph-separate paragraph-start) |
3035 | (setq adaptive-fill-regexp | |
23f87bed | 3036 | (concat quote-prefix-regexp "\\|" adaptive-fill-regexp)) |
f8c0f31e | 3037 | (setq adaptive-fill-first-line-regexp |
23f87bed MB |
3038 | (concat quote-prefix-regexp "\\|" |
3039 | adaptive-fill-first-line-regexp))) | |
3040 | (make-local-variable 'auto-fill-inhibit-regexp) | |
3041 | ;;(setq auto-fill-inhibit-regexp "^[A-Z][^: \n\t]+:") | |
3042 | (setq auto-fill-inhibit-regexp nil) | |
3043 | (make-local-variable 'normal-auto-fill-function) | |
3044 | (setq normal-auto-fill-function 'message-do-auto-fill) | |
3045 | ;; KLUDGE: auto fill might already be turned on in `text-mode-hook'. | |
3046 | ;; In that case, ensure that it uses the right function. The real | |
3047 | ;; solution would be not to use `define-derived-mode', and run | |
3048 | ;; `text-mode-hook' ourself at the end of the mode. | |
3049 | ;; -- Per Abrahamsen <abraham@dina.kvl.dk> Date: 2001-10-19. | |
c70dbcd5 SM |
3050 | ;; This kludge is unneeded in Emacs>=21 since define-derived-mode is |
3051 | ;; now careful to run parent hooks after the body. --Stef | |
23f87bed MB |
3052 | (when auto-fill-function |
3053 | (setq auto-fill-function normal-auto-fill-function))) | |
f8c0f31e | 3054 | |
eec82323 LMI |
3055 | \f |
3056 | ||
3057 | ;;; | |
3058 | ;;; Message mode commands | |
3059 | ;;; | |
3060 | ||
3061 | ;;; Movement commands | |
3062 | ||
3063 | (defun message-goto-to () | |
3064 | "Move point to the To header." | |
3065 | (interactive) | |
89b163db | 3066 | (push-mark) |
eec82323 LMI |
3067 | (message-position-on-field "To")) |
3068 | ||
23f87bed MB |
3069 | (defun message-goto-from () |
3070 | "Move point to the From header." | |
3071 | (interactive) | |
89b163db | 3072 | (push-mark) |
23f87bed MB |
3073 | (message-position-on-field "From")) |
3074 | ||
eec82323 LMI |
3075 | (defun message-goto-subject () |
3076 | "Move point to the Subject header." | |
3077 | (interactive) | |
89b163db | 3078 | (push-mark) |
eec82323 LMI |
3079 | (message-position-on-field "Subject")) |
3080 | ||
3081 | (defun message-goto-cc () | |
3082 | "Move point to the Cc header." | |
3083 | (interactive) | |
89b163db | 3084 | (push-mark) |
eec82323 LMI |
3085 | (message-position-on-field "Cc" "To")) |
3086 | ||
3087 | (defun message-goto-bcc () | |
3088 | "Move point to the Bcc header." | |
3089 | (interactive) | |
89b163db | 3090 | (push-mark) |
eec82323 LMI |
3091 | (message-position-on-field "Bcc" "Cc" "To")) |
3092 | ||
3093 | (defun message-goto-fcc () | |
3094 | "Move point to the Fcc header." | |
3095 | (interactive) | |
89b163db | 3096 | (push-mark) |
eec82323 LMI |
3097 | (message-position-on-field "Fcc" "To" "Newsgroups")) |
3098 | ||
3099 | (defun message-goto-reply-to () | |
3100 | "Move point to the Reply-To header." | |
3101 | (interactive) | |
89b163db | 3102 | (push-mark) |
eec82323 LMI |
3103 | (message-position-on-field "Reply-To" "Subject")) |
3104 | ||
3105 | (defun message-goto-newsgroups () | |
3106 | "Move point to the Newsgroups header." | |
3107 | (interactive) | |
89b163db | 3108 | (push-mark) |
eec82323 LMI |
3109 | (message-position-on-field "Newsgroups")) |
3110 | ||
3111 | (defun message-goto-distribution () | |
3112 | "Move point to the Distribution header." | |
3113 | (interactive) | |
89b163db | 3114 | (push-mark) |
eec82323 LMI |
3115 | (message-position-on-field "Distribution")) |
3116 | ||
3117 | (defun message-goto-followup-to () | |
3118 | "Move point to the Followup-To header." | |
3119 | (interactive) | |
89b163db | 3120 | (push-mark) |
eec82323 LMI |
3121 | (message-position-on-field "Followup-To" "Newsgroups")) |
3122 | ||
23f87bed MB |
3123 | (defun message-goto-mail-followup-to () |
3124 | "Move point to the Mail-Followup-To header." | |
3125 | (interactive) | |
89b163db | 3126 | (push-mark) |
531e5812 | 3127 | (message-position-on-field "Mail-Followup-To" "To")) |
23f87bed | 3128 | |
eec82323 LMI |
3129 | (defun message-goto-keywords () |
3130 | "Move point to the Keywords header." | |
3131 | (interactive) | |
89b163db | 3132 | (push-mark) |
eec82323 LMI |
3133 | (message-position-on-field "Keywords" "Subject")) |
3134 | ||
3135 | (defun message-goto-summary () | |
3136 | "Move point to the Summary header." | |
3137 | (interactive) | |
89b163db | 3138 | (push-mark) |
eec82323 LMI |
3139 | (message-position-on-field "Summary" "Subject")) |
3140 | ||
144b7b5c | 3141 | (defun message-goto-body () |
eec82323 | 3142 | "Move point to the beginning of the message body." |
144b7b5c | 3143 | (interactive) |
da946239 | 3144 | (when (and (gmm-called-interactively-p 'any) |
23f87bed MB |
3145 | (looking-at "[ \t]*\n")) |
3146 | (expand-abbrev)) | |
89b163db | 3147 | (push-mark) |
eec82323 | 3148 | (goto-char (point-min)) |
16409b0b | 3149 | (or (search-forward (concat "\n" mail-header-separator "\n") nil t) |
23f87bed | 3150 | (search-forward-regexp "[^:]+:\\([^\n]\\|\n[ \t]\\)+\n\n" nil t))) |
eec82323 | 3151 | |
0565caeb MB |
3152 | (defun message-in-body-p () |
3153 | "Return t if point is in the message body." | |
cdfca592 RS |
3154 | (>= (point) |
3155 | (save-excursion | |
3156 | (goto-char (point-min)) | |
3157 | (or (search-forward (concat "\n" mail-header-separator "\n") nil t) | |
3158 | (search-forward-regexp "[^:]+:\\([^\n]\\|\n[ \t]\\)+\n\n" nil t)) | |
3159 | (point)))) | |
0565caeb | 3160 | |
6748645f LMI |
3161 | (defun message-goto-eoh () |
3162 | "Move point to the end of the headers." | |
3163 | (interactive) | |
3164 | (message-goto-body) | |
16409b0b | 3165 | (forward-line -1)) |
6748645f | 3166 | |
eec82323 | 3167 | (defun message-goto-signature () |
6748645f LMI |
3168 | "Move point to the beginning of the message signature. |
3169 | If there is no signature in the article, go to the end and | |
3170 | return nil." | |
eec82323 | 3171 | (interactive) |
89b163db | 3172 | (push-mark) |
eec82323 LMI |
3173 | (goto-char (point-min)) |
3174 | (if (re-search-forward message-signature-separator nil t) | |
3175 | (forward-line 1) | |
6748645f LMI |
3176 | (goto-char (point-max)) |
3177 | nil)) | |
eec82323 | 3178 | |
23f87bed MB |
3179 | (defun message-generate-unsubscribed-mail-followup-to (&optional include-cc) |
3180 | "Insert a reasonable MFT header in a post to an unsubscribed list. | |
3181 | When making original posts to a mailing list you are not subscribed to, | |
3182 | you have to type in a MFT header by hand. The contents, usually, are | |
3183 | the addresses of the list and your own address. This function inserts | |
3184 | such a header automatically. It fetches the contents of the To: header | |
3185 | in the current mail buffer, and appends the current `user-mail-address'. | |
3186 | ||
3187 | If the optional argument INCLUDE-CC is non-nil, the addresses in the | |
3188 | Cc: header are also put into the MFT." | |
3189 | ||
3190 | (interactive "P") | |
3191 | (let* (cc tos) | |
3192 | (save-restriction | |
3193 | (message-narrow-to-headers) | |
3194 | (message-remove-header "Mail-Followup-To") | |
3195 | (setq cc (and include-cc (message-fetch-field "Cc"))) | |
3196 | (setq tos (if cc | |
3197 | (concat (message-fetch-field "To") "," cc) | |
3198 | (message-fetch-field "To")))) | |
3199 | (message-goto-mail-followup-to) | |
3200 | (insert (concat tos ", " user-mail-address)))) | |
3201 | ||
eec82323 LMI |
3202 | \f |
3203 | ||
a8151ef7 LMI |
3204 | (defun message-insert-to (&optional force) |
3205 | "Insert a To header that points to the author of the article being replied to. | |
23f87bed MB |
3206 | If the original author requested not to be sent mail, don't insert unless the |
3207 | prefix FORCE is given." | |
a8151ef7 | 3208 | (interactive "P") |
23f87bed | 3209 | (let* ((mct (message-fetch-reply-field "mail-copies-to")) |
01c52d31 | 3210 | (dont (and mct (or (equal (downcase mct) "never") |
23f87bed | 3211 | (equal (downcase mct) "nobody")))) |
01c52d31 MB |
3212 | (to (or (message-fetch-reply-field "mail-reply-to") |
3213 | (message-fetch-reply-field "reply-to") | |
3214 | (message-fetch-reply-field "from")))) | |
23f87bed MB |
3215 | (when (and dont to) |
3216 | (message | |
3217 | (if force | |
3218 | "Ignoring the user request not to have copies sent via mail" | |
3219 | "Complying with the user request not to have copies sent via mail"))) | |
3220 | (when (and force (not to)) | |
3221 | (error "No mail address in the article")) | |
3222 | (when (and to (or force (not dont))) | |
3223 | (message-carefully-insert-headers (list (cons 'To to)))))) | |
3224 | ||
3225 | (defun message-insert-wide-reply () | |
3226 | "Insert To and Cc headers as if you were doing a wide reply." | |
3227 | (interactive) | |
3228 | (let ((headers (message-with-reply-buffer | |
3229 | (message-get-reply-headers t)))) | |
3230 | (message-carefully-insert-headers headers))) | |
3231 | ||
3232 | (defcustom message-header-synonyms | |
14e6dc54 MB |
3233 | '((To Cc Bcc) |
3234 | (Original-To)) | |
23f87bed MB |
3235 | "List of lists of header synonyms. |
3236 | E.g., if this list contains a member list with elements `Cc' and `To', | |
3237 | then `message-carefully-insert-headers' will not insert a `To' header | |
3238 | when the message is already `Cc'ed to the recipient." | |
bf247b6e | 3239 | :version "22.1" |
23f87bed MB |
3240 | :group 'message-headers |
3241 | :link '(custom-manual "(message)Message Headers") | |
3242 | :type '(repeat sexp)) | |
3243 | ||
3244 | (defun message-carefully-insert-headers (headers) | |
3245 | "Insert the HEADERS, an alist, into the message buffer. | |
3246 | Does not insert the headers when they are already present there | |
3247 | or in the synonym headers, defined by `message-header-synonyms'." | |
3248 | ;; FIXME: Should compare only the address and not the full name. Comparison | |
3249 | ;; should be done case-folded (and with `string=' rather than | |
3250 | ;; `string-match'). | |
531e5812 | 3251 | ;; (mail-strip-quoted-names "Foo Bar <foo@bar>, bla@fasel (Bla Fasel)") |
23f87bed MB |
3252 | (dolist (header headers) |
3253 | (let* ((header-name (symbol-name (car header))) | |
01c52d31 MB |
3254 | (new-header (cdr header)) |
3255 | (synonyms (loop for synonym in message-header-synonyms | |
23f87bed | 3256 | when (memq (car header) synonym) return synonym)) |
01c52d31 MB |
3257 | (old-header |
3258 | (loop for synonym in synonyms | |
23f87bed MB |
3259 | for old-header = (mail-fetch-field (symbol-name synonym)) |
3260 | when (and old-header (string-match new-header old-header)) | |
3261 | return synonym))) | |
3262 | (if old-header | |
01c52d31 | 3263 | (message "already have `%s' in `%s'" new-header old-header) |
23f87bed | 3264 | (when (and (message-position-on-field header-name) |
01c52d31 MB |
3265 | (setq old-header (mail-fetch-field header-name)) |
3266 | (not (string-match "\\` *\\'" old-header))) | |
23f87bed | 3267 | (insert ", ")) |
01c52d31 | 3268 | (insert new-header))))) |
eec82323 | 3269 | |
16409b0b GM |
3270 | (defun message-widen-reply () |
3271 | "Widen the reply to include maximum recipients." | |
3272 | (interactive) | |
3273 | (let ((follow-to | |
b1ea3797 | 3274 | (and (bufferp message-reply-buffer) |
16409b0b | 3275 | (buffer-name message-reply-buffer) |
82816ba1 | 3276 | (with-current-buffer message-reply-buffer |
16409b0b GM |
3277 | (message-get-reply-headers t))))) |
3278 | (save-excursion | |
3279 | (save-restriction | |
3280 | (message-narrow-to-headers) | |
3281 | (dolist (elem follow-to) | |
3282 | (message-remove-header (symbol-name (car elem))) | |
3283 | (goto-char (point-min)) | |
3284 | (insert (symbol-name (car elem)) ": " | |
f129a4df | 3285 | (cdr elem) "\n")))))) |
16409b0b | 3286 | |
eec82323 LMI |
3287 | (defun message-insert-newsgroups () |
3288 | "Insert the Newsgroups header from the article being replied to." | |
3289 | (interactive) | |
350a1888 G |
3290 | (let ((old-newsgroups (mail-fetch-field "newsgroups")) |
3291 | (new-newsgroups (message-fetch-reply-field "newsgroups")) | |
3292 | (first t) | |
3293 | insert-newsgroups) | |
3294 | (message-position-on-field "Newsgroups") | |
3295 | (cond | |
3296 | ((not new-newsgroups) | |
3297 | (error "No Newsgroups to insert")) | |
3298 | ((not old-newsgroups) | |
3299 | (insert new-newsgroups)) | |
3300 | (t | |
3301 | (setq new-newsgroups (split-string new-newsgroups "[, ]+") | |
3302 | old-newsgroups (split-string old-newsgroups "[, ]+")) | |
3303 | (dolist (group new-newsgroups) | |
3304 | (unless (member group old-newsgroups) | |
3305 | (push group insert-newsgroups))) | |
3306 | (if (null insert-newsgroups) | |
3307 | (error "Newgroup%s already in the header" | |
3308 | (if (> (length new-newsgroups) 1) | |
3309 | "s" "")) | |
3310 | (when old-newsgroups | |
3311 | (setq first nil)) | |
3312 | (dolist (group insert-newsgroups) | |
3313 | (unless first | |
3314 | (insert ",")) | |
3315 | (setq first nil) | |
3316 | (insert group))))))) | |
eec82323 LMI |
3317 | |
3318 | \f | |
3319 | ||
3320 | ;;; Various commands | |
3321 | ||
6748645f | 3322 | (defun message-delete-not-region (beg end) |
7d829636 | 3323 | "Delete everything in the body of the current message outside of the region." |
6748645f | 3324 | (interactive "r") |
23f87bed MB |
3325 | (let (citeprefix) |
3326 | (save-excursion | |
3327 | (goto-char beg) | |
3328 | ;; snarf citation prefix, if appropriate | |
3329 | (unless (eq (point) (progn (beginning-of-line) (point))) | |
3330 | (when (looking-at message-cite-prefix-regexp) | |
3331 | (setq citeprefix (match-string 0)))) | |
3332 | (goto-char end) | |
3333 | (delete-region (point) (if (not (message-goto-signature)) | |
3334 | (point) | |
3335 | (forward-line -2) | |
3336 | (point))) | |
f129a4df | 3337 | (insert "\n") |
23f87bed MB |
3338 | (goto-char beg) |
3339 | (delete-region beg (progn (message-goto-body) | |
3340 | (forward-line 2) | |
3341 | (point))) | |
3342 | (when citeprefix | |
3343 | (insert citeprefix)))) | |
6748645f LMI |
3344 | (when (message-goto-signature) |
3345 | (forward-line -2))) | |
3346 | ||
01c52d31 MB |
3347 | (defun message-kill-to-signature (&optional arg) |
3348 | "Kill all text up to the signature. | |
2b8f62e9 | 3349 | If a numeric argument or prefix arg is given, leave that number |
01c52d31 MB |
3350 | of lines before the signature intact." |
3351 | (interactive "P") | |
3352 | (save-excursion | |
3353 | (save-restriction | |
3354 | (let ((point (point))) | |
3355 | (narrow-to-region point (point-max)) | |
3356 | (message-goto-signature) | |
3357 | (unless (eobp) | |
3358 | (if (and arg (numberp arg)) | |
3359 | (forward-line (- -1 arg)) | |
3360 | (end-of-line -1))) | |
3361 | (unless (= point (point)) | |
3362 | (kill-region point (point)) | |
3363 | (unless (bolp) | |
3364 | (insert "\n"))))))) | |
6748645f | 3365 | |
23f87bed MB |
3366 | (defun message-newline-and-reformat (&optional arg not-break) |
3367 | "Insert four newlines, and then reformat if inside quoted text. | |
3368 | Prefix arg means justify as well." | |
3369 | (interactive (list (if current-prefix-arg 'full))) | |
01c52d31 | 3370 | (let (quoted point beg end leading-space bolp fill-paragraph-function) |
16409b0b | 3371 | (setq point (point)) |
23f87bed MB |
3372 | (beginning-of-line) |
3373 | (setq beg (point)) | |
3374 | (setq bolp (= beg point)) | |
3375 | ;; Find first line of the paragraph. | |
3376 | (if not-break | |
3377 | (while (and (not (eobp)) | |
3378 | (not (looking-at message-cite-prefix-regexp)) | |
3379 | (looking-at paragraph-start)) | |
3380 | (forward-line 1))) | |
3381 | ;; Find the prefix | |
3382 | (when (looking-at message-cite-prefix-regexp) | |
3383 | (setq quoted (match-string 0)) | |
3384 | (goto-char (match-end 0)) | |
3385 | (looking-at "[ \t]*") | |
3386 | (setq leading-space (match-string 0))) | |
3387 | (if (and quoted | |
3388 | (not not-break) | |
3389 | (not bolp) | |
3390 | (< (- point beg) (length quoted))) | |
3391 | ;; break inside the cite prefix. | |
3392 | (setq quoted nil | |
3393 | end nil)) | |
3394 | (if quoted | |
3395 | (progn | |
3396 | (forward-line 1) | |
3397 | (while (and (not (eobp)) | |
3398 | (not (looking-at paragraph-separate)) | |
3399 | (looking-at message-cite-prefix-regexp) | |
3400 | (equal quoted (match-string 0))) | |
3401 | (goto-char (match-end 0)) | |
3402 | (looking-at "[ \t]*") | |
3403 | (if (> (length leading-space) (length (match-string 0))) | |
3404 | (setq leading-space (match-string 0))) | |
3405 | (forward-line 1)) | |
3406 | (setq end (point)) | |
3407 | (goto-char beg) | |
3408 | (while (and (if (bobp) nil (forward-line -1) t) | |
3409 | (not (looking-at paragraph-start)) | |
3410 | (looking-at message-cite-prefix-regexp) | |
3411 | (equal quoted (match-string 0))) | |
3412 | (setq beg (point)) | |
3413 | (goto-char (match-end 0)) | |
3414 | (looking-at "[ \t]*") | |
3415 | (if (> (length leading-space) (length (match-string 0))) | |
3416 | (setq leading-space (match-string 0))))) | |
3417 | (while (and (not (eobp)) | |
3418 | (not (looking-at paragraph-separate)) | |
3419 | (not (looking-at message-cite-prefix-regexp))) | |
3420 | (forward-line 1)) | |
3421 | (setq end (point)) | |
3422 | (goto-char beg) | |
3423 | (while (and (if (bobp) nil (forward-line -1) t) | |
3424 | (not (looking-at paragraph-start)) | |
3425 | (not (looking-at message-cite-prefix-regexp))) | |
3426 | (setq beg (point)))) | |
6748645f | 3427 | (goto-char point) |
23f87bed MB |
3428 | (save-restriction |
3429 | (narrow-to-region beg end) | |
3430 | (if not-break | |
3431 | (setq point nil) | |
3432 | (if bolp | |
3433 | (newline) | |
3434 | (newline) | |
3435 | (newline)) | |
3436 | (setq point (point)) | |
3437 | ;; (newline 2) doesn't mark both newline's as hard, so call | |
3438 | ;; newline twice. -jas | |
3439 | (newline) | |
3440 | (newline) | |
3441 | (delete-region (point) (re-search-forward "[ \t]*")) | |
3442 | (when (and quoted (not bolp)) | |
3443 | (insert quoted leading-space))) | |
3444 | (undo-boundary) | |
3445 | (if quoted | |
3446 | (let* ((adaptive-fill-regexp | |
3447 | (regexp-quote (concat quoted leading-space))) | |
3448 | (adaptive-fill-first-line-regexp | |
3449 | adaptive-fill-regexp )) | |
3450 | (fill-paragraph arg)) | |
3451 | (fill-paragraph arg)) | |
3452 | (if point (goto-char point))))) | |
3453 | ||
3454 | (defun message-fill-paragraph (&optional arg) | |
01c52d31 MB |
3455 | "Message specific function to fill a paragraph. |
3456 | This function is used as the value of `fill-paragraph-function' in | |
3457 | Message buffers and is not meant to be called directly." | |
23f87bed MB |
3458 | (interactive (list (if current-prefix-arg 'full))) |
3459 | (if (if (boundp 'filladapt-mode) filladapt-mode) | |
3460 | nil | |
01c52d31 MB |
3461 | (if (message-point-in-header-p) |
3462 | (message-fill-field) | |
3463 | (message-newline-and-reformat arg t)) | |
23f87bed | 3464 | t)) |
6748645f | 3465 | |
23f87bed MB |
3466 | (defun message-point-in-header-p () |
3467 | "Return t if point is in the header." | |
3468 | (save-excursion | |
3451795c LMI |
3469 | (and |
3470 | (not | |
3471 | (re-search-backward | |
3472 | (concat "^" (regexp-quote mail-header-separator) "\n") nil t)) | |
3473 | (re-search-forward | |
3474 | (concat "^" (regexp-quote mail-header-separator) "\n") nil t)))) | |
23f87bed MB |
3475 | |
3476 | (defun message-do-auto-fill () | |
3477 | "Like `do-auto-fill', but don't fill in message header." | |
3478 | (unless (message-point-in-header-p) | |
3479 | (do-auto-fill))) | |
10893bb6 | 3480 | |
eec82323 | 3481 | (defun message-insert-signature (&optional force) |
7d829636 | 3482 | "Insert a signature. See documentation for variable `message-signature'." |
eec82323 LMI |
3483 | (interactive (list 0)) |
3484 | (let* ((signature | |
3485 | (cond | |
3486 | ((and (null message-signature) | |
3487 | (eq force 0)) | |
3488 | (save-excursion | |
3489 | (goto-char (point-max)) | |
16409b0b | 3490 | (not (re-search-backward message-signature-separator nil t)))) |
eec82323 LMI |
3491 | ((and (null message-signature) |
3492 | force) | |
3493 | t) | |
23f87bed | 3494 | ((functionp message-signature) |
eec82323 LMI |
3495 | (funcall message-signature)) |
3496 | ((listp message-signature) | |
3497 | (eval message-signature)) | |
3498 | (t message-signature))) | |
01c52d31 MB |
3499 | signature-file) |
3500 | (setq signature | |
eec82323 LMI |
3501 | (cond ((stringp signature) |
3502 | signature) | |
01c52d31 MB |
3503 | ((and (eq t signature) message-signature-file) |
3504 | (setq signature-file | |
3505 | (if (and message-signature-directory | |
3506 | ;; don't actually use the signature directory | |
3507 | ;; if message-signature-file contains a path. | |
3508 | (not (file-name-directory | |
3509 | message-signature-file))) | |
aa8f8277 GM |
3510 | (expand-file-name message-signature-file |
3511 | message-signature-directory) | |
01c52d31 MB |
3512 | message-signature-file)) |
3513 | (file-exists-p signature-file)))) | |
eec82323 LMI |
3514 | (when signature |
3515 | (goto-char (point-max)) | |
3516 | ;; Insert the signature. | |
3517 | (unless (bolp) | |
f129a4df | 3518 | (insert "\n")) |
23f87bed | 3519 | (when message-signature-insert-empty-line |
f129a4df CY |
3520 | (insert "\n")) |
3521 | (insert "-- \n") | |
eec82323 | 3522 | (if (eq signature t) |
01c52d31 | 3523 | (insert-file-contents signature-file) |
eec82323 LMI |
3524 | (insert signature)) |
3525 | (goto-char (point-max)) | |
f129a4df | 3526 | (or (bolp) (insert "\n"))))) |
eec82323 | 3527 | |
23f87bed MB |
3528 | (defun message-insert-importance-high () |
3529 | "Insert header to mark message as important." | |
3530 | (interactive) | |
3531 | (save-excursion | |
3532 | (save-restriction | |
3533 | (message-narrow-to-headers) | |
3534 | (message-remove-header "Importance")) | |
3535 | (message-goto-eoh) | |
f129a4df | 3536 | (insert "Importance: high\n"))) |
23f87bed MB |
3537 | |
3538 | (defun message-insert-importance-low () | |
3539 | "Insert header to mark message as unimportant." | |
3540 | (interactive) | |
3541 | (save-excursion | |
3542 | (save-restriction | |
3543 | (message-narrow-to-headers) | |
3544 | (message-remove-header "Importance")) | |
3545 | (message-goto-eoh) | |
f129a4df | 3546 | (insert "Importance: low\n"))) |
23f87bed MB |
3547 | |
3548 | (defun message-insert-or-toggle-importance () | |
3549 | "Insert a \"Importance: high\" header, or cycle through the header values. | |
3550 | The three allowed values according to RFC 1327 are `high', `normal' | |
3551 | and `low'." | |
3552 | (interactive) | |
3553 | (save-excursion | |
82816ba1 | 3554 | (let ((new "high") |
23f87bed MB |
3555 | cur) |
3556 | (save-restriction | |
3557 | (message-narrow-to-headers) | |
3558 | (when (setq cur (message-fetch-field "Importance")) | |
3559 | (message-remove-header "Importance") | |
3560 | (setq new (cond ((string= cur "high") | |
3561 | "low") | |
3562 | ((string= cur "low") | |
3563 | "normal") | |
3564 | (t | |
3565 | "high"))))) | |
3566 | (message-goto-eoh) | |
f129a4df | 3567 | (insert (format "Importance: %s\n" new))))) |
23f87bed MB |
3568 | |
3569 | (defun message-insert-disposition-notification-to () | |
3570 | "Request a disposition notification (return receipt) to this message. | |
3571 | Note that this should not be used in newsgroups." | |
3572 | (interactive) | |
3573 | (save-excursion | |
3574 | (save-restriction | |
3575 | (message-narrow-to-headers) | |
3576 | (message-remove-header "Disposition-Notification-To")) | |
3577 | (message-goto-eoh) | |
f129a4df | 3578 | (insert (format "Disposition-Notification-To: %s\n" |
23f87bed MB |
3579 | (or (message-field-value "Reply-to") |
3580 | (message-field-value "From") | |
f129a4df | 3581 | (message-make-from)))))) |
23f87bed | 3582 | |
eec82323 | 3583 | (defun message-elide-region (b e) |
7d829636 | 3584 | "Elide the text in the region. |
16409b0b | 3585 | An ellipsis (from `message-elide-ellipsis') will be inserted where the |
6748645f | 3586 | text was killed." |
eec82323 | 3587 | (interactive "r") |
a123622d G |
3588 | (let ((lines (count-lines b e)) |
3589 | (chars (- e b))) | |
3590 | (kill-region b e) | |
3591 | (insert (format-spec message-elide-ellipsis | |
3592 | `((?l . ,lines) | |
3593 | (?c . ,chars)))))) | |
eec82323 LMI |
3594 | |
3595 | (defvar message-caesar-translation-table nil) | |
3596 | ||
3597 | (defun message-caesar-region (b e &optional n) | |
7d829636 | 3598 | "Caesar rotate region B to E by N, default 13, for decrypting netnews." |
eec82323 LMI |
3599 | (interactive |
3600 | (list | |
3601 | (min (point) (or (mark t) (point))) | |
3602 | (max (point) (or (mark t) (point))) | |
3603 | (when current-prefix-arg | |
3604 | (prefix-numeric-value current-prefix-arg)))) | |
3605 | ||
3606 | (setq n (if (numberp n) (mod n 26) 13)) ;canonize N | |
23f87bed | 3607 | (unless (or (zerop n) ; no action needed for a rot of 0 |
eec82323 LMI |
3608 | (= b e)) ; no region to rotate |
3609 | ;; We build the table, if necessary. | |
3610 | (when (or (not message-caesar-translation-table) | |
3611 | (/= (aref message-caesar-translation-table ?a) (+ ?a n))) | |
16409b0b GM |
3612 | (setq message-caesar-translation-table |
3613 | (message-make-caesar-translation-table n))) | |
3614 | (translate-region b e message-caesar-translation-table))) | |
eec82323 LMI |
3615 | |
3616 | (defun message-make-caesar-translation-table (n) | |
3617 | "Create a rot table with offset N." | |
3618 | (let ((i -1) | |
3619 | (table (make-string 256 0))) | |
3620 | (while (< (incf i) 256) | |
3621 | (aset table i i)) | |
3622 | (concat | |
3623 | (substring table 0 ?A) | |
3624 | (substring table (+ ?A n) (+ ?A n (- 26 n))) | |
3625 | (substring table ?A (+ ?A n)) | |
3626 | (substring table (+ ?A 26) ?a) | |
3627 | (substring table (+ ?a n) (+ ?a n (- 26 n))) | |
3628 | (substring table ?a (+ ?a n)) | |
3629 | (substring table (+ ?a 26) 255)))) | |
3630 | ||
01c52d31 | 3631 | (defun message-caesar-buffer-body (&optional rotnum wide) |
7d829636 DL |
3632 | "Caesar rotate all letters in the current buffer by 13 places. |
3633 | Used to encode/decode possibly offensive messages (commonly in rec.humor). | |
eec82323 | 3634 | With prefix arg, specifies the number of places to rotate each letter forward. |
01c52d31 | 3635 | Mail and USENET news headers are not rotated unless WIDE is non-nil." |
eec82323 LMI |
3636 | (interactive (if current-prefix-arg |
3637 | (list (prefix-numeric-value current-prefix-arg)) | |
3638 | (list nil))) | |
3639 | (save-excursion | |
3640 | (save-restriction | |
01c52d31 | 3641 | (when (and (not wide) (message-goto-body)) |
eec82323 LMI |
3642 | (narrow-to-region (point) (point-max))) |
3643 | (message-caesar-region (point-min) (point-max) rotnum)))) | |
3644 | ||
3645 | (defun message-pipe-buffer-body (program) | |
3646 | "Pipe the message body in the current buffer through PROGRAM." | |
3647 | (save-excursion | |
3648 | (save-restriction | |
3649 | (when (message-goto-body) | |
23f87bed | 3650 | (narrow-to-region (point) (point-max))) |
16409b0b GM |
3651 | (shell-command-on-region |
3652 | (point-min) (point-max) program nil t)))) | |
eec82323 LMI |
3653 | |
3654 | (defun message-rename-buffer (&optional enter-string) | |
3655 | "Rename the *message* buffer to \"*message* RECIPIENT\". | |
3656 | If the function is run with a prefix, it will ask for a new buffer | |
3657 | name, rather than giving an automatic name." | |
3658 | (interactive "Pbuffer name: ") | |
3659 | (save-excursion | |
3660 | (save-restriction | |
3661 | (goto-char (point-min)) | |
3662 | (narrow-to-region (point) | |
3663 | (search-forward mail-header-separator nil 'end)) | |
3664 | (let* ((mail-to (or | |
3665 | (if (message-news-p) (message-fetch-field "Newsgroups") | |
3666 | (message-fetch-field "To")) | |
3667 | "")) | |
3668 | (mail-trimmed-to | |
3669 | (if (string-match "," mail-to) | |
3670 | (concat (substring mail-to 0 (match-beginning 0)) ", ...") | |
3671 | mail-to)) | |
3672 | (name-default (concat "*message* " mail-trimmed-to)) | |
3673 | (name (if enter-string | |
3674 | (read-string "New buffer name: " name-default) | |
6748645f | 3675 | name-default))) |
eec82323 LMI |
3676 | (rename-buffer name t))))) |
3677 | ||
3678 | (defun message-fill-yanked-message (&optional justifyp) | |
3679 | "Fill the paragraphs of a message yanked into this one. | |
3680 | Numeric argument means justify as well." | |
3681 | (interactive "P") | |
3682 | (save-excursion | |
3683 | (goto-char (point-min)) | |
3684 | (search-forward (concat "\n" mail-header-separator "\n") nil t) | |
3685 | (let ((fill-prefix message-yank-prefix)) | |
16409b0b | 3686 | (fill-individual-paragraphs (point) (point-max) justifyp)))) |
eec82323 | 3687 | |
01c52d31 | 3688 | (defun message-indent-citation (&optional start end yank-only) |
eec82323 LMI |
3689 | "Modify text just inserted from a message to be cited. |
3690 | The inserted text should be the region. | |
3691 | When this function returns, the region is again around the modified text. | |
3692 | ||
3693 | Normally, indent each nonblank line `message-indentation-spaces' spaces. | |
3694 | However, if `message-yank-prefix' is non-nil, insert that prefix on each line." | |
01c52d31 MB |
3695 | (unless start (setq start (point))) |
3696 | (unless yank-only | |
eec82323 LMI |
3697 | ;; Remove unwanted headers. |
3698 | (when message-ignored-cited-headers | |
3699 | (let (all-removed) | |
3700 | (save-restriction | |
3701 | (narrow-to-region | |
3702 | (goto-char start) | |
3703 | (if (search-forward "\n\n" nil t) | |
3704 | (1- (point)) | |
3705 | (point))) | |
3706 | (message-remove-header message-ignored-cited-headers t) | |
3707 | (when (= (point-min) (point-max)) | |
3708 | (setq all-removed t)) | |
3709 | (goto-char (point-max))) | |
3710 | (if all-removed | |
3711 | (goto-char start) | |
3712 | (forward-line 1)))) | |
3713 | ;; Delete blank lines at the start of the buffer. | |
3714 | (while (and (point-min) | |
3715 | (eolp) | |
3716 | (not (eobp))) | |
3717 | (message-delete-line)) | |
3718 | ;; Delete blank lines at the end of the buffer. | |
3719 | (goto-char (point-max)) | |
a5166359 LMI |
3720 | (unless (eq (preceding-char) ?\n) |
3721 | (insert "\n")) | |
3722 | (while (and (zerop (forward-line -1)) | |
3723 | (looking-at "$")) | |
01c52d31 MB |
3724 | (message-delete-line))) |
3725 | ;; Do the indentation. | |
3726 | (if (null message-yank-prefix) | |
3727 | (indent-rigidly start (or end (mark t)) message-indentation-spaces) | |
3728 | (save-excursion | |
3729 | (goto-char start) | |
3730 | (while (< (point) (or end (mark t))) | |
3731 | (cond ((looking-at ">") | |
3732 | (insert message-yank-cited-prefix)) | |
3733 | ((looking-at "^$") | |
3734 | (insert message-yank-empty-prefix)) | |
3735 | (t | |
3736 | (insert message-yank-prefix))) | |
3737 | (forward-line 1)))) | |
3738 | (goto-char start)) | |
3739 | ||
3740 | (defun message-remove-blank-cited-lines (&optional remove) | |
3741 | "Remove cited lines containing only blanks. | |
3742 | If REMOVE is non-nil, remove newlines, too. | |
3743 | ||
3744 | To use this automatically, you may add this function to | |
3745 | `gnus-message-setup-hook'." | |
3746 | (interactive "P") | |
3747 | (let ((citexp | |
3748 | (concat | |
3749 | "^\\(" | |
437ce4be MB |
3750 | (when (boundp 'message-yank-cited-prefix) |
3751 | (concat message-yank-cited-prefix "\\|")) | |
01c52d31 | 3752 | message-yank-prefix |
437ce4be MB |
3753 | "\\)+ *\n" |
3754 | ))) | |
01c52d31 MB |
3755 | (gnus-message 8 "removing `%s'" citexp) |
3756 | (save-excursion | |
3757 | (message-goto-body) | |
3758 | (while (re-search-forward citexp nil t) | |
437ce4be | 3759 | (replace-match (if remove "" "\n")))))) |
01c52d31 | 3760 | |
a5954fa5 | 3761 | (defun message--yank-original-internal (arg) |
01c52d31 MB |
3762 | (let ((modified (buffer-modified-p)) |
3763 | body-text) | |
706b5974 G |
3764 | (when (and message-reply-buffer |
3765 | message-cite-function) | |
3766 | (when (equal message-cite-reply-position 'above) | |
01c52d31 MB |
3767 | (save-excursion |
3768 | (setq body-text | |
3769 | (buffer-substring (message-goto-body) | |
3770 | (point-max))) | |
706b5974 G |
3771 | (delete-region (message-goto-body) (point-max)))) |
3772 | (if (bufferp message-reply-buffer) | |
3773 | (delete-windows-on message-reply-buffer t)) | |
3774 | (push-mark (save-excursion | |
3775 | (cond | |
3776 | ((bufferp message-reply-buffer) | |
3777 | (insert-buffer-substring message-reply-buffer)) | |
3778 | ((and (consp message-reply-buffer) | |
3779 | (functionp (car message-reply-buffer))) | |
3780 | (apply (car message-reply-buffer) | |
3781 | (cdr message-reply-buffer)))) | |
3782 | (unless (bolp) | |
3783 | (insert ?\n)) | |
3784 | (point))) | |
3785 | (unless arg | |
3786 | (funcall message-cite-function) | |
3787 | (unless (eq (char-before (mark t)) ?\n) | |
3788 | (let ((pt (point))) | |
3789 | (goto-char (mark t)) | |
3790 | (insert-before-markers ?\n) | |
3791 | (goto-char pt)))) | |
0adf5618 SM |
3792 | (case message-cite-reply-position |
3793 | (above | |
706b5974 G |
3794 | (message-goto-body) |
3795 | (insert body-text) | |
3796 | (insert (if (bolp) "\n" "\n\n")) | |
3797 | (message-goto-body)) | |
0adf5618 | 3798 | (below |
706b5974 G |
3799 | (message-goto-signature))) |
3800 | ;; Add a `message-setup-very-last-hook' here? | |
3801 | ;; Add `gnus-article-highlight-citation' here? | |
3802 | (unless modified | |
a5954fa5 G |
3803 | (setq message-checksum (message-checksum)))))) |
3804 | ||
3805 | (defun message-yank-original (&optional arg) | |
3806 | "Insert the message being replied to, if any. | |
3807 | Puts point before the text and mark after. | |
3808 | Normally indents each nonblank line ARG spaces (default 3). However, | |
3809 | if `message-yank-prefix' is non-nil, insert that prefix on each line. | |
3810 | ||
3811 | This function uses `message-cite-function' to do the actual citing. | |
3812 | ||
3813 | Just \\[universal-argument] as argument means don't indent, insert no | |
3814 | prefix, and don't delete any headers." | |
3815 | (interactive "P") | |
3816 | ;; eval the let forms contained in message-cite-style | |
3817 | (eval | |
cc21c235 G |
3818 | `(let ,(if (symbolp message-cite-style) |
3819 | (symbol-value message-cite-style) | |
3820 | message-cite-style) | |
a5954fa5 | 3821 | (message--yank-original-internal ',arg)))) |
eec82323 | 3822 | |
16409b0b GM |
3823 | (defun message-yank-buffer (buffer) |
3824 | "Insert BUFFER into the current buffer and quote it." | |
3825 | (interactive "bYank buffer: ") | |
23f87bed | 3826 | (let ((message-reply-buffer (get-buffer buffer))) |
16409b0b GM |
3827 | (save-window-excursion |
3828 | (message-yank-original)))) | |
3829 | ||
3830 | (defun message-buffers () | |
3831 | "Return a list of active message buffers." | |
3832 | (let (buffers) | |
82816ba1 | 3833 | (save-current-buffer |
16409b0b GM |
3834 | (dolist (buffer (buffer-list t)) |
3835 | (set-buffer buffer) | |
89b163db | 3836 | (when (and (derived-mode-p 'message-mode) |
16409b0b GM |
3837 | (null message-sent-message-via)) |
3838 | (push (buffer-name buffer) buffers)))) | |
3839 | (nreverse buffers))) | |
3840 | ||
01c52d31 MB |
3841 | (defun message-cite-original-1 (strip-signature) |
3842 | "Cite an original message. | |
3843 | If STRIP-SIGNATURE is non-nil, strips off the signature from the | |
3844 | original message. | |
3845 | ||
3846 | This function uses `mail-citation-hook' if that is non-nil." | |
4a55f847 | 3847 | (if (and (boundp 'mail-citation-hook) |
16409b0b | 3848 | mail-citation-hook) |
4a55f847 | 3849 | (run-hooks 'mail-citation-hook) |
23f87bed MB |
3850 | (let* ((start (point)) |
3851 | (end (mark t)) | |
01c52d31 | 3852 | (x-no-archive nil) |
23f87bed MB |
3853 | (functions |
3854 | (when message-indent-citation-function | |
3855 | (if (listp message-indent-citation-function) | |
3856 | message-indent-citation-function | |
3857 | (list message-indent-citation-function)))) | |
3858 | ;; This function may be called by `gnus-summary-yank-message' and | |
3859 | ;; may insert a different article from the original. So, we will | |
3860 | ;; modify the value of `message-reply-headers' with that article. | |
3861 | (message-reply-headers | |
3862 | (save-restriction | |
3863 | (narrow-to-region start end) | |
3864 | (message-narrow-to-head-1) | |
01c52d31 | 3865 | (setq x-no-archive (message-fetch-field "x-no-archive")) |
23f87bed MB |
3866 | (vector 0 |
3867 | (or (message-fetch-field "subject") "none") | |
0327a464 | 3868 | (or (message-fetch-field "from") "nobody") |
23f87bed MB |
3869 | (message-fetch-field "date") |
3870 | (message-fetch-field "message-id" t) | |
3871 | (message-fetch-field "references") | |
3872 | 0 0 "")))) | |
16409b0b | 3873 | (mml-quote-region start end) |
01c52d31 MB |
3874 | (when strip-signature |
3875 | ;; Allow undoing. | |
3876 | (undo-boundary) | |
3877 | (goto-char end) | |
3878 | (when (re-search-backward message-signature-separator start t) | |
3879 | ;; Also peel off any blank lines before the signature. | |
3880 | (forward-line -1) | |
3881 | (while (looking-at "^[ \t]*$") | |
3882 | (forward-line -1)) | |
3883 | (forward-line 1) | |
3884 | (delete-region (point) end) | |
3885 | (unless (search-backward "\n\n" start t) | |
3886 | ;; Insert a blank line if it is peeled off. | |
3887 | (insert "\n")))) | |
4a55f847 | 3888 | (goto-char start) |
01c52d31 | 3889 | (mapc 'funcall functions) |
4a55f847 | 3890 | (when message-citation-line-function |
16409b0b | 3891 | (unless (bolp) |
f129a4df | 3892 | (insert "\n")) |
01c52d31 MB |
3893 | (funcall message-citation-line-function)) |
3894 | (when (and x-no-archive | |
3895 | (not message-cite-articles-with-x-no-archive) | |
3896 | (string-match "yes" x-no-archive)) | |
3897 | (undo-boundary) | |
3898 | (delete-region (point) (mark t)) | |
3899 | (insert "> [Quoted text removed due to X-No-Archive]\n") | |
3900 | (push-mark) | |
3901 | (forward-line -1))))) | |
3902 | ||
3903 | (defun message-cite-original () | |
3904 | "Cite function in the standard Message manner." | |
3905 | (message-cite-original-1 nil)) | |
3906 | ||
237dd97f GM |
3907 | (defvar gnus-extract-address-components) |
3908 | ||
ea7d768a GM |
3909 | (autoload 'format-spec "format-spec") |
3910 | ||
4d8a28ec MB |
3911 | (defun message-insert-formatted-citation-line (&optional from date) |
3912 | "Function that inserts a formatted citation line. | |
01c52d31 MB |
3913 | |
3914 | See `message-citation-line-format'." | |
3915 | ;; The optional args are for testing/debugging. They will disappear later. | |
3916 | ;; Example: | |
3917 | ;; (with-temp-buffer | |
4d8a28ec | 3918 | ;; (message-insert-formatted-citation-line |
01c52d31 MB |
3919 | ;; "John Doe <john.doe@example.invalid>" |
3920 | ;; (current-time)) | |
3921 | ;; (buffer-string)) | |
3922 | (when (or message-reply-headers (and from date)) | |
3923 | (unless from | |
3924 | (setq from (mail-header-from message-reply-headers))) | |
3925 | (let* ((data (condition-case () | |
3926 | (funcall (if (boundp gnus-extract-address-components) | |
3927 | gnus-extract-address-components | |
3928 | 'mail-extract-address-components) | |
3929 | from) | |
3930 | (error nil))) | |
3931 | (name (car data)) | |
3932 | (fname name) | |
3933 | (lname name) | |
3934 | (net (car (cdr data))) | |
3935 | (name-or-net (or (car data) | |
3936 | (car (cdr data)) from)) | |
3937 | (replydate | |
3938 | (or | |
3939 | date | |
3940 | ;; We need Gnus functionality if the user wants date or time from | |
3941 | ;; the original article: | |
3942 | (when (string-match "%[^fnNFL]" message-citation-line-format) | |
3943 | (autoload 'gnus-date-get-time "gnus-util") | |
3944 | (gnus-date-get-time (mail-header-date message-reply-headers))))) | |
3945 | (flist | |
3946 | (let ((i ?A) lst) | |
3947 | (when (stringp name) | |
3948 | ;; Guess first name and last name: | |
047dc965 AS |
3949 | (let* ((names (delq nil (mapcar (lambda (x) |
3950 | (if (string-match "\\`\\(\\w\\|[-.]\\)+\\'" x) x nil)) | |
3951 | (split-string name "[ \t]+")))) | |
3952 | (count (length names))) | |
3953 | (cond ((= count 1) (setq fname (car names) | |
3954 | lname "")) | |
3955 | ((or (= count 2) (= count 3)) (setq fname (car names) | |
3956 | lname (mapconcat 'identity (cdr names) " "))) | |
3957 | ((> count 3) (setq fname (mapconcat 'identity (butlast names (- count 2)) " ") | |
3958 | lname (mapconcat 'identity (nthcdr 2 names) " "))) ) | |
3959 | (when (string-match "\\(.*\\),\\'" fname) | |
3960 | (let ((newlname (match-string 1 fname))) | |
3961 | (setq fname lname lname newlname))))) | |
01c52d31 MB |
3962 | ;; The following letters are not used in `format-time-string': |
3963 | (push ?E lst) (push "<E>" lst) | |
3964 | (push ?F lst) (push fname lst) | |
3965 | ;; We might want to use "" instead of "<X>" later. | |
3966 | (push ?J lst) (push "<J>" lst) | |
3967 | (push ?K lst) (push "<K>" lst) | |
3968 | (push ?L lst) (push lname lst) | |
3969 | (push ?N lst) (push name-or-net lst) | |
3970 | (push ?O lst) (push "<O>" lst) | |
3971 | (push ?P lst) (push "<P>" lst) | |
3972 | (push ?Q lst) (push "<Q>" lst) | |
3973 | (push ?f lst) (push from lst) | |
3974 | (push ?i lst) (push "<i>" lst) | |
3975 | (push ?n lst) (push net lst) | |
3976 | (push ?o lst) (push "<o>" lst) | |
3977 | (push ?q lst) (push "<q>" lst) | |
3978 | (push ?t lst) (push "<t>" lst) | |
3979 | (push ?v lst) (push "<v>" lst) | |
3980 | ;; Delegate the rest to `format-time-string': | |
3981 | (while (<= i ?z) | |
3982 | (when (and (not (memq i lst)) | |
3983 | ;; Skip (Z,a) | |
3984 | (or (<= i ?Z) | |
3985 | (>= i ?a))) | |
3986 | (push i lst) | |
3987 | (push (condition-case nil | |
63348d24 GM |
3988 | (format-time-string (format "%%%c" i) replydate) |
3989 | (error (format ">%c<" i))) | |
01c52d31 MB |
3990 | lst)) |
3991 | (setq i (1+ i))) | |
3992 | (reverse lst))) | |
3993 | (spec (apply 'format-spec-make flist))) | |
3994 | (insert (format-spec message-citation-line-format spec))) | |
3995 | (newline))) | |
3996 | ||
3997 | (defun message-cite-original-without-signature () | |
3998 | "Cite function in the standard Message manner. | |
3999 | This function strips off the signature from the original message." | |
4000 | (message-cite-original-1 t)) | |
eec82323 LMI |
4001 | |
4002 | (defun message-insert-citation-line () | |
7d829636 | 4003 | "Insert a simple citation line." |
eec82323 | 4004 | (when message-reply-headers |
1271a058 CY |
4005 | (insert (mail-header-from message-reply-headers) " writes:") |
4006 | (newline) | |
4007 | (newline))) | |
eec82323 LMI |
4008 | |
4009 | (defun message-position-on-field (header &rest afters) | |
4010 | (let ((case-fold-search t)) | |
4011 | (save-restriction | |
4012 | (narrow-to-region | |
4013 | (goto-char (point-min)) | |
4014 | (progn | |
4015 | (re-search-forward | |
4016 | (concat "^" (regexp-quote mail-header-separator) "$")) | |
4017 | (match-beginning 0))) | |
4018 | (goto-char (point-min)) | |
4019 | (if (re-search-forward (concat "^" (regexp-quote header) ":") nil t) | |
4020 | (progn | |
4021 | (re-search-forward "^[^ \t]" nil 'move) | |
4022 | (beginning-of-line) | |
4023 | (skip-chars-backward "\n") | |
4024 | t) | |
4025 | (while (and afters | |
4026 | (not (re-search-forward | |
4027 | (concat "^" (regexp-quote (car afters)) ":") | |
4028 | nil t))) | |
4029 | (pop afters)) | |
4030 | (when afters | |
4031 | (re-search-forward "^[^ \t]" nil 'move) | |
4032 | (beginning-of-line)) | |
f129a4df | 4033 | (insert header ": \n") |
eec82323 LMI |
4034 | (forward-char -1) |
4035 | nil)))) | |
4036 | ||
eec82323 LMI |
4037 | \f |
4038 | ||
4039 | ;;; | |
4040 | ;;; Sending messages | |
4041 | ;;; | |
4042 | ||
4043 | (defun message-send-and-exit (&optional arg) | |
9d217cac LMI |
4044 | "Send message like `message-send', then, if no errors, exit from mail buffer. |
4045 | The usage of ARG is defined by the instance that called Message. | |
4046 | It should typically alter the sending method in some way or other." | |
eec82323 LMI |
4047 | (interactive "P") |
4048 | (let ((buf (current-buffer)) | |
4049 | (actions message-exit-actions)) | |
4050 | (when (and (message-send arg) | |
4051 | (buffer-name buf)) | |
06a73f4e | 4052 | (message-bury buf) |
eec82323 | 4053 | (if message-kill-buffer-on-exit |
25ca2e61 | 4054 | (kill-buffer buf)) |
6748645f LMI |
4055 | (message-do-actions actions) |
4056 | t))) | |
eec82323 LMI |
4057 | |
4058 | (defun message-dont-send () | |
23f87bed MB |
4059 | "Don't send the message you have been editing. |
4060 | Instead, just auto-save the buffer and then bury it." | |
eec82323 | 4061 | (interactive) |
6748645f LMI |
4062 | (set-buffer-modified-p t) |
4063 | (save-buffer) | |
eec82323 LMI |
4064 | (let ((actions message-postpone-actions)) |
4065 | (message-bury (current-buffer)) | |
4066 | (message-do-actions actions))) | |
4067 | ||
4068 | (defun message-kill-buffer () | |
4069 | "Kill the current buffer." | |
4070 | (interactive) | |
4071 | (when (or (not (buffer-modified-p)) | |
01c52d31 | 4072 | (not message-kill-buffer-query) |
eec82323 | 4073 | (yes-or-no-p "Message modified; kill anyway? ")) |
23f87bed MB |
4074 | (let ((actions message-kill-actions) |
4075 | (draft-article message-draft-article) | |
4076 | (auto-save-file-name buffer-auto-save-file-name) | |
4077 | (file-name buffer-file-name) | |
4078 | (modified (buffer-modified-p))) | |
6748645f | 4079 | (setq buffer-file-name nil) |
eec82323 | 4080 | (kill-buffer (current-buffer)) |
23f87bed MB |
4081 | (when (and (or (and auto-save-file-name |
4082 | (file-exists-p auto-save-file-name)) | |
4083 | (and file-name | |
4084 | (file-exists-p file-name))) | |
97f78c9b MB |
4085 | (progn |
4086 | ;; If the message buffer has lived in a dedicated window, | |
4087 | ;; `kill-buffer' has killed the frame. Thus the | |
4088 | ;; `yes-or-no-p' may show up in a lowered frame. Make sure | |
4089 | ;; that the user can see the question by raising the | |
4090 | ;; current frame: | |
4091 | (raise-frame) | |
4092 | (yes-or-no-p (format "Remove the backup file%s? " | |
4093 | (if modified " too" ""))))) | |
23f87bed MB |
4094 | (ignore-errors |
4095 | (delete-file auto-save-file-name)) | |
4096 | (let ((message-draft-article draft-article)) | |
4097 | (message-disassociate-draft))) | |
eec82323 LMI |
4098 | (message-do-actions actions)))) |
4099 | ||
06a73f4e | 4100 | (defun message-bury (buffer) |
7d829636 | 4101 | "Bury this mail BUFFER." |
e49c0a9a GM |
4102 | ;; Note that this is not quite the same as (bury-buffer buffer), |
4103 | ;; since bury-buffer does extra stuff with a nil argument. | |
4104 | ;; Eg http://lists.gnu.org/archive/html/emacs-devel/2014-01/msg00539.html | |
6affdccc | 4105 | (with-current-buffer buffer (bury-buffer)) |
967f2fbb | 4106 | (if message-return-action |
6affdccc | 4107 | (apply (car message-return-action) (cdr message-return-action)))) |
eec82323 LMI |
4108 | |
4109 | (defun message-send (&optional arg) | |
4110 | "Send the message in the current buffer. | |
16409b0b GM |
4111 | If `message-interactive' is non-nil, wait for success indication or |
4112 | error messages, and inform user. | |
4113 | Otherwise any failure is reported in a message back to the user from | |
4114 | the mailer. | |
4115 | The usage of ARG is defined by the instance that called Message. | |
4116 | It should typically alter the sending method in some way or other." | |
eec82323 | 4117 | (interactive "P") |
16409b0b GM |
4118 | ;; Make it possible to undo the coming changes. |
4119 | (undo-boundary) | |
4120 | (let ((inhibit-read-only t)) | |
4121 | (put-text-property (point-min) (point-max) 'read-only nil)) | |
4122 | (message-fix-before-sending) | |
4123 | (run-hooks 'message-send-hook) | |
16dd13df MB |
4124 | (when message-confirm-send |
4125 | (or (y-or-n-p "Send message? ") | |
4126 | (keyboard-quit))) | |
83595c0c | 4127 | (message message-sending-message) |
16409b0b GM |
4128 | (let ((alist message-send-method-alist) |
4129 | (success t) | |
23f87bed MB |
4130 | elem sent dont-barf-on-no-method |
4131 | (message-options message-options)) | |
4132 | (message-options-set-recipient) | |
16409b0b GM |
4133 | (while (and success |
4134 | (setq elem (pop alist))) | |
83595c0c DL |
4135 | (when (funcall (cadr elem)) |
4136 | (when (and (or (not (memq (car elem) | |
4137 | message-sent-message-via)) | |
23f87bed MB |
4138 | (message-fetch-field "supersedes") |
4139 | (if (or (message-gnksa-enable-p 'multiple-copies) | |
4140 | (not (eq (car elem) 'news))) | |
4141 | (y-or-n-p | |
4142 | (format | |
4143 | "Already sent message via %s; resend? " | |
4144 | (car elem))) | |
4145 | (error "Denied posting -- multiple copies"))) | |
83595c0c DL |
4146 | (setq success (funcall (caddr elem) arg))) |
4147 | (setq sent t)))) | |
23f87bed MB |
4148 | (unless (or sent |
4149 | (not success) | |
4150 | (let ((fcc (message-fetch-field "Fcc")) | |
4151 | (gcc (message-fetch-field "Gcc"))) | |
4152 | (when (or fcc gcc) | |
4153 | (or (eq message-allow-no-recipients 'always) | |
4154 | (and (not (eq message-allow-no-recipients 'never)) | |
4155 | (setq dont-barf-on-no-method | |
4156 | (gnus-y-or-n-p | |
4157 | (format "No receiver, perform %s anyway? " | |
4158 | (cond ((and fcc gcc) "Fcc and Gcc") | |
4159 | (fcc "Fcc") | |
4160 | (t "Gcc")))))))))) | |
16409b0b | 4161 | (error "No methods specified to send by")) |
23f87bed MB |
4162 | (when (or dont-barf-on-no-method |
4163 | (and success sent)) | |
16409b0b GM |
4164 | (message-do-fcc) |
4165 | (save-excursion | |
4166 | (run-hooks 'message-sent-hook)) | |
4167 | (message "Sending...done") | |
01c52d31 | 4168 | ;; Do ecomplete address snarfing. |
6b8382e4 KY |
4169 | (when (and (message-mail-alias-type-p 'ecomplete) |
4170 | (not message-inhibit-ecomplete)) | |
01c52d31 | 4171 | (message-put-addresses-in-ecomplete)) |
16409b0b GM |
4172 | ;; Mark the buffer as unmodified and delete auto-save. |
4173 | (set-buffer-modified-p nil) | |
4174 | (delete-auto-save-file-if-necessary t) | |
4175 | (message-disassociate-draft) | |
4176 | ;; Delete other mail buffers and stuff. | |
4177 | (message-do-send-housekeeping) | |
4178 | (message-do-actions message-send-actions) | |
4179 | ;; Return success. | |
4180 | t))) | |
a8151ef7 LMI |
4181 | |
4182 | (defun message-send-via-mail (arg) | |
6748645f | 4183 | "Send the current message via mail." |
a8151ef7 LMI |
4184 | (message-send-mail arg)) |
4185 | ||
4186 | (defun message-send-via-news (arg) | |
4187 | "Send the current message via news." | |
4188 | (funcall message-send-news-function arg)) | |
eec82323 | 4189 | |
16409b0b GM |
4190 | (defmacro message-check (type &rest forms) |
4191 | "Eval FORMS if TYPE is to be checked." | |
4192 | `(or (message-check-element ,type) | |
4193 | (save-excursion | |
4194 | ,@forms))) | |
4195 | ||
4196 | (put 'message-check 'lisp-indent-function 1) | |
4197 | (put 'message-check 'edebug-form-spec '(form body)) | |
4198 | ||
01c52d31 MB |
4199 | (defun message-text-with-property (prop &optional start end reverse) |
4200 | "Return a list of start and end positions where the text has PROP. | |
4201 | START and END bound the search, they default to `point-min' and | |
4202 | `point-max' respectively. If REVERSE is non-nil, find text which does | |
4203 | not have PROP." | |
4204 | (unless start | |
4205 | (setq start (point-min))) | |
4206 | (unless end | |
4207 | (setq end (point-max))) | |
4208 | (let (next regions) | |
4209 | (if reverse | |
4210 | (while (and start | |
4211 | (setq start (text-property-any start end prop nil))) | |
4212 | (setq next (next-single-property-change start prop nil end)) | |
4213 | (push (cons start (or next end)) regions) | |
4214 | (setq start next)) | |
4215 | (while (and start | |
4216 | (or (get-text-property start prop) | |
4217 | (and (setq start (next-single-property-change | |
4218 | start prop nil end)) | |
4219 | (get-text-property start prop)))) | |
4220 | (setq next (text-property-any start end prop nil)) | |
4221 | (push (cons start (or next end)) regions) | |
4222 | (setq start next))) | |
4223 | (nreverse regions))) | |
23f87bed | 4224 | |
656b4fa1 | 4225 | (defcustom message-bogus-addresses |
656b4fa1 MB |
4226 | '("noreply" "nospam" "invalid" "@@" "[^[:ascii:]].*@" "[ \t]") |
4227 | "List of regexps of potentially bogus mail addresses. | |
4228 | See `message-check-recipients' how to setup checking. | |
4229 | ||
4230 | This list should make it possible to catch typos or warn about | |
4231 | spam-trap addresses. It doesn't aim to verify strict RFC | |
4232 | conformance." | |
330f707b | 4233 | :version "23.1" ;; No Gnus |
437ce4be | 4234 | :group 'message-headers |
656b4fa1 MB |
4235 | :type '(choice |
4236 | (const :tag "None" nil) | |
4237 | (list | |
4238 | (set :inline t | |
4239 | (const "noreply") | |
4240 | (const "nospam") | |
4241 | (const "invalid") | |
4242 | (const :tag "duplicate @" "@@") | |
4243 | (const :tag "non-ascii local part" "[^[:ascii:]].*@") | |
4244 | ;; Already caught by `message-valid-fqdn-regexp' | |
4245 | ;; (const :tag "`_' in domain part" "@.*_") | |
4246 | (const :tag "whitespace" "[ \t]")) | |
4247 | (repeat :inline t | |
4248 | :tag "Other" | |
4249 | (regexp))))) | |
437ce4be | 4250 | |
eec82323 LMI |
4251 | (defun message-fix-before-sending () |
4252 | "Do various things to make the message nice before sending it." | |
4253 | ;; Make sure there's a newline at the end of the message. | |
4254 | (goto-char (point-max)) | |
4255 | (unless (bolp) | |
f129a4df | 4256 | (insert "\n")) |
23f87bed | 4257 | ;; Make the hidden headers visible. |
01c52d31 MB |
4258 | (widen) |
4259 | ;; Sort headers before sending the message. | |
4260 | (message-sort-headers) | |
23f87bed MB |
4261 | ;; Make invisible text visible. |
4262 | ;; It doesn't seem as if this is useful, since the invisible property | |
4263 | ;; is clobbered by an after-change hook anyhow. | |
16409b0b | 4264 | (message-check 'invisible-text |
01c52d31 MB |
4265 | (let ((regions (message-text-with-property 'invisible)) |
4266 | from to) | |
4267 | (when regions | |
4268 | (while regions | |
4269 | (setq from (caar regions) | |
4270 | to (cdar regions) | |
4271 | regions (cdr regions)) | |
4272 | (put-text-property from to 'invisible nil) | |
4273 | (message-overlay-put (message-make-overlay from to) | |
23f87bed MB |
4274 | 'face 'highlight)) |
4275 | (unless (yes-or-no-p | |
4276 | "Invisible text found and made visible; continue sending? ") | |
4277 | (error "Invisible text found and made visible"))))) | |
4278 | (message-check 'illegible-text | |
4d9db491 | 4279 | (let (char found choice nul-chars) |
23f87bed | 4280 | (message-goto-body) |
4d9db491 G |
4281 | (setq nul-chars (save-excursion |
4282 | (search-forward "\000" nil t))) | |
01c52d31 MB |
4283 | (while (progn |
4284 | (skip-chars-forward mm-7bit-chars) | |
4285 | (when (get-text-property (point) 'no-illegible-text) | |
4286 | ;; There is a signed or encrypted raw message part | |
4287 | ;; that is considered to be safe. | |
4288 | (goto-char (or (next-single-property-change | |
4289 | (point) 'no-illegible-text) | |
4290 | (point-max)))) | |
4291 | (setq char (char-after))) | |
4292 | (when (or (< (mm-char-int char) 128) | |
4293 | (and (mm-multibyte-p) | |
4294 | (memq (char-charset char) | |
4295 | '(eight-bit-control eight-bit-graphic | |
e3e955fe MB |
4296 | ;; Emacs 23, Bug#1770: |
4297 | eight-bit | |
01c52d31 MB |
4298 | control-1)) |
4299 | (not (get-text-property | |
4300 | (point) 'untranslated-utf-8)))) | |
23f87bed MB |
4301 | (message-overlay-put (message-make-overlay (point) (1+ (point))) |
4302 | 'face 'highlight) | |
4303 | (setq found t)) | |
01c52d31 | 4304 | (forward-char)) |
23f87bed MB |
4305 | (when found |
4306 | (setq choice | |
4307 | (gnus-multiple-choice | |
4d9db491 G |
4308 | (if nul-chars |
4309 | "NUL characters found, which may cause problems. Continue sending?" | |
4310 | "Non-printable characters found. Continue sending?") | |
4573e0df MB |
4311 | `((?d "Remove non-printable characters and send") |
4312 | (?r ,(format | |
4313 | "Replace non-printable characters with \"%s\" and send" | |
4314 | message-replacement-char)) | |
6c85a14f | 4315 | (?s "Send as is without removing anything") |
23f87bed MB |
4316 | (?e "Continue editing")))) |
4317 | (if (eq choice ?e) | |
4318 | (error "Non-printable characters")) | |
4319 | (message-goto-body) | |
4320 | (skip-chars-forward mm-7bit-chars) | |
4321 | (while (not (eobp)) | |
4322 | (when (let ((char (char-after))) | |
4323 | (or (< (mm-char-int char) 128) | |
4324 | (and (mm-multibyte-p) | |
5f4264e5 | 4325 | ;; FIXME: Wrong for Emacs 23 (unicode) and for |
e3e955fe MB |
4326 | ;; things like undecodable utf-8 (in Emacs 21?). |
4327 | ;; Should at least use find-coding-systems-region. | |
4328 | ;; -- fx | |
23f87bed MB |
4329 | (memq (char-charset char) |
4330 | '(eight-bit-control eight-bit-graphic | |
e3e955fe MB |
4331 | ;; Emacs 23, Bug#1770: |
4332 | eight-bit | |
23f87bed MB |
4333 | control-1)) |
4334 | (not (get-text-property | |
4335 | (point) 'untranslated-utf-8))))) | |
4336 | (if (eq choice ?i) | |
4337 | (message-kill-all-overlays) | |
4338 | (delete-char 1) | |
4339 | (when (eq choice ?r) | |
4573e0df | 4340 | (insert message-replacement-char)))) |
23f87bed | 4341 | (forward-char) |
437ce4be MB |
4342 | (skip-chars-forward mm-7bit-chars))))) |
4343 | (message-check 'bogus-recipient | |
03c673c9 | 4344 | ;; Warn before sending a mail to an invalid address. |
437ce4be MB |
4345 | (message-check-recipients))) |
4346 | ||
4347 | (defun message-bogus-recipient-p (recipients) | |
4348 | "Check if a mail address in RECIPIENTS looks bogus. | |
4349 | ||
4350 | RECIPIENTS is a mail header. Return a list of potentially bogus | |
4351 | addresses. If none is found, return nil. | |
4352 | ||
656b4fa1 MB |
4353 | An address might be bogus if the domain part is not fully |
4354 | qualified, see `message-valid-fqdn-regexp', or if there's a | |
4355 | matching entry in `message-bogus-addresses'." | |
437ce4be MB |
4356 | ;; FIXME: How about "foo@subdomain", when the MTA adds ".domain.tld"? |
4357 | (let (found) | |
4358 | (mapc (lambda (address) | |
3b84b005 | 4359 | (setq address (or (cadr address) "")) |
437ce4be | 4360 | (when |
3b84b005 G |
4361 | (or (string= "" address) |
4362 | (not | |
437ce4be MB |
4363 | (or |
4364 | (not (string-match "@" address)) | |
4365 | (string-match | |
4366 | (concat ".@.*\\(" | |
4367 | message-valid-fqdn-regexp "\\)\\'") address))) | |
656b4fa1 MB |
4368 | (and message-bogus-addresses |
4369 | (let ((re | |
4370 | (if (listp message-bogus-addresses) | |
4371 | (mapconcat 'identity | |
4372 | message-bogus-addresses | |
4373 | "\\|") | |
4374 | message-bogus-addresses))) | |
4375 | (string-match re address)))) | |
3b84b005 | 4376 | (push address found))) |
437ce4be MB |
4377 | ;; |
4378 | (mail-extract-address-components recipients t)) | |
4379 | found)) | |
4380 | ||
4381 | (defun message-check-recipients () | |
4382 | "Warn before composing or sending a mail to an invalid address. | |
4383 | ||
4384 | This function could be useful in `message-setup-hook'." | |
4385 | (interactive) | |
4386 | (save-restriction | |
4387 | (message-narrow-to-headers) | |
4388 | (dolist (hdr '("To" "Cc" "Bcc")) | |
4389 | (let ((addr (message-fetch-field hdr))) | |
4390 | (when (stringp addr) | |
4391 | (dolist (bog (message-bogus-recipient-p addr)) | |
4392 | (and bog | |
4393 | (not (y-or-n-p | |
4394 | (format | |
9e928ac9 G |
4395 | "Address `%s'%s might be bogus. Continue? " |
4396 | bog | |
4397 | ;; If the encoded version of the email address | |
4398 | ;; is different from the unencoded version, | |
4399 | ;; then we likely have invisible characters or | |
4400 | ;; the like. Display the encoded version, | |
4401 | ;; too. | |
4402 | (let ((encoded (rfc2047-encode-string bog))) | |
4403 | (if (string= encoded bog) | |
4404 | "" | |
4405 | (format " (%s)" encoded)))))) | |
d93ec753 | 4406 | (error "Bogus address")))))))) |
eec82323 | 4407 | |
03c673c9 MB |
4408 | (custom-add-option 'message-setup-hook 'message-check-recipients) |
4409 | ||
eec82323 LMI |
4410 | (defun message-add-action (action &rest types) |
4411 | "Add ACTION to be performed when doing an exit of type TYPES." | |
23f87bed MB |
4412 | (while types |
4413 | (add-to-list (intern (format "message-%s-actions" (pop types))) | |
4414 | action))) | |
4415 | ||
4416 | (defun message-delete-action (action &rest types) | |
4417 | "Delete ACTION from lists of actions performed when doing an exit of type TYPES." | |
eec82323 LMI |
4418 | (let (var) |
4419 | (while types | |
4420 | (set (setq var (intern (format "message-%s-actions" (pop types)))) | |
23f87bed | 4421 | (delq action (symbol-value var)))))) |
eec82323 LMI |
4422 | |
4423 | (defun message-do-actions (actions) | |
4424 | "Perform all actions in ACTIONS." | |
4425 | ;; Now perform actions on successful sending. | |
01c52d31 | 4426 | (dolist (action actions) |
eec82323 LMI |
4427 | (ignore-errors |
4428 | (cond | |
4429 | ;; A simple function. | |
01c52d31 MB |
4430 | ((functionp action) |
4431 | (funcall action)) | |
99d99081 | 4432 | ;; Something to be evalled. |
eec82323 | 4433 | (t |
01c52d31 | 4434 | (eval action)))))) |
eec82323 | 4435 | |
16409b0b | 4436 | (defun message-send-mail-partially () |
23f87bed | 4437 | "Send mail as message/partial." |
83595c0c DL |
4438 | ;; replace the header delimiter with a blank line |
4439 | (goto-char (point-min)) | |
4440 | (re-search-forward | |
4441 | (concat "^" (regexp-quote mail-header-separator) "\n")) | |
f129a4df | 4442 | (replace-match "\n") |
83595c0c | 4443 | (run-hooks 'message-send-mail-hook) |
16409b0b GM |
4444 | (let ((p (goto-char (point-min))) |
4445 | (tembuf (message-generate-new-buffer-clone-locals " message temp")) | |
4446 | (curbuf (current-buffer)) | |
4447 | (id (message-make-message-id)) (n 1) | |
99c2a4e3 | 4448 | plist total header) |
16409b0b GM |
4449 | (while (not (eobp)) |
4450 | (if (< (point-max) (+ p message-send-mail-partially-limit)) | |
4451 | (goto-char (point-max)) | |
4452 | (goto-char (+ p message-send-mail-partially-limit)) | |
4453 | (beginning-of-line) | |
4454 | (if (<= (point) p) (forward-line 1))) ;; In case of bad message. | |
4455 | (push p plist) | |
4456 | (setq p (point))) | |
4457 | (setq total (length plist)) | |
4458 | (push (point-max) plist) | |
4459 | (setq plist (nreverse plist)) | |
4460 | (unwind-protect | |
4461 | (save-excursion | |
4462 | (setq p (pop plist)) | |
4463 | (while plist | |
4464 | (set-buffer curbuf) | |
4465 | (copy-to-buffer tembuf p (car plist)) | |
4466 | (set-buffer tembuf) | |
4467 | (goto-char (point-min)) | |
4468 | (if header | |
4469 | (progn | |
4470 | (goto-char (point-min)) | |
4471 | (narrow-to-region (point) (point)) | |
4472 | (insert header)) | |
4473 | (message-goto-eoh) | |
4474 | (setq header (buffer-substring (point-min) (point))) | |
4475 | (goto-char (point-min)) | |
4476 | (narrow-to-region (point) (point)) | |
4477 | (insert header) | |
4478 | (message-remove-header "Mime-Version") | |
4479 | (message-remove-header "Content-Type") | |
4480 | (message-remove-header "Content-Transfer-Encoding") | |
4481 | (message-remove-header "Message-ID") | |
4482 | (message-remove-header "Lines") | |
4483 | (goto-char (point-max)) | |
f129a4df | 4484 | (insert "Mime-Version: 1.0\n") |
23f87bed | 4485 | (setq header (buffer-string))) |
16409b0b | 4486 | (goto-char (point-max)) |
f129a4df CY |
4487 | (insert (format "Content-Type: message/partial; id=\"%s\"; number=%d; total=%d\n\n" |
4488 | id n total)) | |
23f87bed | 4489 | (forward-char -1) |
16409b0b GM |
4490 | (let ((mail-header-separator "")) |
4491 | (when (memq 'Message-ID message-required-mail-headers) | |
f129a4df | 4492 | (insert "Message-ID: " (message-make-message-id) "\n")) |
16409b0b | 4493 | (when (memq 'Lines message-required-mail-headers) |
f129a4df | 4494 | (insert "Lines: " (message-make-lines) "\n")) |
16409b0b GM |
4495 | (message-goto-subject) |
4496 | (end-of-line) | |
4497 | (insert (format " (%d/%d)" n total)) | |
16409b0b | 4498 | (widen) |
89b163db G |
4499 | (if message-send-mail-real-function |
4500 | (funcall message-send-mail-real-function) | |
4501 | (message-multi-smtp-send-mail))) | |
16409b0b GM |
4502 | (setq n (+ n 1)) |
4503 | (setq p (pop plist)) | |
4504 | (erase-buffer))) | |
4505 | (kill-buffer tembuf)))) | |
4506 | ||
aa8f8277 GM |
4507 | (declare-function hashcash-wait-async "hashcash" (&optional buffer)) |
4508 | ||
eec82323 LMI |
4509 | (defun message-send-mail (&optional arg) |
4510 | (require 'mail-utils) | |
16409b0b GM |
4511 | (let* ((tembuf (message-generate-new-buffer-clone-locals " message temp")) |
4512 | (case-fold-search nil) | |
4513 | (news (message-news-p)) | |
4514 | (mailbuf (current-buffer)) | |
4515 | (message-this-is-mail t) | |
aa8f8277 GM |
4516 | ;; gnus-setup-posting-charset is autoloaded in mml.el (FIXME |
4517 | ;; maybe it should not be), which this file requires. Hence | |
4518 | ;; the fboundp test is always true. Loading it from gnus-msg | |
4519 | ;; loads many Gnus files (Bug#5642). If | |
4520 | ;; gnus-group-posting-charset-alist hasn't been customized, | |
4521 | ;; this is just going to return nil anyway. FIXME it would | |
4522 | ;; be good to improve this further, because even if g-g-p-c-a | |
4523 | ;; has been customized, that is likely to just be for news. | |
4524 | ;; Eg either move the definition from gnus-msg, or separate out | |
4525 | ;; the mail and news parts. | |
16409b0b | 4526 | (message-posting-charset |
aa8f8277 GM |
4527 | (if (and (fboundp 'gnus-setup-posting-charset) |
4528 | (boundp 'gnus-group-posting-charset-alist)) | |
16409b0b | 4529 | (gnus-setup-posting-charset nil) |
23f87bed | 4530 | message-posting-charset)) |
f65bd833 KY |
4531 | (headers message-required-mail-headers) |
4532 | options) | |
01c52d31 MB |
4533 | (when (and message-generate-hashcash |
4534 | (not (eq message-generate-hashcash 'opportunistic))) | |
4535 | (message "Generating hashcash...") | |
aa8f8277 | 4536 | (require 'hashcash) |
01c52d31 MB |
4537 | ;; Wait for calculations already started to finish... |
4538 | (hashcash-wait-async) | |
4539 | ;; ...and do calculations not already done. mail-add-payment | |
4540 | ;; will leave existing X-Hashcash headers alone. | |
4541 | (mail-add-payment) | |
4542 | (message "Generating hashcash...done")) | |
eec82323 LMI |
4543 | (save-restriction |
4544 | (message-narrow-to-headers) | |
23f87bed MB |
4545 | ;; Generate the Mail-Followup-To header if the header is not there... |
4546 | (if (and (message-subscribed-p) | |
4547 | (not (mail-fetch-field "mail-followup-to"))) | |
4548 | (setq headers | |
4549 | (cons | |
4550 | (cons "Mail-Followup-To" (message-make-mail-followup-to)) | |
4551 | message-required-mail-headers)) | |
4552 | ;; otherwise, delete the MFT header if the field is empty | |
4553 | (when (equal "" (mail-fetch-field "mail-followup-to")) | |
4554 | (message-remove-header "^Mail-Followup-To:"))) | |
eec82323 LMI |
4555 | ;; Insert some headers. |
4556 | (let ((message-deletable-headers | |
4557 | (if news nil message-deletable-headers))) | |
23f87bed | 4558 | (message-generate-headers headers)) |
f362b760 MB |
4559 | ;; Check continuation headers. |
4560 | (message-check 'continuation-headers | |
4561 | (goto-char (point-min)) | |
4562 | (while (re-search-forward "^[^ \t\n][^ \t\n:]*[ \t\n]" nil t) | |
4563 | (goto-char (match-beginning 0)) | |
4564 | (if (y-or-n-p "Fix continuation lines? ") | |
4565 | (insert " ") | |
4566 | (forward-line 1) | |
4567 | (unless (y-or-n-p "Send anyway? ") | |
4568 | (error "Failed to send the message"))))) | |
eec82323 LMI |
4569 | ;; Let the user do all of the above. |
4570 | (run-hooks 'message-header-hook)) | |
f65bd833 | 4571 | (setq options message-options) |
eec82323 | 4572 | (unwind-protect |
82816ba1 | 4573 | (with-current-buffer tembuf |
eec82323 | 4574 | (erase-buffer) |
f65bd833 | 4575 | (setq message-options options) |
23f87bed | 4576 | ;; Avoid copying text props (except hard newlines). |
ce1ec550 | 4577 | (insert (with-current-buffer mailbuf |
23f87bed MB |
4578 | (mml-buffer-substring-no-properties-except-hard-newlines |
4579 | (point-min) (point-max)))) | |
eec82323 | 4580 | ;; Remove some headers. |
16409b0b | 4581 | (message-encode-message-body) |
eec82323 LMI |
4582 | (save-restriction |
4583 | (message-narrow-to-headers) | |
16409b0b GM |
4584 | ;; We (re)generate the Lines header. |
4585 | (when (memq 'Lines message-required-mail-headers) | |
4586 | (message-generate-headers '(Lines))) | |
eec82323 | 4587 | ;; Remove some headers. |
16409b0b GM |
4588 | (message-remove-header message-ignored-mail-headers t) |
4589 | (let ((mail-parse-charset message-default-charset)) | |
4590 | (mail-encode-encoded-word-buffer))) | |
eec82323 LMI |
4591 | (goto-char (point-max)) |
4592 | ;; require one newline at the end. | |
4593 | (or (= (preceding-char) ?\n) | |
f129a4df | 4594 | (insert ?\n)) |
23f87bed MB |
4595 | (message-cleanup-headers) |
4596 | ;; FIXME: we're inserting the courtesy copy after encoding. | |
4597 | ;; This is wrong if the courtesy copy string contains | |
4598 | ;; non-ASCII characters. -- jh | |
7d829636 | 4599 | (when |
16409b0b GM |
4600 | (save-restriction |
4601 | (message-narrow-to-headers) | |
4602 | (and news | |
4ddab346 G |
4603 | (not (message-fetch-field "List-Post")) |
4604 | (not (message-fetch-field "List-ID")) | |
eec82323 | 4605 | (or (message-fetch-field "cc") |
23f87bed | 4606 | (message-fetch-field "bcc") |
16409b0b | 4607 | (message-fetch-field "to")) |
23f87bed MB |
4608 | (let ((content-type (message-fetch-field |
4609 | "content-type"))) | |
4610 | (and | |
4611 | (or | |
4612 | (not content-type) | |
4613 | (string= "text/plain" | |
4614 | (car | |
4615 | (mail-header-parse-content-type | |
4616 | content-type)))) | |
4617 | (not | |
4618 | (string= "base64" | |
4619 | (message-fetch-field | |
4620 | "content-transfer-encoding"))))))) | |
181cb5fb G |
4621 | (message-insert-courtesy-copy |
4622 | (with-current-buffer mailbuf | |
4623 | message-courtesy-message))) | |
8a986865 SM |
4624 | ;; Let's make sure we encoded all the body. |
4625 | (assert (save-excursion | |
4626 | (goto-char (point-min)) | |
4627 | (not (re-search-forward "[^\000-\377]" nil t)))) | |
4628 | (mm-disable-multibyte) | |
16409b0b | 4629 | (if (or (not message-send-mail-partially-limit) |
3c3979f1 | 4630 | (< (buffer-size) message-send-mail-partially-limit) |
23f87bed MB |
4631 | (not (message-y-or-n-p |
4632 | "The message size is too large, split? " | |
4633 | t | |
4634 | "\ | |
4635 | The message size, " | |
3c3979f1 | 4636 | (/ (buffer-size) 1000) "KB, is too large. |
23f87bed MB |
4637 | |
4638 | Some mail gateways (MTA's) bounce large messages. To avoid the | |
4639 | problem, answer `y', and the message will be split into several | |
4640 | smaller pieces, the size of each is about " | |
4641 | (/ message-send-mail-partially-limit 1000) | |
4642 | "KB except the last | |
4643 | one. | |
4644 | ||
4645 | However, some mail readers (MUA's) can't read split messages, i.e., | |
4646 | mails in message/partially format. Answer `n', and the message will be | |
4647 | sent in one piece. | |
4648 | ||
4649 | The size limit is controlled by `message-send-mail-partially-limit'. | |
4650 | If you always want Gnus to send messages in one piece, set | |
4651 | `message-send-mail-partially-limit' to nil. | |
4652 | "))) | |
8a986865 | 4653 | (progn |
23f87bed | 4654 | (message "Sending via mail...") |
89b163db G |
4655 | (if message-send-mail-real-function |
4656 | (funcall message-send-mail-real-function) | |
4657 | (message-multi-smtp-send-mail))) | |
f65bd833 KY |
4658 | (message-send-mail-partially)) |
4659 | (setq options message-options)) | |
eec82323 LMI |
4660 | (kill-buffer tembuf)) |
4661 | (set-buffer mailbuf) | |
f65bd833 | 4662 | (setq message-options options) |
eec82323 LMI |
4663 | (push 'mail message-sent-message-via))) |
4664 | ||
f0dd243b | 4665 | (defvar sendmail-program) |
89b163db G |
4666 | (defvar smtpmail-smtp-user) |
4667 | ||
4668 | (defun message-multi-smtp-send-mail () | |
4669 | "Send the current buffer to `message-send-mail-function'. | |
4670 | Or, if there's a header that specifies a different method, use | |
4671 | that instead." | |
4672 | (let ((method (message-field-value "X-Message-SMTP-Method"))) | |
4673 | (if (not method) | |
4674 | (funcall message-send-mail-function) | |
4675 | (message-remove-header "X-Message-SMTP-Method") | |
4676 | (setq method (split-string method)) | |
4677 | (cond | |
4678 | ((equal (car method) "sendmail") | |
4679 | (message-send-mail-with-sendmail)) | |
4680 | ((equal (car method) "smtp") | |
4681 | (require 'smtpmail) | |
4682 | (let ((smtpmail-smtp-server (nth 1 method)) | |
4683 | (smtpmail-smtp-service (nth 2 method)) | |
4684 | (smtpmail-smtp-user (or (nth 3 method) smtpmail-smtp-user))) | |
4685 | (message-smtpmail-send-it))) | |
4686 | (t | |
4687 | (error "Unknown method %s" method)))))) | |
f0dd243b | 4688 | |
eec82323 LMI |
4689 | (defun message-send-mail-with-sendmail () |
4690 | "Send off the prepared buffer with sendmail." | |
aa8f8277 | 4691 | (require 'sendmail) |
eec82323 | 4692 | (let ((errbuf (if message-interactive |
16409b0b GM |
4693 | (message-generate-new-buffer-clone-locals |
4694 | " sendmail errors") | |
eec82323 LMI |
4695 | 0)) |
4696 | resend-to-addresses delimline) | |
23f87bed MB |
4697 | (unwind-protect |
4698 | (progn | |
4699 | (let ((case-fold-search t)) | |
4700 | (save-restriction | |
4701 | (message-narrow-to-headers) | |
4702 | (setq resend-to-addresses (message-fetch-field "resent-to"))) | |
4703 | ;; Change header-delimiter to be what sendmail expects. | |
4704 | (goto-char (point-min)) | |
4705 | (re-search-forward | |
4706 | (concat "^" (regexp-quote mail-header-separator) "\n")) | |
f129a4df | 4707 | (replace-match "\n") |
23f87bed MB |
4708 | (backward-char 1) |
4709 | (setq delimline (point-marker)) | |
4710 | (run-hooks 'message-send-mail-hook) | |
4711 | ;; Insert an extra newline if we need it to work around | |
4712 | ;; Sun's bug that swallows newlines. | |
4713 | (goto-char (1+ delimline)) | |
4714 | (when (eval message-mailer-swallows-blank-line) | |
4715 | (newline)) | |
4716 | (when message-interactive | |
01c52d31 | 4717 | (with-current-buffer errbuf |
23f87bed MB |
4718 | (erase-buffer)))) |
4719 | (let* ((default-directory "/") | |
4720 | (coding-system-for-write message-send-coding-system) | |
4721 | (cpr (apply | |
4722 | 'call-process-region | |
4723 | (append | |
9d463c8c | 4724 | (list (point-min) (point-max) sendmail-program |
23f87bed | 4725 | nil errbuf nil "-oi") |
01c52d31 | 4726 | message-sendmail-extra-arguments |
23f87bed MB |
4727 | ;; Always specify who from, |
4728 | ;; since some systems have broken sendmails. | |
4729 | ;; But some systems are more broken with -f, so | |
4730 | ;; we'll let users override this. | |
4332f3ec | 4731 | (and (null message-sendmail-f-is-evil) |
4332f3ec | 4732 | (list "-f" (message-sendmail-envelope-from))) |
23f87bed MB |
4733 | ;; These mean "report errors by mail" |
4734 | ;; and "deliver in background". | |
4735 | (if (null message-interactive) '("-oem" "-odb")) | |
4736 | ;; Get the addresses from the message | |
4737 | ;; unless this is a resend. | |
4738 | ;; We must not do that for a resend | |
4739 | ;; because we would find the original addresses. | |
4740 | ;; For a resend, include the specific addresses. | |
4741 | (if resend-to-addresses | |
4742 | (list resend-to-addresses) | |
4743 | '("-t")))))) | |
4744 | (unless (or (null cpr) (and (numberp cpr) (zerop cpr))) | |
0c189568 VS |
4745 | (when errbuf |
4746 | (pop-to-buffer errbuf) | |
4747 | (setq errbuf nil)) | |
23f87bed MB |
4748 | (error "Sending...failed with exit value %d" cpr))) |
4749 | (when message-interactive | |
82816ba1 | 4750 | (with-current-buffer errbuf |
23f87bed | 4751 | (goto-char (point-min)) |
01c52d31 | 4752 | (while (re-search-forward "\n+ *" nil t) |
23f87bed MB |
4753 | (replace-match "; ")) |
4754 | (if (not (zerop (buffer-size))) | |
4755 | (error "Sending...failed to %s" | |
4756 | (buffer-string)))))) | |
eec82323 LMI |
4757 | (when (bufferp errbuf) |
4758 | (kill-buffer errbuf))))) | |
4759 | ||
4760 | (defun message-send-mail-with-qmail () | |
4761 | "Pass the prepared message buffer to qmail-inject. | |
4762 | Refer to the documentation for the variable `message-send-mail-function' | |
4763 | to find out how to use this." | |
4764 | ;; replace the header delimiter with a blank line | |
4765 | (goto-char (point-min)) | |
4766 | (re-search-forward | |
4767 | (concat "^" (regexp-quote mail-header-separator) "\n")) | |
f129a4df | 4768 | (replace-match "\n") |
eec82323 LMI |
4769 | (run-hooks 'message-send-mail-hook) |
4770 | ;; send the message | |
4771 | (case | |
6748645f LMI |
4772 | (let ((coding-system-for-write message-send-coding-system)) |
4773 | (apply | |
3c3979f1 SM |
4774 | 'call-process-region (point-min) (point-max) |
4775 | message-qmail-inject-program nil nil nil | |
fffa137c | 4776 | ;; qmail-inject's default behavior is to look for addresses on the |
6748645f LMI |
4777 | ;; command line; if there're none, it scans the headers. |
4778 | ;; yes, it does The Right Thing w.r.t. Resent-To and it's kin. | |
4779 | ;; | |
4780 | ;; in general, ALL of qmail-inject's defaults are perfect for simply | |
4781 | ;; reading a formatted (i. e., at least a To: or Resent-To header) | |
4782 | ;; message from stdin. | |
4783 | ;; | |
4784 | ;; qmail also has the advantage of not having been raped by | |
4785 | ;; various vendors, so we don't have to allow for that, either -- | |
4786 | ;; compare this with message-send-mail-with-sendmail and weep | |
4787 | ;; for sendmail's lost innocence. | |
4788 | ;; | |
4789 | ;; all this is way cool coz it lets us keep the arguments entirely | |
4790 | ;; free for -inject-arguments -- a big win for the user and for us | |
4791 | ;; since we don't have to play that double-guessing game and the user | |
4792 | ;; gets full control (no gestapo'ish -f's, for instance). --sj | |
01c52d31 MB |
4793 | (if (functionp message-qmail-inject-args) |
4794 | (funcall message-qmail-inject-args) | |
4795 | message-qmail-inject-args))) | |
eec82323 LMI |
4796 | ;; qmail-inject doesn't say anything on it's stdout/stderr, |
4797 | ;; we have to look at the retval instead | |
4798 | (0 nil) | |
23f87bed | 4799 | (100 (error "qmail-inject reported permanent failure")) |
a8151ef7 | 4800 | (111 (error "qmail-inject reported transient failure")) |
eec82323 | 4801 | ;; should never happen |
a8151ef7 | 4802 | (t (error "qmail-inject reported unknown failure")))) |
eec82323 | 4803 | |
99c2a4e3 SM |
4804 | (defvar mh-previous-window-config) |
4805 | ||
eec82323 LMI |
4806 | (defun message-send-mail-with-mh () |
4807 | "Send the prepared message buffer with mh." | |
4808 | (let ((mh-previous-window-config nil) | |
6748645f | 4809 | (name (mh-new-draft-name))) |
eec82323 LMI |
4810 | (setq buffer-file-name name) |
4811 | ;; MH wants to generate these headers itself. | |
4812 | (when message-mh-deletable-headers | |
4813 | (let ((headers message-mh-deletable-headers)) | |
4814 | (while headers | |
4815 | (goto-char (point-min)) | |
4816 | (and (re-search-forward | |
4817 | (concat "^" (symbol-name (car headers)) ": *") nil t) | |
4818 | (message-delete-line)) | |
4819 | (pop headers)))) | |
4820 | (run-hooks 'message-send-mail-hook) | |
4821 | ;; Pass it on to mh. | |
4822 | (mh-send-letter))) | |
4823 | ||
23f87bed MB |
4824 | (defun message-smtpmail-send-it () |
4825 | "Send the prepared message buffer with `smtpmail-send-it'. | |
daf4b83b GM |
4826 | The only difference from `smtpmail-send-it' is that this command |
4827 | evaluates `message-send-mail-hook' just before sending a message. | |
4828 | It is useful if your ISP requires the POP-before-SMTP | |
4829 | authentication. See the Gnus manual for details." | |
23f87bed MB |
4830 | (run-hooks 'message-send-mail-hook) |
4831 | (smtpmail-send-it)) | |
4832 | ||
2ea6167b RS |
4833 | (defun message-send-mail-with-mailclient () |
4834 | "Send the prepared message buffer with `mailclient-send-it'. | |
daf4b83b GM |
4835 | The only difference from `mailclient-send-it' is that this |
4836 | command evaluates `message-send-mail-hook' just before sending a message." | |
2ea6167b RS |
4837 | (run-hooks 'message-send-mail-hook) |
4838 | (mailclient-send-it)) | |
4839 | ||
23f87bed MB |
4840 | (defun message-canlock-generate () |
4841 | "Return a string that is non-trivial to guess. | |
4842 | Do not use this for anything important, it is cryptographically weak." | |
4843 | (require 'sha1) | |
4844 | (let (sha1-maximum-internal-length) | |
4845 | (sha1 (concat (message-unique-id) | |
0e23ef9d | 4846 | (format "%x%x%x" (random) (random) (random)) |
23f87bed MB |
4847 | (prin1-to-string (recent-keys)) |
4848 | (prin1-to-string (garbage-collect)))))) | |
4849 | ||
aa8f8277 GM |
4850 | (defvar canlock-password) |
4851 | (defvar canlock-password-for-verify) | |
4852 | ||
23f87bed MB |
4853 | (defun message-canlock-password () |
4854 | "The password used by message for cancel locks. | |
4855 | This is the value of `canlock-password', if that option is non-nil. | |
4856 | Otherwise, generate and save a value for `canlock-password' first." | |
aa8f8277 | 4857 | (require 'canlock) |
23f87bed MB |
4858 | (unless canlock-password |
4859 | (customize-save-variable 'canlock-password (message-canlock-generate)) | |
4860 | (setq canlock-password-for-verify canlock-password)) | |
4861 | canlock-password) | |
4862 | ||
4863 | (defun message-insert-canlock () | |
4864 | (when message-insert-canlock | |
4865 | (message-canlock-password) | |
4866 | (canlock-insert-header))) | |
4867 | ||
aa8f8277 GM |
4868 | (autoload 'nnheader-get-report "nnheader") |
4869 | ||
4870 | (declare-function gnus-setup-posting-charset "gnus-msg" (group)) | |
4871 | ||
eec82323 | 4872 | (defun message-send-news (&optional arg) |
aa8f8277 | 4873 | (require 'gnus-msg) |
16409b0b GM |
4874 | (let* ((tembuf (message-generate-new-buffer-clone-locals " *message temp*")) |
4875 | (case-fold-search nil) | |
23f87bed | 4876 | (method (if (functionp message-post-method) |
16409b0b GM |
4877 | (funcall message-post-method arg) |
4878 | message-post-method)) | |
23f87bed MB |
4879 | (newsgroups-field (save-restriction |
4880 | (message-narrow-to-headers-or-head) | |
4881 | (message-fetch-field "Newsgroups"))) | |
4882 | (followup-field (save-restriction | |
4883 | (message-narrow-to-headers-or-head) | |
4884 | (message-fetch-field "Followup-To"))) | |
4885 | ;; BUG: We really need to get the charset for each name in the | |
4886 | ;; Newsgroups and Followup-To lines to allow crossposting | |
c80e3b4a | 4887 | ;; between group names with incompatible character sets. |
23f87bed MB |
4888 | ;; -- Per Abrahamsen <abraham@dina.kvl.dk> 2001-10-08. |
4889 | (group-field-charset | |
4890 | (gnus-group-name-charset method newsgroups-field)) | |
4891 | (followup-field-charset | |
4892 | (gnus-group-name-charset method (or followup-field ""))) | |
16409b0b | 4893 | (rfc2047-header-encoding-alist |
23f87bed MB |
4894 | (append (when group-field-charset |
4895 | (list (cons "Newsgroups" group-field-charset))) | |
4896 | (when followup-field-charset | |
4897 | (list (cons "Followup-To" followup-field-charset))) | |
4898 | rfc2047-header-encoding-alist)) | |
16409b0b GM |
4899 | (messbuf (current-buffer)) |
4900 | (message-syntax-checks | |
23f87bed MB |
4901 | (if (and arg |
4902 | (listp message-syntax-checks)) | |
16409b0b GM |
4903 | (cons '(existing-newsgroups . disabled) |
4904 | message-syntax-checks) | |
4905 | message-syntax-checks)) | |
4906 | (message-this-is-news t) | |
23f87bed MB |
4907 | (message-posting-charset |
4908 | (gnus-setup-posting-charset newsgroups-field)) | |
16409b0b GM |
4909 | result) |
4910 | (if (not (message-check-news-body-syntax)) | |
4911 | nil | |
4912 | (save-restriction | |
4913 | (message-narrow-to-headers) | |
4914 | ;; Insert some headers. | |
4915 | (message-generate-headers message-required-news-headers) | |
23f87bed | 4916 | (message-insert-canlock) |
16409b0b GM |
4917 | ;; Let the user do all of the above. |
4918 | (run-hooks 'message-header-hook)) | |
23f87bed MB |
4919 | ;; Note: This check will be disabled by the ".*" default value for |
4920 | ;; gnus-group-name-charset-group-alist. -- Pa 2001-10-07. | |
4921 | (when (and group-field-charset | |
4922 | (listp message-syntax-checks)) | |
4923 | (setq message-syntax-checks | |
16409b0b GM |
4924 | (cons '(valid-newsgroups . disabled) |
4925 | message-syntax-checks))) | |
4926 | (message-cleanup-headers) | |
23f87bed MB |
4927 | (if (not (let ((message-post-method method)) |
4928 | (message-check-news-syntax))) | |
16409b0b GM |
4929 | nil |
4930 | (unwind-protect | |
82816ba1 | 4931 | (with-current-buffer tembuf |
16409b0b GM |
4932 | (buffer-disable-undo) |
4933 | (erase-buffer) | |
23f87bed MB |
4934 | ;; Avoid copying text props (except hard newlines). |
4935 | (insert | |
4936 | (with-current-buffer messbuf | |
4937 | (mml-buffer-substring-no-properties-except-hard-newlines | |
4938 | (point-min) (point-max)))) | |
16409b0b | 4939 | (message-encode-message-body) |
eec82323 | 4940 | ;; Remove some headers. |
16409b0b GM |
4941 | (save-restriction |
4942 | (message-narrow-to-headers) | |
4943 | ;; We (re)generate the Lines header. | |
4944 | (when (memq 'Lines message-required-mail-headers) | |
4945 | (message-generate-headers '(Lines))) | |
4946 | ;; Remove some headers. | |
4947 | (message-remove-header message-ignored-news-headers t) | |
4948 | (let ((mail-parse-charset message-default-charset)) | |
4949 | (mail-encode-encoded-word-buffer))) | |
4950 | (goto-char (point-max)) | |
4951 | ;; require one newline at the end. | |
4952 | (or (= (preceding-char) ?\n) | |
f129a4df | 4953 | (insert ?\n)) |
16409b0b GM |
4954 | (let ((case-fold-search t)) |
4955 | ;; Remove the delimiter. | |
4956 | (goto-char (point-min)) | |
4957 | (re-search-forward | |
4958 | (concat "^" (regexp-quote mail-header-separator) "\n")) | |
f129a4df | 4959 | (replace-match "\n") |
16409b0b GM |
4960 | (backward-char 1)) |
4961 | (run-hooks 'message-send-news-hook) | |
4962 | (gnus-open-server method) | |
23f87bed | 4963 | (message "Sending news via %s..." (gnus-server-string method)) |
16409b0b GM |
4964 | (setq result (let ((mail-header-separator "")) |
4965 | (gnus-request-post method)))) | |
4966 | (kill-buffer tembuf)) | |
4967 | (set-buffer messbuf) | |
4968 | (if result | |
4969 | (push 'news message-sent-message-via) | |
4970 | (message "Couldn't send message via news: %s" | |
4971 | (nnheader-get-report (car method))) | |
4972 | nil))))) | |
eec82323 LMI |
4973 | |
4974 | ;;; | |
4975 | ;;; Header generation & syntax checking. | |
4976 | ;;; | |
4977 | ||
eec82323 | 4978 | (defun message-check-element (type) |
7d829636 | 4979 | "Return non-nil if this TYPE is not to be checked." |
eec82323 LMI |
4980 | (if (eq message-syntax-checks 'dont-check-for-anything-just-trust-me) |
4981 | t | |
4982 | (let ((able (assq type message-syntax-checks))) | |
4983 | (and (consp able) | |
4984 | (eq (cdr able) 'disabled))))) | |
4985 | ||
4986 | (defun message-check-news-syntax () | |
4987 | "Check the syntax of the message." | |
4988 | (save-excursion | |
4989 | (save-restriction | |
4990 | (widen) | |
16409b0b GM |
4991 | ;; We narrow to the headers and check them first. |
4992 | (save-excursion | |
4993 | (save-restriction | |
4994 | (message-narrow-to-headers) | |
4995 | (message-check-news-header-syntax)))))) | |
eec82323 LMI |
4996 | |
4997 | (defun message-check-news-header-syntax () | |
4998 | (and | |
16409b0b GM |
4999 | ;; Check Newsgroups header. |
5000 | (message-check 'newsgroups | |
5001 | (let ((group (message-fetch-field "newsgroups"))) | |
5002 | (or | |
5003 | (and group | |
5004 | (not (string-match "\\`[ \t]*\\'" group))) | |
5005 | (ignore | |
5006 | (message | |
5007 | "The newsgroups field is empty or missing. Posting is denied."))))) | |
eec82323 LMI |
5008 | ;; Check the Subject header. |
5009 | (message-check 'subject | |
5010 | (let* ((case-fold-search t) | |
5011 | (subject (message-fetch-field "subject"))) | |
5012 | (or | |
5013 | (and subject | |
5014 | (not (string-match "\\`[ \t]*\\'" subject))) | |
5015 | (ignore | |
5016 | (message | |
5017 | "The subject field is empty or missing. Posting is denied."))))) | |
5018 | ;; Check for commands in Subject. | |
5019 | (message-check 'subject-cmsg | |
5020 | (if (string-match "^cmsg " (message-fetch-field "subject")) | |
5021 | (y-or-n-p | |
5022 | "The control code \"cmsg\" is in the subject. Really post? ") | |
5023 | t)) | |
23f87bed MB |
5024 | ;; Check long header lines. |
5025 | (message-check 'long-header-lines | |
99c2a4e3 | 5026 | (let ((header nil) |
23f87bed MB |
5027 | (length 0) |
5028 | found) | |
5029 | (while (and (not found) | |
5030 | (re-search-forward "^\\([^ \t:]+\\): " nil t)) | |
5031 | (if (> (- (point) (match-beginning 0)) 998) | |
5032 | (setq found t | |
5033 | length (- (point) (match-beginning 0))) | |
5034 | (setq header (match-string-no-properties 1))) | |
23f87bed MB |
5035 | (forward-line 1)) |
5036 | (if found | |
5037 | (y-or-n-p (format "Your %s header is too long (%d). Really post? " | |
5038 | header length)) | |
5039 | t))) | |
eec82323 LMI |
5040 | ;; Check for multiple identical headers. |
5041 | (message-check 'multiple-headers | |
5042 | (let (found) | |
5043 | (while (and (not found) | |
5044 | (re-search-forward "^[^ \t:]+: " nil t)) | |
5045 | (save-excursion | |
5046 | (or (re-search-forward | |
5047 | (concat "^" | |
5048 | (regexp-quote | |
5049 | (setq found | |
5050 | (buffer-substring | |
5051 | (match-beginning 0) (- (match-end 0) 2)))) | |
5052 | ":") | |
5053 | nil t) | |
5054 | (setq found nil)))) | |
5055 | (if found | |
5056 | (y-or-n-p (format "Multiple %s headers. Really post? " found)) | |
5057 | t))) | |
5058 | ;; Check for Version and Sendsys. | |
5059 | (message-check 'sendsys | |
5060 | (if (re-search-forward "^Sendsys:\\|^Version:" nil t) | |
5061 | (y-or-n-p | |
5062 | (format "The article contains a %s command. Really post? " | |
5063 | (buffer-substring (match-beginning 0) | |
5064 | (1- (match-end 0))))) | |
5065 | t)) | |
5066 | ;; See whether we can shorten Followup-To. | |
5067 | (message-check 'shorten-followup-to | |
5068 | (let ((newsgroups (message-fetch-field "newsgroups")) | |
5069 | (followup-to (message-fetch-field "followup-to")) | |
5070 | to) | |
5071 | (when (and newsgroups | |
5072 | (string-match "," newsgroups) | |
5073 | (not followup-to) | |
5074 | (not | |
5075 | (zerop | |
5076 | (length | |
5077 | (setq to (completing-read | |
81df110a | 5078 | "Followups to (default no Followup-To header): " |
23f87bed | 5079 | (mapcar #'list |
eec82323 LMI |
5080 | (cons "poster" |
5081 | (message-tokenize-header | |
5082 | newsgroups))))))))) | |
5083 | (goto-char (point-min)) | |
f129a4df | 5084 | (insert "Followup-To: " to "\n")) |
eec82323 LMI |
5085 | t)) |
5086 | ;; Check "Shoot me". | |
5087 | (message-check 'shoot | |
5088 | (if (re-search-forward | |
23f87bed | 5089 | "Message-ID.*.i-did-not-set--mail-host-address--so-tickle-me" nil t) |
eec82323 LMI |
5090 | (y-or-n-p "You appear to have a misconfigured system. Really post? ") |
5091 | t)) | |
5092 | ;; Check for Approved. | |
5093 | (message-check 'approved | |
5094 | (if (re-search-forward "^Approved:" nil t) | |
5095 | (y-or-n-p "The article contains an Approved header. Really post? ") | |
5096 | t)) | |
5097 | ;; Check the Message-ID header. | |
5098 | (message-check 'message-id | |
5099 | (let* ((case-fold-search t) | |
5100 | (message-id (message-fetch-field "message-id" t))) | |
5101 | (or (not message-id) | |
6748645f | 5102 | ;; Is there an @ in the ID? |
eec82323 | 5103 | (and (string-match "@" message-id) |
6748645f LMI |
5104 | ;; Is there a dot in the ID? |
5105 | (string-match "@[^.]*\\." message-id) | |
5106 | ;; Does the ID end with a dot? | |
5107 | (not (string-match "\\.>" message-id))) | |
eec82323 LMI |
5108 | (y-or-n-p |
5109 | (format "The Message-ID looks strange: \"%s\". Really post? " | |
5110 | message-id))))) | |
5111 | ;; Check the Newsgroups & Followup-To headers. | |
5112 | (message-check 'existing-newsgroups | |
5113 | (let* ((case-fold-search t) | |
5114 | (newsgroups (message-fetch-field "newsgroups")) | |
5115 | (followup-to (message-fetch-field "followup-to")) | |
5116 | (groups (message-tokenize-header | |
5117 | (if followup-to | |
5118 | (concat newsgroups "," followup-to) | |
5119 | newsgroups))) | |
23f87bed MB |
5120 | (post-method (if (functionp message-post-method) |
5121 | (funcall message-post-method) | |
5122 | message-post-method)) | |
5123 | ;; KLUDGE to handle nnvirtual groups. Doing this right | |
5124 | ;; would probably involve a new nnoo function. | |
5125 | ;; -- Per Abrahamsen <abraham@dina.kvl.dk>, 2001-10-17. | |
5126 | (method (if (and (consp post-method) | |
5127 | (eq (car post-method) 'nnvirtual) | |
5128 | gnus-message-group-art) | |
5129 | (let ((group (car (nnvirtual-find-group-art | |
5130 | (car gnus-message-group-art) | |
5131 | (cdr gnus-message-group-art))))) | |
5132 | (gnus-find-method-for-group group)) | |
5133 | post-method)) | |
5134 | (known-groups | |
5135 | (mapcar (lambda (n) | |
5136 | (gnus-group-name-decode | |
5137 | (gnus-group-real-name n) | |
5138 | (gnus-group-name-charset method n))) | |
5139 | (gnus-groups-from-server method))) | |
eec82323 | 5140 | errors) |
23f87bed MB |
5141 | (while groups |
5142 | (when (and (not (equal (car groups) "poster")) | |
5143 | (not (member (car groups) known-groups)) | |
5144 | (not (member (car groups) errors))) | |
5145 | (push (car groups) errors)) | |
5146 | (pop groups)) | |
5147 | (cond | |
5148 | ;; Gnus is not running. | |
5149 | ((or (not (and (boundp 'gnus-active-hashtb) | |
5150 | gnus-active-hashtb)) | |
5151 | (not (boundp 'gnus-read-active-file))) | |
5152 | t) | |
5153 | ;; We don't have all the group names. | |
5154 | ((and (or (not gnus-read-active-file) | |
5155 | (eq gnus-read-active-file 'some)) | |
5156 | errors) | |
5157 | (y-or-n-p | |
5158 | (format | |
5159 | "Really use %s possibly unknown group%s: %s? " | |
5160 | (if (= (length errors) 1) "this" "these") | |
5161 | (if (= (length errors) 1) "" "s") | |
5162 | (mapconcat 'identity errors ", ")))) | |
5163 | ;; There were no errors. | |
5164 | ((not errors) | |
5165 | t) | |
5166 | ;; There are unknown groups. | |
5167 | (t | |
5168 | (y-or-n-p | |
5169 | (format | |
5170 | "Really post to %s unknown group%s: %s? " | |
5171 | (if (= (length errors) 1) "this" "these") | |
5172 | (if (= (length errors) 1) "" "s") | |
5173 | (mapconcat 'identity errors ", "))))))) | |
5174 | ;; Check continuation headers. | |
5175 | (message-check 'continuation-headers | |
5176 | (goto-char (point-min)) | |
5177 | (let ((do-posting t)) | |
f362b760 MB |
5178 | (while (re-search-forward "^[^ \t\n][^ \t\n:]*[ \t\n]" nil t) |
5179 | (goto-char (match-beginning 0)) | |
23f87bed | 5180 | (if (y-or-n-p "Fix continuation lines? ") |
f362b760 MB |
5181 | (insert " ") |
5182 | (forward-line 1) | |
23f87bed MB |
5183 | (unless (y-or-n-p "Send anyway? ") |
5184 | (setq do-posting nil)))) | |
5185 | do-posting)) | |
eec82323 LMI |
5186 | ;; Check the Newsgroups & Followup-To headers for syntax errors. |
5187 | (message-check 'valid-newsgroups | |
5188 | (let ((case-fold-search t) | |
5189 | (headers '("Newsgroups" "Followup-To")) | |
5190 | header error) | |
5191 | (while (and headers (not error)) | |
5192 | (when (setq header (mail-fetch-field (car headers))) | |
5193 | (if (or | |
5194 | (not | |
5195 | (string-match | |
5196 | "\\`\\([-+_&.a-zA-Z0-9]+\\)?\\(,[-+_&.a-zA-Z0-9]+\\)*\\'" | |
5197 | header)) | |
5198 | (memq | |
5199 | nil (mapcar | |
5200 | (lambda (g) | |
5201 | (not (string-match "\\.\\'\\|\\.\\." g))) | |
5202 | (message-tokenize-header header ",")))) | |
5203 | (setq error t))) | |
5204 | (unless error | |
5205 | (pop headers))) | |
5206 | (if (not error) | |
5207 | t | |
5208 | (y-or-n-p | |
5209 | (format "The %s header looks odd: \"%s\". Really post? " | |
5210 | (car headers) header))))) | |
a8151ef7 LMI |
5211 | (message-check 'repeated-newsgroups |
5212 | (let ((case-fold-search t) | |
5213 | (headers '("Newsgroups" "Followup-To")) | |
5214 | header error groups group) | |
5215 | (while (and headers | |
5216 | (not error)) | |
5217 | (when (setq header (mail-fetch-field (pop headers))) | |
5218 | (setq groups (message-tokenize-header header ",")) | |
5219 | (while (setq group (pop groups)) | |
5220 | (when (member group groups) | |
5221 | (setq error group | |
5222 | groups nil))))) | |
5223 | (if (not error) | |
5224 | t | |
5225 | (y-or-n-p | |
5226 | (format "Group %s is repeated in headers. Really post? " error))))) | |
eec82323 LMI |
5227 | ;; Check the From header. |
5228 | (message-check 'from | |
5229 | (let* ((case-fold-search t) | |
5230 | (from (message-fetch-field "from")) | |
16409b0b | 5231 | ad) |
eec82323 LMI |
5232 | (cond |
5233 | ((not from) | |
5234 | (message "There is no From line. Posting is denied.") | |
5235 | nil) | |
16409b0b GM |
5236 | ((or (not (string-match |
5237 | "@[^\\.]*\\." | |
5238 | (setq ad (nth 1 (mail-extract-address-components | |
5239 | from))))) ;larsi@ifi | |
23f87bed | 5240 | (string-match "\\.\\." ad) ;larsi@ifi..uio |
eec82323 LMI |
5241 | (string-match "@\\." ad) ;larsi@.ifi.uio |
5242 | (string-match "\\.$" ad) ;larsi@ifi.uio. | |
5243 | (not (string-match "^[^@]+@[^@]+$" ad)) ;larsi.ifi.uio | |
5244 | (string-match "(.*).*(.*)" from)) ;(lars) (lars) | |
5245 | (message | |
5246 | "Denied posting -- the From looks strange: \"%s\"." from) | |
5247 | nil) | |
23f87bed | 5248 | ((let ((addresses (rfc822-addresses from))) |
8a908224 KY |
5249 | ;; `rfc822-addresses' returns a string if parsing fails. |
5250 | (while (and (consp addresses) | |
23f87bed MB |
5251 | (not (eq (string-to-char (car addresses)) ?\())) |
5252 | (setq addresses (cdr addresses))) | |
5253 | addresses) | |
5254 | (message | |
5255 | "Denied posting -- bad From address: \"%s\"." from) | |
5256 | nil) | |
5257 | (t t)))) | |
5258 | ;; Check the Reply-To header. | |
5259 | (message-check 'reply-to | |
5260 | (let* ((case-fold-search t) | |
5261 | (reply-to (message-fetch-field "reply-to")) | |
5262 | ad) | |
5263 | (cond | |
5264 | ((not reply-to) | |
5265 | t) | |
5266 | ((string-match "," reply-to) | |
5267 | (y-or-n-p | |
5268 | (format "Multiple Reply-To addresses: \"%s\". Really post? " | |
5269 | reply-to))) | |
5270 | ((or (not (string-match | |
5271 | "@[^\\.]*\\." | |
5272 | (setq ad (nth 1 (mail-extract-address-components | |
5273 | reply-to))))) ;larsi@ifi | |
5274 | (string-match "\\.\\." ad) ;larsi@ifi..uio | |
5275 | (string-match "@\\." ad) ;larsi@.ifi.uio | |
5276 | (string-match "\\.$" ad) ;larsi@ifi.uio. | |
5277 | (not (string-match "^[^@]+@[^@]+$" ad)) ;larsi.ifi.uio | |
5278 | (string-match "(.*).*(.*)" reply-to)) ;(lars) (lars) | |
5279 | (y-or-n-p | |
5280 | (format | |
5281 | "The Reply-To looks strange: \"%s\". Really post? " | |
5282 | reply-to))) | |
eec82323 LMI |
5283 | (t t)))))) |
5284 | ||
5285 | (defun message-check-news-body-syntax () | |
5286 | (and | |
5287 | ;; Check for long lines. | |
5288 | (message-check 'long-lines | |
5289 | (goto-char (point-min)) | |
5290 | (re-search-forward | |
5291 | (concat "^" (regexp-quote mail-header-separator) "$")) | |
23f87bed | 5292 | (forward-line 1) |
eec82323 | 5293 | (while (and |
23f87bed MB |
5294 | (or (looking-at |
5295 | "<#\\(/\\)?\\(multipart\\|part\\|external\\|mml\\)") | |
5296 | (let ((p (point))) | |
5297 | (end-of-line) | |
5298 | (< (- (point) p) 80))) | |
eec82323 LMI |
5299 | (zerop (forward-line 1)))) |
5300 | (or (bolp) | |
5301 | (eobp) | |
5302 | (y-or-n-p | |
5303 | "You have lines longer than 79 characters. Really post? "))) | |
5304 | ;; Check whether the article is empty. | |
5305 | (message-check 'empty | |
5306 | (goto-char (point-min)) | |
5307 | (re-search-forward | |
5308 | (concat "^" (regexp-quote mail-header-separator) "$")) | |
5309 | (forward-line 1) | |
5310 | (let ((b (point))) | |
5311 | (goto-char (point-max)) | |
5312 | (re-search-backward message-signature-separator nil t) | |
5313 | (beginning-of-line) | |
5314 | (or (re-search-backward "[^ \n\t]" b t) | |
23f87bed MB |
5315 | (if (message-gnksa-enable-p 'empty-article) |
5316 | (y-or-n-p "Empty article. Really post? ") | |
5317 | (message "Denied posting -- Empty article.") | |
5318 | nil)))) | |
eec82323 LMI |
5319 | ;; Check for control characters. |
5320 | (message-check 'control-chars | |
7c3bb5a5 | 5321 | (if (re-search-forward |
c70dbcd5 | 5322 | (mm-string-to-multibyte "[\000-\007\013\015-\032\034-\037\200-\237]") |
7c3bb5a5 | 5323 | nil t) |
eec82323 LMI |
5324 | (y-or-n-p |
5325 | "The article contains control characters. Really post? ") | |
5326 | t)) | |
5327 | ;; Check excessive size. | |
5328 | (message-check 'size | |
5329 | (if (> (buffer-size) 60000) | |
5330 | (y-or-n-p | |
5331 | (format "The article is %d octets long. Really post? " | |
5332 | (buffer-size))) | |
5333 | t)) | |
5334 | ;; Check whether any new text has been added. | |
5335 | (message-check 'new-text | |
5336 | (or | |
5337 | (not message-checksum) | |
6748645f | 5338 | (not (eq (message-checksum) message-checksum)) |
23f87bed MB |
5339 | (if (message-gnksa-enable-p 'quoted-text-only) |
5340 | (y-or-n-p | |
5341 | "It looks like no new text has been added. Really post? ") | |
5342 | (message "Denied posting -- no new text has been added.") | |
5343 | nil))) | |
eec82323 LMI |
5344 | ;; Check the length of the signature. |
5345 | (message-check 'signature | |
e3e955fe MB |
5346 | (let (sig-start sig-end) |
5347 | (goto-char (point-max)) | |
5348 | (if (not (re-search-backward message-signature-separator nil t)) | |
5349 | t | |
5350 | (setq sig-start (1+ (point-at-eol))) | |
5351 | (setq sig-end | |
5352 | (if (re-search-forward | |
5353 | "<#/?\\(multipart\\|part\\|external\\|mml\\)" nil t) | |
5354 | (- (point-at-bol) 1) | |
5355 | (point-max))) | |
5356 | (if (>= (count-lines sig-start sig-end) 5) | |
5357 | (if (message-gnksa-enable-p 'signature) | |
5358 | (y-or-n-p | |
5359 | (format "Signature is excessively long (%d lines). Really post? " | |
5360 | (count-lines sig-start sig-end))) | |
5361 | (message "Denied posting -- Excessive signature.") | |
5362 | nil) | |
5363 | t)))) | |
16409b0b GM |
5364 | ;; Ensure that text follows last quoted portion. |
5365 | (message-check 'quoting-style | |
5366 | (goto-char (point-max)) | |
5367 | (let ((no-problem t)) | |
23f87bed MB |
5368 | (when (search-backward-regexp "^>[^\n]*\n" nil t) |
5369 | (setq no-problem (search-forward-regexp "^[ \t]*[^>\n]" nil t))) | |
16409b0b GM |
5370 | (if no-problem |
5371 | t | |
23f87bed MB |
5372 | (if (message-gnksa-enable-p 'quoted-text-only) |
5373 | (y-or-n-p "Your text should follow quoted text. Really post? ") | |
5374 | ;; Ensure that | |
5375 | (goto-char (point-min)) | |
5376 | (re-search-forward | |
5377 | (concat "^" (regexp-quote mail-header-separator) "$")) | |
5378 | (if (search-forward-regexp "^[ \t]*[^>\n]" nil t) | |
5379 | (y-or-n-p "Your text should follow quoted text. Really post? ") | |
5380 | (message "Denied posting -- only quoted text.") | |
5381 | nil))))))) | |
eec82323 LMI |
5382 | |
5383 | (defun message-checksum () | |
5384 | "Return a \"checksum\" for the current buffer." | |
5385 | (let ((sum 0)) | |
5386 | (save-excursion | |
5387 | (goto-char (point-min)) | |
5388 | (re-search-forward | |
5389 | (concat "^" (regexp-quote mail-header-separator) "$")) | |
5390 | (while (not (eobp)) | |
5391 | (when (not (looking-at "[ \t\n]")) | |
23f87bed MB |
5392 | (setq sum (logxor (ash sum 1) (if (natnump sum) 0 1) |
5393 | (char-after)))) | |
eec82323 LMI |
5394 | (forward-char 1))) |
5395 | sum)) | |
5396 | ||
5397 | (defun message-do-fcc () | |
5398 | "Process Fcc headers in the current buffer." | |
5399 | (let ((case-fold-search t) | |
5400 | (buf (current-buffer)) | |
23f87bed MB |
5401 | list file |
5402 | (mml-externalize-attachments message-fcc-externalize-attachments)) | |
eec82323 | 5403 | (save-excursion |
eec82323 LMI |
5404 | (save-restriction |
5405 | (message-narrow-to-headers) | |
23f87bed MB |
5406 | (setq file (message-fetch-field "fcc" t))) |
5407 | (when file | |
5408 | (set-buffer (get-buffer-create " *message temp*")) | |
5409 | (erase-buffer) | |
5410 | (insert-buffer-substring buf) | |
5411 | (message-encode-message-body) | |
5412 | (save-restriction | |
5413 | (message-narrow-to-headers) | |
5414 | (while (setq file (message-fetch-field "fcc" t)) | |
5415 | (push file list) | |
5416 | (message-remove-header "fcc" nil t)) | |
5417 | (let ((mail-parse-charset message-default-charset) | |
5418 | (rfc2047-header-encoding-alist | |
5419 | (cons '("Newsgroups" . default) | |
5420 | rfc2047-header-encoding-alist))) | |
5421 | (mail-encode-encoded-word-buffer))) | |
5422 | (goto-char (point-min)) | |
5423 | (when (re-search-forward | |
5424 | (concat "^" (regexp-quote mail-header-separator) "$") | |
5425 | nil t) | |
5426 | (replace-match "" t t )) | |
5427 | ;; Process FCC operations. | |
5428 | (while list | |
5429 | (setq file (pop list)) | |
5430 | (if (string-match "^[ \t]*|[ \t]*\\(.*\\)[ \t]*$" file) | |
5431 | ;; Pipe the article to the program in question. | |
5432 | (call-process-region (point-min) (point-max) shell-file-name | |
5433 | nil nil nil shell-command-switch | |
5434 | (match-string 1 file)) | |
5435 | ;; Save the article. | |
5436 | (setq file (expand-file-name file)) | |
5437 | (unless (file-exists-p (file-name-directory file)) | |
5438 | (make-directory (file-name-directory file) t)) | |
5439 | (if (and message-fcc-handler-function | |
5440 | (not (eq message-fcc-handler-function 'rmail-output))) | |
5441 | (funcall message-fcc-handler-function file) | |
95ccabb5 GM |
5442 | ;; FIXME this option, rmail-output (also used if |
5443 | ;; message-fcc-handler-function is nil) is not | |
5444 | ;; documented anywhere AFAICS. It should work in Emacs | |
5445 | ;; 23; I suspect it does not work in Emacs 22. | |
5446 | ;; FIXME I don't see the need for the two different cases here. | |
5447 | ;; mail-use-rfc822 makes no difference (in Emacs 23),and | |
5448 | ;; the third argument just controls \"Wrote file\" message. | |
23f87bed MB |
5449 | (if (and (file-readable-p file) (mail-file-babyl-p file)) |
5450 | (rmail-output file 1 nil t) | |
5451 | (let ((mail-use-rfc822 t)) | |
5452 | (rmail-output file 1 t t)))))) | |
5453 | (kill-buffer (current-buffer)))))) | |
eec82323 LMI |
5454 | |
5455 | (defun message-output (filename) | |
7d829636 | 5456 | "Append this article to Unix/babyl mail file FILENAME." |
872b1b16 GM |
5457 | (if (or (and (file-readable-p filename) |
5458 | (mail-file-babyl-p filename)) | |
5459 | ;; gnus-output-to-mail does the wrong thing with live, mbox | |
5460 | ;; Rmail buffers in Emacs 23. | |
5461 | ;; http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=597255 | |
5462 | (let ((buff (find-buffer-visiting filename))) | |
5463 | (and buff (with-current-buffer buff | |
5464 | (eq major-mode 'rmail-mode))))) | |
eec82323 LMI |
5465 | (gnus-output-to-rmail filename t) |
5466 | (gnus-output-to-mail filename t))) | |
5467 | ||
5468 | (defun message-cleanup-headers () | |
5469 | "Do various automatic cleanups of the headers." | |
5470 | ;; Remove empty lines in the header. | |
5471 | (save-restriction | |
5472 | (message-narrow-to-headers) | |
6748645f | 5473 | ;; Remove blank lines. |
eec82323 | 5474 | (while (re-search-forward "^[ \t]*\n" nil t) |
6748645f | 5475 | (replace-match "" t t)) |
eec82323 | 5476 | |
6748645f LMI |
5477 | ;; Correct Newsgroups and Followup-To headers: Change sequence of |
5478 | ;; spaces to comma and eliminate spaces around commas. Eliminate | |
5479 | ;; embedded line breaks. | |
5480 | (goto-char (point-min)) | |
5481 | (while (re-search-forward "^\\(Newsgroups\\|Followup-To\\): +" nil t) | |
5482 | (save-restriction | |
5483 | (narrow-to-region | |
5484 | (point) | |
5485 | (if (re-search-forward "^[^ \t]" nil t) | |
5486 | (match-beginning 0) | |
5487 | (forward-line 1) | |
5488 | (point))) | |
5489 | (goto-char (point-min)) | |
5490 | (while (re-search-forward "\n[ \t]+" nil t) | |
23f87bed | 5491 | (replace-match " " t t)) ;No line breaks (too confusing) |
6748645f LMI |
5492 | (goto-char (point-min)) |
5493 | (while (re-search-forward "[ \t\n]*,[ \t\n]*\\|[ \t]+" nil t) | |
5494 | (replace-match "," t t)) | |
5495 | (goto-char (point-min)) | |
5496 | ;; Remove trailing commas. | |
5497 | (when (re-search-forward ",+$" nil t) | |
5498 | (replace-match "" t t)))))) | |
eec82323 | 5499 | |
16409b0b GM |
5500 | (defun message-make-date (&optional now) |
5501 | "Make a valid data header. | |
5502 | If NOW, use that time instead." | |
01c52d31 MB |
5503 | (let ((system-time-locale "C")) |
5504 | (format-time-string "%a, %d %b %Y %T %z" now))) | |
5505 | ||
5506 | (defun message-insert-expires (days) | |
5507 | "Insert the Expires header. Expiry in DAYS days." | |
5508 | (interactive "NExpire article in how many days? ") | |
5509 | (save-excursion | |
5510 | (message-position-on-field "Expires" "X-Draft-From") | |
5511 | (insert (message-make-expires-date days)))) | |
5512 | ||
5513 | (defun message-make-expires-date (days) | |
5514 | "Make date string for the Expires header. Expiry in DAYS days. | |
5515 | ||
5516 | In posting styles use `(\"Expires\" (make-expires-date 30))'." | |
5517 | (let* ((cur (decode-time (current-time))) | |
5518 | (nday (+ days (nth 3 cur)))) | |
5519 | (setf (nth 3 cur) nday) | |
5520 | (message-make-date (apply 'encode-time cur)))) | |
eec82323 LMI |
5521 | |
5522 | (defun message-make-message-id () | |
5523 | "Make a unique Message-ID." | |
5524 | (concat "<" (message-unique-id) | |
a8151ef7 LMI |
5525 | (let ((psubject (save-excursion (message-fetch-field "subject"))) |
5526 | (psupersedes | |
5527 | (save-excursion (message-fetch-field "supersedes")))) | |
5528 | (if (or | |
5529 | (and message-reply-headers | |
5530 | (mail-header-references message-reply-headers) | |
5531 | (mail-header-subject message-reply-headers) | |
5532 | psubject | |
a8151ef7 LMI |
5533 | (not (string= |
5534 | (message-strip-subject-re | |
5535 | (mail-header-subject message-reply-headers)) | |
5536 | (message-strip-subject-re psubject)))) | |
5537 | (and psupersedes | |
5538 | (string-match "_-_@" psupersedes))) | |
eec82323 LMI |
5539 | "_-_" "")) |
5540 | "@" (message-make-fqdn) ">")) | |
5541 | ||
5542 | (defvar message-unique-id-char nil) | |
5543 | ||
5544 | ;; If you ever change this function, make sure the new version | |
5545 | ;; cannot generate IDs that the old version could. | |
5546 | ;; You might for example insert a "." somewhere (not next to another dot | |
5547 | ;; or string boundary), or modify the "fsf" string. | |
5548 | (defun message-unique-id () | |
5549 | ;; Don't use microseconds from (current-time), they may be unsupported. | |
5550 | ;; Instead we use this randomly inited counter. | |
5551 | (setq message-unique-id-char | |
ae97e645 G |
5552 | (% (1+ (or message-unique-id-char |
5553 | (logand (random most-positive-fixnum) (1- (lsh 1 20))))) | |
eec82323 LMI |
5554 | ;; (current-time) returns 16-bit ints, |
5555 | ;; and 2^16*25 just fits into 4 digits i base 36. | |
5556 | (* 25 25))) | |
5557 | (let ((tm (current-time))) | |
5558 | (concat | |
f5ec697d | 5559 | (if (or (eq system-type 'ms-dos) |
531bedc3 MB |
5560 | ;; message-number-base36 doesn't handle bigints. |
5561 | (floatp (user-uid))) | |
eec82323 LMI |
5562 | (let ((user (downcase (user-login-name)))) |
5563 | (while (string-match "[^a-z0-9_]" user) | |
5564 | (aset user (match-beginning 0) ?_)) | |
5565 | user) | |
5566 | (message-number-base36 (user-uid) -1)) | |
23f87bed | 5567 | (message-number-base36 (+ (car tm) |
eec82323 LMI |
5568 | (lsh (% message-unique-id-char 25) 16)) 4) |
5569 | (message-number-base36 (+ (nth 1 tm) | |
5570 | (lsh (/ message-unique-id-char 25) 16)) 4) | |
23f87bed MB |
5571 | ;; Append a given name, because while the generated ID is unique |
5572 | ;; to this newsreader, other newsreaders might otherwise generate | |
5573 | ;; the same ID via another algorithm. | |
eec82323 LMI |
5574 | ".fsf"))) |
5575 | ||
5576 | (defun message-number-base36 (num len) | |
5577 | (if (if (< len 0) | |
5578 | (<= num 0) | |
5579 | (= len 0)) | |
5580 | "" | |
5581 | (concat (message-number-base36 (/ num 36) (1- len)) | |
5582 | (char-to-string (aref "zyxwvutsrqponmlkjihgfedcba9876543210" | |
5583 | (% num 36)))))) | |
5584 | ||
5585 | (defun message-make-organization () | |
5586 | "Make an Organization header." | |
5587 | (let* ((organization | |
6748645f | 5588 | (when message-user-organization |
23f87bed | 5589 | (if (functionp message-user-organization) |
16409b0b GM |
5590 | (funcall message-user-organization) |
5591 | message-user-organization)))) | |
23f87bed MB |
5592 | (with-temp-buffer |
5593 | (mm-enable-multibyte) | |
eec82323 LMI |
5594 | (cond ((stringp organization) |
5595 | (insert organization)) | |
5596 | ((and (eq t organization) | |
5597 | message-user-organization-file | |
5598 | (file-exists-p message-user-organization-file)) | |
5599 | (insert-file-contents message-user-organization-file))) | |
5600 | (goto-char (point-min)) | |
5601 | (while (re-search-forward "[\t\n]+" nil t) | |
5602 | (replace-match "" t t)) | |
5603 | (unless (zerop (buffer-size)) | |
5604 | (buffer-string))))) | |
5605 | ||
5606 | (defun message-make-lines () | |
5607 | "Count the number of lines and return numeric string." | |
5608 | (save-excursion | |
5609 | (save-restriction | |
5610 | (widen) | |
23f87bed | 5611 | (message-goto-body) |
eec82323 LMI |
5612 | (int-to-string (count-lines (point) (point-max)))))) |
5613 | ||
23f87bed MB |
5614 | (defun message-make-references () |
5615 | "Return the References header for this message." | |
5616 | (when message-reply-headers | |
aa8f8277 | 5617 | (let ((message-id (mail-header-id message-reply-headers)) |
82816ba1 | 5618 | (references (mail-header-references message-reply-headers))) |
23f87bed MB |
5619 | (if (or references message-id) |
5620 | (concat (or references "") (and references " ") | |
5621 | (or message-id "")) | |
5622 | nil)))) | |
5623 | ||
eec82323 LMI |
5624 | (defun message-make-in-reply-to () |
5625 | "Return the In-Reply-To header for this message." | |
5626 | (when message-reply-headers | |
23f87bed MB |
5627 | (let ((from (mail-header-from message-reply-headers)) |
5628 | (date (mail-header-date message-reply-headers)) | |
aa8f8277 | 5629 | (msg-id (mail-header-id message-reply-headers))) |
23f87bed MB |
5630 | (when from |
5631 | (let ((name (mail-extract-address-components from))) | |
45cb30ee MB |
5632 | (concat |
5633 | msg-id (if msg-id " (") | |
5634 | (if (car name) | |
5635 | (if (string-match "[^\000-\177]" (car name)) | |
5636 | ;; Quote a string containing non-ASCII characters. | |
5637 | ;; It will make the RFC2047 encoder cause an error | |
5638 | ;; if there are special characters. | |
fa98585c SM |
5639 | (mm-with-multibyte-buffer |
5640 | (insert (car name)) | |
5641 | (goto-char (point-min)) | |
5642 | (while (search-forward "\"" nil t) | |
5643 | (when (prog2 | |
5644 | (backward-char) | |
5645 | (zerop (% (skip-chars-backward "\\\\") 2)) | |
5646 | (goto-char (match-beginning 0))) | |
5647 | (insert "\\")) | |
5648 | (forward-char)) | |
5649 | ;; Those quotes will be removed by the RFC2047 encoder. | |
5650 | (concat "\"" (buffer-string) "\"")) | |
45cb30ee MB |
5651 | (car name)) |
5652 | (nth 1 name)) | |
5653 | "'s message of \"" | |
5654 | (if (or (not date) (string= date "")) | |
5655 | "(unknown date)" date) | |
5656 | "\"" (if msg-id ")"))))))) | |
eec82323 LMI |
5657 | |
5658 | (defun message-make-distribution () | |
5659 | "Make a Distribution header." | |
5660 | (let ((orig-distribution (message-fetch-reply-field "distribution"))) | |
23f87bed | 5661 | (cond ((functionp message-distribution-function) |
eec82323 LMI |
5662 | (funcall message-distribution-function)) |
5663 | (t orig-distribution)))) | |
5664 | ||
5665 | (defun message-make-expires () | |
5666 | "Return an Expires header based on `message-expires'." | |
5667 | (let ((current (current-time)) | |
5668 | (future (* 1.0 message-expires 60 60 24))) | |
5669 | ;; Add the future to current. | |
5670 | (setcar current (+ (car current) (round (/ future (expt 2 16))))) | |
5671 | (setcar (cdr current) (+ (nth 1 current) (% (round future) (expt 2 16)))) | |
16409b0b | 5672 | (message-make-date current))) |
eec82323 LMI |
5673 | |
5674 | (defun message-make-path () | |
5675 | "Return uucp path." | |
5676 | (let ((login-name (user-login-name))) | |
5677 | (cond ((null message-user-path) | |
5678 | (concat (system-name) "!" login-name)) | |
5679 | ((stringp message-user-path) | |
5680 | ;; Support GENERICPATH. Suggested by vixie@decwrl.dec.com. | |
5681 | (concat message-user-path "!" login-name)) | |
5682 | (t login-name)))) | |
5683 | ||
60ece9b0 | 5684 | (defun message-make-from (&optional name address) |
eec82323 LMI |
5685 | "Make a From header." |
5686 | (let* ((style message-from-style) | |
01c52d31 MB |
5687 | (login (or address (message-make-address))) |
5688 | (fullname (or name | |
5689 | (and (boundp 'user-full-name) | |
5690 | user-full-name) | |
5691 | (user-full-name)))) | |
eec82323 LMI |
5692 | (when (string= fullname "&") |
5693 | (setq fullname (user-login-name))) | |
23f87bed MB |
5694 | (with-temp-buffer |
5695 | (mm-enable-multibyte) | |
eec82323 LMI |
5696 | (cond |
5697 | ((or (null style) | |
5698 | (equal fullname "")) | |
5699 | (insert login)) | |
5700 | ((or (eq style 'angles) | |
5701 | (and (not (eq style 'parens)) | |
5702 | ;; Use angles if no quoting is needed, or if parens would | |
5703 | ;; need quoting too. | |
5704 | (or (not (string-match "[^- !#-'*+/-9=?A-Z^-~]" fullname)) | |
5705 | (let ((tmp (concat fullname nil))) | |
5706 | (while (string-match "([^()]*)" tmp) | |
5707 | (aset tmp (match-beginning 0) ?-) | |
5708 | (aset tmp (1- (match-end 0)) ?-)) | |
5709 | (string-match "[\\()]" tmp))))) | |
5710 | (insert fullname) | |
5711 | (goto-char (point-min)) | |
01c52d31 MB |
5712 | ;; Look for a character that cannot appear unquoted |
5713 | ;; according to RFC 822. | |
5714 | (when (re-search-forward "[^- !#-'*+/-9=?A-Z^-~]" nil 1) | |
5715 | ;; Quote fullname, escaping specials. | |
5716 | (goto-char (point-min)) | |
5717 | (insert "\"") | |
5718 | (while (re-search-forward "[\"\\]" nil 1) | |
5719 | (replace-match "\\\\\\&" t)) | |
5720 | (insert "\"")) | |
eec82323 LMI |
5721 | (insert " <" login ">")) |
5722 | (t ; 'parens or default | |
5723 | (insert login " (") | |
5724 | (let ((fullname-start (point))) | |
5725 | (insert fullname) | |
5726 | (goto-char fullname-start) | |
5727 | ;; RFC 822 says \ and nonmatching parentheses | |
5728 | ;; must be escaped in comments. | |
5729 | ;; Escape every instance of ()\ ... | |
5730 | (while (re-search-forward "[()\\]" nil 1) | |
5731 | (replace-match "\\\\\\&" t)) | |
5732 | ;; ... then undo escaping of matching parentheses, | |
5733 | ;; including matching nested parentheses. | |
5734 | (goto-char fullname-start) | |
5735 | (while (re-search-forward | |
5736 | "\\(\\=\\|[^\\]\\(\\\\\\\\\\)*\\)\\\\(\\(\\([^\\]\\|\\\\\\\\\\)*\\)\\\\)" | |
5737 | nil 1) | |
5738 | (replace-match "\\1(\\3)" t) | |
5739 | (goto-char fullname-start))) | |
5740 | (insert ")"))) | |
5741 | (buffer-string)))) | |
5742 | ||
5743 | (defun message-make-sender () | |
5744 | "Return the \"real\" user address. | |
5745 | This function tries to ignore all user modifications, and | |
5746 | give as trustworthy answer as possible." | |
5747 | (concat (user-login-name) "@" (system-name))) | |
5748 | ||
5749 | (defun message-make-address () | |
5750 | "Make the address of the user." | |
5751 | (or (message-user-mail-address) | |
5752 | (concat (user-login-name) "@" (message-make-domain)))) | |
5753 | ||
5754 | (defun message-user-mail-address () | |
5755 | "Return the pertinent part of `user-mail-address'." | |
23f87bed MB |
5756 | (when (and user-mail-address |
5757 | (string-match "@.*\\." user-mail-address)) | |
eec82323 LMI |
5758 | (if (string-match " " user-mail-address) |
5759 | (nth 1 (mail-extract-address-components user-mail-address)) | |
5760 | user-mail-address))) | |
5761 | ||
23f87bed MB |
5762 | (defun message-sendmail-envelope-from () |
5763 | "Return the envelope from." | |
5764 | (cond ((eq message-sendmail-envelope-from 'header) | |
5765 | (nth 1 (mail-extract-address-components | |
5766 | (message-fetch-field "from")))) | |
5767 | ((stringp message-sendmail-envelope-from) | |
5768 | message-sendmail-envelope-from) | |
5769 | (t | |
5770 | (message-make-address)))) | |
5771 | ||
eec82323 LMI |
5772 | (defun message-make-fqdn () |
5773 | "Return user's fully qualified domain name." | |
23f87bed MB |
5774 | (let* ((system-name (system-name)) |
5775 | (user-mail (message-user-mail-address)) | |
5776 | (user-domain | |
5777 | (if (and user-mail | |
5778 | (string-match "@\\(.*\\)\\'" user-mail)) | |
5779 | (match-string 1 user-mail))) | |
5780 | (case-fold-search t)) | |
eec82323 | 5781 | (cond |
23f87bed MB |
5782 | ((and message-user-fqdn |
5783 | (stringp message-user-fqdn) | |
5784 | (string-match message-valid-fqdn-regexp message-user-fqdn) | |
5785 | (not (string-match message-bogus-system-names message-user-fqdn))) | |
11e95b02 | 5786 | ;; `message-user-fqdn' seems to be valid |
23f87bed | 5787 | message-user-fqdn) |
23f87bed MB |
5788 | ((and (string-match message-valid-fqdn-regexp system-name) |
5789 | (not (string-match message-bogus-system-names system-name))) | |
eec82323 LMI |
5790 | ;; `system-name' returned the right result. |
5791 | system-name) | |
5792 | ;; Try `mail-host-address'. | |
5793 | ((and (boundp 'mail-host-address) | |
5794 | (stringp mail-host-address) | |
23f87bed MB |
5795 | (string-match message-valid-fqdn-regexp mail-host-address) |
5796 | (not (string-match message-bogus-system-names mail-host-address))) | |
eec82323 LMI |
5797 | mail-host-address) |
5798 | ;; We try `user-mail-address' as a backup. | |
23f87bed MB |
5799 | ((and user-domain |
5800 | (stringp user-domain) | |
5801 | (string-match message-valid-fqdn-regexp user-domain) | |
5802 | (not (string-match message-bogus-system-names user-domain))) | |
5803 | user-domain) | |
eec82323 LMI |
5804 | ;; Default to this bogus thing. |
5805 | (t | |
23f87bed MB |
5806 | (concat system-name |
5807 | ".i-did-not-set--mail-host-address--so-tickle-me"))))) | |
eec82323 | 5808 | |
eec82323 LMI |
5809 | (defun message-make-domain () |
5810 | "Return the domain name." | |
5811 | (or mail-host-address | |
5812 | (message-make-fqdn))) | |
5813 | ||
23f87bed MB |
5814 | (defun message-to-list-only () |
5815 | "Send a message to the list only. | |
5816 | Remove all addresses but the list address from To and Cc headers." | |
5817 | (interactive) | |
5818 | (let ((listaddr (message-make-mail-followup-to t))) | |
5819 | (when listaddr | |
5820 | (save-excursion | |
5821 | (message-remove-header "to") | |
5822 | (message-remove-header "cc") | |
5823 | (message-position-on-field "To" "X-Draft-From") | |
5824 | (insert listaddr))))) | |
5825 | ||
5826 | (defun message-make-mail-followup-to (&optional only-show-subscribed) | |
5827 | "Return the Mail-Followup-To header. | |
5828 | If passed the optional argument ONLY-SHOW-SUBSCRIBED only return the | |
5829 | subscribed address (and not the additional To and Cc header contents)." | |
5830 | (let* ((case-fold-search t) | |
5831 | (to (message-fetch-field "To")) | |
5832 | (cc (message-fetch-field "cc")) | |
5833 | (msg-recipients (concat to (and to cc ", ") cc)) | |
5834 | (recipients | |
5835 | (mapcar 'mail-strip-quoted-names | |
5836 | (message-tokenize-header msg-recipients))) | |
5837 | (file-regexps | |
5838 | (if message-subscribed-address-file | |
5839 | (let (begin end item re) | |
5840 | (save-excursion | |
5841 | (with-temp-buffer | |
5842 | (insert-file-contents message-subscribed-address-file) | |
5843 | (while (not (eobp)) | |
5844 | (setq begin (point)) | |
5845 | (forward-line 1) | |
5846 | (setq end (point)) | |
5847 | (if (bolp) (setq end (1- end))) | |
5848 | (setq item (regexp-quote (buffer-substring begin end))) | |
5849 | (if re (setq re (concat re "\\|" item)) | |
5850 | (setq re (concat "\\`\\(" item)))) | |
5851 | (and re (list (concat re "\\)\\'")))))))) | |
5852 | (mft-regexps (apply 'append message-subscribed-regexps | |
5853 | (mapcar 'regexp-quote | |
5854 | message-subscribed-addresses) | |
5855 | file-regexps | |
5856 | (mapcar 'funcall | |
5857 | message-subscribed-address-functions)))) | |
5858 | (save-match-data | |
82816ba1 | 5859 | (let ((list |
23f87bed MB |
5860 | (loop for recipient in recipients |
5861 | when (loop for regexp in mft-regexps | |
5862 | when (string-match regexp recipient) return t) | |
5863 | return recipient))) | |
5864 | (when list | |
5865 | (if only-show-subscribed | |
5866 | list | |
5867 | msg-recipients)))))) | |
5868 | ||
5869 | (defun message-idna-to-ascii-rhs-1 (header) | |
5870 | "Interactively potentially IDNA encode domain names in HEADER." | |
5871 | (let ((field (message-fetch-field header)) | |
99c2a4e3 | 5872 | ace) |
23f87bed | 5873 | (when field |
53cfefc8 | 5874 | (dolist (rhs |
8753ddee | 5875 | (mm-delete-duplicates |
53cfefc8 | 5876 | (mapcar (lambda (rhs) (or (cadr (split-string rhs "@")) "")) |
1bd6cd6c | 5877 | (mapcar 'downcase |
53cfefc8 | 5878 | (mapcar |
71e691a5 G |
5879 | (lambda (elem) |
5880 | (or (cadr elem) | |
5881 | "")) | |
4520e527 | 5882 | (mail-extract-address-components field t)))))) |
1bd6cd6c KY |
5883 | ;; Note that `rhs' will be "" if the address does not have |
5884 | ;; the domain part, i.e., if it is a local user's address. | |
5885 | (setq ace (if (string-match "\\`[[:ascii:]]*\\'" rhs) | |
b890d447 MB |
5886 | rhs |
5887 | (downcase (idna-to-ascii rhs)))) | |
23f87bed MB |
5888 | (when (and (not (equal rhs ace)) |
5889 | (or (not (eq message-use-idna 'ask)) | |
53cfefc8 MB |
5890 | (y-or-n-p (format "Replace %s with %s in %s:? " |
5891 | rhs ace header)))) | |
23f87bed MB |
5892 | (goto-char (point-min)) |
5893 | (while (re-search-forward (concat "^" header ":") nil t) | |
5894 | (message-narrow-to-field) | |
5895 | (while (search-forward (concat "@" rhs) nil t) | |
5896 | (replace-match (concat "@" ace) t t)) | |
5897 | (goto-char (point-max)) | |
5898 | (widen))))))) | |
5899 | ||
5900 | (defun message-idna-to-ascii-rhs () | |
5901 | "Possibly IDNA encode non-ASCII domain names in From:, To: and Cc: headers. | |
5902 | See `message-idna-encode'." | |
5903 | (interactive) | |
5904 | (when message-use-idna | |
5905 | (save-excursion | |
5906 | (save-restriction | |
1bd6cd6c KY |
5907 | ;; `message-narrow-to-head' that recognizes only the first empty |
5908 | ;; line as the message header separator used to be used here. | |
5909 | ;; However, since there is the "--text follows this line--" line | |
5910 | ;; normally, it failed in narrowing to the headers and potentially | |
5911 | ;; caused the IDNA encoding on lines that look like headers in | |
5912 | ;; the message body. | |
f66de92f | 5913 | (message-narrow-to-headers-or-head) |
23f87bed MB |
5914 | (message-idna-to-ascii-rhs-1 "From") |
5915 | (message-idna-to-ascii-rhs-1 "To") | |
53cfefc8 MB |
5916 | (message-idna-to-ascii-rhs-1 "Reply-To") |
5917 | (message-idna-to-ascii-rhs-1 "Mail-Reply-To") | |
5918 | (message-idna-to-ascii-rhs-1 "Mail-Followup-To") | |
23f87bed MB |
5919 | (message-idna-to-ascii-rhs-1 "Cc"))))) |
5920 | ||
99c2a4e3 SM |
5921 | (defvar Date) |
5922 | (defvar Message-ID) | |
5923 | (defvar Organization) | |
5924 | (defvar From) | |
5925 | (defvar Path) | |
5926 | (defvar Subject) | |
5927 | (defvar Newsgroups) | |
5928 | (defvar In-Reply-To) | |
5929 | (defvar References) | |
5930 | (defvar To) | |
5931 | (defvar Distribution) | |
5932 | (defvar Lines) | |
5933 | (defvar User-Agent) | |
5934 | (defvar Expires) | |
5935 | ||
eec82323 LMI |
5936 | (defun message-generate-headers (headers) |
5937 | "Prepare article HEADERS. | |
5938 | Headers already prepared in the buffer are not modified." | |
23f87bed | 5939 | (setq headers (append headers message-required-headers)) |
eec82323 LMI |
5940 | (save-restriction |
5941 | (message-narrow-to-headers) | |
5942 | (let* ((Date (message-make-date)) | |
5943 | (Message-ID (message-make-message-id)) | |
5944 | (Organization (message-make-organization)) | |
5945 | (From (message-make-from)) | |
5946 | (Path (message-make-path)) | |
5947 | (Subject nil) | |
5948 | (Newsgroups nil) | |
5949 | (In-Reply-To (message-make-in-reply-to)) | |
23f87bed | 5950 | (References (message-make-references)) |
eec82323 LMI |
5951 | (To nil) |
5952 | (Distribution (message-make-distribution)) | |
5953 | (Lines (message-make-lines)) | |
16409b0b | 5954 | (User-Agent message-newsreader) |
eec82323 LMI |
5955 | (Expires (message-make-expires)) |
5956 | (case-fold-search t) | |
23f87bed MB |
5957 | (optionalp nil) |
5958 | header value elem header-string) | |
eec82323 LMI |
5959 | ;; First we remove any old generated headers. |
5960 | (let ((headers message-deletable-headers)) | |
a8151ef7 LMI |
5961 | (unless (buffer-modified-p) |
5962 | (setq headers (delq 'Message-ID (copy-sequence headers)))) | |
eec82323 LMI |
5963 | (while headers |
5964 | (goto-char (point-min)) | |
5965 | (and (re-search-forward | |
5966 | (concat "^" (symbol-name (car headers)) ": *") nil t) | |
5967 | (get-text-property (1+ (match-beginning 0)) 'message-deletable) | |
5968 | (message-delete-line)) | |
5969 | (pop headers))) | |
5970 | ;; Go through all the required headers and see if they are in the | |
5971 | ;; articles already. If they are not, or are empty, they are | |
5972 | ;; inserted automatically - except for Subject, Newsgroups and | |
5973 | ;; Distribution. | |
5974 | (while headers | |
5975 | (goto-char (point-min)) | |
5976 | (setq elem (pop headers)) | |
5977 | (if (consp elem) | |
5978 | (if (eq (car elem) 'optional) | |
23f87bed MB |
5979 | (setq header (cdr elem) |
5980 | optionalp t) | |
eec82323 LMI |
5981 | (setq header (car elem))) |
5982 | (setq header elem)) | |
23f87bed MB |
5983 | (setq header-string (if (stringp header) |
5984 | header | |
5985 | (symbol-name header))) | |
eec82323 | 5986 | (when (or (not (re-search-forward |
6748645f | 5987 | (concat "^" |
23f87bed | 5988 | (regexp-quote (downcase header-string)) |
6748645f | 5989 | ":") |
eec82323 LMI |
5990 | nil t)) |
5991 | (progn | |
5992 | ;; The header was found. We insert a space after the | |
5993 | ;; colon, if there is none. | |
16409b0b | 5994 | (if (/= (char-after) ? ) (insert " ") (forward-char 1)) |
23f87bed | 5995 | ;; Find out whether the header is empty. |
16409b0b | 5996 | (looking-at "[ \t]*\n[^ \t]"))) |
eec82323 LMI |
5997 | ;; So we find out what value we should insert. |
5998 | (setq value | |
5999 | (cond | |
23f87bed MB |
6000 | ((and (consp elem) |
6001 | (eq (car elem) 'optional) | |
6002 | (not (member header-string message-inserted-headers))) | |
eec82323 LMI |
6003 | ;; This is an optional header. If the cdr of this |
6004 | ;; is something that is nil, then we do not insert | |
6005 | ;; this header. | |
6006 | (setq header (cdr elem)) | |
23f87bed MB |
6007 | (or (and (functionp (cdr elem)) |
6008 | (funcall (cdr elem))) | |
6009 | (and (boundp (cdr elem)) | |
6010 | (symbol-value (cdr elem))))) | |
eec82323 LMI |
6011 | ((consp elem) |
6012 | ;; The element is a cons. Either the cdr is a | |
6013 | ;; string to be inserted verbatim, or it is a | |
6014 | ;; function, and we insert the value returned from | |
6015 | ;; this function. | |
23f87bed MB |
6016 | (or (and (stringp (cdr elem)) |
6017 | (cdr elem)) | |
6018 | (and (functionp (cdr elem)) | |
6019 | (funcall (cdr elem))))) | |
6020 | ((and (boundp header) | |
6021 | (symbol-value header)) | |
eec82323 LMI |
6022 | ;; The element is a symbol. We insert the value |
6023 | ;; of this symbol, if any. | |
6024 | (symbol-value header)) | |
e8beac8a MB |
6025 | ((not (message-check-element |
6026 | (intern (downcase (symbol-name header))))) | |
eec82323 LMI |
6027 | ;; We couldn't generate a value for this header, |
6028 | ;; so we just ask the user. | |
6029 | (read-from-minibuffer | |
6030 | (format "Empty header for %s; enter value: " header))))) | |
6031 | ;; Finally insert the header. | |
6032 | (when (and value | |
6033 | (not (equal value ""))) | |
6034 | (save-excursion | |
6035 | (if (bolp) | |
6036 | (progn | |
6037 | ;; This header didn't exist, so we insert it. | |
6038 | (goto-char (point-max)) | |
23f87bed MB |
6039 | (let ((formatter |
6040 | (cdr (assq header message-header-format-alist)))) | |
6041 | (if formatter | |
6042 | (funcall formatter header value) | |
6043 | (insert header-string ": " value)) | |
731533fd | 6044 | (push header-string message-inserted-headers) |
01c52d31 | 6045 | (goto-char (message-fill-field)) |
23f87bed | 6046 | ;; We check whether the value was ended by a |
01c52d31 | 6047 | ;; newline. If not, we insert one. |
23f87bed | 6048 | (unless (bolp) |
f129a4df | 6049 | (insert "\n")) |
23f87bed | 6050 | (forward-line -1))) |
eec82323 LMI |
6051 | ;; The value of this header was empty, so we clear |
6052 | ;; totally and insert the new value. | |
01c52d31 | 6053 | (delete-region (point) (point-at-eol)) |
23f87bed | 6054 | ;; If the header is optional, and the header was |
01c52d31 | 6055 | ;; empty, we can't insert it anyway. |
23f87bed MB |
6056 | (unless optionalp |
6057 | (push header-string message-inserted-headers) | |
01c52d31 MB |
6058 | (insert value) |
6059 | (message-fill-field))) | |
eec82323 LMI |
6060 | ;; Add the deletable property to the headers that require it. |
6061 | (and (memq header message-deletable-headers) | |
6062 | (progn (beginning-of-line) (looking-at "[^:]+: ")) | |
6063 | (add-text-properties | |
6064 | (point) (match-end 0) | |
6065 | '(message-deletable t face italic) (current-buffer))))))) | |
6066 | ;; Insert new Sender if the From is strange. | |
6067 | (let ((from (message-fetch-field "from")) | |
6068 | (sender (message-fetch-field "sender")) | |
6069 | (secure-sender (message-make-sender))) | |
6070 | (when (and from | |
6071 | (not (message-check-element 'sender)) | |
6072 | (not (string= | |
6073 | (downcase | |
6074 | (cadr (mail-extract-address-components from))) | |
6075 | (downcase secure-sender))) | |
6076 | (or (null sender) | |
6077 | (not | |
6078 | (string= | |
6079 | (downcase | |
6080 | (cadr (mail-extract-address-components sender))) | |
6081 | (downcase secure-sender))))) | |
6082 | (goto-char (point-min)) | |
6083 | ;; Rename any old Sender headers to Original-Sender. | |
6084 | (when (re-search-forward "^\\(Original-\\)*Sender:" nil t) | |
6085 | (beginning-of-line) | |
6086 | (insert "Original-") | |
6087 | (beginning-of-line)) | |
6088 | (when (or (message-news-p) | |
6748645f | 6089 | (string-match "@.+\\.." secure-sender)) |
f129a4df | 6090 | (insert "Sender: " secure-sender "\n")))) |
23f87bed MB |
6091 | ;; Check for IDNA |
6092 | (message-idna-to-ascii-rhs)))) | |
eec82323 | 6093 | |
181cb5fb | 6094 | (defun message-insert-courtesy-copy (message) |
eec82323 LMI |
6095 | "Insert a courtesy message in mail copies of combined messages." |
6096 | (let (newsgroups) | |
6097 | (save-excursion | |
6098 | (save-restriction | |
6099 | (message-narrow-to-headers) | |
6100 | (when (setq newsgroups (message-fetch-field "newsgroups")) | |
6101 | (goto-char (point-max)) | |
f129a4df | 6102 | (insert "Posted-To: " newsgroups "\n"))) |
eec82323 | 6103 | (forward-line 1) |
181cb5fb | 6104 | (when message |
eec82323 | 6105 | (cond |
181cb5fb G |
6106 | ((string-match "%s" message) |
6107 | (insert (format message newsgroups))) | |
eec82323 | 6108 | (t |
181cb5fb | 6109 | (insert message))))))) |
eec82323 LMI |
6110 | |
6111 | ;;; | |
6112 | ;;; Setting up a message buffer | |
6113 | ;;; | |
6114 | ||
01c52d31 MB |
6115 | (defun message-skip-to-next-address () |
6116 | (let ((end (save-excursion | |
6117 | (message-next-header) | |
6118 | (point))) | |
6119 | quoted char) | |
6120 | (when (looking-at ",") | |
6121 | (forward-char 1)) | |
6122 | (while (and (not (= (point) end)) | |
6123 | (or (not (eq char ?,)) | |
6124 | quoted)) | |
0000d0d5 | 6125 | (skip-chars-forward "^,\"" end) |
01c52d31 MB |
6126 | (when (eq (setq char (following-char)) ?\") |
6127 | (setq quoted (not quoted))) | |
6128 | (unless (= (point) end) | |
6129 | (forward-char 1))) | |
6130 | (skip-chars-forward " \t\n"))) | |
6131 | ||
23f87bed MB |
6132 | (defun message-split-line () |
6133 | "Split current line, moving portion beyond point vertically down. | |
6134 | If the current line has `message-yank-prefix', insert it on the new line." | |
6135 | (interactive "*") | |
6136 | (condition-case nil | |
bf247b6e | 6137 | (split-line message-yank-prefix) ;; Emacs 22.1+ supports arg. |
23f87bed MB |
6138 | (error |
6139 | (split-line)))) | |
bf247b6e | 6140 | |
01c52d31 MB |
6141 | (defun message-insert-header (header value) |
6142 | (insert (capitalize (symbol-name header)) | |
6143 | ": " | |
6144 | (if (consp value) (car value) value))) | |
6145 | ||
6146 | (defun message-field-name () | |
6147 | (save-excursion | |
6148 | (goto-char (point-min)) | |
6149 | (when (looking-at "\\([^:]+\\):") | |
6150 | (intern (capitalize (match-string 1)))))) | |
6151 | ||
6152 | (defun message-fill-field () | |
6153 | (save-excursion | |
6154 | (save-restriction | |
6155 | (message-narrow-to-field) | |
6156 | (let ((field-name (message-field-name))) | |
6157 | (funcall (or (cadr (assq field-name message-field-fillers)) | |
6158 | 'message-fill-field-general))) | |
6159 | (point-max)))) | |
6160 | ||
6161 | (defun message-fill-field-address () | |
0000d0d5 KY |
6162 | (let (end last) |
6163 | (while (not end) | |
6164 | (message-skip-to-next-address) | |
6165 | (cond ((bolp) | |
6166 | (end-of-line 0) | |
6167 | (setq end 1)) | |
6168 | ((eobp) | |
6169 | (setq end 0))) | |
6170 | (when (and (> (current-column) 78) | |
6171 | last) | |
6172 | (save-excursion | |
6173 | (goto-char last) | |
6174 | (delete-char (- (skip-chars-backward " \t"))) | |
6175 | (insert "\n\t"))) | |
6176 | (setq last (point))) | |
6177 | (forward-line end))) | |
01c52d31 MB |
6178 | |
6179 | (defun message-fill-field-general () | |
eec82323 | 6180 | (let ((begin (point)) |
16409b0b | 6181 | (fill-column 78) |
eec82323 | 6182 | (fill-prefix "\t")) |
01c52d31 MB |
6183 | (while (and (search-forward "\n" nil t) |
6184 | (not (eobp))) | |
6185 | (replace-match " " t t)) | |
6186 | (fill-region-as-paragraph begin (point-max)) | |
6187 | ;; Tapdance around looong Message-IDs. | |
6188 | (forward-line -1) | |
6189 | (when (looking-at "[ \t]*$") | |
6190 | (message-delete-line)) | |
6191 | (goto-char begin) | |
6192 | (search-forward ":" nil t) | |
6193 | (when (looking-at "\n[ \t]+") | |
6194 | (replace-match " " t t)) | |
6195 | (goto-char (point-max)))) | |
eec82323 | 6196 | |
16409b0b | 6197 | (defun message-shorten-1 (list cut surplus) |
7d829636 | 6198 | "Cut SURPLUS elements out of LIST, beginning with CUTth one." |
16409b0b GM |
6199 | (setcdr (nthcdr (- cut 2) list) |
6200 | (nthcdr (+ (- cut 2) surplus 1) list))) | |
6201 | ||
6748645f | 6202 | (defun message-shorten-references (header references) |
23f87bed | 6203 | "Trim REFERENCES to be 21 Message-ID long or less, and fold them. |
01c52d31 MB |
6204 | When sending via news, also check that the REFERENCES are less |
6205 | than 988 characters long, and if they are not, trim them until | |
6206 | they are." | |
22bcf204 | 6207 | ;; 21 is the number suggested by USAGE. |
23f87bed | 6208 | (let ((maxcount 21) |
16409b0b | 6209 | (count 0) |
23f87bed | 6210 | (cut 2) |
6748645f | 6211 | refs) |
16409b0b | 6212 | (with-temp-buffer |
6748645f LMI |
6213 | (insert references) |
6214 | (goto-char (point-min)) | |
9b3ebcb6 MB |
6215 | ;; Cons a list of valid references. GNKSA says we must not include MIDs |
6216 | ;; with whitespace or missing brackets (7.a "Does not propagate broken | |
6217 | ;; Message-IDs in original References"). | |
6218 | (while (re-search-forward "<[^ <]+@[^ <]+>" nil t) | |
6748645f | 6219 | (push (match-string 0) refs)) |
16409b0b GM |
6220 | (setq refs (nreverse refs) |
6221 | count (length refs))) | |
6222 | ||
6223 | ;; If the list has more than MAXCOUNT elements, trim it by | |
6224 | ;; removing the CUTth element and the required number of | |
6225 | ;; elements that follow. | |
6226 | (when (> count maxcount) | |
6227 | (let ((surplus (- count maxcount))) | |
6228 | (message-shorten-1 refs cut surplus) | |
6229 | (decf count surplus))) | |
6230 | ||
01c52d31 MB |
6231 | ;; When sending via news, make sure the total folded length will |
6232 | ;; be less than 998 characters. This is to cater to broken INN | |
6233 | ;; 2.3 which counts the total number of characters in a header | |
6234 | ;; rather than the physical line length of each line, as it should. | |
16409b0b | 6235 | ;; |
01c52d31 MB |
6236 | ;; This hack should be removed when it's believed than INN 2.3 is |
6237 | ;; no longer widely used. | |
6238 | ;; | |
6239 | ;; At this point the headers have not been generated, thus we use | |
6240 | ;; message-this-is-news directly. | |
6241 | (when message-this-is-news | |
6242 | (while (< 998 | |
6243 | (with-temp-buffer | |
6244 | (message-insert-header | |
6245 | header (mapconcat #'identity refs " ")) | |
6246 | (buffer-size))) | |
6247 | (message-shorten-1 refs cut 1))) | |
16409b0b GM |
6248 | ;; Finally, collect the references back into a string and insert |
6249 | ;; it into the buffer. | |
01c52d31 | 6250 | (message-insert-header header (mapconcat #'identity refs " ")))) |
6748645f | 6251 | |
eec82323 LMI |
6252 | (defun message-position-point () |
6253 | "Move point to where the user probably wants to find it." | |
6254 | (message-narrow-to-headers) | |
6255 | (cond | |
6256 | ((re-search-forward "^[^:]+:[ \t]*$" nil t) | |
6257 | (search-backward ":" ) | |
6258 | (widen) | |
6259 | (forward-char 1) | |
16409b0b | 6260 | (if (eq (char-after) ? ) |
eec82323 LMI |
6261 | (forward-char 1) |
6262 | (insert " "))) | |
6263 | (t | |
6264 | (goto-char (point-max)) | |
6265 | (widen) | |
6266 | (forward-line 1) | |
6267 | (unless (looking-at "$") | |
6268 | (forward-line 2))) | |
6269 | (sit-for 0))) | |
6270 | ||
23f87bed MB |
6271 | (defcustom message-beginning-of-line t |
6272 | "Whether \\<message-mode-map>\\[message-beginning-of-line]\ | |
6273 | goes to beginning of header values." | |
bf247b6e | 6274 | :version "22.1" |
23f87bed MB |
6275 | :group 'message-buffers |
6276 | :link '(custom-manual "(message)Movement") | |
6277 | :type 'boolean) | |
6278 | ||
cdd3286f DG |
6279 | (defvar visual-line-mode) |
6280 | (declare-function beginning-of-visual-line "simple" (&optional n)) | |
6281 | ||
23f87bed MB |
6282 | (defun message-beginning-of-line (&optional n) |
6283 | "Move point to beginning of header value or to beginning of line. | |
6284 | The prefix argument N is passed directly to `beginning-of-line'. | |
6285 | ||
6286 | This command is identical to `beginning-of-line' if point is | |
6287 | outside the message header or if the option `message-beginning-of-line' | |
6288 | is nil. | |
6289 | ||
6290 | If point is in the message header and on a (non-continued) header | |
f5d01350 SM |
6291 | line, move point to the beginning of the header value or the beginning of line, |
6292 | whichever is closer. If point is already at beginning of line, move point to | |
6293 | beginning of header value. Therefore, repeated calls will toggle point | |
6294 | between beginning of field and beginning of line." | |
23f87bed MB |
6295 | (interactive "p") |
6296 | (let ((zrs 'zmacs-region-stays)) | |
a445370f | 6297 | (when (and (featurep 'xemacs) (interactive-p) (boundp zrs)) |
23f87bed MB |
6298 | (set zrs t))) |
6299 | (if (and message-beginning-of-line | |
6300 | (message-point-in-header-p)) | |
6301 | (let* ((here (point)) | |
6302 | (bol (progn (beginning-of-line n) (point))) | |
01c52d31 | 6303 | (eol (point-at-eol)) |
23f87bed | 6304 | (eoh (re-search-forward ": *" eol t))) |
f5d01350 SM |
6305 | (goto-char |
6306 | (if (and eoh (or (< eoh here) (= bol here))) | |
6307 | eoh bol))) | |
cdd3286f DG |
6308 | (if (and (boundp 'visual-line-mode) visual-line-mode) |
6309 | (beginning-of-visual-line n) | |
6310 | (beginning-of-line n)))) | |
23f87bed | 6311 | |
eec82323 LMI |
6312 | (defun message-buffer-name (type &optional to group) |
6313 | "Return a new (unique) buffer name based on TYPE and TO." | |
6314 | (cond | |
16409b0b | 6315 | ;; Generate a new buffer name The Message Way. |
815b81c8 | 6316 | ((memq message-generate-new-buffers '(unique t)) |
16409b0b GM |
6317 | (generate-new-buffer-name |
6318 | (concat "*" type | |
6319 | (if to | |
6320 | (concat " to " | |
6321 | (or (car (mail-extract-address-components to)) | |
6322 | to) "") | |
6323 | "") | |
6324 | (if (and group (not (string= group ""))) (concat " on " group) "") | |
6325 | "*"))) | |
eec82323 LMI |
6326 | ;; Check whether `message-generate-new-buffers' is a function, |
6327 | ;; and if so, call it. | |
23f87bed | 6328 | ((functionp message-generate-new-buffers) |
eec82323 | 6329 | (funcall message-generate-new-buffers type to group)) |
16409b0b | 6330 | ((eq message-generate-new-buffers 'unsent) |
eec82323 | 6331 | (generate-new-buffer-name |
16409b0b | 6332 | (concat "*unsent " type |
eec82323 LMI |
6333 | (if to |
6334 | (concat " to " | |
6335 | (or (car (mail-extract-address-components to)) | |
6336 | to) "") | |
6337 | "") | |
6338 | (if (and group (not (string= group ""))) (concat " on " group) "") | |
6339 | "*"))) | |
815b81c8 | 6340 | ;; Search for the existing message buffer with the specified name. |
eec82323 | 6341 | (t |
815b81c8 MB |
6342 | (let* ((new (if (eq message-generate-new-buffers 'standard) |
6343 | (generate-new-buffer-name (concat "*" type " message*")) | |
6344 | (let ((message-generate-new-buffers 'unique)) | |
6345 | (message-buffer-name type to group)))) | |
6346 | (regexp (concat "\\`" | |
6347 | (regexp-quote | |
6348 | (if (string-match "<[0-9]+>\\'" new) | |
6349 | (substring new 0 (match-beginning 0)) | |
6350 | new)) | |
6351 | "\\(?:<\\([0-9]+\\)>\\)?\\'")) | |
6352 | (case-fold-search nil)) | |
6353 | (or (cdar | |
6354 | (last | |
6355 | (sort | |
6356 | (delq nil | |
6357 | (mapcar | |
6358 | (lambda (b) | |
6359 | (when (and (string-match regexp (setq b (buffer-name b))) | |
6360 | (eq (with-current-buffer b major-mode) | |
6361 | 'message-mode)) | |
6362 | (cons (string-to-number (or (match-string 1 b) "1")) | |
6363 | b))) | |
6364 | (buffer-list))) | |
6365 | 'car-less-than-car))) | |
6366 | new))))) | |
eec82323 | 6367 | |
4a508479 | 6368 | (defun message-pop-to-buffer (name &optional switch-function) |
eec82323 LMI |
6369 | "Pop to buffer NAME, and warn if it already exists and is modified." |
6370 | (let ((buffer (get-buffer name))) | |
6371 | (if (and buffer | |
6372 | (buffer-name buffer)) | |
815b81c8 MB |
6373 | (let ((window (get-buffer-window buffer 0))) |
6374 | (if window | |
6375 | ;; Raise the frame already displaying the message buffer. | |
6376 | (progn | |
6377 | (gnus-select-frame-set-input-focus (window-frame window)) | |
6378 | (select-window window)) | |
52749742 | 6379 | (funcall (or switch-function #'pop-to-buffer) buffer) |
4a508479 | 6380 | (set-buffer buffer)) |
eec82323 | 6381 | (when (and (buffer-modified-p) |
815b81c8 MB |
6382 | (not (prog1 |
6383 | (y-or-n-p | |
6384 | "Message already being composed; erase? ") | |
6385 | (message nil)))) | |
eec82323 | 6386 | (error "Message being composed"))) |
1ea1c2fa KY |
6387 | (funcall (or switch-function |
6388 | (if (fboundp #'pop-to-buffer-same-window) | |
6389 | #'pop-to-buffer-same-window | |
6390 | #'pop-to-buffer)) | |
6391 | name) | |
4a508479 | 6392 | (set-buffer name)) |
6748645f LMI |
6393 | (erase-buffer) |
6394 | (message-mode))) | |
eec82323 LMI |
6395 | |
6396 | (defun message-do-send-housekeeping () | |
6397 | "Kill old message buffers." | |
6398 | ;; We might have sent this buffer already. Delete it from the | |
6399 | ;; list of buffers. | |
6400 | (setq message-buffer-list (delq (current-buffer) message-buffer-list)) | |
6401 | (while (and message-max-buffers | |
23f87bed | 6402 | message-buffer-list |
eec82323 LMI |
6403 | (>= (length message-buffer-list) message-max-buffers)) |
6404 | ;; Kill the oldest buffer -- unless it has been changed. | |
6405 | (let ((buffer (pop message-buffer-list))) | |
6406 | (when (and (buffer-name buffer) | |
6407 | (not (buffer-modified-p buffer))) | |
6408 | (kill-buffer buffer)))) | |
6409 | ;; Rename the buffer. | |
6410 | (if message-send-rename-function | |
6411 | (funcall message-send-rename-function) | |
a5166359 | 6412 | (message-default-send-rename-function)) |
eec82323 LMI |
6413 | ;; Push the current buffer onto the list. |
6414 | (when message-max-buffers | |
6415 | (setq message-buffer-list | |
6416 | (nconc message-buffer-list (list (current-buffer)))))) | |
6417 | ||
a5166359 LMI |
6418 | (defun message-default-send-rename-function () |
6419 | ;; Note: mail-abbrevs of XEmacs renames buffer name behind Gnus. | |
6420 | (when (string-match | |
6421 | "\\`\\*\\(sent \\|unsent \\)?\\(.+\\)\\*[^\\*]*\\|\\`mail to " | |
6422 | (buffer-name)) | |
6423 | (let ((name (match-string 2 (buffer-name))) | |
6424 | to group) | |
6425 | (if (not (or (null name) | |
6426 | (string-equal name "mail") | |
6427 | (string-equal name "posting"))) | |
6428 | (setq name (concat "*sent " name "*")) | |
6429 | (message-narrow-to-headers) | |
6430 | (setq to (message-fetch-field "to")) | |
6431 | (setq group (message-fetch-field "newsgroups")) | |
6432 | (widen) | |
6433 | (setq name | |
6434 | (cond | |
6435 | (to (concat "*sent mail to " | |
6436 | (or (car (mail-extract-address-components to)) | |
6437 | to) "*")) | |
6438 | ((and group (not (string= group ""))) | |
6439 | (concat "*sent posting on " group "*")) | |
6440 | (t "*sent mail*")))) | |
6441 | (unless (string-equal name (buffer-name)) | |
6442 | (rename-buffer name t))))) | |
6443 | ||
88818fbe SZ |
6444 | (defun message-mail-user-agent () |
6445 | (let ((mua (cond | |
6446 | ((not message-mail-user-agent) nil) | |
6447 | ((eq message-mail-user-agent t) mail-user-agent) | |
6448 | (t message-mail-user-agent)))) | |
6449 | (if (memq mua '(message-user-agent gnus-user-agent)) | |
6450 | nil | |
6451 | mua))) | |
6452 | ||
b1ea3797 CY |
6453 | ;; YANK-ACTION, if non-nil, can be a buffer or a yank action of the |
6454 | ;; form (FUNCTION . ARGS). | |
6455 | (defun message-setup (headers &optional yank-action actions | |
25ca2e61 | 6456 | continue switch-function return-action) |
88818fbe | 6457 | (let ((mua (message-mail-user-agent)) |
b1ea3797 | 6458 | subject to field) |
88818fbe | 6459 | (if (not (and message-this-is-mail mua)) |
25ca2e61 | 6460 | (message-setup-1 headers yank-action actions return-action) |
88818fbe SZ |
6461 | (setq headers (copy-sequence headers)) |
6462 | (setq field (assq 'Subject headers)) | |
6463 | (when field | |
6464 | (setq subject (cdr field)) | |
6465 | (setq headers (delq field headers))) | |
6466 | (setq field (assq 'To headers)) | |
6467 | (when field | |
6468 | (setq to (cdr field)) | |
6469 | (setq headers (delq field headers))) | |
6470 | (let ((mail-user-agent mua)) | |
7d829636 | 6471 | (compose-mail to subject |
88818fbe SZ |
6472 | (mapcar (lambda (item) |
6473 | (cons | |
6474 | (format "%s" (car item)) | |
6475 | (cdr item))) | |
6476 | headers) | |
b1ea3797 CY |
6477 | continue switch-function |
6478 | (if (bufferp yank-action) | |
6479 | (list 'insert-buffer yank-action) | |
6480 | yank-action) | |
6481 | actions))))) | |
a1506d29 | 6482 | |
23f87bed MB |
6483 | (defun message-headers-to-generate (headers included-headers excluded-headers) |
6484 | "Return a list that includes all headers from HEADERS. | |
c86d4601 | 6485 | If INCLUDED-HEADERS is a list, just include those headers. If it is |
23f87bed MB |
6486 | t, include all headers. In any case, headers from EXCLUDED-HEADERS |
6487 | are not included." | |
6488 | (let ((result nil) | |
6489 | header-name) | |
6490 | (dolist (header headers) | |
6491 | (setq header-name (cond | |
6492 | ((and (consp header) | |
6493 | (eq (car header) 'optional)) | |
6494 | ;; On the form (optional . Header) | |
6495 | (cdr header)) | |
6496 | ((consp header) | |
6497 | ;; On the form (Header . function) | |
6498 | (car header)) | |
6499 | (t | |
6500 | ;; Just a Header. | |
6501 | header))) | |
6502 | (when (and (not (memq header-name excluded-headers)) | |
6503 | (or (eq included-headers t) | |
6504 | (memq header-name included-headers))) | |
6505 | (push header result))) | |
6506 | (nreverse result))) | |
6507 | ||
25ca2e61 | 6508 | (defun message-setup-1 (headers &optional yank-action actions return-action) |
0c773047 SZ |
6509 | (dolist (action actions) |
6510 | (condition-case nil | |
6511 | (add-to-list 'message-send-actions | |
6512 | `(apply ',(car action) ',(cdr action))))) | |
25ca2e61 | 6513 | (setq message-return-action return-action) |
f53a6590 CY |
6514 | (setq message-reply-buffer |
6515 | (if (and (consp yank-action) | |
6516 | (eq (car yank-action) 'insert-buffer)) | |
6517 | (nth 1 yank-action) | |
6518 | yank-action)) | |
eec82323 LMI |
6519 | (goto-char (point-min)) |
6520 | ;; Insert all the headers. | |
6521 | (mail-header-format | |
6522 | (let ((h headers) | |
6523 | (alist message-header-format-alist)) | |
6524 | (while h | |
6525 | (unless (assq (caar h) message-header-format-alist) | |
6526 | (push (list (caar h)) alist)) | |
6527 | (pop h)) | |
6528 | alist) | |
6529 | headers) | |
6530 | (delete-region (point) (progn (forward-line -1) (point))) | |
6531 | (when message-default-headers | |
d806c872 G |
6532 | (insert |
6533 | (if (functionp message-default-headers) | |
6534 | (funcall message-default-headers) | |
6535 | message-default-headers)) | |
f129a4df | 6536 | (or (bolp) (insert ?\n))) |
ec72bf63 | 6537 | (insert (concat mail-header-separator "\n")) |
eec82323 | 6538 | (forward-line -1) |
0d1c2cc8 G |
6539 | ;; If a crash happens while replying, the auto-save file would *not* have a |
6540 | ;; `References:' header if `message-generate-headers-first' was nil. | |
6541 | ;; Therefore, always generate it first. | |
6542 | (let ((message-generate-headers-first | |
184469b7 JD |
6543 | (if (eq message-generate-headers-first t) |
6544 | t | |
6545 | (append message-generate-headers-first '(References))))) | |
0d1c2cc8 G |
6546 | (when (message-news-p) |
6547 | (when message-default-news-headers | |
6548 | (insert message-default-news-headers) | |
6549 | (or (bolp) (insert ?\n))) | |
eec82323 | 6550 | (message-generate-headers |
23f87bed | 6551 | (message-headers-to-generate |
0d1c2cc8 G |
6552 | (append message-required-news-headers |
6553 | message-required-headers) | |
6554 | message-generate-headers-first | |
6555 | '(Lines Subject)))) | |
6556 | (when (message-mail-p) | |
6557 | (when message-default-mail-headers | |
6558 | (insert message-default-mail-headers) | |
6559 | (or (bolp) (insert ?\n))) | |
eec82323 | 6560 | (message-generate-headers |
23f87bed | 6561 | (message-headers-to-generate |
0d1c2cc8 G |
6562 | (append message-required-mail-headers |
6563 | message-required-headers) | |
6564 | message-generate-headers-first | |
6565 | '(Lines Subject))))) | |
eec82323 LMI |
6566 | (run-hooks 'message-signature-setup-hook) |
6567 | (message-insert-signature) | |
eec82323 LMI |
6568 | (save-restriction |
6569 | (message-narrow-to-headers) | |
6570 | (run-hooks 'message-header-setup-hook)) | |
a8151ef7 | 6571 | (setq buffer-undo-list nil) |
01c52d31 MB |
6572 | (when message-generate-hashcash |
6573 | ;; Generate hashcash headers for recipients already known | |
6574 | (mail-add-payment-async)) | |
9b3ebcb6 MB |
6575 | ;; Gnus posting styles are applied via buffer-local `message-setup-hook' |
6576 | ;; values. | |
eec82323 | 6577 | (run-hooks 'message-setup-hook) |
46e8fe3d MB |
6578 | ;; Do this last to give it precedence over posting styles, etc. |
6579 | (when (message-mail-p) | |
6580 | (save-restriction | |
6581 | (message-narrow-to-headers) | |
6582 | (if message-alternative-emails | |
6583 | (message-use-alternative-email-as-from)))) | |
eec82323 | 6584 | (message-position-point) |
9b3ebcb6 MB |
6585 | ;; Allow correct handling of `message-checksum' in `message-yank-original': |
6586 | (set-buffer-modified-p nil) | |
9af32a1a EZ |
6587 | (undo-boundary) |
6588 | ;; rmail-start-mail expects message-mail to return t (Bug#9392) | |
6589 | t) | |
eec82323 LMI |
6590 | |
6591 | (defun message-set-auto-save-file-name () | |
6592 | "Associate the message buffer with a file in the drafts directory." | |
83e905f3 | 6593 | (when message-auto-save-directory |
8c6f6f4b DL |
6594 | (unless (file-directory-p |
6595 | (directory-file-name message-auto-save-directory)) | |
62363b21 | 6596 | (make-directory message-auto-save-directory t)) |
6748645f LMI |
6597 | (if (gnus-alive-p) |
6598 | (setq message-draft-article | |
6599 | (nndraft-request-associate-buffer "drafts")) | |
e52cac88 MB |
6600 | |
6601 | ;; If Gnus were alive, draft messages would be saved in the drafts folder. | |
6602 | ;; But Gnus is not alive, so arrange to save the draft message in a | |
6603 | ;; regular file in message-auto-save-directory. Append a unique | |
6604 | ;; time-based suffix to the filename to allow multiple drafts to be saved | |
6605 | ;; simultaneously without overwriting each other (which mimics the | |
6606 | ;; functionality of the Gnus drafts folder). | |
23f87bed | 6607 | (setq buffer-file-name (expand-file-name |
e52cac88 | 6608 | (concat |
23f87bed | 6609 | (if (memq system-type |
f5ec697d | 6610 | '(ms-dos windows-nt cygwin)) |
23f87bed MB |
6611 | "message" |
6612 | "*message*") | |
e52cac88 | 6613 | (format-time-string "-%Y%m%d-%H%M%S")) |
23f87bed | 6614 | message-auto-save-directory)) |
6748645f | 6615 | (setq buffer-auto-save-file-name (make-auto-save-file-name))) |
16409b0b GM |
6616 | (clear-visited-file-modtime) |
6617 | (setq buffer-file-coding-system message-draft-coding-system))) | |
6748645f LMI |
6618 | |
6619 | (defun message-disassociate-draft () | |
6620 | "Disassociate the message buffer from the drafts directory." | |
6621 | (when message-draft-article | |
6622 | (nndraft-request-expire-articles | |
b1d6cda3 | 6623 | (list message-draft-article) "drafts" nil t))) |
eec82323 | 6624 | |
16409b0b GM |
6625 | (defun message-insert-headers () |
6626 | "Generate the headers for the article." | |
6627 | (interactive) | |
6628 | (save-excursion | |
6629 | (save-restriction | |
6630 | (message-narrow-to-headers) | |
6631 | (when (message-news-p) | |
6632 | (message-generate-headers | |
6633 | (delq 'Lines | |
6634 | (delq 'Subject | |
6635 | (copy-sequence message-required-news-headers))))) | |
6636 | (when (message-mail-p) | |
6637 | (message-generate-headers | |
6638 | (delq 'Lines | |
6639 | (delq 'Subject | |
6640 | (copy-sequence message-required-mail-headers)))))))) | |
6641 | ||
eec82323 LMI |
6642 | \f |
6643 | ||
6644 | ;;; | |
6645 | ;;; Commands for interfacing with message | |
6646 | ;;; | |
6647 | ||
6648 | ;;;###autoload | |
25ca2e61 CY |
6649 | (defun message-mail (&optional to subject other-headers continue |
6650 | switch-function yank-action send-actions | |
6651 | return-action &rest ignored) | |
6748645f | 6652 | "Start editing a mail message to be sent. |
815b81c8 MB |
6653 | OTHER-HEADERS is an alist of header/value pairs. CONTINUE says whether |
6654 | to continue editing a message already being composed. SWITCH-FUNCTION | |
6655 | is a function used to switch to and display the mail buffer." | |
eec82323 | 6656 | (interactive) |
b1ea3797 | 6657 | (let ((message-this-is-mail t)) |
88818fbe | 6658 | (unless (message-mail-user-agent) |
4a508479 | 6659 | (message-pop-to-buffer |
815b81c8 MB |
6660 | ;; Search for the existing message buffer if `continue' is non-nil. |
6661 | (let ((message-generate-new-buffers | |
6662 | (when (or (not continue) | |
6663 | (eq message-generate-new-buffers 'standard) | |
6664 | (functionp message-generate-new-buffers)) | |
6665 | message-generate-new-buffers))) | |
4a508479 CY |
6666 | (message-buffer-name "mail" to)) |
6667 | switch-function)) | |
eec82323 LMI |
6668 | (message-setup |
6669 | (nconc | |
6670 | `((To . ,(or to "")) (Subject . ,(or subject ""))) | |
ae01d7ca GM |
6671 | ;; C-h f compose-mail says that headers should be specified as |
6672 | ;; (string . value); however all the rest of message expects | |
6673 | ;; headers to be symbols, not strings (eg message-header-format-alist). | |
6674 | ;; http://lists.gnu.org/archive/html/emacs-devel/2011-01/msg00337.html | |
6675 | ;; We need to convert any string input, eg from rmail-start-mail. | |
6676 | (dolist (h other-headers other-headers) | |
6677 | (if (stringp (car h)) (setcar h (intern (capitalize (car h))))))) | |
25ca2e61 | 6678 | yank-action send-actions continue switch-function |
0d1c2cc8 | 6679 | return-action))) |
eec82323 LMI |
6680 | |
6681 | ;;;###autoload | |
6682 | (defun message-news (&optional newsgroups subject) | |
6683 | "Start editing a news article to be sent." | |
6684 | (interactive) | |
6685 | (let ((message-this-is-news t)) | |
23f87bed | 6686 | (message-pop-to-buffer (message-buffer-name "posting" nil newsgroups)) |
eec82323 LMI |
6687 | (message-setup `((Newsgroups . ,(or newsgroups "")) |
6688 | (Subject . ,(or subject "")))))) | |
6689 | ||
9b3ebcb6 MB |
6690 | (defun message-alter-recipients-discard-bogus-full-name (addrcell) |
6691 | "Discard mail address in full names. | |
6692 | When the full name in reply headers contains the mail | |
6693 | address (e.g. \"foo@bar <foo@bar>\"), discard full name. | |
6694 | ADDRCELL is a cons cell where the car is the mail address and the | |
6695 | cdr is the complete address (full name and mail address)." | |
6696 | (if (string-match (concat (regexp-quote (car addrcell)) ".*" | |
6697 | (regexp-quote (car addrcell))) | |
6698 | (cdr addrcell)) | |
6699 | (cons (car addrcell) (car addrcell)) | |
6700 | addrcell)) | |
6701 | ||
6702 | (defcustom message-alter-recipients-function nil | |
6703 | "Function called to allow alteration of reply header structures. | |
6704 | It is called in `message-get-reply-headers' for each recipient. | |
6705 | The function is called with one parameter, a cons cell ..." | |
6706 | :type '(choice (const :tag "None" nil) | |
6707 | (const :tag "Discard bogus full name" | |
6708 | message-alter-recipients-discard-bogus-full-name) | |
6709 | function) | |
6710 | :version "23.1" ;; No Gnus | |
6711 | :group 'message-headers) | |
6712 | ||
23f87bed | 6713 | (defun message-get-reply-headers (wide &optional to-address address-headers) |
01c52d31 | 6714 | (let (follow-to mct never-mct to cc author mft recipients extra) |
2cdd366f | 6715 | ;; Find all relevant headers we need. |
23f87bed MB |
6716 | (save-restriction |
6717 | (message-narrow-to-headers-or-head) | |
6718 | ;; Gmane renames "To". Look at "Original-To", too, if it is present in | |
6719 | ;; message-header-synonyms. | |
6720 | (setq to (or (message-fetch-field "to") | |
6721 | (and (loop for synonym in message-header-synonyms | |
6722 | when (memq 'Original-To synonym) | |
6723 | return t) | |
6724 | (message-fetch-field "original-to"))) | |
6725 | cc (message-fetch-field "cc") | |
01c52d31 MB |
6726 | extra (when message-extra-wide-headers |
6727 | (mapconcat 'identity | |
6728 | (mapcar 'message-fetch-field | |
6729 | message-extra-wide-headers) | |
6730 | ", ")) | |
23f87bed MB |
6731 | mct (message-fetch-field "mail-copies-to") |
6732 | author (or (message-fetch-field "mail-reply-to") | |
70678cd5 | 6733 | (message-fetch-field "reply-to")) |
23f87bed | 6734 | mft (and message-use-mail-followup-to |
70678cd5 KY |
6735 | (message-fetch-field "mail-followup-to"))) |
6736 | ;; Make sure this message goes to the author if this is a wide | |
3394be35 | 6737 | ;; reply, since Reply-To address may be a list address a mailing |
70678cd5 KY |
6738 | ;; list server added. |
6739 | (when (and wide author) | |
6740 | (setq cc (concat author ", " cc))) | |
6741 | (when (or wide (not author)) | |
6742 | (setq author (or (message-fetch-field "from") "")))) | |
16409b0b GM |
6743 | |
6744 | ;; Handle special values of Mail-Copies-To. | |
6745 | (when mct | |
6746 | (cond ((or (equal (downcase mct) "never") | |
6747 | (equal (downcase mct) "nobody")) | |
6748 | (setq never-mct t) | |
6749 | (setq mct nil)) | |
6750 | ((or (equal (downcase mct) "always") | |
6751 | (equal (downcase mct) "poster")) | |
23f87bed | 6752 | (setq mct author)))) |
16409b0b | 6753 | |
23f87bed MB |
6754 | (save-match-data |
6755 | ;; Build (textual) list of new recipient addresses. | |
6756 | (cond | |
2755ee78 LMI |
6757 | (to-address |
6758 | (setq recipients (concat ", " to-address)) | |
6759 | ;; If the author explicitly asked for a copy, we don't deny it to them. | |
6760 | (if mct (setq recipients (concat recipients ", " mct)))) | |
23f87bed MB |
6761 | ((not wide) |
6762 | (setq recipients (concat ", " author))) | |
6763 | (address-headers | |
6764 | (dolist (header address-headers) | |
6765 | (let ((value (message-fetch-field header))) | |
6766 | (when value | |
6767 | (setq recipients (concat recipients ", " value)))))) | |
6768 | ((and mft | |
6769 | (string-match "[^ \t,]" mft) | |
6770 | (or (not (eq message-use-mail-followup-to 'ask)) | |
6771 | (message-y-or-n-p "Obey Mail-Followup-To? " t "\ | |
6772 | You should normally obey the Mail-Followup-To: header. In this | |
6773 | article, it has the value of | |
6774 | ||
6775 | " mft " | |
6776 | ||
6777 | which directs your response to " (if (string-match "," mft) | |
6778 | "the specified addresses" | |
6779 | "that address only") ". | |
6780 | ||
6781 | Most commonly, Mail-Followup-To is used by a mailing list poster to | |
6782 | express that responses should be sent to just the list, and not the | |
6783 | poster as well. | |
6784 | ||
6785 | If a message is posted to several mailing lists, Mail-Followup-To may | |
6786 | also be used to direct the following discussion to one list only, | |
6787 | because discussions that are spread over several lists tend to be | |
6788 | fragmented and very difficult to follow. | |
6789 | ||
6790 | Also, some source/announcement lists are not intended for discussion; | |
2fd561a9 MB |
6791 | responses here are directed to other addresses. |
6792 | ||
6793 | You may customize the variable `message-use-mail-followup-to', if you | |
6794 | want to get rid of this query permanently."))) | |
23f87bed | 6795 | (setq recipients (concat ", " mft))) |
23f87bed MB |
6796 | (t |
6797 | (setq recipients (if never-mct "" (concat ", " author))) | |
01c52d31 MB |
6798 | (if to (setq recipients (concat recipients ", " to))) |
6799 | (if cc (setq recipients (concat recipients ", " cc))) | |
6800 | (if extra (setq recipients (concat recipients ", " extra))) | |
23f87bed MB |
6801 | (if mct (setq recipients (concat recipients ", " mct))))) |
6802 | (if (>= (length recipients) 2) | |
6803 | ;; Strip the leading ", ". | |
6804 | (setq recipients (substring recipients 2))) | |
6805 | ;; Squeeze whitespace. | |
6806 | (while (string-match "[ \t][ \t]+" recipients) | |
6807 | (setq recipients (replace-match " " t t recipients))) | |
0fb40182 JD |
6808 | ;; Remove addresses that match `mail-dont-reply-to-names'. |
6809 | (let ((mail-dont-reply-to-names (message-dont-reply-to-names))) | |
6810 | (setq recipients (mail-dont-reply-to recipients))) | |
23f87bed MB |
6811 | ;; Perhaps "Mail-Copies-To: never" removed the only address? |
6812 | (if (string-equal recipients "") | |
6813 | (setq recipients author)) | |
6814 | ;; Convert string to a list of (("foo@bar" . "Name <Foo@BAR>") ...). | |
6815 | (setq recipients | |
6816 | (mapcar | |
6817 | (lambda (addr) | |
9b3ebcb6 MB |
6818 | (if message-alter-recipients-function |
6819 | (funcall message-alter-recipients-function | |
6820 | (cons (downcase (mail-strip-quoted-names addr)) | |
6821 | addr)) | |
6822 | (cons (downcase (mail-strip-quoted-names addr)) addr))) | |
23f87bed | 6823 | (message-tokenize-header recipients))) |
ecd283b9 | 6824 | ;; Remove all duplicates. |
23f87bed MB |
6825 | (let ((s recipients)) |
6826 | (while s | |
ecd283b9 LMI |
6827 | (let ((address (car (pop s)))) |
6828 | (while (assoc address s) | |
6829 | (setq recipients (delq (assoc address s) recipients) | |
6830 | s (delq (assoc address s) s)))))) | |
23f87bed MB |
6831 | |
6832 | ;; Remove hierarchical lists that are contained within each other, | |
6833 | ;; if message-hierarchical-addresses is defined. | |
6834 | (when message-hierarchical-addresses | |
6835 | (let ((plain-addrs (mapcar 'car recipients)) | |
6836 | subaddrs recip) | |
6837 | (while plain-addrs | |
6838 | (setq subaddrs (assoc (car plain-addrs) | |
6839 | message-hierarchical-addresses) | |
6840 | plain-addrs (cdr plain-addrs)) | |
6841 | (when subaddrs | |
6842 | (setq subaddrs (cdr subaddrs)) | |
6843 | (while subaddrs | |
6844 | (setq recip (assoc (car subaddrs) recipients) | |
6845 | subaddrs (cdr subaddrs)) | |
6846 | (if recip | |
6847 | (setq recipients (delq recip recipients)))))))) | |
6848 | ||
2cdd366f | 6849 | (setq recipients (message-prune-recipients recipients)) |
c9fc72fa | 6850 | |
23f87bed MB |
6851 | ;; Build the header alist. Allow the user to be asked whether |
6852 | ;; or not to reply to all recipients in a wide reply. | |
6853 | (setq follow-to (list (cons 'To (cdr (pop recipients))))) | |
6854 | (when (and recipients | |
6855 | (or (not message-wide-reply-confirm-recipients) | |
6856 | (y-or-n-p "Reply to all recipients? "))) | |
6857 | (setq recipients (mapconcat | |
6858 | (lambda (addr) (cdr addr)) recipients ", ")) | |
6859 | (if (string-match "^ +" recipients) | |
6860 | (setq recipients (substring recipients (match-end 0)))) | |
6861 | (push (cons 'Cc recipients) follow-to))) | |
16409b0b GM |
6862 | follow-to)) |
6863 | ||
2cdd366f KY |
6864 | (defun message-prune-recipients (recipients) |
6865 | (dolist (rule message-prune-recipient-rules) | |
6866 | (let ((match (car rule)) | |
c9fc72fa | 6867 | dup-match |
2cdd366f KY |
6868 | address) |
6869 | (dolist (recipient recipients) | |
6870 | (setq address (car recipient)) | |
6871 | (when (string-match match address) | |
6872 | (setq dup-match (replace-match (cadr rule) nil nil address)) | |
6873 | (dolist (recipient recipients) | |
6874 | ;; Don't delete the address that triggered this. | |
6875 | (when (and (not (eq address (car recipient))) | |
6876 | (string-match dup-match (car recipient))) | |
6877 | (setq recipients (delq recipient recipients)))))))) | |
6878 | recipients) | |
6879 | ||
4573e0df MB |
6880 | (defcustom message-simplify-subject-functions |
6881 | '(message-strip-list-identifiers | |
6882 | message-strip-subject-re | |
6883 | message-strip-subject-trailing-was | |
6884 | message-strip-subject-encoded-words) | |
6885 | "List of functions taking a string argument that simplify subjects. | |
6886 | The functions are applied when replying to a message. | |
6887 | ||
6888 | Useful functions to put in this list include: | |
6889 | `message-strip-list-identifiers', `message-strip-subject-re', | |
6890 | `message-strip-subject-trailing-was', and | |
6891 | `message-strip-subject-encoded-words'." | |
6892 | :version "22.1" ;; Gnus 5.10.9 | |
6893 | :group 'message-various | |
6894 | :type '(repeat function)) | |
6895 | ||
6896 | (defun message-simplify-subject (subject &optional functions) | |
6897 | "Return simplified SUBJECT." | |
6898 | (unless functions | |
6899 | ;; Simplify fully: | |
6900 | (setq functions message-simplify-subject-functions)) | |
6901 | (when (and (memq 'message-strip-list-identifiers functions) | |
6902 | gnus-list-identifiers) | |
6903 | (setq subject (message-strip-list-identifiers subject))) | |
6904 | (when (memq 'message-strip-subject-re functions) | |
6905 | (setq subject (concat "Re: " (message-strip-subject-re subject)))) | |
6906 | (when (and (memq 'message-strip-subject-trailing-was functions) | |
6907 | message-subject-trailing-was-query) | |
6908 | (setq subject (message-strip-subject-trailing-was subject))) | |
6909 | (when (memq 'message-strip-subject-encoded-words functions) | |
6910 | (setq subject (message-strip-subject-encoded-words subject))) | |
6911 | subject) | |
6912 | ||
eec82323 | 6913 | ;;;###autoload |
5415d076 | 6914 | (defun message-reply (&optional to-address wide switch-function) |
eec82323 LMI |
6915 | "Start editing a reply to the article in the current buffer." |
6916 | (interactive) | |
16409b0b | 6917 | (require 'gnus-sum) ; for gnus-list-identifiers |
eec82323 | 6918 | (let ((cur (current-buffer)) |
99c2a4e3 | 6919 | from subject date |
eec82323 LMI |
6920 | references message-id follow-to |
6921 | (inhibit-point-motion-hooks t) | |
16409b0b GM |
6922 | (message-this-is-mail t) |
6923 | gnus-warning) | |
eec82323 | 6924 | (save-restriction |
158d6e07 | 6925 | (message-narrow-to-head-1) |
eec82323 LMI |
6926 | ;; Allow customizations to have their say. |
6927 | (if (not wide) | |
6928 | ;; This is a regular reply. | |
23f87bed | 6929 | (when (functionp message-reply-to-function) |
eec82323 | 6930 | (save-excursion |
23f87bed MB |
6931 | (setq follow-to (funcall message-reply-to-function)))) |
6932 | ;; This is a followup. | |
6933 | (when (functionp message-wide-reply-to-function) | |
6934 | (save-excursion | |
6935 | (setq follow-to | |
6936 | (funcall message-wide-reply-to-function))))) | |
16409b0b | 6937 | (setq message-id (message-fetch-field "message-id" t) |
eec82323 | 6938 | references (message-fetch-field "references") |
16409b0b | 6939 | date (message-fetch-field "date") |
0327a464 | 6940 | from (or (message-fetch-field "from") "nobody") |
16409b0b | 6941 | subject (or (message-fetch-field "subject") "none")) |
4573e0df MB |
6942 | |
6943 | ;; Strip list identifiers, "Re: ", and "was:" | |
6944 | (setq subject (message-simplify-subject subject)) | |
eec82323 | 6945 | |
23f87bed MB |
6946 | (when (and (setq gnus-warning (message-fetch-field "gnus-warning")) |
6947 | (string-match "<[^>]+>" gnus-warning)) | |
6948 | (setq message-id (match-string 0 gnus-warning))) | |
eec82323 | 6949 | |
23f87bed MB |
6950 | (unless follow-to |
6951 | (setq follow-to (message-get-reply-headers wide to-address)))) | |
eec82323 | 6952 | |
c152c1d6 LMI |
6953 | (let ((headers |
6954 | `((Subject . ,subject) | |
6955 | ,@follow-to))) | |
6956 | (unless (message-mail-user-agent) | |
6957 | (message-pop-to-buffer | |
6958 | (message-buffer-name | |
6959 | (if wide "wide reply" "reply") from | |
6960 | (if wide to-address nil)) | |
6961 | switch-function)) | |
6962 | (setq message-reply-headers | |
6963 | (vector 0 (cdr (assq 'Subject headers)) | |
6964 | from date message-id references 0 0 "")) | |
6965 | (message-setup headers cur)))) | |
eec82323 LMI |
6966 | |
6967 | ;;;###autoload | |
6748645f | 6968 | (defun message-wide-reply (&optional to-address) |
eec82323 LMI |
6969 | "Make a \"wide\" reply to the message in the current buffer." |
6970 | (interactive) | |
6748645f | 6971 | (message-reply to-address t)) |
eec82323 LMI |
6972 | |
6973 | ;;;###autoload | |
6974 | (defun message-followup (&optional to-newsgroups) | |
6975 | "Follow up to the message in the current buffer. | |
6976 | If TO-NEWSGROUPS, use that as the new Newsgroups line." | |
6977 | (interactive) | |
16409b0b | 6978 | (require 'gnus-sum) ; for gnus-list-identifiers |
eec82323 | 6979 | (let ((cur (current-buffer)) |
23f87bed | 6980 | from subject date reply-to mrt mct |
eec82323 LMI |
6981 | references message-id follow-to |
6982 | (inhibit-point-motion-hooks t) | |
6983 | (message-this-is-news t) | |
6984 | followup-to distribution newsgroups gnus-warning posted-to) | |
6985 | (save-restriction | |
6986 | (narrow-to-region | |
6987 | (goto-char (point-min)) | |
6988 | (if (search-forward "\n\n" nil t) | |
6989 | (1- (point)) | |
6990 | (point-max))) | |
23f87bed | 6991 | (when (functionp message-followup-to-function) |
eec82323 LMI |
6992 | (setq follow-to |
6993 | (funcall message-followup-to-function))) | |
6994 | (setq from (message-fetch-field "from") | |
6995 | date (message-fetch-field "date") | |
6996 | subject (or (message-fetch-field "subject") "none") | |
6997 | references (message-fetch-field "references") | |
6998 | message-id (message-fetch-field "message-id" t) | |
6999 | followup-to (message-fetch-field "followup-to") | |
7000 | newsgroups (message-fetch-field "newsgroups") | |
7001 | posted-to (message-fetch-field "posted-to") | |
7002 | reply-to (message-fetch-field "reply-to") | |
23f87bed | 7003 | mrt (message-fetch-field "mail-reply-to") |
eec82323 LMI |
7004 | distribution (message-fetch-field "distribution") |
7005 | mct (message-fetch-field "mail-copies-to")) | |
7006 | (when (and (setq gnus-warning (message-fetch-field "gnus-warning")) | |
7007 | (string-match "<[^>]+>" gnus-warning)) | |
7008 | (setq message-id (match-string 0 gnus-warning))) | |
7009 | ;; Remove bogus distribution. | |
7010 | (when (and (stringp distribution) | |
7011 | (let ((case-fold-search t)) | |
7012 | (string-match "world" distribution))) | |
7013 | (setq distribution nil)) | |
4573e0df MB |
7014 | ;; Strip list identifiers, "Re: ", and "was:" |
7015 | (setq subject (message-simplify-subject subject)) | |
eec82323 LMI |
7016 | (widen)) |
7017 | ||
7018 | (message-pop-to-buffer (message-buffer-name "followup" from newsgroups)) | |
7019 | ||
23f87bed MB |
7020 | (setq message-reply-headers |
7021 | (vector 0 subject from date message-id references 0 0 "")) | |
7022 | ||
eec82323 LMI |
7023 | (message-setup |
7024 | `((Subject . ,subject) | |
7025 | ,@(cond | |
7026 | (to-newsgroups | |
7027 | (list (cons 'Newsgroups to-newsgroups))) | |
7028 | (follow-to follow-to) | |
7029 | ((and followup-to message-use-followup-to) | |
7030 | (list | |
7031 | (cond | |
7032 | ((equal (downcase followup-to) "poster") | |
7033 | (if (or (eq message-use-followup-to 'use) | |
7034 | (message-y-or-n-p "Obey Followup-To: poster? " t "\ | |
7035 | You should normally obey the Followup-To: header. | |
7036 | ||
7037 | `Followup-To: poster' sends your response via e-mail instead of news. | |
7038 | ||
7039 | A typical situation where `Followup-To: poster' is used is when the poster | |
2fd561a9 MB |
7040 | does not read the newsgroup, so he wouldn't see any replies sent to it. |
7041 | ||
7042 | You may customize the variable `message-use-followup-to', if you | |
7043 | want to get rid of this query permanently.")) | |
eec82323 LMI |
7044 | (progn |
7045 | (setq message-this-is-news nil) | |
23f87bed | 7046 | (cons 'To (or mrt reply-to from ""))) |
eec82323 LMI |
7047 | (cons 'Newsgroups newsgroups))) |
7048 | (t | |
7049 | (if (or (equal followup-to newsgroups) | |
7050 | (not (eq message-use-followup-to 'ask)) | |
7051 | (message-y-or-n-p | |
7052 | (concat "Obey Followup-To: " followup-to "? ") t "\ | |
7053 | You should normally obey the Followup-To: header. | |
7054 | ||
7055 | `Followup-To: " followup-to "' | |
7056 | directs your response to " (if (string-match "," followup-to) | |
7057 | "the specified newsgroups" | |
7058 | "that newsgroup only") ". | |
7059 | ||
7060 | If a message is posted to several newsgroups, Followup-To is often | |
7061 | used to direct the following discussion to one newsgroup only, | |
7062 | because discussions that are spread over several newsgroup tend to | |
7063 | be fragmented and very difficult to follow. | |
7064 | ||
23f87bed | 7065 | Also, some source/announcement newsgroups are not intended for discussion; |
2fd561a9 MB |
7066 | responses here are directed to other newsgroups. |
7067 | ||
7068 | You may customize the variable `message-use-followup-to', if you | |
7069 | want to get rid of this query permanently.")) | |
eec82323 LMI |
7070 | (cons 'Newsgroups followup-to) |
7071 | (cons 'Newsgroups newsgroups)))))) | |
7072 | (posted-to | |
7073 | `((Newsgroups . ,posted-to))) | |
7074 | (t | |
7075 | `((Newsgroups . ,newsgroups)))) | |
7076 | ,@(and distribution (list (cons 'Distribution distribution))) | |
eec82323 | 7077 | ,@(when (and mct |
16409b0b GM |
7078 | (not (or (equal (downcase mct) "never") |
7079 | (equal (downcase mct) "nobody")))) | |
7080 | (list (cons 'Cc (if (or (equal (downcase mct) "always") | |
7081 | (equal (downcase mct) "poster")) | |
23f87bed | 7082 | (or mrt reply-to from "") |
eec82323 LMI |
7083 | mct))))) |
7084 | ||
23f87bed | 7085 | cur))) |
eec82323 | 7086 | |
23f87bed MB |
7087 | (defun message-is-yours-p () |
7088 | "Non-nil means current article is yours. | |
768e2c65 | 7089 | If you have added 'cancel-messages to `message-shoot-gnksa-feet', all articles |
23f87bed | 7090 | are yours except those that have Cancel-Lock header not belonging to you. |
768e2c65 | 7091 | Instead of shooting GNKSA feet, you should modify `message-alternative-emails' |
23f87bed MB |
7092 | regexp to match all of yours addresses." |
7093 | ;; Canlock-logic as suggested by Per Abrahamsen | |
7094 | ;; <abraham@dina.kvl.dk> | |
7095 | ;; | |
7096 | ;; IF article has cancel-lock THEN | |
7097 | ;; IF we can verify it THEN | |
7098 | ;; issue cancel | |
7099 | ;; ELSE | |
7100 | ;; error: cancellock: article is not yours | |
7101 | ;; ELSE | |
7102 | ;; Use old rules, comparing sender... | |
7103 | (save-excursion | |
7104 | (save-restriction | |
7105 | (message-narrow-to-head-1) | |
4dcb0d7a LMI |
7106 | (if (and (message-fetch-field "Cancel-Lock") |
7107 | (message-gnksa-enable-p 'canlock-verify)) | |
23f87bed MB |
7108 | (if (null (canlock-verify)) |
7109 | t | |
7110 | (error "Failed to verify Cancel-lock: This article is not yours")) | |
7111 | (let (sender from) | |
7112 | (or | |
7113 | (message-gnksa-enable-p 'cancel-messages) | |
7114 | (and (setq sender (message-fetch-field "sender")) | |
7115 | (string-equal (downcase sender) | |
7116 | (downcase (message-make-sender)))) | |
7117 | ;; Email address in From field equals to our address | |
7118 | (and (setq from (message-fetch-field "from")) | |
7119 | (string-equal | |
01c52d31 MB |
7120 | (downcase (car (mail-header-parse-address from))) |
7121 | (downcase (car (mail-header-parse-address | |
7122 | (message-make-from)))))) | |
23f87bed MB |
7123 | ;; Email address in From field matches |
7124 | ;; 'message-alternative-emails' regexp | |
7125 | (and from | |
7126 | message-alternative-emails | |
7127 | (string-match | |
7128 | message-alternative-emails | |
01c52d31 | 7129 | (car (mail-header-parse-address from)))))))))) |
eec82323 LMI |
7130 | |
7131 | ;;;###autoload | |
16409b0b GM |
7132 | (defun message-cancel-news (&optional arg) |
7133 | "Cancel an article you posted. | |
7134 | If ARG, allow editing of the cancellation message." | |
7135 | (interactive "P") | |
eec82323 LMI |
7136 | (unless (message-news-p) |
7137 | (error "This is not a news article; canceling is impossible")) | |
23f87bed MB |
7138 | (let (from newsgroups message-id distribution buf) |
7139 | (save-excursion | |
7140 | ;; Get header info from original article. | |
7141 | (save-restriction | |
7142 | (message-narrow-to-head-1) | |
7143 | (setq from (message-fetch-field "from") | |
7144 | newsgroups (message-fetch-field "newsgroups") | |
7145 | message-id (message-fetch-field "message-id" t) | |
7146 | distribution (message-fetch-field "distribution"))) | |
7147 | ;; Make sure that this article was written by the user. | |
7148 | (unless (message-is-yours-p) | |
7149 | (error "This article is not yours")) | |
7150 | (when (yes-or-no-p "Do you really want to cancel this article? ") | |
eec82323 | 7151 | ;; Make control message. |
16409b0b GM |
7152 | (if arg |
7153 | (message-news) | |
7154 | (setq buf (set-buffer (get-buffer-create " *message cancel*")))) | |
eec82323 | 7155 | (erase-buffer) |
f129a4df CY |
7156 | (insert "Newsgroups: " newsgroups "\n" |
7157 | "From: " from "\n" | |
6ed7a66a | 7158 | "Subject: cancel " message-id "\n" |
f129a4df | 7159 | "Control: cancel " message-id "\n" |
eec82323 | 7160 | (if distribution |
f129a4df | 7161 | (concat "Distribution: " distribution "\n") |
eec82323 | 7162 | "") |
f129a4df | 7163 | mail-header-separator "\n" |
eec82323 | 7164 | message-cancel-message) |
16409b0b GM |
7165 | (run-hooks 'message-cancel-hook) |
7166 | (unless arg | |
7167 | (message "Canceling your article...") | |
7168 | (if (let ((message-syntax-checks | |
7169 | 'dont-check-for-anything-just-trust-me)) | |
7170 | (funcall message-send-news-function)) | |
7171 | (message "Canceling your article...done")) | |
7172 | (kill-buffer buf)))))) | |
eec82323 LMI |
7173 | |
7174 | ;;;###autoload | |
7175 | (defun message-supersede () | |
7176 | "Start composing a message to supersede the current message. | |
7177 | This is done simply by taking the old article and adding a Supersedes | |
7178 | header line with the old Message-ID." | |
7179 | (interactive) | |
23f87bed | 7180 | (let ((cur (current-buffer))) |
eec82323 | 7181 | ;; Check whether the user owns the article that is to be superseded. |
23f87bed | 7182 | (unless (message-is-yours-p) |
eec82323 LMI |
7183 | (error "This article is not yours")) |
7184 | ;; Get a normal message buffer. | |
7185 | (message-pop-to-buffer (message-buffer-name "supersede")) | |
7186 | (insert-buffer-substring cur) | |
16409b0b | 7187 | (mime-to-mml) |
158d6e07 | 7188 | (message-narrow-to-head-1) |
eec82323 LMI |
7189 | ;; Remove unwanted headers. |
7190 | (when message-ignored-supersedes-headers | |
7191 | (message-remove-header message-ignored-supersedes-headers t)) | |
7192 | (goto-char (point-min)) | |
7193 | (if (not (re-search-forward "^Message-ID: " nil t)) | |
7194 | (error "No Message-ID in this article") | |
7195 | (replace-match "Supersedes: " t t)) | |
7196 | (goto-char (point-max)) | |
7197 | (insert mail-header-separator) | |
7198 | (widen) | |
7199 | (forward-line 1))) | |
7200 | ||
7201 | ;;;###autoload | |
7202 | (defun message-recover () | |
7203 | "Reread contents of current buffer from its last auto-save file." | |
7204 | (interactive) | |
7205 | (let ((file-name (make-auto-save-file-name))) | |
7206 | (cond ((save-window-excursion | |
7c2fb837 DN |
7207 | (with-output-to-temp-buffer "*Directory*" |
7208 | (with-current-buffer standard-output | |
7209 | (fundamental-mode)) ; for Emacs 20.4+ | |
7210 | (buffer-disable-undo standard-output) | |
7211 | (let ((default-directory "/")) | |
7212 | (call-process | |
7213 | "ls" nil standard-output nil "-l" file-name))) | |
eec82323 LMI |
7214 | (yes-or-no-p (format "Recover auto save file %s? " file-name))) |
7215 | (let ((buffer-read-only nil)) | |
7216 | (erase-buffer) | |
7217 | (insert-file-contents file-name nil))) | |
d5081c1e | 7218 | (t (error "message-recover canceled"))))) |
eec82323 | 7219 | |
6748645f LMI |
7220 | ;;; Washing Subject: |
7221 | ||
7222 | (defun message-wash-subject (subject) | |
7d829636 | 7223 | "Remove junk like \"Re:\", \"(fwd)\", etc. added to subject string SUBJECT. |
40ba43b4 | 7224 | Previous forwarders, repliers, etc. may add it." |
16409b0b | 7225 | (with-temp-buffer |
47b63dfa | 7226 | (insert subject) |
6748645f LMI |
7227 | (goto-char (point-min)) |
7228 | ;; strip Re/Fwd stuff off the beginning | |
7229 | (while (re-search-forward | |
7230 | "\\([Rr][Ee]:\\|[Ff][Ww][Dd]\\(\\[[0-9]*\\]\\)?:\\|[Ff][Ww]:\\)" nil t) | |
7231 | (replace-match "")) | |
7232 | ||
7233 | ;; and gnus-style forwards [foo@bar.com] subject | |
7234 | (goto-char (point-min)) | |
7235 | (while (re-search-forward "\\[[^ \t]*\\(@\\|\\.\\)[^ \t]*\\]" nil t) | |
7236 | (replace-match "")) | |
7237 | ||
7238 | ;; and off the end | |
7239 | (goto-char (point-max)) | |
7240 | (while (re-search-backward "([Ff][Ww][Dd])" nil t) | |
7241 | (replace-match "")) | |
7242 | ||
7243 | ;; and finally, any whitespace that was left-over | |
7244 | (goto-char (point-min)) | |
7245 | (while (re-search-forward "^[ \t]+" nil t) | |
7246 | (replace-match "")) | |
7247 | (goto-char (point-max)) | |
7248 | (while (re-search-backward "[ \t]+$" nil t) | |
7249 | (replace-match "")) | |
7250 | ||
7251 | (buffer-string))) | |
7252 | ||
eec82323 LMI |
7253 | ;;; Forwarding messages. |
7254 | ||
42b7180c SZ |
7255 | (defvar message-forward-decoded-p nil |
7256 | "Non-nil means the original message is decoded.") | |
7257 | ||
23f87bed MB |
7258 | (defun message-forward-subject-name-subject (subject) |
7259 | "Generate a SUBJECT for a forwarded message. | |
7260 | The form is: [Source] Subject, where if the original message was mail, | |
7261 | Source is the name of the sender, and if the original message was | |
7262 | news, Source is the list of newsgroups is was posted to." | |
7263 | (let* ((group (message-fetch-field "newsgroups")) | |
7264 | (from (message-fetch-field "from")) | |
7265 | (prefix | |
7266 | (if group | |
7267 | (gnus-group-decoded-name group) | |
01c52d31 MB |
7268 | (or (and from (or |
7269 | (car (gnus-extract-address-components from)) | |
7270 | (cadr (gnus-extract-address-components from)))) | |
23f87bed MB |
7271 | "(nowhere)")))) |
7272 | (concat "[" | |
7273 | (if message-forward-decoded-p | |
7274 | prefix | |
7275 | (mail-decode-encoded-word-string prefix)) | |
7276 | "] " subject))) | |
7277 | ||
6748645f | 7278 | (defun message-forward-subject-author-subject (subject) |
7d829636 | 7279 | "Generate a SUBJECT for a forwarded message. |
6748645f LMI |
7280 | The form is: [Source] Subject, where if the original message was mail, |
7281 | Source is the sender, and if the original message was news, Source is | |
7282 | the list of newsgroups is was posted to." | |
23f87bed MB |
7283 | (let* ((group (message-fetch-field "newsgroups")) |
7284 | (prefix | |
7285 | (if group | |
7286 | (gnus-group-decoded-name group) | |
7287 | (or (message-fetch-field "from") | |
7288 | "(nowhere)")))) | |
7289 | (concat "[" | |
7290 | (if message-forward-decoded-p | |
7291 | prefix | |
7292 | (mail-decode-encoded-word-string prefix)) | |
7293 | "] " subject))) | |
6748645f LMI |
7294 | |
7295 | (defun message-forward-subject-fwd (subject) | |
7d829636 | 7296 | "Generate a SUBJECT for a forwarded message. |
6748645f LMI |
7297 | The form is: Fwd: Subject, where Subject is the original subject of |
7298 | the message." | |
23f87bed MB |
7299 | (if (string-match "^Fwd: " subject) |
7300 | subject | |
7301 | (concat "Fwd: " subject))) | |
6748645f | 7302 | |
42b7180c | 7303 | (defun message-make-forward-subject () |
eec82323 LMI |
7304 | "Return a Subject header suitable for the message in the current buffer." |
7305 | (save-excursion | |
7306 | (save-restriction | |
158d6e07 | 7307 | (message-narrow-to-head-1) |
6748645f | 7308 | (let ((funcs message-make-forward-subject-function) |
158d6e07 SZ |
7309 | (subject (message-fetch-field "Subject"))) |
7310 | (setq subject | |
7311 | (if subject | |
42b7180c | 7312 | (if message-forward-decoded-p |
1653df0f SZ |
7313 | subject |
7314 | (mail-decode-encoded-word-string subject)) | |
158d6e07 | 7315 | "")) |
01c52d31 MB |
7316 | (when message-wash-forwarded-subjects |
7317 | (setq subject (message-wash-subject subject))) | |
6748645f LMI |
7318 | ;; Make sure funcs is a list. |
7319 | (and funcs | |
7320 | (not (listp funcs)) | |
7321 | (setq funcs (list funcs))) | |
7322 | ;; Apply funcs in order, passing subject generated by previous | |
7323 | ;; func to the next one. | |
01c52d31 MB |
7324 | (dolist (func funcs) |
7325 | (when (functionp func) | |
7326 | (setq subject (funcall func subject)))) | |
6748645f | 7327 | subject)))) |
eec82323 | 7328 | |
9efa445f | 7329 | (defvar gnus-article-decoded-p) |
42b7180c | 7330 | |
0c773047 | 7331 | |
eec82323 | 7332 | ;;;###autoload |
16409b0b | 7333 | (defun message-forward (&optional news digest) |
eec82323 | 7334 | "Forward the current message via mail. |
16409b0b GM |
7335 | Optional NEWS will use news to forward instead of mail. |
7336 | Optional DIGEST will use digest to forward." | |
eec82323 | 7337 | (interactive "P") |
42b7180c | 7338 | (let* ((cur (current-buffer)) |
0c773047 | 7339 | (message-forward-decoded-p |
42b7180c | 7340 | (if (local-variable-p 'gnus-article-decoded-p (current-buffer)) |
0c773047 | 7341 | gnus-article-decoded-p ;; In an article buffer. |
42b7180c | 7342 | message-forward-decoded-p)) |
0c773047 | 7343 | (subject (message-make-forward-subject))) |
16409b0b GM |
7344 | (if news |
7345 | (message-news nil subject) | |
7346 | (message-mail nil subject)) | |
0c773047 SZ |
7347 | (message-forward-make-body cur digest))) |
7348 | ||
23f87bed MB |
7349 | (defun message-forward-make-body-plain (forward-buffer) |
7350 | (insert | |
f129a4df | 7351 | "\n-------------------- Start of forwarded message --------------------\n") |
d8a7b272 | 7352 | (let ((b (point)) |
8b320e66 KY |
7353 | (contents (with-current-buffer forward-buffer (buffer-string))) |
7354 | e) | |
7355 | (unless (featurep 'xemacs) | |
706deb23 | 7356 | (unless (mm-multibyte-string-p contents) |
8b320e66 KY |
7357 | (error "Attempt to insert unibyte string from the buffer \"%s\"\ |
7358 | to the multibyte buffer \"%s\"" | |
7359 | (if (bufferp forward-buffer) | |
7360 | (buffer-name forward-buffer) | |
7361 | forward-buffer) | |
7362 | (buffer-name)))) | |
7363 | (insert (mm-with-multibyte-buffer | |
7364 | (insert contents) | |
7365 | (mime-to-mml) | |
7366 | (goto-char (point-min)) | |
7367 | (when (looking-at "From ") | |
7368 | (replace-match "X-From-Line: ")) | |
7369 | (buffer-string))) | |
7370 | (unless (bolp) (insert "\n")) | |
23f87bed MB |
7371 | (setq e (point)) |
7372 | (insert | |
8b320e66 | 7373 | "-------------------- End of forwarded message --------------------\n") |
01c52d31 MB |
7374 | (message-remove-ignored-headers b e))) |
7375 | ||
7376 | (defun message-remove-ignored-headers (b e) | |
7377 | (when message-forward-ignored-headers | |
7378 | (save-restriction | |
7379 | (narrow-to-region b e) | |
7380 | (goto-char b) | |
7381 | (narrow-to-region (point) | |
7382 | (or (search-forward "\n\n" nil t) (point))) | |
7383 | (let ((ignored (if (stringp message-forward-ignored-headers) | |
7384 | (list message-forward-ignored-headers) | |
7385 | message-forward-ignored-headers))) | |
7386 | (dolist (elem ignored) | |
7387 | (message-remove-header elem t)))))) | |
23f87bed | 7388 | |
4c0a6d4a | 7389 | (defun message-forward-make-body-mime (forward-buffer &optional beg end) |
01c52d31 MB |
7390 | (let ((b (point))) |
7391 | (insert "\n\n<#part type=message/rfc822 disposition=inline raw=t>\n") | |
23f87bed MB |
7392 | (save-restriction |
7393 | (narrow-to-region (point) (point)) | |
4c0a6d4a RS |
7394 | (insert-buffer-substring forward-buffer beg end) |
7395 | (mml-quote-region (point-min) (point-max)) | |
23f87bed MB |
7396 | (goto-char (point-min)) |
7397 | (when (looking-at "From ") | |
7398 | (replace-match "X-From-Line: ")) | |
7399 | (goto-char (point-max))) | |
01c52d31 MB |
7400 | (insert "<#/part>\n") |
7401 | ;; Consider there is no illegible text. | |
7402 | (add-text-properties | |
7403 | b (point) | |
7404 | `(no-illegible-text t rear-nonsticky t start-open t)))) | |
23f87bed MB |
7405 | |
7406 | (defun message-forward-make-body-mml (forward-buffer) | |
f129a4df | 7407 | (insert "\n\n<#mml type=message/rfc822 disposition=inline>\n") |
23f87bed MB |
7408 | (let ((b (point)) e) |
7409 | (if (not message-forward-decoded-p) | |
8b320e66 KY |
7410 | (let ((contents (with-current-buffer forward-buffer (buffer-string)))) |
7411 | (unless (featurep 'xemacs) | |
706deb23 | 7412 | (unless (mm-multibyte-string-p contents) |
8b320e66 KY |
7413 | (error "Attempt to insert unibyte string from the buffer \"%s\"\ |
7414 | to the multibyte buffer \"%s\"" | |
7415 | (if (bufferp forward-buffer) | |
7416 | (buffer-name forward-buffer) | |
7417 | forward-buffer) | |
7418 | (buffer-name)))) | |
7419 | (insert (mm-with-multibyte-buffer | |
7420 | (insert contents) | |
7421 | (mime-to-mml) | |
7422 | (goto-char (point-min)) | |
7423 | (when (looking-at "From ") | |
7424 | (replace-match "X-From-Line: ")) | |
7425 | (buffer-string)))) | |
23f87bed MB |
7426 | (save-restriction |
7427 | (narrow-to-region (point) (point)) | |
7428 | (mml-insert-buffer forward-buffer) | |
7429 | (goto-char (point-min)) | |
7430 | (when (looking-at "From ") | |
7431 | (replace-match "X-From-Line: ")) | |
7432 | (goto-char (point-max)))) | |
7433 | (setq e (point)) | |
f129a4df | 7434 | (insert "<#/mml>\n") |
ad136a7c | 7435 | (when (and (not message-forward-decoded-p) |
23f87bed | 7436 | message-forward-ignored-headers) |
01c52d31 | 7437 | (message-remove-ignored-headers b e)))) |
23f87bed MB |
7438 | |
7439 | (defun message-forward-make-body-digest-plain (forward-buffer) | |
7440 | (insert | |
f129a4df | 7441 | "\n-------------------- Start of forwarded message --------------------\n") |
011f86c7 LI |
7442 | (mml-insert-buffer forward-buffer) |
7443 | (insert | |
7444 | "\n-------------------- End of forwarded message --------------------\n")) | |
23f87bed MB |
7445 | |
7446 | (defun message-forward-make-body-digest-mime (forward-buffer) | |
f129a4df | 7447 | (insert "\n<#multipart type=digest>\n") |
23f87bed MB |
7448 | (let ((b (point)) e) |
7449 | (insert-buffer-substring forward-buffer) | |
7450 | (setq e (point)) | |
f129a4df | 7451 | (insert "<#/multipart>\n") |
23f87bed MB |
7452 | (save-restriction |
7453 | (narrow-to-region b e) | |
7454 | (goto-char b) | |
7455 | (narrow-to-region (point) | |
7456 | (or (search-forward "\n\n" nil t) (point))) | |
7457 | (delete-region (point-min) (point-max))))) | |
7458 | ||
7459 | (defun message-forward-make-body-digest (forward-buffer) | |
7460 | (if message-forward-as-mime | |
7461 | (message-forward-make-body-digest-mime forward-buffer) | |
7462 | (message-forward-make-body-digest-plain forward-buffer))) | |
7463 | ||
8abf1b22 GM |
7464 | (autoload 'mm-uu-dissect-text-parts "mm-uu") |
7465 | (autoload 'mm-uu-dissect "mm-uu") | |
01c52d31 MB |
7466 | |
7467 | (defun message-signed-or-encrypted-p (&optional dont-emulate-mime handles) | |
7468 | "Say whether the current buffer contains signed or encrypted message. | |
7469 | If DONT-EMULATE-MIME is nil, this function does the MIME emulation on | |
7470 | messages that don't conform to PGP/MIME described in RFC2015. HANDLES | |
7471 | is for the internal use." | |
7472 | (unless handles | |
7473 | (let ((mm-decrypt-option 'never) | |
7474 | (mm-verify-option 'never)) | |
7475 | (if (setq handles (mm-dissect-buffer nil t)) | |
7476 | (unless dont-emulate-mime | |
7477 | (mm-uu-dissect-text-parts handles)) | |
7478 | (unless dont-emulate-mime | |
7479 | (setq handles (mm-uu-dissect)))))) | |
7480 | ;; Check text/plain message in which there is a signed or encrypted | |
7481 | ;; body that has been encoded by B or Q. | |
7482 | (unless (or handles dont-emulate-mime) | |
7483 | (let ((cur (current-buffer)) | |
7484 | (mm-decrypt-option 'never) | |
7485 | (mm-verify-option 'never)) | |
7486 | (with-temp-buffer | |
7487 | (insert-buffer-substring cur) | |
7488 | (when (setq handles (mm-dissect-buffer t t)) | |
f5b71bc0 | 7489 | (if (and (bufferp (car handles)) |
01c52d31 MB |
7490 | (equal (mm-handle-media-type handles) "text/plain")) |
7491 | (progn | |
f5b71bc0 KY |
7492 | (erase-buffer) |
7493 | (insert-buffer-substring (car handles)) | |
01c52d31 MB |
7494 | (mm-decode-content-transfer-encoding |
7495 | (mm-handle-encoding handles)) | |
f5b71bc0 | 7496 | (mm-destroy-parts handles) |
01c52d31 | 7497 | (setq handles (mm-uu-dissect))) |
f5b71bc0 | 7498 | (mm-destroy-parts handles) |
01c52d31 MB |
7499 | (setq handles nil)))))) |
7500 | (when handles | |
7501 | (prog1 | |
7502 | (catch 'found | |
7503 | (dolist (handle (if (stringp (car handles)) | |
7504 | (if (member (car handles) | |
7505 | '("multipart/signed" | |
7506 | "multipart/encrypted")) | |
7507 | (throw 'found t) | |
7508 | (cdr handles)) | |
7509 | (list handles))) | |
7510 | (if (stringp (car handle)) | |
7511 | (when (message-signed-or-encrypted-p dont-emulate-mime handle) | |
7512 | (throw 'found t)) | |
7513 | (when (and (bufferp (car handle)) | |
7514 | (equal (mm-handle-media-type handle) | |
7515 | "message/rfc822")) | |
7516 | (with-current-buffer (mm-handle-buffer handle) | |
7517 | (when (message-signed-or-encrypted-p dont-emulate-mime) | |
7518 | (throw 'found t))))))) | |
7519 | (mm-destroy-parts handles)))) | |
7520 | ||
0c773047 SZ |
7521 | ;;;###autoload |
7522 | (defun message-forward-make-body (forward-buffer &optional digest) | |
7523 | ;; Put point where we want it before inserting the forwarded | |
7524 | ;; message. | |
7525 | (if message-forward-before-signature | |
7526 | (message-goto-body) | |
7527 | (goto-char (point-max))) | |
23f87bed MB |
7528 | (if digest |
7529 | (message-forward-make-body-digest forward-buffer) | |
0c773047 | 7530 | (if message-forward-as-mime |
23f87bed MB |
7531 | (if (and message-forward-show-mml |
7532 | (not (and (eq message-forward-show-mml 'best) | |
01c52d31 MB |
7533 | ;; Use the raw form in the body if it contains |
7534 | ;; signed or encrypted message so as not to be | |
7535 | ;; destroyed by re-encoding. | |
23f87bed | 7536 | (with-current-buffer forward-buffer |
01c52d31 MB |
7537 | (condition-case nil |
7538 | (message-signed-or-encrypted-p) | |
7539 | (error t)))))) | |
23f87bed MB |
7540 | (message-forward-make-body-mml forward-buffer) |
7541 | (message-forward-make-body-mime forward-buffer)) | |
7542 | (message-forward-make-body-plain forward-buffer))) | |
0c773047 SZ |
7543 | (message-position-point)) |
7544 | ||
c07dfdd9 GM |
7545 | (declare-function rmail-toggle-header "rmail" (&optional arg)) |
7546 | ||
0c773047 SZ |
7547 | ;;;###autoload |
7548 | (defun message-forward-rmail-make-body (forward-buffer) | |
7549 | (save-window-excursion | |
7550 | (set-buffer forward-buffer) | |
5ea0e32b | 7551 | (if (rmail-msg-is-pruned) |
c07dfdd9 GM |
7552 | (if (fboundp 'rmail-msg-restore-non-pruned-header) |
7553 | (rmail-msg-restore-non-pruned-header) ; Emacs 22 | |
7554 | (rmail-toggle-header 0)))) ; Emacs 23 | |
0c773047 SZ |
7555 | (message-forward-make-body forward-buffer)) |
7556 | ||
23f87bed | 7557 | ;; Fixme: Should have defcustom. |
0c773047 SZ |
7558 | ;;;###autoload |
7559 | (defun message-insinuate-rmail () | |
23f87bed | 7560 | "Let RMAIL use message to forward." |
0c773047 SZ |
7561 | (interactive) |
7562 | (setq rmail-enable-mime-composing t) | |
a1506d29 | 7563 | (setq rmail-insert-mime-forwarded-message-function |
0c773047 | 7564 | 'message-forward-rmail-make-body)) |
eec82323 | 7565 | |
99c2a4e3 SM |
7566 | (defvar message-inhibit-body-encoding nil) |
7567 | ||
eec82323 LMI |
7568 | ;;;###autoload |
7569 | (defun message-resend (address) | |
7570 | "Resend the current article to ADDRESS." | |
16409b0b GM |
7571 | (interactive |
7572 | (list (message-read-from-minibuffer "Resend message to: "))) | |
eec82323 LMI |
7573 | (message "Resending message to %s..." address) |
7574 | (save-excursion | |
7575 | (let ((cur (current-buffer)) | |
89b163db | 7576 | gcc beg) |
eec82323 | 7577 | ;; We first set up a normal mail buffer. |
88818fbe SZ |
7578 | (unless (message-mail-user-agent) |
7579 | (set-buffer (get-buffer-create " *message resend*")) | |
2b7feab0 LI |
7580 | (let ((inhibit-read-only t)) |
7581 | (erase-buffer))) | |
23f87bed | 7582 | (let ((message-this-is-mail t) |
01c52d31 | 7583 | message-generate-hashcash |
23f87bed | 7584 | message-setup-hook) |
88818fbe | 7585 | (message-setup `((To . ,address)))) |
eec82323 | 7586 | ;; Insert our usual headers. |
23f87bed | 7587 | (message-generate-headers '(From Date To Message-ID)) |
eec82323 | 7588 | (message-narrow-to-headers) |
89b163db G |
7589 | (when (setq gcc (mail-fetch-field "gcc" nil t)) |
7590 | (message-remove-header "gcc")) | |
23f87bed MB |
7591 | ;; Remove X-Draft-From header etc. |
7592 | (message-remove-header message-ignored-mail-headers t) | |
eec82323 | 7593 | ;; Rename them all to "Resent-*". |
23f87bed | 7594 | (goto-char (point-min)) |
eec82323 LMI |
7595 | (while (re-search-forward "^[A-Za-z]" nil t) |
7596 | (forward-char -1) | |
7597 | (insert "Resent-")) | |
7598 | (widen) | |
7599 | (forward-line) | |
2b7feab0 LI |
7600 | (let ((inhibit-read-only t)) |
7601 | (delete-region (point) (point-max))) | |
eec82323 LMI |
7602 | (setq beg (point)) |
7603 | ;; Insert the message to be resent. | |
7604 | (insert-buffer-substring cur) | |
7605 | (goto-char (point-min)) | |
7606 | (search-forward "\n\n") | |
7607 | (forward-char -1) | |
7608 | (save-restriction | |
7609 | (narrow-to-region beg (point)) | |
7610 | (message-remove-header message-ignored-resent-headers t) | |
7611 | (goto-char (point-max))) | |
7612 | (insert mail-header-separator) | |
7613 | ;; Rename all old ("Also-")Resent headers. | |
6748645f | 7614 | (while (re-search-backward "^\\(Also-\\)*Resent-" beg t) |
eec82323 LMI |
7615 | (beginning-of-line) |
7616 | (insert "Also-")) | |
7617 | ;; Quote any "From " lines at the beginning. | |
7618 | (goto-char beg) | |
7619 | (when (looking-at "From ") | |
7620 | (replace-match "X-From-Line: ")) | |
7621 | ;; Send it. | |
a7f6e5b9 LMI |
7622 | (let ((message-inhibit-body-encoding |
7623 | ;; Don't do any further encoding if it looks like the | |
7624 | ;; message has already been encoded. | |
7625 | (let ((case-fold-search t)) | |
7626 | (re-search-forward "^mime-version:" nil t))) | |
6b8382e4 | 7627 | (message-inhibit-ecomplete t) |
10ace8ea | 7628 | message-required-mail-headers |
01c52d31 | 7629 | message-generate-hashcash |
10ace8ea | 7630 | rfc2047-encode-encoded-words) |
16409b0b | 7631 | (message-send-mail)) |
89b163db G |
7632 | (when gcc |
7633 | (message-goto-eoh) | |
7634 | (insert "Gcc: " gcc "\n")) | |
7635 | (run-hooks 'message-sent-hook) | |
eec82323 LMI |
7636 | (kill-buffer (current-buffer))) |
7637 | (message "Resending message to %s...done" address))) | |
7638 | ||
7639 | ;;;###autoload | |
7640 | (defun message-bounce () | |
7641 | "Re-mail the current message. | |
16409b0b | 7642 | This only makes sense if the current message is a bounce message that |
eec82323 LMI |
7643 | contains some mail you have written which has been bounced back to |
7644 | you." | |
7645 | (interactive) | |
16409b0b | 7646 | (let ((handles (mm-dissect-buffer t)) |
eec82323 LMI |
7647 | boundary) |
7648 | (message-pop-to-buffer (message-buffer-name "bounce")) | |
16409b0b GM |
7649 | (if (stringp (car handles)) |
7650 | ;; This is a MIME bounce. | |
7651 | (mm-insert-part (car (last handles))) | |
7652 | ;; This is a non-MIME bounce, so we try to remove things | |
7653 | ;; manually. | |
7654 | (mm-insert-part handles) | |
7655 | (undo-boundary) | |
7656 | (goto-char (point-min)) | |
23f87bed MB |
7657 | (re-search-forward "\n\n+" nil t) |
7658 | (setq boundary (point)) | |
16409b0b | 7659 | ;; We remove everything before the bounced mail. |
23f87bed MB |
7660 | (if (or (re-search-forward message-unsent-separator nil t) |
7661 | (progn | |
7662 | (search-forward "\n\n" nil 'move) | |
7663 | (re-search-backward "^Return-Path:.*\n" boundary t))) | |
7664 | (progn | |
7665 | (forward-line 1) | |
7666 | (delete-region (point-min) | |
7667 | (if (re-search-forward "^[^ \n\t]+:" nil t) | |
7668 | (match-beginning 0) | |
7669 | (point)))) | |
7670 | (goto-char boundary) | |
7671 | (when (re-search-backward "^.?From .*\n" nil t) | |
7672 | (delete-region (match-beginning 0) (match-end 0))))) | |
2f62a044 | 7673 | (mime-to-mml) |
eec82323 | 7674 | (save-restriction |
158d6e07 | 7675 | (message-narrow-to-head-1) |
eec82323 LMI |
7676 | (message-remove-header message-ignored-bounced-headers t) |
7677 | (goto-char (point-max)) | |
7678 | (insert mail-header-separator)) | |
7679 | (message-position-point))) | |
7680 | ||
7681 | ;;; | |
7682 | ;;; Interactive entry points for new message buffers. | |
7683 | ;;; | |
7684 | ||
7685 | ;;;###autoload | |
7686 | (defun message-mail-other-window (&optional to subject) | |
7687 | "Like `message-mail' command, but display mail buffer in another window." | |
7688 | (interactive) | |
88818fbe | 7689 | (unless (message-mail-user-agent) |
37ac18a3 CY |
7690 | (message-pop-to-buffer (message-buffer-name "mail" to) |
7691 | 'switch-to-buffer-other-window)) | |
6748645f | 7692 | (let ((message-this-is-mail t)) |
88818fbe | 7693 | (message-setup `((To . ,(or to "")) (Subject . ,(or subject ""))) |
815b81c8 | 7694 | nil nil nil 'switch-to-buffer-other-window))) |
eec82323 LMI |
7695 | |
7696 | ;;;###autoload | |
7697 | (defun message-mail-other-frame (&optional to subject) | |
7698 | "Like `message-mail' command, but display mail buffer in another frame." | |
7699 | (interactive) | |
88818fbe | 7700 | (unless (message-mail-user-agent) |
37ac18a3 CY |
7701 | (message-pop-to-buffer (message-buffer-name "mail" to) |
7702 | 'switch-to-buffer-other-frame)) | |
6748645f | 7703 | (let ((message-this-is-mail t)) |
88818fbe | 7704 | (message-setup `((To . ,(or to "")) (Subject . ,(or subject ""))) |
815b81c8 | 7705 | nil nil nil 'switch-to-buffer-other-frame))) |
eec82323 LMI |
7706 | |
7707 | ;;;###autoload | |
7708 | (defun message-news-other-window (&optional newsgroups subject) | |
7709 | "Start editing a news article to be sent." | |
7710 | (interactive) | |
37ac18a3 CY |
7711 | (message-pop-to-buffer (message-buffer-name "posting" nil newsgroups) |
7712 | 'switch-to-buffer-other-window) | |
6748645f LMI |
7713 | (let ((message-this-is-news t)) |
7714 | (message-setup `((Newsgroups . ,(or newsgroups "")) | |
7715 | (Subject . ,(or subject "")))))) | |
eec82323 LMI |
7716 | |
7717 | ;;;###autoload | |
7718 | (defun message-news-other-frame (&optional newsgroups subject) | |
7719 | "Start editing a news article to be sent." | |
7720 | (interactive) | |
37ac18a3 CY |
7721 | (message-pop-to-buffer (message-buffer-name "posting" nil newsgroups) |
7722 | 'switch-to-buffer-other-frame) | |
6748645f LMI |
7723 | (let ((message-this-is-news t)) |
7724 | (message-setup `((Newsgroups . ,(or newsgroups "")) | |
7725 | (Subject . ,(or subject "")))))) | |
eec82323 LMI |
7726 | |
7727 | ;;; underline.el | |
7728 | ||
7729 | ;; This code should be moved to underline.el (from which it is stolen). | |
7730 | ||
7731 | ;;;###autoload | |
01c52d31 | 7732 | (defun message-bold-region (start end) |
eec82323 LMI |
7733 | "Bold all nonblank characters in the region. |
7734 | Works by overstriking characters. | |
7735 | Called from program, takes two arguments START and END | |
7736 | which specify the range to operate on." | |
7737 | (interactive "r") | |
7738 | (save-excursion | |
7739 | (let ((end1 (make-marker))) | |
7740 | (move-marker end1 (max start end)) | |
7741 | (goto-char (min start end)) | |
7742 | (while (< (point) end1) | |
7743 | (or (looking-at "[_\^@- ]") | |
16409b0b | 7744 | (insert (char-after) "\b")) |
eec82323 LMI |
7745 | (forward-char 1))))) |
7746 | ||
7747 | ;;;###autoload | |
01c52d31 | 7748 | (defun message-unbold-region (start end) |
eec82323 LMI |
7749 | "Remove all boldness (overstruck characters) in the region. |
7750 | Called from program, takes two arguments START and END | |
7751 | which specify the range to operate on." | |
7752 | (interactive "r") | |
7753 | (save-excursion | |
7754 | (let ((end1 (make-marker))) | |
7755 | (move-marker end1 (max start end)) | |
7756 | (goto-char (min start end)) | |
01c52d31 | 7757 | (while (search-forward "\b" end1 t) |
16409b0b | 7758 | (if (eq (char-after) (char-after (- (point) 2))) |
eec82323 LMI |
7759 | (delete-char -2)))))) |
7760 | ||
23f87bed MB |
7761 | (defun message-exchange-point-and-mark () |
7762 | "Exchange point and mark, but don't activate region if it was inactive." | |
61f49e0b CY |
7763 | (goto-char (prog1 (mark t) |
7764 | (set-marker (mark-marker) (point))))) | |
23f87bed MB |
7765 | |
7766 | (defalias 'message-make-overlay 'make-overlay) | |
7767 | (defalias 'message-delete-overlay 'delete-overlay) | |
7768 | (defalias 'message-overlay-put 'overlay-put) | |
7769 | (defun message-kill-all-overlays () | |
7770 | (if (featurep 'xemacs) | |
7771 | (map-extents (lambda (extent ignore) (delete-extent extent))) | |
7772 | (mapcar #'delete-overlay (overlays-in (point-min) (point-max))))) | |
eec82323 LMI |
7773 | |
7774 | ;; Support for toolbar | |
9efa445f | 7775 | (defvar tool-bar-mode) |
23f87bed | 7776 | |
18c06a99 RS |
7777 | ;; Note: The :set function in the `message-tool-bar*' variables will only |
7778 | ;; affect _new_ message buffers. We might add a function that walks thru all | |
7779 | ;; message-mode buffers and force the update. | |
7780 | (defun message-tool-bar-update (&optional symbol value) | |
7781 | "Update message mode toolbar. | |
7782 | Setter function for custom variables." | |
7783 | (setq-default message-tool-bar-map nil) | |
7784 | (when symbol | |
7785 | ;; When used as ":set" function: | |
7786 | (set-default symbol value))) | |
7787 | ||
7788 | (defcustom message-tool-bar (if (eq gmm-tool-bar-style 'gnome) | |
7789 | 'message-tool-bar-gnome | |
7790 | 'message-tool-bar-retro) | |
7791 | "Specifies the message mode tool bar. | |
7792 | ||
fac916bf | 7793 | It can be either a list or a symbol referring to a list. See |
18c06a99 RS |
7794 | `gmm-tool-bar-from-list' for the format of the list. The |
7795 | default key map is `message-mode-map'. | |
7796 | ||
7797 | Pre-defined symbols include `message-tool-bar-gnome' and | |
7798 | `message-tool-bar-retro'." | |
7799 | :type '(repeat gmm-tool-bar-list-item) | |
7800 | :type '(choice (const :tag "GNOME style" message-tool-bar-gnome) | |
7801 | (const :tag "Retro look" message-tool-bar-retro) | |
7802 | (repeat :tag "User defined list" gmm-tool-bar-item) | |
7803 | (symbol)) | |
330f707b | 7804 | :version "23.1" ;; No Gnus |
18c06a99 RS |
7805 | :initialize 'custom-initialize-default |
7806 | :set 'message-tool-bar-update | |
7807 | :group 'message) | |
7808 | ||
7809 | (defcustom message-tool-bar-gnome | |
7810 | '((ispell-message "spell" nil | |
25ca2e61 | 7811 | :vert-only t |
18c06a99 RS |
7812 | :visible (or (not (boundp 'flyspell-mode)) |
7813 | (not flyspell-mode))) | |
7814 | (flyspell-buffer "spell" t | |
25ca2e61 | 7815 | :vert-only t |
18c06a99 RS |
7816 | :visible (and (boundp 'flyspell-mode) |
7817 | flyspell-mode) | |
7818 | :help "Flyspell whole buffer") | |
25ca2e61 | 7819 | (message-send-and-exit "mail/send" t :label "Send") |
18c06a99 | 7820 | (message-dont-send "mail/save-draft") |
25ca2e61 | 7821 | (mml-attach-file "attach" mml-mode-map :vert-only t) |
18c06a99 | 7822 | (mml-preview "mail/preview" mml-mode-map) |
01c52d31 | 7823 | (mml-secure-message-sign-encrypt "lock" mml-mode-map :visible nil) |
18c06a99 RS |
7824 | (message-insert-importance-high "important" nil :visible nil) |
7825 | (message-insert-importance-low "unimportant" nil :visible nil) | |
25ca2e61 | 7826 | (message-insert-disposition-notification-to "receipt" nil :visible nil)) |
18c06a99 RS |
7827 | "List of items for the message tool bar (GNOME style). |
7828 | ||
7829 | See `gmm-tool-bar-from-list' for details on the format of the list." | |
7830 | :type '(repeat gmm-tool-bar-item) | |
330f707b | 7831 | :version "23.1" ;; No Gnus |
18c06a99 RS |
7832 | :initialize 'custom-initialize-default |
7833 | :set 'message-tool-bar-update | |
7834 | :group 'message) | |
7835 | ||
7836 | (defcustom message-tool-bar-retro | |
7837 | '(;; Old Emacs 21 icon for consistency. | |
77fb0e18 | 7838 | (message-send-and-exit "gnus/mail-send") |
18c06a99 RS |
7839 | (message-kill-buffer "close") |
7840 | (message-dont-send "cancel") | |
7841 | (mml-attach-file "attach" mml-mode-map) | |
7842 | (ispell-message "spell") | |
7843 | (mml-preview "preview" mml-mode-map) | |
7844 | (message-insert-importance-high "gnus/important") | |
7845 | (message-insert-importance-low "gnus/unimportant") | |
7846 | (message-insert-disposition-notification-to "gnus/receipt")) | |
7847 | "List of items for the message tool bar (retro style). | |
7848 | ||
7849 | See `gmm-tool-bar-from-list' for details on the format of the list." | |
7850 | :type '(repeat gmm-tool-bar-item) | |
330f707b | 7851 | :version "23.1" ;; No Gnus |
18c06a99 RS |
7852 | :initialize 'custom-initialize-default |
7853 | :set 'message-tool-bar-update | |
7854 | :group 'message) | |
7855 | ||
7856 | (defcustom message-tool-bar-zap-list | |
7857 | '(new-file open-file dired kill-buffer write-file | |
7858 | print-buffer customize help) | |
7859 | "List of icon items from the global tool bar. | |
7860 | These items are not displayed on the message mode tool bar. | |
7861 | ||
7862 | See `gmm-tool-bar-from-list' for the format of the list." | |
7863 | :type 'gmm-tool-bar-zap-list | |
330f707b | 7864 | :version "23.1" ;; No Gnus |
18c06a99 RS |
7865 | :initialize 'custom-initialize-default |
7866 | :set 'message-tool-bar-update | |
7867 | :group 'message) | |
7868 | ||
7869 | (defvar image-load-path) | |
7870 | ||
7871 | (defun message-make-tool-bar (&optional force) | |
7872 | "Make a message mode tool bar from `message-tool-bar-list'. | |
7873 | When FORCE, rebuild the tool bar." | |
7874 | (when (and (not (featurep 'xemacs)) | |
7875 | (boundp 'tool-bar-mode) | |
23f87bed | 7876 | tool-bar-mode |
18c06a99 RS |
7877 | (or (not message-tool-bar-map) force)) |
7878 | (setq message-tool-bar-map | |
7879 | (let* ((load-path | |
7880 | (gmm-image-load-path-for-library "message" | |
7881 | "mail/save-draft.xpm" | |
7882 | nil t)) | |
7883 | (image-load-path (cons (car load-path) | |
7884 | (when (boundp 'image-load-path) | |
7885 | image-load-path)))) | |
7886 | (gmm-tool-bar-from-list message-tool-bar | |
7887 | message-tool-bar-zap-list | |
7888 | 'message-mode-map)))) | |
7889 | message-tool-bar-map) | |
eec82323 LMI |
7890 | |
7891 | ;;; Group name completion. | |
7892 | ||
23f87bed | 7893 | (defcustom message-newgroups-header-regexp |
eec82323 | 7894 | "^\\(Newsgroups\\|Followup-To\\|Posted-To\\|Gcc\\):" |
23f87bed MB |
7895 | "Regexp that match headers that lists groups." |
7896 | :group 'message | |
7897 | :type 'regexp) | |
7898 | ||
7899 | (defcustom message-completion-alist | |
7900 | (list (cons message-newgroups-header-regexp 'message-expand-group) | |
7901 | '("^\\(Resent-\\)?\\(To\\|B?Cc\\):" . message-expand-name) | |
7902 | '("^\\(Reply-To\\|From\\|Mail-Followup-To\\|Mail-Copies-To\\):" | |
7903 | . message-expand-name) | |
7904 | '("^\\(Disposition-Notification-To\\|Return-Receipt-To\\):" | |
7905 | . message-expand-name)) | |
7906 | "Alist of (RE . FUN). Use FUN for completion on header lines matching RE." | |
bf247b6e | 7907 | :version "22.1" |
23f87bed MB |
7908 | :group 'message |
7909 | :type '(alist :key-type regexp :value-type function)) | |
7910 | ||
01c52d31 | 7911 | (defcustom message-expand-name-databases |
674c5ccf | 7912 | '(bbdb eudc) |
01c52d31 MB |
7913 | "List of databases to try for name completion (`message-expand-name'). |
7914 | Each element is a symbol and can be `bbdb' or `eudc'." | |
7915 | :group 'message | |
7916 | :type '(set (const bbdb) (const eudc))) | |
7917 | ||
23f87bed MB |
7918 | (defcustom message-tab-body-function nil |
7919 | "*Function to execute when `message-tab' (TAB) is executed in the body. | |
7920 | If nil, the function bound in `text-mode-map' or `global-map' is executed." | |
bf247b6e | 7921 | :version "22.1" |
23f87bed MB |
7922 | :group 'message |
7923 | :link '(custom-manual "(message)Various Commands") | |
4a2358e9 MB |
7924 | :type '(choice (const nil) |
7925 | function)) | |
eec82323 | 7926 | |
b43906b6 GM |
7927 | (declare-function mail-abbrev-in-expansion-header-p "mailabbrev" ()) |
7928 | ||
eec82323 | 7929 | (defun message-tab () |
23f87bed | 7930 | "Complete names according to `message-completion-alist'. |
ab4dc1e1 LI |
7931 | Execute function specified by `message-tab-body-function' when |
7932 | not in those headers. If that variable is nil, indent with the | |
7933 | regular text mode tabbing command." | |
eec82323 | 7934 | (interactive) |
674c5ccf SM |
7935 | (cond |
7936 | ((if (and (boundp 'completion-fail-discreetly) | |
7937 | (fboundp 'completion-at-point)) | |
7938 | (let ((completion-fail-discreetly t)) (completion-at-point)) | |
7939 | (funcall (or (message-completion-function) #'ignore))) | |
7940 | ;; Completion was performed; nothing else to do. | |
7941 | nil) | |
7942 | (message-tab-body-function (funcall message-tab-body-function)) | |
7943 | (t (funcall (or (lookup-key text-mode-map "\t") | |
7944 | (lookup-key global-map "\t") | |
7945 | 'indent-relative))))) | |
7946 | ||
99c2a4e3 SM |
7947 | (defvar mail-abbrev-mode-regexp) |
7948 | ||
674c5ccf | 7949 | (defun message-completion-function () |
23f87bed MB |
7950 | (let ((alist message-completion-alist)) |
7951 | (while (and alist | |
7952 | (let ((mail-abbrev-mode-regexp (caar alist))) | |
7953 | (not (mail-abbrev-in-expansion-header-p)))) | |
7954 | (setq alist (cdr alist))) | |
6e724ca2 SM |
7955 | (when (cdar alist) |
7956 | (lexical-let ((fun (cdar alist))) | |
7957 | ;; Even if completion fails, return a non-nil value, so as to avoid | |
7958 | ;; falling back to message-tab-body-function. | |
7959 | (lambda () (funcall fun) 'completion-attempted))))) | |
eec82323 | 7960 | |
eec82323 | 7961 | (defun message-expand-group () |
6748645f | 7962 | "Expand the group name under point." |
c464dbc3 KY |
7963 | (let ((b (save-excursion |
7964 | (save-restriction | |
7965 | (narrow-to-region | |
7966 | (save-excursion | |
7967 | (beginning-of-line) | |
7968 | (skip-chars-forward "^:") | |
7969 | (1+ (point))) | |
7970 | (point)) | |
7971 | (skip-chars-backward "^, \t\n") (point)))) | |
7972 | (completion-ignore-case t) | |
7973 | (e (progn (skip-chars-forward "^,\t\n ") (point))) | |
7974 | group collection) | |
7975 | (when (and (boundp 'gnus-active-hashtb) | |
7976 | gnus-active-hashtb) | |
7977 | (mapatoms | |
7978 | (lambda (symbol) | |
7979 | (setq group (symbol-name symbol)) | |
7980 | (push (if (string-match "[^\000-\177]" group) | |
7981 | (gnus-group-decoded-name group) | |
7982 | group) | |
7983 | collection)) | |
7984 | gnus-active-hashtb)) | |
1149ffdb | 7985 | (message-completion-in-region b e collection))) |
96bdcdc4 SM |
7986 | |
7987 | (defalias 'message-completion-in-region | |
7988 | (if (fboundp 'completion-in-region) | |
7989 | 'completion-in-region | |
1149ffdb | 7990 | (lambda (b e hashtb) |
96bdcdc4 SM |
7991 | (let* ((string (buffer-substring b e)) |
7992 | (completions (all-completions string hashtb)) | |
7993 | comp) | |
7994 | (delete-region b (point)) | |
7995 | (cond | |
7996 | ((= (length completions) 1) | |
7997 | (if (string= (car completions) string) | |
7998 | (progn | |
7999 | (insert string) | |
8000 | (message "Only matching group")) | |
8001 | (insert (car completions)))) | |
8002 | ((and (setq comp (try-completion string hashtb)) | |
8003 | (not (string= comp string))) | |
8004 | (insert comp)) | |
8005 | (t | |
8006 | (insert string) | |
8007 | (if (not comp) | |
8008 | (message "No matching groups") | |
8009 | (save-selected-window | |
8010 | (pop-to-buffer "*Completions*") | |
8011 | (buffer-disable-undo) | |
8012 | (let ((buffer-read-only nil)) | |
8013 | (erase-buffer) | |
8014 | (let ((standard-output (current-buffer))) | |
4a8bb694 | 8015 | (display-completion-list (sort completions 'string<))) |
96bdcdc4 SM |
8016 | (setq buffer-read-only nil) |
8017 | (goto-char (point-min)) | |
8018 | (delete-region (point) | |
8019 | (progn (forward-line 3) (point)))))))))))) | |
eec82323 | 8020 | |
23f87bed | 8021 | (defun message-expand-name () |
01c52d31 MB |
8022 | (cond ((and (memq 'eudc message-expand-name-databases) |
8023 | (boundp 'eudc-protocol) | |
8024 | eudc-protocol) | |
8025 | (eudc-expand-inline)) | |
8026 | ((and (memq 'bbdb message-expand-name-databases) | |
8027 | (fboundp 'bbdb-complete-name)) | |
99c2a4e3 SM |
8028 | (let ((starttick (buffer-modified-tick))) |
8029 | (or (bbdb-complete-name) | |
8030 | ;; Apparently, bbdb-complete-name can return nil even when | |
8031 | ;; completion took place. So let's double check the buffer was | |
8032 | ;; not modified. | |
8033 | (/= starttick (buffer-modified-tick))))) | |
01c52d31 MB |
8034 | (t |
8035 | (expand-abbrev)))) | |
23f87bed | 8036 | |
eec82323 LMI |
8037 | ;;; Help stuff. |
8038 | ||
8039 | (defun message-talkative-question (ask question show &rest text) | |
8df72730 RS |
8040 | "Call FUNCTION with argument QUESTION; optionally display TEXT... args. |
8041 | If SHOW is non-nil, the arguments TEXT... are displayed in a temp buffer. | |
eec82323 LMI |
8042 | The following arguments may contain lists of values." |
8043 | (if (and show | |
8044 | (setq text (message-flatten-list text))) | |
8045 | (save-window-excursion | |
82816ba1 SM |
8046 | (with-output-to-temp-buffer " *MESSAGE information message*" |
8047 | (with-current-buffer " *MESSAGE information message*" | |
16409b0b | 8048 | (fundamental-mode) ; for Emacs 20.4+ |
01c52d31 | 8049 | (mapc 'princ text) |
eec82323 LMI |
8050 | (goto-char (point-min)))) |
8051 | (funcall ask question)) | |
8052 | (funcall ask question))) | |
8053 | ||
8054 | (defun message-flatten-list (list) | |
8055 | "Return a new, flat list that contains all elements of LIST. | |
8056 | ||
8057 | \(message-flatten-list '(1 (2 3 (4 5 (6))) 7)) | |
8058 | => (1 2 3 4 5 6 7)" | |
8059 | (cond ((consp list) | |
8060 | (apply 'append (mapcar 'message-flatten-list list))) | |
8061 | (list | |
8062 | (list list)))) | |
8063 | ||
8064 | (defun message-generate-new-buffer-clone-locals (name &optional varstr) | |
23f87bed | 8065 | "Create and return a buffer with name based on NAME using `generate-new-buffer'. |
eec82323 LMI |
8066 | Then clone the local variables and values from the old buffer to the |
8067 | new one, cloning only the locals having a substring matching the | |
23f87bed | 8068 | regexp VARSTR." |
a8151ef7 | 8069 | (let ((oldbuf (current-buffer))) |
82816ba1 | 8070 | (with-current-buffer (generate-new-buffer name) |
16409b0b | 8071 | (message-clone-locals oldbuf varstr) |
eec82323 LMI |
8072 | (current-buffer)))) |
8073 | ||
16409b0b | 8074 | (defun message-clone-locals (buffer &optional varstr) |
a8151ef7 | 8075 | "Clone the local variables from BUFFER to the current buffer." |
82816ba1 | 8076 | (let ((locals (with-current-buffer buffer (buffer-local-variables))) |
f4dd4ae8 | 8077 | (regexp "^gnus\\|^nn\\|^message\\|^sendmail\\|^smtp\\|^user-mail-address")) |
a8151ef7 LMI |
8078 | (mapcar |
8079 | (lambda (local) | |
6748645f LMI |
8080 | (when (and (consp local) |
8081 | (car local) | |
16409b0b GM |
8082 | (string-match regexp (symbol-name (car local))) |
8083 | (or (null varstr) | |
8084 | (string-match varstr (symbol-name (car local))))) | |
a8151ef7 LMI |
8085 | (ignore-errors |
8086 | (set (make-local-variable (car local)) | |
8087 | (cdr local))))) | |
8088 | locals))) | |
8089 | ||
16409b0b GM |
8090 | ;;; |
8091 | ;;; MIME functions | |
8092 | ;;; | |
8093 | ||
16409b0b | 8094 | (defun message-encode-message-body () |
7d829636 | 8095 | (unless message-inhibit-body-encoding |
16409b0b GM |
8096 | (let ((mail-parse-charset (or mail-parse-charset |
8097 | message-default-charset)) | |
8098 | (case-fold-search t) | |
8099 | lines content-type-p) | |
8100 | (message-goto-body) | |
8101 | (save-restriction | |
8102 | (narrow-to-region (point) (point-max)) | |
8103 | (let ((new (mml-generate-mime))) | |
8104 | (when new | |
8105 | (delete-region (point-min) (point-max)) | |
8106 | (insert new) | |
8107 | (goto-char (point-min)) | |
8108 | (if (eq (aref new 0) ?\n) | |
8109 | (delete-char 1) | |
8110 | (search-forward "\n\n") | |
8111 | (setq lines (buffer-substring (point-min) (1- (point)))) | |
8112 | (delete-region (point-min) (point)))))) | |
8113 | (save-restriction | |
8114 | (message-narrow-to-headers-or-head) | |
8115 | (message-remove-header "Mime-Version") | |
8116 | (goto-char (point-max)) | |
f129a4df | 8117 | (insert "MIME-Version: 1.0\n") |
16409b0b GM |
8118 | (when lines |
8119 | (insert lines)) | |
8120 | (setq content-type-p | |
23f87bed MB |
8121 | (or mml-boundary |
8122 | (re-search-backward "^Content-Type:" nil t)))) | |
16409b0b GM |
8123 | (save-restriction |
8124 | (message-narrow-to-headers-or-head) | |
8125 | (message-remove-first-header "Content-Type") | |
8126 | (message-remove-first-header "Content-Transfer-Encoding")) | |
23f87bed MB |
8127 | ;; We always make sure that the message has a Content-Type |
8128 | ;; header. This is because some broken MTAs and MUAs get | |
8129 | ;; awfully confused when confronted with a message with a | |
8130 | ;; MIME-Version header and without a Content-Type header. For | |
8131 | ;; instance, Solaris' /usr/bin/mail. | |
16409b0b GM |
8132 | (unless content-type-p |
8133 | (goto-char (point-min)) | |
0c773047 SZ |
8134 | ;; For unknown reason, MIME-Version doesn't exist. |
8135 | (when (re-search-forward "^MIME-Version:" nil t) | |
8136 | (forward-line 1) | |
f129a4df | 8137 | (insert "Content-Type: text/plain; charset=us-ascii\n")))))) |
16409b0b | 8138 | |
23f87bed | 8139 | (defun message-read-from-minibuffer (prompt &optional initial-contents) |
16409b0b GM |
8140 | "Read from the minibuffer while providing abbrev expansion." |
8141 | (if (fboundp 'mail-abbrevs-setup) | |
d5fdf93f | 8142 | (let ((minibuffer-setup-hook 'mail-abbrevs-setup) |
066f0e09 | 8143 | (minibuffer-local-map message-minibuffer-local-map)) |
9ad948e2 | 8144 | (read-from-minibuffer prompt initial-contents)) |
23f87bed MB |
8145 | (let ((minibuffer-setup-hook 'mail-abbrev-minibuffer-setup-hook) |
8146 | (minibuffer-local-map message-minibuffer-local-map)) | |
8147 | (read-string prompt initial-contents)))) | |
eec82323 | 8148 | |
83595c0c | 8149 | (defun message-use-alternative-email-as-from () |
46e8fe3d MB |
8150 | "Set From field of the outgoing message to the first matching |
8151 | address in `message-alternative-emails', looking at To, Cc and | |
8152 | From headers in the original article." | |
83595c0c | 8153 | (require 'mail-utils) |
01c52d31 | 8154 | (let* ((fields '("To" "Cc" "From")) |
83595c0c DL |
8155 | (emails |
8156 | (split-string | |
8157 | (mail-strip-quoted-names | |
8158 | (mapconcat 'message-fetch-reply-field fields ",")) | |
8159 | "[ \f\t\n\r\v,]+")) | |
8160 | email) | |
8161 | (while emails | |
8162 | (if (string-match message-alternative-emails (car emails)) | |
8163 | (setq email (car emails) | |
8164 | emails nil)) | |
8165 | (pop emails)) | |
8166 | (unless (or (not email) (equal email user-mail-address)) | |
46e8fe3d | 8167 | (message-remove-header "From") |
83595c0c | 8168 | (goto-char (point-max)) |
01c52d31 MB |
8169 | (insert "From: " (let ((user-mail-address email)) (message-make-from)) |
8170 | "\n")))) | |
83595c0c | 8171 | |
23f87bed MB |
8172 | (defun message-options-get (symbol) |
8173 | (cdr (assq symbol message-options))) | |
8174 | ||
8175 | (defun message-options-set (symbol value) | |
8176 | (let ((the-cons (assq symbol message-options))) | |
8177 | (if the-cons | |
8178 | (if value | |
8179 | (setcdr the-cons value) | |
8180 | (setq message-options (delq the-cons message-options))) | |
8181 | (and value | |
8182 | (push (cons symbol value) message-options)))) | |
8183 | value) | |
8184 | ||
8185 | (defun message-options-set-recipient () | |
8186 | (save-restriction | |
8187 | (message-narrow-to-headers-or-head) | |
8188 | (message-options-set 'message-sender | |
8189 | (mail-strip-quoted-names | |
8190 | (message-fetch-field "from"))) | |
8191 | (message-options-set 'message-recipients | |
8192 | (mail-strip-quoted-names | |
8193 | (let ((to (message-fetch-field "to")) | |
8194 | (cc (message-fetch-field "cc")) | |
8195 | (bcc (message-fetch-field "bcc"))) | |
8196 | (concat | |
8197 | (or to "") | |
8198 | (if (and to cc) ", ") | |
8199 | (or cc "") | |
8200 | (if (and (or to cc) bcc) ", ") | |
8201 | (or bcc ""))))))) | |
8202 | ||
8203 | (defun message-hide-headers () | |
8204 | "Hide headers based on the `message-hidden-headers' variable." | |
8205 | (let ((regexps (if (stringp message-hidden-headers) | |
8206 | (list message-hidden-headers) | |
8207 | message-hidden-headers)) | |
8208 | (inhibit-point-motion-hooks t) | |
01c52d31 | 8209 | (after-change-functions nil) |
c70dbcd5 | 8210 | (end-of-headers (point-min))) |
23f87bed MB |
8211 | (when regexps |
8212 | (save-excursion | |
8213 | (save-restriction | |
8214 | (message-narrow-to-headers) | |
8215 | (goto-char (point-min)) | |
8216 | (while (not (eobp)) | |
8217 | (if (not (message-hide-header-p regexps)) | |
8218 | (message-next-header) | |
01c52d31 MB |
8219 | (let ((begin (point)) |
8220 | header header-len) | |
23f87bed | 8221 | (message-next-header) |
01c52d31 MB |
8222 | (setq header (buffer-substring begin (point)) |
8223 | header-len (- (point) begin)) | |
8224 | (delete-region begin (point)) | |
c70dbcd5 | 8225 | (goto-char end-of-headers) |
01c52d31 MB |
8226 | (insert header) |
8227 | (setq end-of-headers | |
8228 | (+ end-of-headers header-len)))))))) | |
c70dbcd5 | 8229 | (narrow-to-region end-of-headers (point-max)))) |
23f87bed MB |
8230 | |
8231 | (defun message-hide-header-p (regexps) | |
8232 | (let ((result nil) | |
8233 | (reverse nil)) | |
8234 | (when (eq (car regexps) 'not) | |
8235 | (setq reverse t) | |
8236 | (pop regexps)) | |
8237 | (dolist (regexp regexps) | |
8238 | (setq result (or result (looking-at regexp)))) | |
8239 | (if reverse | |
8240 | (not result) | |
8241 | result))) | |
8242 | ||
aa8f8277 GM |
8243 | (declare-function ecomplete-add-item "ecomplete" (type key text)) |
8244 | (declare-function ecomplete-save "ecomplete" ()) | |
8245 | ||
01c52d31 | 8246 | (defun message-put-addresses-in-ecomplete () |
aa8f8277 | 8247 | (require 'ecomplete) |
01c52d31 MB |
8248 | (dolist (header '("to" "cc" "from" "reply-to")) |
8249 | (let ((value (message-field-value header))) | |
8250 | (dolist (string (mail-header-parse-addresses value 'raw)) | |
8251 | (setq string | |
8252 | (gnus-replace-in-string | |
8253 | (gnus-replace-in-string string "^ +\\| +$" "") "\n" "")) | |
8254 | (ecomplete-add-item 'mail (car (mail-header-parse-address string)) | |
8255 | string)))) | |
8256 | (ecomplete-save)) | |
8257 | ||
aa8f8277 GM |
8258 | (autoload 'ecomplete-display-matches "ecomplete") |
8259 | ||
01c52d31 MB |
8260 | (defun message-display-abbrev (&optional choose) |
8261 | "Display the next possible abbrev for the text before point." | |
8262 | (interactive (list t)) | |
8263 | (when (and (memq (char-after (point-at-bol)) '(?C ?T ?\t ? )) | |
8264 | (message-point-in-header-p) | |
8265 | (save-excursion | |
8266 | (beginning-of-line) | |
8267 | (while (and (memq (char-after) '(?\t ? )) | |
8268 | (zerop (forward-line -1)))) | |
8269 | (looking-at "To:\\|Cc:"))) | |
8270 | (let* ((end (point)) | |
8271 | (start (save-excursion | |
8272 | (and (re-search-backward "[\n\t ]" nil t) | |
8273 | (1+ (point))))) | |
8274 | (word (when start (buffer-substring start end))) | |
8275 | (match (when (and word | |
8276 | (not (zerop (length word)))) | |
8277 | (ecomplete-display-matches 'mail word choose)))) | |
8278 | (when (and choose match) | |
8279 | (delete-region start end) | |
8280 | (insert match))))) | |
8281 | ||
8f7abae3 MB |
8282 | ;; To send pre-formatted letters like the example below, you can use |
8283 | ;; `message-send-form-letter': | |
8284 | ;; --8<---------------cut here---------------start------------->8--- | |
8285 | ;; To: alice@invalid.invalid | |
8286 | ;; Subject: Verification of your contact information | |
8287 | ;; From: Contact verification <admin@foo.invalid> | |
8288 | ;; --text follows this line-- | |
8289 | ;; Hi Alice, | |
8290 | ;; please verify that your contact information is still valid: | |
8291 | ;; Alice A, A avenue 11, 1111 A town, Austria | |
8292 | ;; ----------next form letter message follows this line---------- | |
8293 | ;; To: bob@invalid.invalid | |
8294 | ;; Subject: Verification of your contact information | |
8295 | ;; From: Contact verification <admin@foo.invalid> | |
8296 | ;; --text follows this line-- | |
8297 | ;; Hi Bob, | |
8298 | ;; please verify that your contact information is still valid: | |
8299 | ;; Bob, B street 22, 22222 Be town, Belgium | |
8300 | ;; ----------next form letter message follows this line---------- | |
8301 | ;; To: charlie@invalid.invalid | |
8302 | ;; Subject: Verification of your contact information | |
8303 | ;; From: Contact verification <admin@foo.invalid> | |
8304 | ;; --text follows this line-- | |
8305 | ;; Hi Charlie, | |
8306 | ;; please verify that your contact information is still valid: | |
8307 | ;; Charlie Chaplin, C plaza 33, 33333 C town, Chile | |
8308 | ;; --8<---------------cut here---------------end--------------->8--- | |
8309 | ||
8310 | ;; FIXME: What is the most common term (circular letter, form letter, serial | |
8311 | ;; letter, standard letter) for such kind of letter? See also | |
8312 | ;; <http://en.wikipedia.org/wiki/Form_letter> | |
8313 | ||
8314 | ;; FIXME: Maybe extent message-mode's font-lock support to recognize | |
8315 | ;; `message-form-letter-separator', i.e. highlight each message like a single | |
8316 | ;; message. | |
8317 | ||
8318 | (defcustom message-form-letter-separator | |
8319 | "\n----------next form letter message follows this line----------\n" | |
8320 | "Separator for `message-send-form-letter'." | |
8321 | ;; :group 'message-form-letter | |
8322 | :group 'message-various | |
8323 | :version "23.1" ;; No Gnus | |
8324 | :type 'string) | |
8325 | ||
8326 | (defcustom message-send-form-letter-delay 1 | |
8327 | "Delay in seconds when sending a message with `message-send-form-letter'. | |
8328 | Only used when `message-send-form-letter' is called with non-nil | |
8329 | argument `force'." | |
8330 | ;; :group 'message-form-letter | |
8331 | :group 'message-various | |
8332 | :version "23.1" ;; No Gnus | |
8333 | :type 'integer) | |
8334 | ||
8335 | (defun message-send-form-letter (&optional force) | |
8336 | "Sent all form letter messages from current buffer. | |
8337 | Unless FORCE, prompt before sending. | |
8338 | ||
8339 | The messages are separated by `message-form-letter-separator'. | |
8340 | Header and body are separated by `mail-header-separator'." | |
8341 | (interactive "P") | |
8342 | (let ((sent 0) (skipped 0) | |
8343 | start end text | |
8344 | buff | |
8345 | to done) | |
8346 | (goto-char (point-min)) | |
8347 | (while (not done) | |
8348 | (setq start (point) | |
8349 | end (if (search-forward message-form-letter-separator nil t) | |
8350 | (- (point) (length message-form-letter-separator) -1) | |
8351 | (setq done t) | |
8352 | (point-max))) | |
8353 | (setq text | |
8354 | (buffer-substring-no-properties start end)) | |
8355 | (setq buff (generate-new-buffer "*mail - form letter*")) | |
8356 | (with-current-buffer buff | |
8357 | (insert text) | |
8358 | (message-mode) | |
8359 | (setq to (message-fetch-field "To")) | |
8360 | (switch-to-buffer buff) | |
8361 | (when force | |
8362 | (sit-for message-send-form-letter-delay)) | |
8363 | (if (or force | |
8364 | (y-or-n-p (format "Send message to `%s'? " to))) | |
8365 | (progn | |
8366 | (setq sent (1+ sent)) | |
8367 | (message-send-and-exit)) | |
8368 | (message (format "Message to `%s' skipped." to)) | |
8369 | (setq skipped (1+ skipped))) | |
8370 | (when (buffer-live-p buff) | |
8371 | (kill-buffer buff)))) | |
8372 | (message "%s message(s) sent, %s skipped." sent skipped))) | |
8373 | ||
9b3ebcb6 MB |
8374 | (defun message-replace-header (header new-value &optional after force) |
8375 | "Remove HEADER and insert the NEW-VALUE. | |
8376 | If AFTER, insert after this header. If FORCE, insert new field | |
8377 | even if NEW-VALUE is empty." | |
8378 | ;; Similar to `nnheader-replace-header' but for message buffers. | |
8379 | (save-excursion | |
8380 | (save-restriction | |
8381 | (message-narrow-to-headers) | |
8382 | (message-remove-header header)) | |
8383 | (when (or force (> (length new-value) 0)) | |
8384 | (if after | |
8385 | (message-position-on-field header after) | |
8386 | (message-position-on-field header)) | |
8387 | (insert new-value)))) | |
8388 | ||
8389 | (defcustom message-recipients-without-full-name | |
8390 | (list "ding@gnus.org" | |
8391 | "bugs@gnus.org" | |
8392 | "emacs-devel@gnu.org" | |
8393 | "emacs-pretest-bug@gnu.org" | |
8394 | "bug-gnu-emacs@gnu.org") | |
8395 | "Mail addresses that have no full name. | |
8396 | Used in `message-simplify-recipients'." | |
8397 | ;; Maybe the addresses could be extracted from | |
8398 | ;; `gnus-parameter-to-list-alist'? | |
8399 | :type '(choice (const :tag "None" nil) | |
8400 | (repeat string)) | |
8401 | :version "23.1" ;; No Gnus | |
8402 | :group 'message-headers) | |
8403 | ||
8404 | (defun message-simplify-recipients () | |
8405 | (interactive) | |
8406 | (dolist (hdr '("Cc" "To")) | |
8407 | (message-replace-header | |
8408 | hdr | |
8409 | (mapconcat | |
8410 | (lambda (addrcomp) | |
8411 | (if (and message-recipients-without-full-name | |
8412 | (string-match | |
8413 | (regexp-opt message-recipients-without-full-name) | |
8414 | (cadr addrcomp))) | |
8415 | (cadr addrcomp) | |
8416 | (if (car addrcomp) | |
8417 | (message-make-from (car addrcomp) (cadr addrcomp)) | |
8418 | (cadr addrcomp)))) | |
8419 | (when (message-fetch-field hdr) | |
8420 | (mail-extract-address-components | |
8421 | (message-fetch-field hdr) t)) | |
8422 | ", ")))) | |
8423 | ||
23f87bed MB |
8424 | (when (featurep 'xemacs) |
8425 | (require 'messagexmas) | |
8426 | (message-xmas-redefine)) | |
8427 | ||
eec82323 LMI |
8428 | (provide 'message) |
8429 | ||
16409b0b GM |
8430 | (run-hooks 'message-load-hook) |
8431 | ||
8432 | ;; Local Variables: | |
c38e0c97 | 8433 | ;; coding: utf-8 |
16409b0b GM |
8434 | ;; End: |
8435 | ||
eec82323 | 8436 | ;;; message.el ends here |