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