71bd9b51ac68e9f8f4da020d05bd4998adda315d
[gnus] / lisp / message.el
1 ;;; message.el --- composing mail and news messages
2 ;; Copyright (C) 1996 Free Software Foundation, Inc.
3
4 ;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
5 ;; Keywords: mail, news
6
7 ;; This file is part of GNU Emacs.
8
9 ;; GNU Emacs is free software; you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation; either version 2, or (at your option)
12 ;; any later version.
13
14 ;; GNU Emacs is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 ;; GNU General Public License for more details.
18
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
21 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 ;; Boston, MA 02111-1307, USA.
23
24 ;;; Commentary:
25
26 ;; This mode provides mail-sending facilities from within Emacs.  It
27 ;; consists mainly of large chunks of code from the sendmail.el,
28 ;; gnus-msg.el and rnewspost.el files.
29
30 ;;; Code:
31
32 (eval-when-compile 
33   (require 'cl))
34 (require 'mail-header)
35
36 (defvar message-fcc-handler-function 'rmail-output
37   "*A function called to save outgoing articles.
38 This function will be called with the same of the file to store the
39 article in. The default function is `rmail-output' which saves in Unix
40 mailbox format.")
41
42 (defvar message-courtesy-message
43   "The following message is a courtesy copy of an article\nthat has been posted as well.\n\n"
44   "*This is inserted at the start of a mailed copy of a posted message.
45 If this variable is nil, no such courtesy message will be added.")
46
47 (defvar message-ignored-bounced-headers "^\\(Received\\):"
48   "*Regexp that matches headers to be removed in resent bounced mail.")
49
50 (defvar message-from-style 'angles 
51   "*Specifies how \"From\" headers look.
52
53 If `nil', they contain just the return address like:
54         king@grassland.com
55 If `parens', they look like:
56         king@grassland.com (Elvis Parsley)
57 If `angles', they look like:
58         Elvis Parsley <king@grassland.com>")
59
60 (defvar message-syntax-checks
61   '(subject-cmsg multiple-headers sendsys message-id from
62                  long-lines control-chars size new-text
63                  redirected-followup signature approved sender 
64                  empty empty-headers)
65   "In non-nil, message will attempt to run some checks on outgoing posts.
66 If this variable is t, message will check everything it can.  If it is
67 a list, then those elements in that list will be checked.")
68
69 (defvar message-required-news-headers
70   '(From Date Newsgroups Subject Message-ID Organization Lines 
71          (optional . X-Newsreader))
72   "*Headers to be generated or prompted for when posting an article.
73 RFC977 and RFC1036 require From, Date, Newsgroups, Subject,
74 Message-ID.  Organization, Lines, In-Reply-To, Expires, and
75 X-Newsreader are optional.  If don't you want message to insert some
76 header, remove it from this list.")
77
78 (defvar message-required-mail-headers 
79   '(From Date To Subject (optional . In-Reply-To) Message-ID Lines
80          (optional . X-Mailer))
81   "*Headers to be generated or prompted for when mailing a message.
82 RFC822 required that From, Date, To, Subject and Message-ID be
83 included.  Organization, Lines and X-Mailer are optional.")
84
85 (defvar message-deletable-headers '(Message-ID Date)
86   "*Headers to be deleted if they already exists and were generated by message previously.")
87
88 (defvar message-ignored-news-headers 
89   "^NNTP-Posting-Host:\\|^Xref:\\|^Bcc:\\|^Gcc:"
90   "*Regexp of headers to be removed unconditionally before posting.")
91
92 (defvar message-ignored-supersedes-headers
93   "^Path:\\|^Date\\|^NNTP-Posting-Host:\\|^Xref:\\|^Lines:\\|^Received:\\|^X-From-Line:\\|Return-Path:"
94   "*Header lines matching this regexp will be deleted before posting.
95 It's best to delete old Path and Date headers before posting to avoid
96 any confusion.")
97
98 (defvar message-signature-separator "^-- *$"
99   "Regexp matching signature separator.")
100
101 (defvar message-interactive nil 
102   "Non-nil means when sending a message wait for and display errors.
103 nil means let mailer mail back a message to report errors.")
104
105 (defvar gnus-local-organization)
106 (defvar message-user-organization 
107   (if (boundp 'gnus-local-organization)
108       gnus-local-organization t)
109   "*String to be used as an Organization header.
110 If t, use `message-user-organization-file'.")
111
112 (defvar message-user-organization-file "/usr/lib/news/organization"
113   "*Local news organization file.")
114
115 (defvar message-autosave-directory "~/Mail/drafts/"
116   "*Directory where message autosaves buffers.
117 If nil, message won't autosave.")
118
119 (defvar message-forward-start-separator 
120   "------- Start of forwarded message -------\n"
121   "*Delimiter inserted before forwarded messages.")
122
123 (defvar message-forward-end-separator
124   "------- End of forwarded message -------\n"
125   "*Delimiter inserted after forwarded messages.")
126
127 (defvar message-signature-before-forwarded-message t
128   "*If non-nil, put the signature before any included forwarded message.")
129
130 (defvar message-included-forward-headers 
131   "^From:\\|^Newsgroups:\\|^Subject:\\|^Date:\\|^Followup-To:\\|^Reply-To:\\|^Organization:\\|^Summary:\\|^Keywords:\\|^To:\\|^Cc:\\|^Posted-To:\\|^Mail-Copies-To:\\|^Apparently-To:\\|^Gnus-Warning:\\|^Resent-\\|^Message-ID:\\|^References:"
132   "*Regexp matching headers to be included in forwarded messages.")
133
134 (defvar message-ignored-resent-headers "^Return-receipt"
135   "*All headers that match this regexp will be deleted when resending a message.")
136
137 ;;;###autoload
138 (defvar message-ignored-cited-headers ":"
139   "Delete these headers from the messages you yank.")
140
141 ;; Useful to set in site-init.el
142 ;;;###autoload
143 (defvar message-send-mail-function 'message-send-mail 
144   "Function to call to send the current buffer as mail.
145 The headers should be delimited by a line whose contents match the
146 variable `message-header-separator'.")
147
148 (defvar message-send-news-function 'message-send-news
149   "Function to call to send the current buffer as news.
150 The headers should be delimited by a line whose contents match the
151 variable `message-header-separator'.")
152
153 (defvar message-reply-to-function nil
154   "Function that should return a list of headers.")
155
156 (defvar message-wide-reply-to-function nil
157   "Function that should return a list of headers.")
158
159 (defvar message-followup-to-function nil
160   "Function that should return a list of headers.")
161
162 (defvar message-use-followup-to 'ask
163   "*Specifies what to do with Followup-To header.
164 If nil, ignore the header. If it is t, use its value, but ignore
165 \"poster\".  If it is the symbol `ask', query the user whether to
166 ignore the \"poster\" value.  If it is the symbol `use', always use
167 the value.")
168
169 (defvar message-post-method 
170   (cond ((boundp 'gnus-post-method)
171          gnus-post-method)
172         ((boundp 'gnus-select-method)
173          gnus-select-method)
174         (t '(nnspool "")))
175   "Method used to post news.")
176
177 (defvar message-generate-headers-first nil
178   "*If non-nil, generate all possible headers before composing.")
179
180 ;;;###autoload
181 (defvar message-header-separator "--text follows this line--" 
182   "*Line used to separate headers from text in messages being composed.")
183
184 ;;;###autoload
185 (defvar message-alias-file nil
186   "*If non-nil, the name of a file to use instead of `/usr/lib/aliases'.
187 This file defines aliases to be expanded by the mailer; this is a different
188 feature from that of defining aliases in `.mailrc' to be expanded in Emacs.
189 This variable has no effect unless your system uses sendmail as its mailer.")
190
191 ;;;###autoload
192 (defvar message-personal-alias-file "~/.mailrc"
193   "*If non-nil, the name of the user's personal mail alias file.
194 This file typically should be in same format as the `.mailrc' file used by
195 the `Mail' or `mailx' program.
196 This file need not actually exist.")
197
198 (defvar message-setup-hook nil
199   "Normal hook, run each time a new outgoing message is initialized.
200 The function `message-setup' runs this hook.")
201
202 (defvar message-header-setup-hook nil
203   "Hook called narrowed to the headers when setting up a message buffer.")
204
205 (defvar message-citation-line-function 'message-insert-citation-line
206   "*Function called to insert the \"Whomever writes:\" line.")
207
208 (defvar message-aliases t
209   "Alist of mail address aliases.
210 If t, initialized from your mail aliases file.
211 \(The file's name is normally `~/.mailrc', but your MAILRC environment
212 variable can override that name.)
213 The alias definitions in the file have this form:
214     alias ALIAS MEANING")
215
216 (defvar message-alias-modtime nil
217   "The modification time of your mail alias file when it was last examined.")
218
219 (defvar message-yank-prefix "> "
220   "*Prefix inserted on the lines of yanked messages.
221 nil means use indentation.")
222
223 (defvar message-indentation-spaces 3
224   "*Number of spaces to insert at the beginning of each cited line.
225 Used by `message-yank-original' via `message-yank-cite'.")
226
227 (defvar message-indent-citation-function 'message-indent-citation
228   "*Function for modifying a citation just inserted in the mail buffer.
229 This can also be a list of functions.  Each function can find the
230 citation between (point) and (mark t).  And each function should leave
231 point and mark around the citation text as modified.")
232
233 (defvar message-abbrevs-loaded nil)
234
235 (autoload 'build-mail-aliases "mailalias"
236   "Read mail aliases from user's personal aliases file and set `mail-aliases'."
237   nil)
238
239 (autoload 'expand-mail-aliases "mailalias"
240   "Expand all mail aliases in suitable header fields found between BEG and END.
241 Suitable header fields are `To', `Cc' and `Bcc' and their `Resent-' variants.
242 Optional second arg EXCLUDE may be a regular expression defining text to be
243 removed from alias expansions."
244   nil)
245
246 (defvar message-signature t
247   "*String to be inserted at the and the the message buffer.
248 If t, the `message-signature-file' file will be inserted instead.
249 If a function, the result from the function will be used instead.
250 If a form, the result from the form will be used instead.")
251
252 (defvar message-signature-file "~/.signature"
253   "*File containing the text inserted at end of mail buffer.")
254
255 (defvar message-distribution-function nil
256   "*Function called to return a Distribution header.")
257
258 (defvar message-expires 14
259   "*Number of days before your article expires.")
260
261 (defvar message-user-path nil
262   "If nil, use the NNTP server name in the Path header.
263 If stringp, use this; if non-nil, use no host name (user name only).")
264
265 (defvar message-generic-domain nil
266   "If nil, the full host name will be the system name prepended to the domain name.
267 If this is a string, the full host name will be this string.
268 If this is non-nil, non-string, the domain name will be used as the
269 full host name.")
270
271 (defvar message-reply-buffer nil)
272 (defvar message-reply-headers nil)
273 (defvar message-newsreader nil)
274 (defvar message-mailer nil)
275 (defvar message-sent-message-via nil)
276 (defvar message-send-actions nil
277   "A list of actions to be performed upon successful sending of a message.")
278
279 (defvar message-default-headers nil
280   "*A string containing header lines to be inserted in outgoing messages.
281 It is inserted before you edit the message, so you can edit or delete
282 these lines.")
283
284 (defvar message-default-mail-headers nil
285   "*A string of header lines to be inserted in outgoing mails.")
286
287 (defvar message-default-news-headers nil
288   "*A string of header lines to be inserted in outgoing news articles.")
289
290 ;; Note: could use /usr/ucb/mail instead of sendmail;
291 ;; options -t, and -v if not interactive.
292 (defvar message-mailer-swallows-blank-line
293   (if (and (string-match "sparc-sun-sunos\\(\\'\\|[^5]\\)" 
294                          system-configuration)
295            (file-readable-p "/etc/sendmail.cf")
296            (let ((buffer (get-buffer-create " *temp*")))
297              (unwind-protect
298                  (save-excursion
299                    (set-buffer buffer)
300                    (insert-file-contents "/etc/sendmail.cf")
301                    (goto-char (point-min))
302                    (let ((case-fold-search nil))
303                      (re-search-forward "^OR\\>" nil t)))
304                (kill-buffer buffer))))
305       ;; According to RFC822, "The field-name must be composed of printable
306       ;; ASCII characters (i.e. characters that have decimal values between
307       ;; 33 and 126, except colon)", i.e. any chars except ctl chars,
308       ;; space, or colon.
309       '(looking-at "[ \t]\\|[][!\"#$%&'()*+,-./0-9;<=>?@A-Z\\\\^_`a-z{|}~]+:"))
310   "Set this non-nil if the system's mailer runs the header and body together.
311 \(This problem exists on Sunos 4 when sendmail is run in remote mode.)
312 The value should be an expression to test whether the problem will
313 actually occur.")
314
315 (defvar message-mode-syntax-table 
316   (let ((table (copy-syntax-table text-mode-syntax-table)))
317     (modify-syntax-entry ?% ". " table)
318     table)
319   "Syntax table used while in message mode.")
320
321 (defvar message-font-lock-keywords
322   (let* ((cite-prefix "A-Za-z") (cite-suffix (concat cite-prefix "0-9_.@-")))
323     (list '("^To:" . font-lock-function-name-face)
324           '("^B?CC:\\|^Reply-To:" . font-lock-keyword-face)
325           '("^\\(Subject:\\)[ \t]*\\(.+\\)?"
326             (1 font-lock-comment-face) (2 font-lock-type-face nil t))
327           (list (concat "^\\(" (regexp-quote message-header-separator) "\\)$")
328                 1 'font-lock-comment-face)
329           (cons (concat "^[ \t]*"
330                         "\\([" cite-prefix "]+[" cite-suffix "]*\\)?"
331                         "[>|}].*")
332                 'font-lock-reference-face)
333           '("^\\(X-[A-Za-z0-9-]+\\|In-reply-to\\):.*"
334             . font-lock-string-face)))
335   "Additional expressions to highlight in Mail mode.")
336
337 (defvar message-send-hook nil
338   "Hook run before sending messages.")
339
340 (defvar message-sent-hook nil
341   "Hook run after sending messages.")
342
343 (defvar message-header-format-alist 
344   `((Newsgroups)
345     (To . message-fill-header) 
346     (Cc . message-fill-header)
347     (Subject)
348     (In-Reply-To)
349     (Fcc)
350     (Bcc)
351     (Date)
352     (Organization)
353     (Distribution)
354     (Lines)
355     (Expires)
356     (Message-ID)
357     (References . message-fill-header)
358     (X-Mailer)
359     (X-Newsreader))
360   "Alist used for formatting headers.")
361
362 \f
363
364 ;;; 
365 ;;; Utility functions.
366 ;;;
367
368 (defun message-point-at-bol ()
369   "Return point at the beginning of the line."
370   (let ((p (point)))
371     (beginning-of-line)
372     (prog1
373         (point)
374       (goto-char p))))
375
376 (defun message-point-at-eol ()
377   "Return point at the end of the line."
378   (let ((p (point)))
379     (end-of-line)
380     (prog1
381         (point)
382       (goto-char p))))
383
384 ;; Delete the current line (and the next N lines.);
385 (defmacro message-delete-line (&optional n)
386   `(delete-region (progn (beginning-of-line) (point))
387                   (progn (forward-line ,(or n 1)) (point))))
388
389 (defun message-tokenize-header (header &optional separator)
390   "Split HEADER into a list of header elements.
391 \",\" is used as the separator."
392   (let* ((beg 0)
393          (separator (or separator ","))
394          (regexp
395           (format "[ \t]*\\([^%s]+\\)?\\([%s]+\\|\\'\\)" separator separator))
396          elems)
397     (while (and (string-match regexp header beg)
398                 (< beg (length header)))
399       (when (match-beginning 1)
400         (push (match-string 1 header) elems))
401       (setq beg (match-end 0)))
402     (nreverse elems)))
403
404 (defun message-fetch-reply-field (header)
405   "Fetch FIELD from the message we're replying to."
406   (when (and message-reply-buffer
407              (buffer-name message-reply-buffer))
408     (save-excursion
409       (set-buffer message-reply-buffer)
410       (mail-fetch-field header))))
411
412 (defun message-set-work-buffer ()
413   (if (get-buffer " *message work*")
414       (progn
415         (set-buffer " *message work*")
416         (erase-buffer))
417     (set-buffer (get-buffer-create " *message work*"))
418     (kill-all-local-variables)
419     (buffer-disable-undo (current-buffer))))
420
421 (defun message-functionp (form)
422   "Return non-nil if FORM is funcallable."
423   (or (and (symbolp form) (fboundp form))
424       (and (listp form) (eq (car form) 'lambda))))
425
426 (defun message-strip-subject-re (subject)
427   "Remove \"Re:\" from subject lines."
428   (if (string-match "^[Rr][Ee]: *" subject)
429       (substring subject (match-end 0))
430     subject))
431
432 (defun message-remove-header (header &optional is-regexp first)
433   "Remove HEADER in the narrowed buffer.
434 If REGEXP, HEADER is a regular expression.
435 If FIRST, only remove the first instance of the header.
436 Return the number of headers removed."
437   (goto-char (point-min))
438   (let ((regexp (if is-regexp header (concat "^" header ":")))
439         (number 0)
440         (case-fold-search t)
441         last)
442     (while (and (re-search-forward regexp nil t)
443                 (not last))
444       (incf number)
445       (when first
446         (setq last t))
447       (delete-region
448        (message-point-at-bol)
449        ;; There might be a continuation header, so we have to search
450        ;; until we find a new non-continuation line.
451        (if (re-search-forward "^[^ \t]" nil t)
452            (goto-char (match-beginning 0))
453          (point-max))))
454     number))
455
456 (defun message-narrow-to-headers ()
457   "Narrow the buffer to the head of the message."
458   (widen)
459   (narrow-to-region
460    (goto-char (point-min))
461    (if (re-search-forward
462         (concat "^" (regexp-quote message-header-separator) "\n") nil t)
463        (match-beginning 0)
464      (point-max)))
465   (goto-char (point-min)))
466
467 (defun message-narrow-to-head ()
468   "Narrow the buffer to the head of the message."
469   (widen)
470   (narrow-to-region
471    (goto-char (point-min))
472    (if (search-forward "\n\n" nil 1)
473        (1- (point))
474      (point-max)))
475   (goto-char (point-min)))
476
477 (defun message-news-p ()
478   "Say whether the current buffer contains a news message."
479   (mail-fetch-field "newsgroups"))
480
481 (defun message-mail-p ()
482   "Say whether the current buffer contains a mail message."
483   (or (mail-fetch-field "to")
484       (mail-fetch-field "cc")
485       (mail-fetch-field "bcc")))
486
487 \f
488
489 ;;;
490 ;;; Message mode
491 ;;;
492
493 ;;; Set up keymap.
494
495 (defvar message-mode-map nil)
496
497 (unless message-mode-map
498   (setq message-mode-map (nconc (make-sparse-keymap) text-mode-map))
499   (define-key message-mode-map "\C-c?" 'describe-mode)
500
501   (define-key message-mode-map "\C-c\C-f\C-t" 'message-goto-to)
502   (define-key message-mode-map "\C-c\C-f\C-b" 'message-goto-bcc)
503   (define-key message-mode-map "\C-c\C-f\C-f" 'message-goto-fcc)
504   (define-key message-mode-map "\C-c\C-f\C-c" 'message-goto-cc)
505   (define-key message-mode-map "\C-c\C-f\C-s" 'message-goto-subject)
506   (define-key message-mode-map "\C-c\C-f\C-r" 'message-goto-reply-to)
507   (define-key message-mode-map "\C-c\C-f\C-n" 'message-goto-newsgroups)
508   (define-key message-mode-map "\C-c\C-f\C-d" 'message-goto-distribution)
509   (define-key message-mode-map "\C-c\C-f\C-o" 'message-goto-followup-to)
510   (define-key message-mode-map "\C-c\C-f\C-k" 'message-goto-keywords)
511   (define-key message-mode-map "\C-c\C-f\C-u" 'message-goto-summary)
512   (define-key message-mode-map "\C-c\C-b" 'message-goto-body)
513   (define-key message-mode-map "\C-c\C-s" 'message-goto-signature)
514
515   (define-key message-mode-map "\C-c\C-t" 'message-insert-to)
516   (define-key message-mode-map "\C-c\C-n" 'message-insert-newsgroups)
517   
518   (define-key message-mode-map "\C-c\C-y" 'message-yank-original)
519   (define-key message-mode-map "\C-c\C-q" 'message-fill-yanked-message)
520   (define-key message-mode-map "\C-c\C-w" 'message-insert-signature)
521   (define-key message-mode-map "\C-c\C-r" 'message-caesar-buffer-body)
522
523   (define-key message-mode-map "\C-c\C-c" 'message-send-and-exit)
524   (define-key message-mode-map "\C-c\C-s" 'message-send))
525
526 (easy-menu-define
527  message-menu message-mode-map ""
528  '("Mail"
529    ["Fill Citation" message-fill-yanked-message t]))
530
531 ;;;###autoload
532 (defun message-mode ()
533   "Major mode for editing mail to be sent.
534 Like Text Mode but with these additional commands:
535 C-c C-s  message-send (send the message)    C-c C-c  message-send-and-exit
536 C-c C-f  move to a header field (and create it if there isn't):
537          C-c C-f C-t  move to To:       C-c C-f C-s  move to Subject:
538          C-c C-f C-c  move to CC:       C-c C-f C-b  move to BCC:
539          C-c C-f C-f  move to FCC:
540 C-c C-t  message-text (move to beginning of message text).
541 C-c C-w  message-signature (insert `message-signature-file' file).
542 C-c C-y  message-yank-original (insert current message, in Rmail).
543 C-c C-q  message-fill-yanked-message (fill what was yanked).
544 C-c C-v  message-sent-via (add a Sent-via field for each To or CC)."
545   (interactive)
546   (kill-all-local-variables)
547   (make-local-variable 'message-reply-buffer)
548   (setq message-reply-buffer nil)
549   (make-local-variable 'message-send-actions)
550   (set-syntax-table message-mode-syntax-table)
551   (use-local-map message-mode-map)
552   (setq local-abbrev-table text-mode-abbrev-table)
553   (setq major-mode 'message-mode)
554   (setq mode-name "Message")
555   (setq buffer-offer-save t)
556   (make-local-variable 'font-lock-defaults)
557   (setq font-lock-defaults '(message-font-lock-keywords t))
558   (make-local-variable 'paragraph-separate)
559   (make-local-variable 'paragraph-start)
560   (setq paragraph-start (concat (regexp-quote message-header-separator)
561                                 "$\\|[ \t]*[-_][-_][-_]+$\\|"
562                                 paragraph-start))
563   (setq paragraph-separate (concat (regexp-quote message-header-separator)
564                                    "$\\|[ \t]*[-_][-_][-_]+$\\|"
565                                    paragraph-separate))
566   (make-local-variable 'message-reply-headers)
567   (make-local-variable 'message-newsreader)
568   (make-local-variable 'message-mailer)
569   (make-local-variable 'message-post-method)
570   (make-local-variable 'message-sent-message-via)
571   (run-hooks 'text-mode-hook 'message-mode-hook))
572
573 \f
574
575 ;;;
576 ;;; Message mode commands
577 ;;;
578
579 ;;; Movement commands
580
581 (defun message-goto-to ()
582   "Move point to the To header."
583   (interactive)
584   (message-position-on-field "To"))
585
586 (defun message-goto-subject ()
587   "Move point to the Subject header."
588   (interactive)
589   (message-position-on-field "Subject"))
590
591 (defun message-goto-cc ()
592   "Move point to the Cc header."
593   (interactive)
594   (message-position-on-field "Cc" "To"))
595
596 (defun message-goto-bcc ()
597   "Move point to the Bcc  header."
598   (interactive)
599   (message-position-on-field "Bcc" "Cc" "To"))
600
601 (defun message-goto-fcc ()
602   "Move point to the Followup-To header."
603   (interactive)
604   (message-position-on-field "Fcc" "To" "Newsgroups"))
605
606 (defun message-goto-reply-to ()
607   "Move point to the Reply-To header."
608   (interactive)
609   (message-position-on-field "Reply-To" "Subject"))
610
611 (defun message-goto-newsgroups ()
612   "Move point to the Newsgroups header."
613   (interactive)
614   (message-position-on-field "Newsgroups"))
615
616 (defun message-goto-distribution ()
617   "Move point to the Distribution header."
618   (interactive)
619   (message-position-on-field "Distribution"))
620
621 (defun message-goto-followup-to ()
622   "Move point to the Followup-To header."
623   (interactive)
624   (message-position-on-field "Followup-To" "Newsgroups"))
625
626 (defun message-goto-keywords ()
627   "Move point to the Keywords header."
628   (interactive)
629   (message-position-on-field "Keywords" "Subject"))
630
631 (defun message-goto-summary ()
632   "Move point to the Summary header."
633   (interactive)
634   (message-position-on-field "Summary" "Subject"))
635
636 (defun message-goto-body ()
637   "Move point to the beginning of the message body."
638   (interactive)
639   (goto-char (point-min))
640   (search-forward (concat "\n" message-header-separator "\n") nil t))
641
642 \f
643
644 (defun message-insert-to ()
645   "Insert a To header that points to the author of the message being replied to."
646   (interactive)
647   (message-position-on-field "To")
648   (insert (or (message-fetch-reply-field "reply-to")
649               (message-fetch-reply-field "from") "")))
650
651 (defun message-insert-newsgroups ()
652   "Insert the Newsgroups header from the article being replied to."
653   (interactive)
654   (message-position-on-field "newsgroups")
655   (insert (or (message-fetch-reply-field "newsgroups") "")))
656
657 \f
658
659 ;;; Various commands
660
661 (defun message-insert-signature ()
662   "Insert a signature."
663   (interactive)
664   (let* ((signature 
665           (cond ((message-functionp message-signature)
666                  (funcall message-signature))
667                 ((listp message-signature)
668                  (eval message-signature))
669                 (t message-signature)))
670          (signature
671           (cond ((stringp signature)
672                  signature)
673                 ((and (eq t signature)
674                       message-signature-file
675                       (file-exists-p message-signature-file))
676                  signature))))
677     (when signature
678       ;; Remove blank lines at the end of the message.
679       (goto-char (point-max))
680       (skip-chars-backward " \t\n")
681       (end-of-line)
682       (delete-region (point) (point-max))
683       ;; Insert the signature.
684       (insert "\n\n-- \n")
685       (if (eq signature t)
686           (insert-file-contents message-signature-file)
687         (insert signature))
688       (goto-char (point-max))
689       (or (bolp) (insert "\n")))))
690
691 (defvar message-caesar-translation-table nil)
692
693 (defun message-caesar-region (b e &optional n)
694   "Caesar rotation of region by N, default 13, for decrypting netnews."
695   (interactive
696    (list
697     (min (point) (or (mark t) (point)))
698     (max (point) (or (mark t) (point)))
699     (when current-prefix-arg
700       (prefix-numeric-value current-prefix-arg))))
701
702   (setq n (if (numberp n) (mod n 26) 13)) ;canonize N
703   (unless (or (zerop n)                 ; no action needed for a rot of 0
704               (= b e))                  ; no region to rotate
705     ;; We build the table, if necessary.
706     (when (or (not message-caesar-translation-table)
707               (/= (aref message-caesar-translation-table ?a) (+ ?a n)))
708       (let ((i -1) 
709             (table (make-string 256 0)))
710         (while (< (incf i) 256)
711           (aset table i i))
712         (setq table
713               (concat
714                (substring table 0 ?A)
715                (substring table (+ ?A n) (+ ?A n (- 26 n)))
716                (substring table ?A (+ ?A n))
717                (substring table (+ ?A 26) ?a)
718                (substring table (+ ?a n) (+ ?a n (- 26 n)))
719                (substring table ?a (+ ?a n))
720                (substring table (+ ?a 26) 255)))
721         (setq message-caesar-translation-table table)))
722     ;; Then we translate the region.  Do it this way to retain 
723     ;; text properties.
724     (while (< b e)
725       (subst-char-in-region 
726        b (1+ b) (char-after b)
727        (aref message-caesar-translation-table (char-after b)))
728       (incf b))))
729
730 (defun message-caesar-buffer-body (&optional rotnum)
731   "Caesar rotates all letters in the current buffer by 13 places.
732 Used to encode/decode possibly offensive messages (commonly in net.jokes).
733 With prefix arg, specifies the number of places to rotate each letter forward.
734 Mail and USENET news headers are not rotated."
735   (interactive (if current-prefix-arg
736                    (list (prefix-numeric-value current-prefix-arg))
737                  (list nil)))
738   (save-excursion
739     (save-restriction
740       (when (message-goto-body)
741         (narrow-to-region (point) (point-max)))
742       (message-caesar-region (point-min) (point-max) rotnum))))
743
744 (defun message-fill-yanked-message (&optional justifyp)
745   "Fill the paragraphs of a message yanked into this one.
746 Numeric argument means justify as well."
747   (interactive "P")
748   (save-excursion
749     (goto-char (point-min))
750     (search-forward (concat "\n" message-header-separator "\n") nil t)
751     (fill-individual-paragraphs (point)
752                                 (point-max)
753                                 justifyp
754                                 t)))
755
756 (defun message-indent-citation ()
757   "Modify text just inserted from a message to be cited.
758 The inserted text should be the region.
759 When this function returns, the region is again around the modified text.
760
761 Normally, indent each nonblank line `message-indentation-spaces' spaces.
762 However, if `message-yank-prefix' is non-nil, insert that prefix on each line."
763   (let ((start (point)))
764     ;; Remove unwanted headers.
765     (when message-ignored-cited-headers
766       (save-restriction
767         (narrow-to-region 
768          (goto-char start)
769          (if (search-forward "\n\n" nil t)
770              (1- (point))
771            (point)))
772         (message-remove-header message-ignored-cited-headers t)))
773     ;; Do the indentation.
774     (if (null message-yank-prefix)
775         (indent-rigidly start (mark t) message-indentation-spaces)
776       (save-excursion
777         (goto-char start)
778         (while (< (point) (mark t))
779           (insert message-yank-prefix)
780           (forward-line 1)))
781       (goto-char start))))
782
783 (defun message-yank-original (&optional arg)
784   "Insert the message being replied to, if any (in rmail).
785 Puts point before the text and mark after.
786 Normally indents each nonblank line ARG spaces (default 3).  However,
787 if `message-yank-prefix' is non-nil, insert that prefix on each line.
788
789 Just \\[universal-argument] as argument means don't indent, insert no
790 prefix, and don't delete any headers."
791   (interactive "P")
792   (when message-reply-buffer
793     (let ((start (point))
794           (functions 
795            (when message-indent-citation-function
796              (if (listp message-indent-citation-function)
797                  message-indent-citation-function
798                (list message-indent-citation-function)))))
799       ;; If the original message is in another window in the same frame,
800       ;; delete that window to save screen space.
801       ;; t means don't alter other frames.
802       (delete-windows-on message-reply-buffer t)
803       (insert-buffer message-reply-buffer)
804       (unless (consp arg)
805         (goto-char start)
806         (let ((message-indentation-spaces
807                (if arg (prefix-numeric-value arg)
808                  message-indentation-spaces)))
809           (while functions
810             (funcall (pop functions)))))
811       (when message-citation-line-function
812         (unless (bolp)
813           (insert "\n"))
814         (funcall message-citation-line-function))
815       ;; This is like exchange-point-and-mark, but doesn't activate the mark.
816       ;; It is cleaner to avoid activation, even though the command
817       ;; loop would deactivate the mark because we inserted text.
818       (goto-char (prog1 (mark t)
819                    (set-marker (mark-marker) (point) (current-buffer))))
820       (unless (bolp)
821         (insert ?\n)))))
822
823 (defun message-insert-citation-line ()
824   "Function that inserts a simple citation line."
825   (when message-reply-headers
826     (insert (mail-header-from message-reply-headers) " writes:\n\n")))
827
828 (defun message-position-on-field (header &rest afters)
829   (let ((case-fold-search t))
830     (save-restriction
831       (narrow-to-region
832        (goto-char (point-min))
833        (progn
834          (re-search-forward 
835           (concat "^" (regexp-quote message-header-separator) "$"))
836          (match-beginning 0)))
837       (goto-char (point-min))
838       (if (re-search-forward (concat "^" (regexp-quote header) ":") nil t)
839           (progn
840             (re-search-forward "^[^ \t]" nil 'move)
841             (beginning-of-line)
842             (skip-chars-backward "\n")
843             t)
844         (while (and afters
845                     (not (re-search-forward 
846                           (concat "^" (regexp-quote (car afters)) ":")
847                           nil t)))
848           (pop afters))
849         (when afters
850           (re-search-forward "^[^ \t]" nil 'move)
851           (beginning-of-line))
852         (insert header ": \n")
853         (forward-char -1)
854         nil))))
855
856 (defun message-remove-signature ()
857   "Remove the signature from the text between point and mark.
858 The text will also be indented the normal way.
859 This function can be used in `message-citation-hook', for instance."
860   (save-excursion
861     (let ((start (point))
862           mark)
863     (if (not (re-search-forward message-signature-separator (mark t) t))
864         ;; No signature here, so we just indent the cited text.
865         (message-indent-citation)
866       ;; Find the last non-empty line.
867       (forward-line -1)
868       (while (looking-at "[ \t]*$")
869         (forward-line -1))
870       (forward-line 1)
871       (setq mark (set-marker (make-marker) (point)))
872       (goto-char start)
873       (message-indent-citation)
874       ;; Enable undoing the deletion.
875       (undo-boundary)
876       (delete-region mark (mark t))
877       (set-marker mark nil)))))
878
879 \f
880
881 ;;;
882 ;;; Sending messages
883 ;;;
884
885 (defun message-send-and-exit (&optional arg)
886   "Send message like `message-send', then, if no errors, exit from mail buffer.
887 Prefix arg means don't delete this window."
888   (interactive "P")
889   (message-send)
890   ;(message-bury arg)
891   )
892
893 (defun message-dont-send (&optional arg)
894   "Don't send the message you have been editing.
895 Prefix arg means don't delete this window."
896   (interactive "P")
897   (message-bury arg))
898
899 (defun message-bury (arg)
900   "Bury this mail buffer."
901   (let ((newbuf (other-buffer (current-buffer))))
902     (bury-buffer (current-buffer))
903     (if (and (fboundp 'frame-parameters)
904              (cdr (assq 'dedicated (frame-parameters)))
905              (not (null (delq (selected-frame) (visible-frame-list)))))
906         (delete-frame (selected-frame))
907       (switch-to-buffer newbuf))))
908
909 (defun message-send (&optional arg)
910   "Send the message in the current buffer.
911 If `message-interactive' is non-nil, wait for success indication
912 or error messages, and inform user.
913 Otherwise any failure is reported in a message back to
914 the user from the mailer."
915   (interactive "P")
916   (when (if buffer-file-name
917             (y-or-n-p (format "Send buffer contents as %s message? "
918                               (if (message-mail-p)
919                                   (if (message-news-p) "main and news" "news")
920                                 "news")))
921           (or (buffer-modified-p)
922               (y-or-n-p "Message already sent; resend? ")))
923     ;; Make it possible to undo the coming changes.
924     (undo-boundary)
925     (run-hooks 'message-send-hook)
926     (message "Sending...")
927     (when (and (or (not (message-news-p))
928                    (and (or (not (memq 'news message-sent-message-via))
929                             (y-or-n-p
930                              "Already sent message via news; resend? "))
931                         (funcall message-send-news-function arg)))
932                (or (not (message-mail-p))
933                    (and (or (not (memq 'mail message-sent-message-via))
934                             (y-or-n-p
935                              "Already sent message via mail; resend? "))
936                         (funcall message-send-mail-function arg))))
937       (message-do-fcc)
938       (run-hooks 'message-sent-hook)
939       (message "Sending...done")
940       ;; If buffer has no file, mark it as unmodified and delete autosave.
941       (unless buffer-file-name
942         (set-buffer-modified-p nil)
943         (delete-auto-save-file-if-necessary t))
944       ;; Now perform actions on successful sending.
945       (let ((actions message-send-actions))
946         (while actions
947           (condition-case nil
948               (apply (caar actions) (cdar actions))
949             (error))
950           (pop actions))))))
951
952 (defun message-send-mail (&optional arg)
953   (require 'mail-utils)
954   (let ((errbuf (if message-interactive
955                     (generate-new-buffer " sendmail errors")
956                   0))
957         (tembuf (generate-new-buffer " message temp"))
958         (case-fold-search nil)
959         (news (message-news-p))
960         (resend-to-addresses (mail-fetch-field "resent-to"))
961         delimline
962         (mailbuf (current-buffer)))
963     (save-restriction
964       (message-narrow-to-headers)
965       ;; Remove some headers.
966       (message-remove-header message-ignored-news-headers t)
967       ;; Insert some headers.
968       (message-generate-headers message-required-mail-headers)
969       ;; Let the user do all of the above.
970       (run-hooks 'message-header-hook))
971     (unwind-protect
972         (save-excursion
973           (set-buffer tembuf)
974           (erase-buffer)
975           (insert-buffer-substring mailbuf)
976           (goto-char (point-max))
977           ;; require one newline at the end.
978           (or (= (preceding-char) ?\n)
979               (insert ?\n))
980           (when (and news
981                      (or (mail-fetch-field "cc")
982                          (mail-fetch-field "to")))
983             (message-insert-courtesy-copy))
984           (let ((case-fold-search t))
985             ;; Change header-delimiter to be what sendmail expects.
986             (goto-char (point-min))
987             (re-search-forward
988              (concat "^" (regexp-quote message-header-separator) "\n"))
989             (replace-match "\n")
990             (backward-char 1)
991             (setq delimline (point-marker))
992             (sendmail-synch-aliases)
993             (when message-aliases
994               (expand-mail-aliases (point-min) delimline))
995             ;; Insert an extra newline if we need it to work around
996             ;; Sun's bug that swallows newlines.
997             (goto-char (1+ delimline))
998             (when (eval message-mailer-swallows-blank-line)
999               (newline))
1000             (when message-interactive
1001               (save-excursion
1002                 (set-buffer errbuf)
1003                 (erase-buffer))))
1004           (let ((default-directory "/"))
1005             (apply 'call-process-region
1006                    (append (list (point-min) (point-max)
1007                                  (if (boundp 'sendmail-program)
1008                                      sendmail-program
1009                                    "/usr/lib/sendmail")
1010                                  nil errbuf nil "-oi")
1011                            ;; Always specify who from,
1012                            ;; since some systems have broken sendmails.
1013                            (list "-f" (user-login-name))
1014                            (and message-alias-file
1015                                 (list (concat "-oA" message-alias-file)))
1016                            ;; These mean "report errors by mail"
1017                            ;; and "deliver in background".
1018                            (if (null message-interactive) '("-oem" "-odb"))
1019                            ;; Get the addresses from the message
1020                            ;; unless this is a resend.
1021                            ;; We must not do that for a resend
1022                            ;; because we would find the original addresses.
1023                            ;; For a resend, include the specific addresses.
1024                            (or resend-to-addresses
1025                                '("-t")))))
1026           (when message-interactive
1027             (save-excursion
1028               (set-buffer errbuf)
1029               (goto-char (point-min))
1030               (while (re-search-forward "\n\n* *" nil t)
1031                 (replace-match "; "))
1032               (if (not (zerop (buffer-size)))
1033                   (error "Sending...failed to %s"
1034                          (buffer-substring (point-min) (point-max)))))))
1035       (kill-buffer tembuf)
1036       (when (bufferp errbuf)
1037         (kill-buffer errbuf)))
1038     (push 'mail message-sent-message-via)))
1039
1040 (defun message-send-news (&optional arg)
1041   (let ((tembuf (generate-new-buffer " *message temp*"))
1042         (case-fold-search nil)
1043         (method (if (message-functionp message-post-method)
1044                     (funcall message-post-method arg)
1045                   message-post-method))
1046         (messbuf (current-buffer)))
1047     (save-restriction
1048       (message-narrow-to-headers)
1049       ;; Remove some headers.
1050       (message-remove-header message-ignored-news-headers t)
1051       ;; Insert some headers.
1052       (message-generate-headers message-required-news-headers)
1053       ;; Let the user do all of the above.
1054       (run-hooks 'message-header-hook))
1055     ;; Insert the proper mail headers.
1056     (unwind-protect
1057         (save-excursion
1058           (set-buffer tembuf)
1059           (buffer-disable-undo (current-buffer))
1060           (erase-buffer)
1061           (insert-buffer-substring messbuf)
1062           (goto-char (point-max))
1063           ;; require one newline at the end.
1064           (or (= (preceding-char) ?\n)
1065               (insert ?\n))
1066           (let ((case-fold-search t))
1067             ;; Remove the delimeter.
1068             (goto-char (point-min))
1069             (re-search-forward
1070              (concat "^" (regexp-quote message-header-separator) "\n"))
1071             (replace-match "\n")
1072             (backward-char 1))
1073           (require (car method))
1074           (funcall (intern (format "%s-open-server" (car method)))
1075                    (cadr method) (cddr method))
1076           (funcall (intern (format "%s-request-post"
1077                                    (car method)))))
1078       (kill-buffer tembuf))
1079     (push 'news message-sent-message-via)))
1080
1081 ;;;
1082 ;;; Header generation & syntax checking.
1083 ;;;
1084
1085 (defun message-check-news-syntax ()
1086   "Check the syntax of the message."
1087   (or
1088    (not message-syntax-checks)
1089    (and 
1090     ;; We narrow to the headers and check them first.
1091     (save-excursion
1092       (save-restriction
1093         (message-narrow-to-headers)
1094         (and 
1095          ;; Check for commands in Subject.
1096          (or 
1097           (message-check-element 'subject-cmsg)
1098           (save-excursion
1099             (if (string-match "^cmsg " (mail-fetch-field "subject"))
1100                 (y-or-n-p
1101                  "The control code \"cmsg \" is in the subject. Really post? ")
1102               t)))
1103          ;; Check for multiple identical headers.
1104          (or (message-check-element 'multiple-headers)
1105              (save-excursion
1106                (let (found)
1107                  (while (and (not found) 
1108                              (re-search-forward "^[^ \t:]+: " nil t))
1109                    (save-excursion
1110                      (or (re-search-forward 
1111                           (concat "^" (setq found
1112                                             (buffer-substring 
1113                                              (match-beginning 0) 
1114                                              (- (match-end 0) 2))))
1115                           nil t)
1116                          (setq found nil))))
1117                  (if found
1118                      (y-or-n-p 
1119                       (format "Multiple %s headers. Really post? " found))
1120                    t))))
1121          ;; Check for Version and Sendsys.
1122          (or (message-check-element 'sendsys)
1123              (save-excursion
1124                (if (re-search-forward "^Sendsys:\\|^Version:" nil t)
1125                    (y-or-n-p
1126                     (format "The article contains a %s command. Really post? "
1127                             (buffer-substring (match-beginning 0) 
1128                                               (1- (match-end 0)))))
1129                  t)))
1130          ;; See whether we can shorten Followup-To.
1131          (or (message-check-element 'shorten-followup-to)
1132              (let ((newsgroups (mail-fetch-field "newsgroups"))
1133                    (followup-to (mail-fetch-field "followup-to"))
1134                    to)
1135                (when (and newsgroups (string-match "," newsgroups)
1136                           (not followup-to)
1137                           (not
1138                            (zerop
1139                             (length
1140                              (setq to (completing-read 
1141                                        "Followups to: (default all groups) " 
1142                                        (mapcar (lambda (g) (list g))
1143                                                (cons "poster" 
1144                                                      (message-tokenize-header 
1145                                                       newsgroups)))))))))
1146                  (goto-char (point-min))
1147                  (insert "Followup-To: " to "\n"))))
1148
1149          ;; Check for Approved.
1150          (or (message-check-element 'approved)
1151              (save-excursion
1152                (if (re-search-forward "^Approved:" nil t)
1153                    (y-or-n-p
1154                     "The article contains an Approved header. Really post? ")
1155                  t))))))
1156     ;; Check for long lines.
1157     (or (message-check-element 'long-lines)
1158         (save-excursion
1159           (goto-char (point-min))
1160           (re-search-forward
1161            (concat "^" (regexp-quote mail-header-separator) "$"))
1162           (while (and
1163                   (progn
1164                     (end-of-line)
1165                     (< (current-column) 80))
1166                   (zerop (forward-line 1))))
1167           (or (bolp)
1168               (eobp)
1169               (y-or-n-p
1170                "You have lines longer than 79 characters.  Really post? "))))
1171     ;; Check whether the article is empty.
1172     (or (message-check-element 'empty)
1173         (save-excursion
1174           (goto-char (point-min))
1175           (re-search-forward
1176            (concat "^" (regexp-quote mail-header-separator) "$"))
1177           (forward-line 1)
1178           (or (re-search-forward "[^ \n\t]" nil t)
1179               (y-or-n-p "Empty article.  Really post?"))))
1180     ;; Check for control characters.
1181     (or (message-check-element 'control-chars)
1182         (save-excursion
1183           (if (re-search-forward "[\000-\007\013\015-\037\200-\237]" nil t)
1184               (y-or-n-p 
1185                "The article contains control characters. Really post? ")
1186             t)))
1187     ;; Check excessive size.
1188     (or (message-check-element 'size)
1189         (if (> (buffer-size) 60000)
1190             (y-or-n-p
1191              (format "The article is %d octets long. Really post? "
1192                      (buffer-size)))
1193           t))
1194     ;; Check the length of the signature.
1195     (or (message-check-element 'signature)
1196         (progn
1197           (goto-char (point-max))
1198           (if (not (re-search-backward "^-- $" nil t))
1199               t
1200             (if (> (count-lines (point) (point-max)) 5)
1201                 (y-or-n-p
1202                  (format
1203                   "Your .sig is %d lines; it should be max 4.  Really post? "
1204                   (count-lines (point) (point-max))))
1205               t)))))))
1206
1207 ;; Returns non-nil if this type is not to be checked.
1208 (defun message-check-element (type)
1209   (not 
1210    (or (not message-syntax-checks)
1211        (if (listp message-syntax-checks)
1212            (memq type message-syntax-checks)
1213          t))))
1214
1215 (defun message-do-fcc ()
1216   "Process Fcc headers in the current buffer."
1217   (let ((case-fold-search t)
1218         list file)
1219     (save-excursion
1220       (save-restriction
1221         (nnheader-narrow-to-headers)
1222         (while (setq file (mail-fetch-field "fcc"))
1223           (push file list)
1224           (message-remove-header "fcc" nil t))
1225         ;; Process FCC operations.
1226         (widen)
1227         (while list
1228           (setq file (pop list))
1229           (if (string-match "^[ \t]*|[ \t]*\\(.*\\)[ \t]*$" file)
1230               ;; Pipe the article to the program in question.
1231               (call-process-region (point-min) (point-max) shell-file-name
1232                                    nil nil nil "-c" (match-string 1 file))
1233             ;; Save the article.
1234             (setq file (expand-file-name file))
1235             (unless (file-exists-p (file-name-directory file))
1236               (make-directory (file-name-directory file) t))
1237             (if (and message-fcc-handler-function
1238                      (not (eq message-fcc-handler-function 'rmail-output)))
1239                 (funcall message-fcc-handler-function file)
1240               (if (and (file-readable-p file) (mail-file-babyl-p file))
1241                   (rmail-output file 1)
1242                 (let ((mail-use-rfc822 t))
1243                   (rmail-output file 1 t t))))))))))
1244
1245 (defun message-cleanup-headers ()
1246   "Do various automatic cleanups of the headers."
1247   ;; Remove empty lines in the header.
1248   (save-restriction
1249     (message-narrow-to-headers)
1250     (while (re-search-forward "^[ \t]*\n" nil t)
1251       (replace-match "" t t)))
1252
1253   ;; Correct Newsgroups and Followup-To headers: change sequence of
1254   ;; spaces to comma and eliminate spaces around commas.  Eliminate
1255   ;; imbedded line breaks.
1256   (goto-char (point-min))
1257   (while (re-search-forward "^\\(Newsgroups\\|Followup-To\\): +" nil t)
1258     (save-restriction
1259       (narrow-to-region
1260        (point)
1261        (if (re-search-forward "^[^ \t]" nil t)
1262            (match-beginning 0)
1263          (forward-line 1)
1264          (point)))
1265       (goto-char (point-min))
1266       (while (re-search-forward "\n[ \t]+" nil t)
1267         (replace-match " " t t))        ;No line breaks (too confusing)
1268       (goto-char (point-min))
1269       (while (re-search-forward "[ \t\n]*,[ \t\n]*\\|[ \t]+" nil t)
1270         (replace-match "," t t))
1271       (goto-char (point-min))
1272       ;; Remove trailing commas.
1273       (when (re-search-forward ",+$" nil t)
1274         (replace-match "" t t)))))
1275
1276 (defun message-make-date ()
1277   "Make a valid data header."
1278   (let ((now (current-time)))
1279     (timezone-make-date-arpa-standard 
1280      (current-time-string now) (current-time-zone now))))
1281
1282 (defun message-make-message-id ()
1283   "Make a unique Message-ID."
1284   (concat "<" (message-unique-id) "@" (message-make-fqdm) ">"))
1285
1286 (defvar message-unique-id-char nil)
1287
1288 ;; If you ever change this function, make sure the new version
1289 ;; cannot generate IDs that the old version could.
1290 ;; You might for example insert a "." somewhere (not next to another dot
1291 ;; or string boundary), or modify the "fsf" string.
1292 (defun message-unique-id ()
1293   ;; Don't use microseconds from (current-time), they may be unsupported.
1294   ;; Instead we use this randomly inited counter.
1295   (setq message-unique-id-char
1296         (% (1+ (or message-unique-id-char (logand (random t) (1- (lsh 1 20)))))
1297            ;; (current-time) returns 16-bit ints,
1298            ;; and 2^16*25 just fits into 4 digits i base 36.
1299            (* 25 25)))
1300   (let ((tm (current-time)))
1301     (concat
1302      (if (memq system-type '(ms-dos emx vax-vms))
1303          (let ((user (downcase (user-login-name))))
1304            (while (string-match "[^a-z0-9_]" user)
1305              (aset user (match-beginning 0) ?_))
1306            user)
1307        (message-number-base36 (user-uid) -1))
1308      (message-number-base36 (+ (car   tm) 
1309                                (lsh (% message-unique-id-char 25) 16)) 4)
1310      (message-number-base36 (+ (nth 1 tm)
1311                                (lsh (/ message-unique-id-char 25) 16)) 4)
1312      ;; Append the newsreader name, because while the generated
1313      ;; ID is unique to this newsreader, other newsreaders might
1314      ;; otherwise generate the same ID via another algorithm.
1315      ".fsf")))
1316
1317 (defun message-number-base36 (num len)
1318   (if (if (< len 0) (<= num 0) (= len 0))
1319       ""
1320     (concat (message-number-base36 (/ num 36) (1- len))
1321             (char-to-string (aref "zyxwvutsrqponmlkjihgfedcba9876543210"
1322                                   (% num 36))))))
1323
1324 (defun message-make-organization ()
1325   "Make an Organization header."
1326   (let* ((organization 
1327           (or (getenv "ORGANIZATION")
1328               (when message-user-organization
1329                 (if (message-functionp message-user-organization)
1330                     (funcall message-user-organization)
1331                   message-user-organization)))))
1332     (save-excursion
1333       (message-set-work-buffer)
1334       (cond ((stringp message-user-organization)
1335              (insert message-user-organization))
1336             ((and (eq t message-user-organization)
1337                   message-user-organization-file
1338                   (file-exists-p message-user-organization-file))
1339              (insert-file-contents message-user-organization-file)))
1340       (goto-char (point-min))
1341       (when (re-search-forward "[ \t\n]*" nil t)
1342         (replace-match "" t t))
1343       (unless (zerop (buffer-size))
1344         (buffer-string)))))
1345
1346 (defun message-make-lines ()
1347   "Count the number of lines and return numeric string."
1348   (save-excursion
1349     (save-restriction
1350       (widen)
1351       (goto-char (point-min))
1352       (re-search-forward 
1353        (concat "^" (regexp-quote message-header-separator) "$"))
1354       (forward-line 1)
1355       (int-to-string (count-lines (point) (point-max))))))
1356
1357 (defun message-make-in-reply-to ()
1358   "Return the In-Reply-To header for this message."
1359   (when message-reply-headers
1360     (let ((from (mail-header-from message-reply-headers))
1361           (date (mail-header-date message-reply-headers)))
1362       (when from
1363         (let ((stop-pos 
1364                (string-match "  *at \\|  *@ \\| *(\\| *<" from)))
1365           (concat (if stop-pos (substring from 0 stop-pos) from)
1366                   "'s message of " 
1367                   (if (or (not date) (string= date ""))
1368                       "(unknown date)" date)))))))
1369
1370 (defun message-make-distribution ()
1371   "Make a Distribution header."
1372   (let ((orig-distribution (message-fetch-reply-field "distribution")))
1373     (cond ((message-functionp message-distribution-function)
1374            (funcall message-distribution-function))
1375           (t orig-distribution))))
1376
1377 (defun message-make-expires ()
1378   "Return an Expires header based on `message-expires'."
1379   (let ((current (current-time))
1380         (future (* 1.0 message-expires 60 60 24)))
1381     ;; Add the future to current.
1382     (setcar current (+ (car current) (round (/ future (expt 2 16)))))
1383     (setcar (cdr current) (+ (nth 1 current) (% (round future) (expt 2 16))))
1384     ;; Return the date in the future in UT.
1385     (timezone-make-date-arpa-standard 
1386      (current-time-string current) (current-time-zone current) '(0 "UT"))))
1387
1388 (defun message-make-path ()
1389   "Return uucp path."
1390   (let ((login-name (user-login-name)))
1391     (cond ((null message-user-path)
1392            (concat (system-name) "!" login-name))
1393           ((stringp message-user-path)
1394            ;; Support GENERICPATH.  Suggested by vixie@decwrl.dec.com.
1395            (concat message-user-path "!" login-name))
1396           (t login-name))))
1397
1398 (defun message-make-from ()
1399   "Make a From header."
1400   (let* ((login (message-make-address))
1401          (fullname (user-full-name)))
1402     (when (string= fullname "&")
1403       (setq fullname (user-login-name)))
1404     (save-excursion
1405       (message-set-work-buffer)
1406       (cond 
1407        ((eq message-from-style 'angles)
1408         (insert fullname)
1409         (goto-char (point-min))
1410         ;; Look for a character that cannot appear unquoted
1411         ;; according to RFC 822.
1412         (when (re-search-forward "[^- !#-'*+/-9=?A-Z^-~]" nil 1)
1413           ;; Quote fullname, escaping specials.
1414           (goto-char (point-min))
1415           (insert "\"")
1416           (while (re-search-forward "[\"\\]" nil 1)
1417             (replace-match "\\\\\\&" t))
1418           (insert "\""))
1419         (insert " <" login ">"))
1420        ((eq message-from-style 'parens)
1421         (insert login " (")
1422         (let ((fullname-start (point)))
1423           (insert fullname)
1424           (goto-char fullname-start)
1425           ;; RFC 822 says \ and nonmatching parentheses
1426           ;; must be escaped in comments.
1427           ;; Escape every instance of ()\ ...
1428           (while (re-search-forward "[()\\]" nil 1)
1429             (replace-match "\\\\\\&" t))
1430           ;; ... then undo escaping of matching parentheses,
1431           ;; including matching nested parentheses.
1432           (goto-char fullname-start)
1433           (while (re-search-forward 
1434                   "\\(\\=\\|[^\\]\\(\\\\\\\\\\)*\\)\\\\(\\(\\([^\\]\\|\\\\\\\\\\)*\\)\\\\)"
1435                     nil 1)
1436             (replace-match "\\1(\\3)" t)
1437             (goto-char fullname-start)))
1438         (insert ")"))
1439        ((null message-from-style)
1440         (insert login "\n")))
1441       (buffer-string))))
1442
1443 (defun message-make-sender ()
1444   "Return the \"real\" user address.
1445 This function tries to ignore all user modifications, and 
1446 give as trustworthy answer as possible."
1447   (concat (user-login-name) "@" (system-name)))
1448
1449 (defun message-make-address ()
1450   "Make the address of the user."
1451   (concat (user-login-name) "@" (message-make-domain)))
1452
1453 (defun message-make-fqdm ()
1454   "Return user's fully qualified domain name."
1455   (let ((system-name (system-name)))
1456     (if (string-match "[^.]\\.[^.]" system-name)
1457         ;; `system-name' returned the right result.
1458         system-name
1459       ;; We try `user-mail-address' as a backup.
1460       (if (string-match "@\\([^ ]+\\)\\($\\| \\)" user-mail-address)
1461           (match-string 1 user-mail-address)
1462         (concat system-name ".i-have-a-misconfigured-system-so-shoot-me")))))
1463
1464 (defun message-make-host-name ()
1465   "Return the name of the host."
1466   (let ((fqdm (message-make-fqdm)))
1467     (string-match "^[^.]+\\." fqdm)
1468     (substring fqdm 0 (1- (match-end 0)))))
1469
1470 (defun message-make-domain ()
1471   "Return the domain name."
1472   (let ((fqdm (message-make-fqdm)))
1473     (if message-generic-domain
1474         (progn
1475           (string-match "^[^.]+\\." fqdm)
1476           (substring fqdm (match-end 0)))
1477       fqdm)))
1478
1479 (defun message-generate-headers (headers)
1480   "Prepare article HEADERS.
1481 Headers already prepared in the buffer are not modified."
1482   (save-restriction
1483     (message-narrow-to-headers)
1484     (let* ((Date (message-make-date))
1485            (Message-ID (message-make-message-id))
1486            (Organization (message-make-organization))
1487            (From (message-make-from))
1488            (Path (message-make-path))
1489            (Subject nil)
1490            (Newsgroups nil)
1491            (In-Reply-To (message-make-in-reply-to))
1492            (To nil)
1493            (Distribution (message-make-distribution))
1494            (Lines (message-make-lines))
1495            (X-Newsreader message-newsreader)
1496            (X-Mailer message-mailer)
1497            (Expires (message-make-expires))
1498            (case-fold-search t)
1499            header value elem)
1500       ;; First we remove any old generated headers.
1501       (let ((headers message-deletable-headers))
1502         (while headers
1503           (goto-char (point-min))
1504           (and (re-search-forward 
1505                 (concat "^" (symbol-name (car headers)) ": *") nil t)
1506                (get-text-property (1+ (match-beginning 0)) 'message-deletable)
1507                (message-delete-line))
1508           (pop headers)))
1509       ;; If there are References, and the subject has changed, then
1510       ;; we have to change the Message-ID.  See Son-of-1036.
1511       (when (and message-reply-headers
1512                  (mail-fetch-field "references"))
1513         (let ((psubject (mail-fetch-field "subject")))
1514           (and psubject (mail-header-subject message-reply-headers)
1515                (string= (message-strip-subject-re
1516                          (mail-header-subject message-reply-headers))
1517                         (message-strip-subject-re
1518                          psubject))
1519                (progn
1520                  (string-match "@" Message-ID)
1521                  (setq Message-ID
1522                        (concat (substring Message-ID 0 (match-beginning 0))
1523                                "_-_" 
1524                                (substring Message-ID (match-beginning 0))))))))
1525       ;; Go through all the required headers and see if they are in the
1526       ;; articles already. If they are not, or are empty, they are
1527       ;; inserted automatically - except for Subject, Newsgroups and
1528       ;; Distribution. 
1529       (while headers
1530         (goto-char (point-min))
1531         (setq elem (pop headers))
1532         (if (consp elem)
1533             (setq header (car elem))
1534           (setq header elem))
1535         (when (or (not (re-search-forward 
1536                         (concat "^" (downcase (symbol-name header)) ":") nil t))
1537                   (progn
1538                     ;; The header was found. We insert a space after the
1539                     ;; colon, if there is none.
1540                     (if (/= (following-char) ? ) (insert " ") (forward-char 1))
1541                     ;; Find out whether the header is empty...
1542                     (looking-at "[ \t]*$")))
1543           ;; So we find out what value we should insert.
1544           (setq value
1545                 (cond 
1546                  ((and (consp elem) (eq (car elem) 'optional))
1547                   ;; This is an optional header.  If the cdr of this
1548                   ;; is something that is nil, then we do not insert
1549                   ;; this header.
1550                   (setq header (cdr elem))
1551                   (or (and (fboundp (cdr elem)) (funcall (cdr elem)))
1552                       (and (boundp (cdr elem)) (symbol-value (cdr elem)))))
1553                  ((consp elem)
1554                   ;; The element is a cons.  Either the cdr is a
1555                   ;; string to be inserted verbatim, or it is a
1556                   ;; function, and we insert the value returned from
1557                   ;; this function.
1558                   (or (and (stringp (cdr elem)) (cdr elem))
1559                       (and (fboundp (cdr elem)) (funcall (cdr elem)))))
1560                  ((and (boundp header) (symbol-value header))
1561                   ;; The element is a symbol.  We insert the value
1562                   ;; of this symbol, if any.
1563                   (symbol-value header))
1564                  (t
1565                   ;; We couldn't generate a value for this header,
1566                   ;; so we just ask the user.
1567                   (read-from-minibuffer
1568                    (format "Empty header for %s; enter value: " header)))))
1569           ;; Finally insert the header.
1570           (when (and value 
1571                      (not (equal value "")))
1572             (save-excursion
1573               (if (bolp)
1574                   (progn
1575                     ;; This header didn't exist, so we insert it.
1576                     (goto-char (point-max))
1577                     (insert (symbol-name header) ": " value "\n")
1578                     (forward-line -1))
1579                 ;; The value of this header was empty, so we clear
1580                 ;; totally and insert the new value.
1581                 (delete-region (point) (message-point-at-eol))
1582                 (insert value))
1583               ;; Add the deletable property to the headers that require it.
1584               (and (memq header message-deletable-headers)
1585                    (progn (beginning-of-line) (looking-at "[^:]+: "))
1586                    (add-text-properties 
1587                     (point) (match-end 0)
1588                     '(message-deletable t face italic) (current-buffer)))))))
1589       ;; Insert new Sender if the From is strange. 
1590       (let ((from (mail-fetch-field "from"))
1591             (sender (mail-fetch-field "sender"))
1592             (secure-sender (message-make-sender)))
1593         (when (and from 
1594                    (not (message-check-element 'sender))
1595                    (not (string=
1596                          (downcase (cadr (mail-extract-address-components from)))
1597                          (downcase secure-sender)))
1598                    (or (null sender)
1599                        (not 
1600                         (string=
1601                          (downcase
1602                           (cadr (mail-extract-address-components sender)))
1603                          (downcase secure-sender)))))
1604           (goto-char (point-min))    
1605           ;; Rename any old Sender headers to Original-Sender.
1606           (when (re-search-forward "^Sender:" nil t)
1607             (beginning-of-line)
1608             (insert "Original-")
1609             (beginning-of-line))
1610           (insert "Sender: " secure-sender "\n"))))))
1611
1612 (defun message-insert-courtesy-copy ()
1613   "Insert a courtesy message in mail copies of combined messages."
1614   (save-excursion
1615     (save-restriction
1616       (message-narrow-to-headers)
1617       (let ((newsgroups (mail-fetch-field "newsgroups")))
1618         (goto-char (point-max))
1619         (insert "Posted-To: " newsgroups "\n")))
1620     (forward-line 1)
1621     (insert message-courtesy-message)))
1622     
1623 ;;;
1624 ;;; Setting up a message buffer
1625 ;;;
1626
1627 (defun message-fill-header (header value)
1628   (let ((begin (point))
1629         (fill-column 78)
1630         (fill-prefix "\t"))
1631     (insert (capitalize (symbol-name header))
1632             ": "
1633             (if (consp value) (car value) value)
1634             "\n")
1635     (fill-region-as-paragraph begin (1- (point)))))
1636
1637 (defun sendmail-synch-aliases ()
1638   (let ((modtime (nth 5 (file-attributes message-personal-alias-file))))
1639     (or (equal message-alias-modtime modtime)
1640         (setq message-alias-modtime modtime
1641               message-aliases t))))
1642
1643 (defun message-position-point ()
1644   "Move point to where the user probably wants to find it."
1645   (message-narrow-to-headers)
1646   (cond 
1647    ((re-search-forward "^[^:]+:[ \t]*$" nil t)
1648     (search-backward ":" )
1649     (widen)
1650     (forward-char 1)
1651     (if (= (following-char) ? )
1652         (forward-char 1)
1653       (insert " ")))
1654    (t
1655     (goto-char (point-max))
1656     (widen)
1657     (forward-line 1)
1658     (unless (looking-at "$")
1659       (forward-line 2)))
1660    (sit-for 0)))
1661
1662 (defun message-pop-to-buffer (name)
1663   "Pop to buffer NAME, and warn if it already exists and is modified."
1664   (let ((buffer (get-buffer name)))
1665     (if (and buffer
1666              (buffer-name buffer))
1667         (progn
1668           (set-buffer (pop-to-buffer buffer))
1669           (when (and (buffer-modified-p)
1670                      (not (y-or-n-p
1671                            "Message already being composed; erase? ")))
1672             (error "Message being composed")))
1673       (set-buffer (pop-to-buffer name)))
1674     (erase-buffer)
1675     (message-mode)))
1676
1677 (defun message-setup (headers &optional replybuffer actions)
1678   (sendmail-synch-aliases)
1679   (when (eq message-aliases t)
1680     (setq message-aliases nil)
1681     (when (file-exists-p message-personal-alias-file)
1682       (build-mail-aliases)))
1683   (setq message-send-actions actions)
1684   (setq message-reply-buffer replybuffer)
1685   (goto-char (point-min))
1686   ;; Insert all the headers.
1687   (mail-header-format 
1688    (let ((h headers)
1689          (alist message-header-format-alist))
1690      (while h
1691        (unless (assq (caar h) message-header-format-alist)
1692          (push (list (caar h)) alist))
1693        (pop h))
1694      alist)
1695    headers)
1696   (when message-default-headers
1697     (insert message-default-headers))
1698   (when (and (message-news-p)
1699              message-default-news-headers)
1700     (when message-generate-headers-first
1701       (message-generate-headers message-required-news-headers))
1702     (insert message-default-news-headers))
1703   (when (and (message-mail-p)
1704              message-default-mail-headers)
1705     (when message-generate-headers-first
1706       (message-generate-headers message-required-mail-headers))
1707     (insert message-default-mail-headers))
1708   (forward-line -1)
1709   (insert message-header-separator "\n")
1710   (message-insert-signature)
1711   (message-set-auto-save-file-name)
1712   (save-restriction
1713     (message-narrow-to-headers)
1714     (run-hooks 'message-header-setup-hook))
1715   (set-buffer-modified-p nil)
1716   (run-hooks 'message-setup-hook)
1717   (message-position-point))
1718
1719 (defun message-set-auto-save-file-name ()
1720   "Associate the message buffer with a file in the drafts directory."
1721   (when message-autosave-directory
1722     (unless (file-exists-p message-autosave-directory)
1723       (make-directory message-autosave-directory t))
1724     (let ((name (make-temp-name
1725                  (concat (file-name-as-directory message-autosave-directory)
1726                          "msg."))))
1727       (setq buffer-auto-save-file-name
1728             (save-excursion
1729               (prog1
1730                   (progn
1731                     (set-buffer (get-buffer-create " *draft tmp*"))
1732                     (setq buffer-file-name name)
1733                     (make-auto-save-file-name))
1734                 (kill-buffer (current-buffer)))))
1735       (clear-visited-file-modtime))))
1736
1737 \f
1738
1739 ;;;
1740 ;;; Commands for interfacing with message
1741 ;;;
1742
1743 ;;;###autoload
1744 (defun message-mail (&optional to subject)
1745   "Start editing a mail message to be sent."
1746   (interactive)
1747   (message-pop-to-buffer "*mail message*")
1748   (message-setup `((To . ,(or to "")) (Subject . ,(or subject "")))))
1749
1750 ;;;###autoload
1751 (defun message-news (&optional newsgroups subject)
1752   "Start editing a news article to be sent."
1753   (interactive)
1754   (message-pop-to-buffer "*news message*")
1755   (message-setup `((Newsgroups . ,(or newsgroups "")) 
1756                    (Subject . ,(or subject "")))))
1757
1758 ;;;###autoload
1759 (defun message-reply (&optional to-address wide)
1760   "Start editing a reply to the article in the current buffer."
1761   (interactive)
1762   (let ((cur (current-buffer))
1763         from subject date reply-to message-of to cc
1764         references message-id sender follow-to sendto elt new-cc new-to
1765         mct never-mct gnus-warning)
1766     (save-restriction
1767       (narrow-to-region
1768        (goto-char (point-min))
1769        (if (search-forward "\n\n" nil t)
1770            (1- (point))
1771          (point-max)))
1772       ;; Allow customizations to have their say.
1773       (if (not wide)
1774           ;; This is a regular reply.
1775           (if (message-functionp message-reply-to-function)
1776               (setq follow-to (funcall message-reply-to-function)))
1777         ;; This is a followup.
1778         (if (gnus-functionp message-wide-reply-to-function)
1779             (save-excursion
1780               (setq follow-to
1781                     (funcall message-wide-reply-to-function)))))
1782       ;; Find all relevant headers we need.
1783       (setq from (mail-fetch-field "from")
1784             date (mail-fetch-field "date") 
1785             sender (mail-fetch-field "sender")
1786             subject (or (mail-fetch-field "subject") "none")
1787             to (mail-fetch-field "to")
1788             cc (mail-fetch-field "cc")
1789             mct (mail-fetch-field "mail-copies-to")
1790             reply-to (mail-fetch-field "reply-to")
1791             references (mail-fetch-field "references")
1792             message-id (mail-fetch-field "message-id"))
1793       ;; Remove any (buggy) Re:'s that are present and make a
1794       ;; proper one.
1795       (when (string-match "^[ \t]*[Re][Ee]:[ \t]*" subject)
1796         (setq subject (substring subject (match-end 0))))
1797       (setq subject (concat "Re: " subject))
1798
1799       (when (and (setq gnus-warning (mail-fetch-field "gnus-warning"))
1800                  (string-match "<[^>]+>" gnus-warning))
1801         (setq message-id (match-string 0 gnus-warning)))
1802             
1803       ;; Handle special values of Mail-Copies-To.
1804       (when mct
1805         (cond ((equal (downcase mct) "never")
1806                (setq never-mct t)
1807                (setq mct nil))
1808               ((equal (downcase mct) "always")
1809                (setq mct (or reply-to from)))))
1810
1811       (unless follow-to
1812         (if (or (not wide)
1813                 to-address)
1814             (setq follow-to (list (cons 'To (or to-address reply-to from))))
1815           (let (ccalist)
1816             (save-excursion
1817               (message-set-work-buffer)
1818               (unless never-mct
1819                 (insert (or reply-to from "")))
1820               (insert 
1821                (if (bolp) "" ", ") (or to "")
1822                (if mct (concat (if (bolp) "" ", ") mct) "")
1823                (if cc (concat (if (bolp) "" ", ") cc) ""))
1824               ;; Remove addresses that match `rmail-dont-reply-to-names'. 
1825               (insert (prog1 (rmail-dont-reply-to (buffer-string))
1826                         (erase-buffer)))
1827               (goto-char (point-min))
1828               (setq ccalist
1829                     (mapcar
1830                      (lambda (addr)
1831                        (cons (mail-strip-quoted-names addr) addr))
1832                      (nreverse (mail-parse-comma-list))))
1833               (let ((s ccalist))
1834                 (while s
1835                   (setq ccalist (delq (assoc (car (pop s)) s) ccalist)))))
1836             (setq follow-to (list (cons 'To (cdr (pop ccalist)))))
1837             (when ccalist
1838               (push (cons 'Cc
1839                           (mapconcat (lambda (addr) (cdr addr)) ccalist ", "))
1840                     follow-to)))))
1841       (widen))
1842
1843     (message-pop-to-buffer "*mail message*")
1844
1845     (setq message-reply-headers
1846           (vector 0 subject from date message-id references 0 0 ""))
1847
1848     (message-setup
1849      `((Subject . ,subject)
1850        ,@follow-to 
1851        (References . ,(concat (or references "") (and references " ")
1852                               (or message-id ""))))
1853      cur)))
1854
1855 ;;;###autoload
1856 (defun message-wide-reply (&optional to-address)
1857   (interactive)
1858   (message-reply to-address t))
1859
1860 ;;;###autoload
1861 (defun message-followup ()
1862   (interactive)
1863   (let ((cur (current-buffer))
1864         from subject date message-of reply-to mct
1865         references message-id follow-to sendto elt 
1866         followup-to distribution newsgroups gnus-warning)
1867     (save-restriction
1868       (narrow-to-region
1869        (goto-char (point-min))
1870        (if (search-forward "\n\n" nil t)
1871            (1- (point))
1872          (point-max)))
1873       (when (message-functionp message-followup-to-function)
1874         (setq follow-to
1875               (funcall message-followup-to-function)))
1876       (setq from (mail-fetch-field "from")
1877             date (mail-fetch-field "date") 
1878             subject (or (mail-fetch-field "subject") "none")
1879             references (mail-fetch-field "references")
1880             message-id (mail-fetch-field "message-id")
1881             followup-to (mail-fetch-field "followup-to")
1882             newsgroups (mail-fetch-field "newsgroups")
1883             reply-to (mail-fetch-field "reply-to")
1884             distribution (mail-fetch-field "distribution")
1885             mct (mail-fetch-field "mail-copies-to"))
1886       (when (and (setq gnus-warning (mail-fetch-field "gnus-warning"))
1887                  (string-match "<[^>]+>" gnus-warning))
1888         (setq message-id (match-string 0 gnus-warning)))
1889       ;; Remove bogus distribution.
1890       (and (stringp distribution)
1891            (string-match "world" distribution)
1892            (setq distribution nil))
1893       ;; Remove any (buggy) Re:'s that are present and make a
1894       ;; proper one.
1895       (when (string-match "^[ \t]*[Re][Ee]:[ \t]*" subject)
1896         (setq subject (substring subject (match-end 0))))
1897       (setq subject (concat "Re: " subject))
1898       (widen))
1899
1900     (message-pop-to-buffer "*news message*")
1901
1902     (message-setup
1903      `((Subject . ,subject)
1904        ,@(cond 
1905           (follow-to follow-to)
1906           ((and followup-to message-use-followup-to)
1907            (list
1908             (cond 
1909              ((equal (downcase followup-to) "poster")
1910               (if (or (eq message-use-followup-to 'use)
1911                       (y-or-n-p "Use Followup-To \"poster\"? "))
1912                   (cons 'To (or reply-to from ""))
1913                 (cons 'Newsgroups newsgroups)))
1914              (t
1915               (if (or (equal followup-to newsgroups)
1916                       (not (eq message-use-followup-to 'ask))
1917                       (y-or-n-p (format "Use Followup-To %s? " followup-to)))
1918                   (cons 'Newsgroups followup-to)
1919                 (cons 'Newsgroups newsgroups))))))
1920           (t
1921            `((Newsgroups . ,newsgroups))))
1922        ,@(and distribution (list (cons 'Distribution distribution)))
1923        (References . ,(concat (or references "") (and references " ")
1924                               (or message-id "")))
1925        ,@(when (and mct
1926                     (not (equal (downcase mct) "never")))
1927            (list (cons 'Cc (if (equal (downcase mct) "always")
1928                                (or reply-to from "")
1929                              mct)))))
1930      cur)))
1931
1932 ;;;###autoload
1933 (defun message-cancel-news ()
1934   "Cancel an article you posted."
1935   (interactive)
1936   (unless (message-news-p)
1937     (error "This is not a news article; canceling is impossible"))
1938   (when (yes-or-no-p "Do you really want to cancel this article? "))
1939   (let (from newsgroups message-id distribution buf)
1940     (save-excursion
1941       ;; Get header info. from original article.
1942       (save-restriction
1943         (message-narrow-to-head)
1944         (setq from (mail-fetch-field "from")
1945               newsgroups (mail-fetch-field "newsgroups")
1946               message-id (mail-fetch-field "message-id")
1947               distribution (mail-fetch-field "distribution")))
1948       ;; Make sure that this article was written by the user.
1949       (unless (string-equal
1950                (downcase (mail-strip-quoted-names from))
1951                (downcase (message-make-address)))
1952         (error "This article is not yours"))
1953       ;; Make control message.
1954       (setq buf (set-buffer (get-buffer-create " *message cancel*")))
1955       (buffer-disable-undo (current-buffer))
1956       (erase-buffer)
1957       (insert "Newsgroups: " newsgroups "\n"
1958               "From: " (message-make-from) "\n"
1959               "Subject: cmsg cancel " message-id "\n"
1960               "Control: cancel " message-id "\n"
1961               (if distribution
1962                   (concat "Distribution: " distribution "\n")
1963                 "")
1964               message-header-separator "\n"
1965               "This is a cancel message from " from ".\n")
1966       (message "Canceling your article...")
1967       (funcall message-send-news-function)
1968       (message "Canceling your article...done")
1969       (kill-buffer buf))))
1970
1971 ;;;###autoload
1972 (defun message-supersede ()
1973   "Start composing a message to supersede the current message.
1974 This is done simply by taking the old article and adding a Supersedes
1975 header line with the old Message-ID."
1976   (interactive)
1977   (let ((cur (current-buffer)))
1978     ;; Check whether the user owns the article that is to be superseded. 
1979     (unless (string-equal
1980              (downcase (mail-strip-quoted-names (mail-fetch-field "from")))
1981              (downcase (mail-strip-quoted-names (message-make-address))))
1982       (error "This article is not yours"))
1983     ;; Get a normal message buffer.
1984     (message-pop-to-buffer "*supersede message*")
1985     (insert-buffer-substring cur)
1986     (message-narrow-to-head)
1987     ;; Remove unwanted headers.
1988     (when message-ignored-supersedes-headers
1989       (message-remove-header message-ignored-supersedes-headers t))
1990     (goto-char (point-min))
1991     (if (not (re-search-forward "^Message-ID: " nil t))
1992         (error "No Message-ID in this article")
1993       (replace-match "Supersedes: " t t))
1994     (goto-char (point-max))
1995     (insert message-header-separator)
1996     (widen)
1997     (forward-line 1)))
1998
1999 ;;;###autoload
2000 (defun message-recover ()
2001   "Reread contents of current buffer from its last auto-save file."
2002   (interactive)
2003   (let ((file-name (make-auto-save-file-name)))
2004     (cond ((save-window-excursion
2005              (if (not (eq system-type 'vax-vms))
2006                  (with-output-to-temp-buffer "*Directory*"
2007                    (buffer-disable-undo standard-output)
2008                    (let ((default-directory "/"))
2009                      (call-process
2010                       "ls" nil standard-output nil "-l" file-name))))
2011              (yes-or-no-p (format "Recover auto save file %s? " file-name)))
2012            (let ((buffer-read-only nil))
2013              (erase-buffer)
2014              (insert-file-contents file-name nil)))
2015           (t (error "message-recover cancelled")))))
2016
2017 ;;; Forwarding messages.
2018
2019 (defun message-make-forward-subject ()
2020   "Return a Subject header suitable for the message in the current buffer."
2021   (concat "[" (mail-fetch-field (if (message-news-p) "newsgroups" "from"))
2022           "] " (or (mail-fetch-field "Subject") "")))
2023
2024 ;;;###autoload
2025 (defun message-forward (&optional news)
2026   (interactive "P")
2027   (let ((cur (current-buffer))
2028         (subject (message-make-forward-subject)))
2029     (if news (message-news nil subject) (message-mail nil subject))
2030     ;; Put point where we want it before inserting the forwarded
2031     ;; message. 
2032     (if message-signature-before-forwarded-message
2033         (goto-char (point-max))
2034       (message-goto-body))
2035     ;; Narrow to the area we are to insert.
2036     (narrow-to-region (point) (point))
2037     ;; Insert the separators and the forwarded buffer.
2038     (insert message-forward-start-separator)
2039     (insert-buffer-substring cur)
2040     (goto-char (point-max))
2041     (insert message-forward-end-separator)
2042     (set-text-properties (point-min) (point-max) nil)
2043     ;; Remove all unwanted headers.
2044     (goto-char (point-min))
2045     (forward-line 1)
2046     (narrow-to-region (point) (if (search-forward "\n\n" nil t)
2047                                   (1- (point))
2048                                 (point)))
2049     (goto-char (point-min))
2050     (message-remove-header message-included-forward-headers t)
2051     (widen)
2052     (message-position-point)))
2053
2054 ;;;###autoload
2055 (defun message-resend (address)
2056   "Resend the current article to ADDRESS."
2057   (interactive "sResend message to: ")
2058   (save-excursion
2059     (let ((cur (current-buffer))
2060           beg)
2061       ;; We first set up a normal mail buffer.
2062       (message-set-work-buffer)
2063       (message-setup `((To . ,address)))
2064       ;; Insert our usual headers.
2065       (message-narrow-to-headers)
2066       (message-generate-headers '(From Date To))
2067       (goto-char (point-min))
2068       ;; Rename them all to "Resent-*".
2069       (while (re-search-forward "^[A-Za-z]" nil t)
2070         (forward-char -1)
2071         (insert "Resent-"))
2072       (widen)
2073       (forward-line)
2074       (delete-region (point) (point-max))
2075       (setq beg (point))
2076       ;; Insert the message to be resent.
2077       (insert-buffer-substring cur)
2078       (goto-char (point-min))
2079       (search-forward "\n\n")
2080       (forward-char -1)
2081       (save-restriction
2082         (narrow-to-region beg (point))
2083         (message-remove-header message-ignored-resent-headers t)
2084         (goto-char (point-max)))
2085       (insert mail-header-separator)
2086       ;; Rename all old ("Also-")Resent headers.
2087       (while (re-search-backward "^\\(Also-\\)?Resent-" beg t)
2088         (beginning-of-line)
2089         (insert "Also-"))
2090       ;; Send it.
2091       (funcall message-send-mail-function))))
2092
2093 ;;;###autoload
2094 (defun message-bounce ()
2095   "Re-mail the current message.
2096 This only makes sense if the current message is a bounce message than
2097 contains some mail you have written which has been bounced back to
2098 you."
2099   (interactive "P")
2100   (let ((cur (current-buffer)))
2101     (message-pop-to-buffer "*mail message*")
2102     (insert-buffer-substring cur)
2103     (goto-char (point-min))
2104     (or (and (re-search-forward mail-unsent-separator nil t)
2105              (forward-line 1))
2106         (and (search-forward "\n\n" nil t)
2107              (re-search-forward "^Return-Path:.*\n" nil t)))
2108     ;; We remove everything before the bounced mail.
2109     (delete-region 
2110      (point-min)
2111      (if (re-search-forward "[^ \t]*:" nil t)
2112          (match-beginning 0)
2113        (point)))
2114     (save-restriction
2115       (message-narrow-to-head)
2116       (message-remove-header message-ignored-bounced-headers t)
2117       (goto-char (point-max))
2118       (insert mail-header-separator))
2119     (message-position-point)))
2120
2121 (provide 'message)
2122
2123 ;;; message.el ends here