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