*** empty log message ***
[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 'mailheader)
35 (require 'nnheader)
36 (require 'timezone)
37 (require 'easymenu)
38 (if (string-match "XEmacs\\|Lucid" emacs-version)
39     (require 'mail-abbrevs)
40   (require 'mailabbrev))
41
42 ;;;###autoload
43 (defvar message-directory "~/Mail/"
44   "*Directory from which all other mail file variables are derived.")
45
46 (defvar message-max-buffers 10
47   "*How many buffers to keep before starting to kill them off.")
48
49 ;;;###autoload
50 (defvar message-fcc-handler-function 'rmail-output
51   "*A function called to save outgoing articles.
52 This function will be called with the name of the file to store the
53 article in. The default function is `rmail-output' which saves in Unix
54 mailbox format.")
55
56 ;;;###autoload
57 (defvar message-courtesy-message
58   "The following message is a courtesy copy of an article\nthat has been posted as well.\n\n"
59   "*This is inserted at the start of a mailed copy of a posted message.
60 If this variable is nil, no such courtesy message will be added.")
61
62 ;;;###autoload
63 (defvar message-ignored-bounced-headers "^\\(Received\\|Return-Path\\):"
64   "*Regexp that matches headers to be removed in resent bounced mail.")
65
66 ;;;###autoload
67 (defvar message-from-style 'default
68   "*Specifies how \"From\" headers look.
69
70 If `nil', they contain just the return address like:
71         king@grassland.com
72 If `parens', they look like:
73         king@grassland.com (Elvis Parsley)
74 If `angles', they look like:
75         Elvis Parsley <king@grassland.com>
76
77 Otherwise, most addresses look like `angles', but they look like
78 `parens' if `angles' would need quoting and `parens' would not.")
79
80 ;;;###autoload
81 (defvar message-syntax-checks nil
82   "Controls what syntax checks should not be performed on outgoing posts.
83 To disable checking of long signatures, for instance, add
84  `(signature . disabled)' to this list.
85
86 Don't touch this variable unless you really know what you're doing.
87
88 Checks include subject-cmsg multiple-headers sendsys message-id from
89 long-lines control-chars size new-text redirected-followup signature
90 approved sender empty empty-headers message-id from subject.")
91
92 ;;;###autoload
93 (defvar message-required-news-headers
94   '(From Newsgroups Subject Date Message-ID 
95          (optional . Organization) Lines 
96          (optional . X-Newsreader))
97   "*Headers to be generated or prompted for when posting an article.
98 RFC977 and RFC1036 require From, Date, Newsgroups, Subject,
99 Message-ID.  Organization, Lines, In-Reply-To, Expires, and
100 X-Newsreader are optional.  If don't you want message to insert some
101 header, remove it from this list.")
102
103 ;;;###autoload
104 (defvar message-required-mail-headers 
105   '(From Subject Date (optional . In-Reply-To) Message-ID Lines
106          (optional . X-Mailer))
107   "*Headers to be generated or prompted for when mailing a message.
108 RFC822 required that From, Date, To, Subject and Message-ID be
109 included.  Organization, Lines and X-Mailer are optional.")
110
111 ;;;###autoload
112 (defvar message-deletable-headers '(Message-ID Date)
113   "*Headers to be deleted if they already exist and were generated by message previously.")
114
115 ;;;###autoload
116 (defvar message-ignored-news-headers 
117   "^NNTP-Posting-Host:\\|^Xref:\\|^Bcc:\\|^Gcc:\\|^Fcc:"
118   "*Regexp of headers to be removed unconditionally before posting.")
119
120 ;;;###autoload
121 (defvar message-ignored-mail-headers "^Gcc:\\|^Fcc:"
122   "*Regexp of headers to be removed unconditionally before mailing.")
123
124 ;;;###autoload
125 (defvar message-ignored-supersedes-headers
126   "^Path:\\|^Date\\|^NNTP-Posting-Host:\\|^Xref:\\|^Lines:\\|^Received:\\|^X-From-Line:\\|Return-Path:"
127   "*Header lines matching this regexp will be deleted before posting.
128 It's best to delete old Path and Date headers before posting to avoid
129 any confusion.")
130
131 ;;;###autoload
132 (defvar message-signature-separator "^-- *$"
133   "Regexp matching the signature separator.")
134
135 ;;;###autoload
136 (defvar message-interactive nil 
137   "Non-nil means when sending a message wait for and display errors.
138 nil means let mailer mail back a message to report errors.")
139
140 ;;;###autoload
141 (defvar message-generate-new-buffers t
142   "*Non-nil means that a new message buffer will be created whenever `mail-setup' is called.")
143
144 ;;;###autoload
145 (defvar message-kill-buffer-on-exit nil
146   "*Non-nil means that the message buffer will be killed after sending a message.")
147
148 (defvar gnus-local-organization)
149 ;;;###autoload
150 (defvar message-user-organization 
151   (or (and (boundp 'gnus-local-organization)
152            gnus-local-organization)
153       (getenv "ORGANIZATION")
154       t)
155   "*String to be used as an Organization header.
156 If t, use `message-user-organization-file'.")
157
158 ;;;###autoload
159 (defvar message-user-organization-file "/usr/lib/news/organization"
160   "*Local news organization file.")
161
162 ;;;###autoload
163 (defvar message-autosave-directory
164   (concat (file-name-as-directory message-directory) "drafts/")
165   "*Directory where message autosaves buffers.
166 If nil, message won't autosave.")
167
168 (defvar message-forward-start-separator 
169   "------- Start of forwarded message -------\n"
170   "*Delimiter inserted before forwarded messages.")
171
172 (defvar message-forward-end-separator
173   "------- End of forwarded message -------\n"
174   "*Delimiter inserted after forwarded messages.")
175
176 ;;;###autoload
177 (defvar message-signature-before-forwarded-message t
178   "*If non-nil, put the signature before any included forwarded message.")
179
180 ;;;###autoload
181 (defvar message-included-forward-headers 
182   "^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:"
183   "*Regexp matching headers to be included in forwarded messages.")
184
185 ;;;###autoload
186 (defvar message-ignored-resent-headers "^Return-receipt"
187   "*All headers that match this regexp will be deleted when resending a message.")
188
189 ;;;###autoload
190 (defvar message-ignored-cited-headers "."
191   "Delete these headers from the messages you yank.")
192
193 ;; Useful to set in site-init.el
194 ;;;###autoload
195 (defvar message-send-mail-function 'message-send-mail-with-sendmail
196   "Function to call to send the current buffer as mail.
197 The headers should be delimited by a line whose contents match the
198 variable `mail-header-separator'.
199
200 Legal values include `message-send-mail-with-mh' and
201 `message-send-mail-with-sendmail', which is the default.")
202
203 ;;;###autoload
204 (defvar message-send-news-function 'message-send-news
205   "Function to call to send the current buffer as news.
206 The headers should be delimited by a line whose contents match the
207 variable `mail-header-separator'.")
208
209 ;;;###autoload
210 (defvar message-reply-to-function nil
211   "Function that should return a list of headers.
212 This function should pick out addresses from the To, Cc, and From headers
213 and respond with new To and Cc headers.")
214
215 ;;;###autoload
216 (defvar message-wide-reply-to-function nil
217   "Function that should return a list of headers.
218 This function should pick out addresses from the To, Cc, and From headers
219 and respond with new To and Cc headers.")
220
221 ;;;###autoload
222 (defvar message-followup-to-function nil
223   "Function that should return a list of headers.
224 This function should pick out addresses from the To, Cc, and From headers
225 and respond with new To and Cc headers.")
226
227 ;;;###autoload
228 (defvar message-use-followup-to 'ask
229   "*Specifies what to do with Followup-To header.
230 If nil, ignore the header. If it is t, use its value, but query before
231 using the \"poster\" value.  If it is the symbol `ask', query the user
232 whether to ignore the \"poster\" value.  If it is the symbol `use',
233 always use the value.")
234
235 (defvar gnus-post-method)
236 (defvar gnus-select-method)
237 ;;;###autoload
238 (defvar message-post-method 
239   (cond ((and (boundp 'gnus-post-method)
240               gnus-post-method)
241          gnus-post-method)
242         ((boundp 'gnus-select-method)
243          gnus-select-method)
244         (t '(nnspool "")))
245   "Method used to post news.")
246
247 ;;;###autoload
248 (defvar message-generate-headers-first nil
249   "*If non-nil, generate all possible headers before composing.")
250
251 (defvar message-setup-hook nil
252   "Normal hook, run each time a new outgoing message is initialized.
253 The function `message-setup' runs this hook.")
254
255 (defvar message-mode-hook nil
256   "Hook run in message mode buffers.")
257
258 (defvar message-header-hook nil
259   "Hook run in a message mode buffer narrowed to the headers.")
260
261 (defvar message-header-setup-hook nil
262   "Hook called narrowed to the headers when setting up a message buffer.")
263
264 ;;;###autoload
265 (defvar message-citation-line-function 'message-insert-citation-line
266   "*Function called to insert the \"Whomever writes:\" line.")
267
268 ;;;###autoload
269 (defvar message-yank-prefix "> "
270   "*Prefix inserted on the lines of yanked messages.
271 nil means use indentation.")
272
273 (defvar message-indentation-spaces 3
274   "*Number of spaces to insert at the beginning of each cited line.
275 Used by `message-yank-original' via `message-yank-cite'.")
276
277 ;;;###autoload
278 (defvar message-cite-function 'message-cite-original
279   "*Function for citing an original message.")
280
281 ;;;###autoload
282 (defvar message-indent-citation-function 'message-indent-citation
283   "*Function for modifying a citation just inserted in the mail buffer.
284 This can also be a list of functions.  Each function can find the
285 citation between (point) and (mark t).  And each function should leave
286 point and mark around the citation text as modified.")
287
288 (defvar message-abbrevs-loaded nil)
289
290 ;;;###autoload
291 (defvar message-signature t
292   "*String to be inserted at the end of the message buffer.
293 If t, the `message-signature-file' file will be inserted instead.
294 If a function, the result from the function will be used instead.
295 If a form, the result from the form will be used instead.")
296
297 ;;;###autoload
298 (defvar message-signature-file "~/.signature"
299   "*File containing the text inserted at end of message. buffer.")
300
301 (defvar message-distribution-function nil
302   "*Function called to return a Distribution header.")
303
304 (defvar message-expires 14
305   "*Number of days before your article expires.")
306
307 (defvar message-user-path nil
308   "If nil, use the NNTP server name in the Path header.
309 If stringp, use this; if non-nil, use no host name (user name only).")
310
311 (defvar message-reply-buffer nil)
312 (defvar message-reply-headers nil)
313 (defvar message-newsreader nil)
314 (defvar message-mailer nil)
315 (defvar message-sent-message-via nil)
316 (defvar message-checksum nil)
317 (defvar message-send-actions nil
318   "A list of actions to be performed upon successful sending of a message.")
319 (defvar message-exit-actions nil
320   "A list of actions to be performed upon exiting after sending a message.")
321 (defvar message-kill-actions nil
322   "A list of actions to be performed before killing a message buffer.")
323 (defvar message-postpone-actions nil
324   "A list of actions to be performed after postponing a message.")
325
326 ;;;###autoload
327 (defvar message-default-headers nil
328   "*A string containing header lines to be inserted in outgoing messages.
329 It is inserted before you edit the message, so you can edit or delete
330 these lines.")
331
332 ;;;###autoload
333 (defvar message-default-mail-headers nil
334   "*A string of header lines to be inserted in outgoing mails.")
335
336 ;;;###autoload
337 (defvar message-default-news-headers nil
338   "*A string of header lines to be inserted in outgoing news articles.")
339
340 ;; Note: could use /usr/ucb/mail instead of sendmail;
341 ;; options -t, and -v if not interactive.
342 (defvar message-mailer-swallows-blank-line
343   (if (and (string-match "sparc-sun-sunos\\(\\'\\|[^5]\\)" 
344                          system-configuration)
345            (file-readable-p "/etc/sendmail.cf")
346            (let ((buffer (get-buffer-create " *temp*")))
347              (unwind-protect
348                  (save-excursion
349                    (set-buffer buffer)
350                    (insert-file-contents "/etc/sendmail.cf")
351                    (goto-char (point-min))
352                    (let ((case-fold-search nil))
353                      (re-search-forward "^OR\\>" nil t)))
354                (kill-buffer buffer))))
355       ;; According to RFC822, "The field-name must be composed of printable
356       ;; ASCII characters (i.e. characters that have decimal values between
357       ;; 33 and 126, except colon)", i.e. any chars except ctl chars,
358       ;; space, or colon.
359       '(looking-at "[ \t]\\|[][!\"#$%&'()*+,-./0-9;<=>?@A-Z\\\\^_`a-z{|}~]+:"))
360   "Set this non-nil if the system's mailer runs the header and body together.
361 \(This problem exists on Sunos 4 when sendmail is run in remote mode.)
362 The value should be an expression to test whether the problem will
363 actually occur.")
364
365 (defvar message-mode-syntax-table 
366   (let ((table (copy-syntax-table text-mode-syntax-table)))
367     (modify-syntax-entry ?% ". " table)
368     table)
369   "Syntax table used while in Message mode.")
370
371 (defvar message-font-lock-keywords
372   (let* ((cite-prefix "A-Za-z") (cite-suffix (concat cite-prefix "0-9_.@-")))
373     (list '("^To:" . font-lock-function-name-face)
374           '("^[GBF]?[Cc][Cc]:\\|^Reply-To:" . font-lock-keyword-face)
375           '("^\\(Subject:\\)[ \t]*\\(.+\\)?"
376             (1 font-lock-comment-face) (2 font-lock-type-face nil t))
377           (list (concat "^\\(" (regexp-quote mail-header-separator) "\\)$")
378                 1 'font-lock-comment-face)
379           (cons (concat "^[ \t]*"
380                         "\\([" cite-prefix "]+[" cite-suffix "]*\\)?"
381                         "[>|}].*")
382                 'font-lock-reference-face)
383           '("^\\(X-[A-Za-z0-9-]+\\|In-reply-to\\):.*"
384             . font-lock-string-face)))
385   "Additional expressions to highlight in Message mode.")
386
387 (defvar message-face-alist
388   '((bold . bold-region)
389     (underline . underline-region)
390     (default . (lambda (b e) 
391                  (unbold-region b e)
392                  (ununderline-region b e))))
393   "Alist of mail and news faces for facemenu.
394 The cdr of ech entry is a function for applying the face to a region.")
395
396 (defvar message-send-hook nil
397   "Hook run before sending messages.")
398
399 (defvar message-sent-hook nil
400   "Hook run after sending messages.")
401
402 ;;; Internal variables.
403
404 (defvar message-buffer-list nil)
405
406 ;;; Regexp matching the delimiter of messages in UNIX mail format
407 ;;; (UNIX From lines), minus the initial ^.  
408 (defvar message-unix-mail-delimiter
409   (let ((time-zone-regexp
410          (concat "\\([A-Z]?[A-Z]?[A-Z][A-Z]\\( DST\\)?"
411                  "\\|[-+]?[0-9][0-9][0-9][0-9]"
412                  "\\|"
413                  "\\) *")))
414     (concat
415      "From "
416
417      ;; Username, perhaps with a quoted section that can contain spaces.
418      "\\("
419      "[^ \n]*"
420      "\\(\\|\".*\"[^ \n]*\\)"
421      "\\|<[^<>\n]+>"
422      "\\)  ?"
423
424      ;; The time the message was sent.
425      "\\([^ \n]*\\) *"                  ; day of the week
426      "\\([^ ]*\\) *"                    ; month
427      "\\([0-9]*\\) *"                   ; day of month
428      "\\([0-9:]*\\) *"                  ; time of day
429
430      ;; Perhaps a time zone, specified by an abbreviation, or by a
431      ;; numeric offset.
432      time-zone-regexp
433
434      ;; The year.
435      " [0-9][0-9]\\([0-9]*\\) *"
436
437      ;; On some systems the time zone can appear after the year, too.
438      time-zone-regexp
439
440      ;; Old uucp cruft.
441      "\\(remote from .*\\)?"
442
443      "\n")))
444
445 (defvar message-unsent-separator
446   (concat "^ *---+ +Unsent message follows +---+ *$\\|"
447           "^ *---+ +Returned message +---+ *$\\|"
448           "^Start of returned message$\\|"
449           "^ *---+ +Original message +---+ *$\\|"
450           "^ *--+ +begin message +--+ *$\\|"
451           "^ *---+ +Original message follows +---+ *$\\|"
452           "^|? *---+ +Message text follows: +---+ *|?$")
453   "A regexp that matches the separator before the text of a failed message.")
454
455 (defvar message-header-format-alist 
456   `((Newsgroups)
457     (To . message-fill-header) 
458     (Cc . message-fill-header)
459     (Subject)
460     (In-Reply-To)
461     (Fcc)
462     (Bcc)
463     (Date)
464     (Organization)
465     (Distribution)
466     (Lines)
467     (Expires)
468     (Message-ID)
469     (References . message-fill-header)
470     (X-Mailer)
471     (X-Newsreader))
472   "Alist used for formatting headers.")
473
474 (eval-and-compile
475   (autoload 'message-setup-toolbar "messagexmas")
476   (autoload 'mh-send-letter "mh-comp"))
477
478 \f
479
480 ;;; 
481 ;;; Utility functions.
482 ;;;
483
484 (defun message-point-at-bol ()
485   "Return point at the beginning of the line."
486   (let ((p (point)))
487     (beginning-of-line)
488     (prog1
489         (point)
490       (goto-char p))))
491
492 (defun message-point-at-eol ()
493   "Return point at the end of the line."
494   (let ((p (point)))
495     (end-of-line)
496     (prog1
497         (point)
498       (goto-char p))))
499
500 ;; Delete the current line (and the next N lines.);
501 (defmacro message-delete-line (&optional n)
502   `(delete-region (progn (beginning-of-line) (point))
503                   (progn (forward-line ,(or n 1)) (point))))
504
505 (defun message-tokenize-header (header &optional separator)
506   "Split HEADER into a list of header elements.
507 \",\" is used as the separator."
508   (let* ((beg 0)
509          (separator (or separator ","))
510          (regexp
511           (format "[ \t]*\\([^%s]+\\)?\\([%s]+\\|\\'\\)" separator separator))
512          elems)
513     (while (and (string-match regexp header beg)
514                 (< beg (length header)))
515       (when (match-beginning 1)
516         (push (match-string 1 header) elems))
517       (setq beg (match-end 0)))
518     (nreverse elems)))
519
520 (defun message-fetch-field (header)
521   "The same as `mail-fetch-field', only remove all newlines."
522   (let ((value (mail-fetch-field header)))
523     (when value
524       (nnheader-replace-chars-in-string value ?\n ? ))))
525
526 (defun message-fetch-reply-field (header)
527   "Fetch FIELD from the message we're replying to."
528   (when (and message-reply-buffer
529              (buffer-name message-reply-buffer))
530     (save-excursion
531       (set-buffer message-reply-buffer)
532       (message-fetch-field header))))
533
534 (defun message-set-work-buffer ()
535   (if (get-buffer " *message work*")
536       (progn
537         (set-buffer " *message work*")
538         (erase-buffer))
539     (set-buffer (get-buffer-create " *message work*"))
540     (kill-all-local-variables)
541     (buffer-disable-undo (current-buffer))))
542
543 (defun message-functionp (form)
544   "Return non-nil if FORM is funcallable."
545   (or (and (symbolp form) (fboundp form))
546       (and (listp form) (eq (car form) 'lambda))))
547
548 (defun message-strip-subject-re (subject)
549   "Remove \"Re:\" from subject lines."
550   (if (string-match "^[Rr][Ee]: *" subject)
551       (substring subject (match-end 0))
552     subject))
553
554 (defun message-remove-header (header &optional is-regexp first reverse)
555   "Remove HEADER in the narrowed buffer.
556 If REGEXP, HEADER is a regular expression.
557 If FIRST, only remove the first instance of the header.
558 Return the number of headers removed."
559   (goto-char (point-min))
560   (let ((regexp (if is-regexp header (concat "^" header ":")))
561         (number 0)
562         (case-fold-search t)
563         last)
564     (while (and (not (eobp))
565                 (not last))
566       (if (if reverse
567               (not (looking-at regexp))
568             (looking-at regexp))
569           (progn
570             (incf number)
571             (when first
572               (setq last t))
573             (delete-region
574              (point)
575              ;; There might be a continuation header, so we have to search
576              ;; until we find a new non-continuation line.
577              (progn
578                (forward-line 1)
579                (if (re-search-forward "^[^ \t]" nil t)
580                    (goto-char (match-beginning 0))
581                  (point-max)))))
582         (forward-line 1)
583         (if (re-search-forward "^[^ \t]" nil t)
584             (goto-char (match-beginning 0))
585           (point-max))))
586     number))
587
588 (defun message-narrow-to-headers ()
589   "Narrow the buffer to the head of the message."
590   (widen)
591   (narrow-to-region
592    (goto-char (point-min))
593    (if (re-search-forward
594         (concat "^" (regexp-quote mail-header-separator) "\n") nil t)
595        (match-beginning 0)
596      (point-max)))
597   (goto-char (point-min)))
598
599 (defun message-narrow-to-head ()
600   "Narrow the buffer to the head of the message."
601   (widen)
602   (narrow-to-region
603    (goto-char (point-min))
604    (if (search-forward "\n\n" nil 1)
605        (1- (point))
606      (point-max)))
607   (goto-char (point-min)))
608
609 (defun message-news-p ()
610   "Say whether the current buffer contains a news message."
611   (save-excursion
612     (save-restriction
613       (message-narrow-to-headers)
614       (message-fetch-field "newsgroups"))))
615
616 (defun message-mail-p ()
617   "Say whether the current buffer contains a mail message."
618   (save-excursion
619     (save-restriction
620       (message-narrow-to-headers)
621       (or (message-fetch-field "to")
622           (message-fetch-field "cc")
623           (message-fetch-field "bcc")))))
624
625 (defun message-next-header ()
626   "Go to the beginning of the next header."
627   (beginning-of-line)
628   (or (eobp) (forward-char 1))
629   (not (if (re-search-forward "^[^ \t]" nil t)
630            (beginning-of-line)
631          (goto-char (point-max)))))
632     
633 (defun message-sort-headers-1 ()
634   "Sort the buffer as headers using `message-rank' text props."
635   (goto-char (point-min))
636   (sort-subr 
637    nil 'message-next-header 
638    (lambda ()
639      (message-next-header)
640      (unless (bobp)
641        (forward-char -1)))
642    (lambda ()
643      (or (get-text-property (point) 'message-rank)
644          0))))
645
646 (defun message-sort-headers ()
647   "Sort the headers of the current message according to `message-header-format-alist'."
648   (interactive)
649   (save-excursion
650     (save-restriction
651       (let ((max (1+ (length message-header-format-alist)))
652             rank)
653         (message-narrow-to-headers)
654         (while (re-search-forward "^[^ \n]+:" nil t)
655           (put-text-property
656            (match-beginning 0) (1+ (match-beginning 0))
657            'message-rank
658            (if (setq rank (length (memq (assq (intern (buffer-substring
659                                                        (match-beginning 0)
660                                                        (1- (match-end 0))))
661                                               message-header-format-alist)
662                                         message-header-format-alist)))
663                (- max rank)
664              (1+ max)))))
665       (message-sort-headers-1))))
666
667 \f
668
669 ;;;
670 ;;; Message mode
671 ;;;
672
673 ;;; Set up keymap.
674
675 (defvar message-mode-map nil)
676
677 (unless message-mode-map
678   (setq message-mode-map (copy-keymap text-mode-map))
679   (define-key message-mode-map "\C-c?" 'describe-mode)
680
681   (define-key message-mode-map "\C-c\C-f\C-t" 'message-goto-to)
682   (define-key message-mode-map "\C-c\C-f\C-b" 'message-goto-bcc)
683   (define-key message-mode-map "\C-c\C-f\C-w" 'message-goto-fcc)
684   (define-key message-mode-map "\C-c\C-f\C-c" 'message-goto-cc)
685   (define-key message-mode-map "\C-c\C-f\C-s" 'message-goto-subject)
686   (define-key message-mode-map "\C-c\C-f\C-r" 'message-goto-reply-to)
687   (define-key message-mode-map "\C-c\C-f\C-n" 'message-goto-newsgroups)
688   (define-key message-mode-map "\C-c\C-f\C-d" 'message-goto-distribution)
689   (define-key message-mode-map "\C-c\C-f\C-f" 'message-goto-followup-to)
690   (define-key message-mode-map "\C-c\C-f\C-k" 'message-goto-keywords)
691   (define-key message-mode-map "\C-c\C-f\C-u" 'message-goto-summary)
692   (define-key message-mode-map "\C-c\C-b" 'message-goto-body)
693   (define-key message-mode-map "\C-c\C-i" 'message-goto-signature)
694
695   (define-key message-mode-map "\C-c\C-t" 'message-insert-to)
696   (define-key message-mode-map "\C-c\C-n" 'message-insert-newsgroups)
697   
698   (define-key message-mode-map "\C-c\C-y" 'message-yank-original)
699   (define-key message-mode-map "\C-c\C-q" 'message-fill-yanked-message)
700   (define-key message-mode-map "\C-c\C-w" 'message-insert-signature)
701   (define-key message-mode-map "\C-c\C-r" 'message-caesar-buffer-body)
702   (define-key message-mode-map "\C-c\C-o" 'message-sort-headers)
703   (define-key message-mode-map "\C-c\M-r" 'message-rename-buffer)
704
705   (define-key message-mode-map "\C-c\C-c" 'message-send-and-exit)
706   (define-key message-mode-map "\C-c\C-s" 'message-send)
707   (define-key message-mode-map "\C-c\C-k" 'message-kill-buffer)
708   (define-key message-mode-map "\C-c\C-d" 'message-dont-send)
709
710   (define-key message-mode-map "\t" 'message-tab))
711
712 (easy-menu-define message-mode-menu message-mode-map
713   "Message Menu."
714   '("Message"
715     "Go to Field:"
716     "----"
717     ["To" message-goto-to t]
718     ["Subject" message-goto-subject t]
719     ["Cc" message-goto-cc t]
720     ["Reply-to" message-goto-reply-to t]
721     ["Summary" message-goto-summary t]
722     ["Keywords" message-goto-keywords t]
723     ["Newsgroups" message-goto-newsgroups t]
724     ["Followup-To" message-goto-followup-to t]
725     ["Distribution" message-goto-distribution t]
726     ["Body" message-goto-body t]
727     ["Signature" message-goto-signature t]
728     "----"
729     "Miscellaneous Commands:"
730     "----"
731     ["Sort Headers" message-sort-headers t]
732     ["Yank Original" message-yank-original t]
733     ["Fill Yanked Message" message-fill-yanked-message t]
734     ["Insert Signature" message-insert-signature t]
735     ["Caesar (rot13) Message" message-caesar-buffer-body t]
736     ["Rename buffer" message-rename-buffer t]
737     ["Spellcheck" ispell-message t]
738     "----"
739     ["Send Message" message-send-and-exit t]
740     ["Abort Message" message-dont-send t]))
741
742 (defvar facemenu-add-face-function)
743 (defvar facemenu-remove-face-function)
744
745 ;;;###autoload
746 (defun message-mode ()
747   "Major mode for editing mail and news to be sent.
748 Like Text Mode but with these additional commands:
749 C-c C-s  message-send (send the message)    C-c C-c  message-send-and-exit
750 C-c C-f  move to a header field (and create it if there isn't):
751          C-c C-f C-t  move to To        C-c C-f C-s  move to Subject
752          C-c C-f C-c  move to Cc        C-c C-f C-b  move to Bcc
753          C-c C-f C-f  move to Fcc       C-c C-f C-r  move to Reply-To
754          C-c C-f C-u  move to Summary   C-c C-f C-n  move to Newsgroups
755          C-c C-f C-k  move to Keywords  C-c C-f C-d  move to Distribution
756          C-c C-f C-o  move to Followup-To
757 C-c C-t  message-insert-to (add a To header to a news followup)
758 C-c C-n  message-insert-newsgroups (add a Newsgroup header to a news reply)
759 C-c C-b  message-goto-body (move to beginning of message text).
760 C-c C-i  message-goto-signature (move to the beginning of the signature).
761 C-c C-w  message-insert-signature (insert `message-signature-file' file).
762 C-c C-y  message-yank-original (insert current message, if any).
763 C-c C-q  message-fill-yanked-message (fill what was yanked).
764 C-c C-r  message-ceasar-buffer-body (rot13 the message body)."
765   (interactive)
766   (kill-all-local-variables)
767   (make-local-variable 'message-reply-buffer)
768   (setq message-reply-buffer nil)
769   (make-local-variable 'message-send-actions)
770   (make-local-variable 'message-exit-actions)
771   (make-local-variable 'message-kill-actions)
772   (make-local-variable 'message-postpone-actions)
773   (set-syntax-table message-mode-syntax-table)
774   (use-local-map message-mode-map)
775   (setq local-abbrev-table text-mode-abbrev-table)
776   (setq major-mode 'message-mode)
777   (setq mode-name "Message")
778   (setq buffer-offer-save t)
779   (make-local-variable 'font-lock-defaults)
780   (setq font-lock-defaults '(message-font-lock-keywords t))
781   (make-local-variable 'facemenu-add-face-function)
782   (make-local-variable 'facemenu-remove-face-function)
783   (setq facemenu-add-face-function
784         (lambda (face end)
785           (let ((face-fun (cdr (assq face message-face-alist))))
786             (if face-fun
787                 (funcall face-fun (point) end)
788               (error "Face %s not configured for %s mode" face mode-name)))
789           "")
790         facemenu-remove-face-function t)
791   (make-local-variable 'paragraph-separate)
792   (make-local-variable 'paragraph-start)
793   (setq paragraph-start (concat (regexp-quote mail-header-separator)
794                                 "$\\|[ \t]*[-_][-_][-_]+$\\|"
795                                 paragraph-start))
796   (setq paragraph-separate (concat (regexp-quote mail-header-separator)
797                                    "$\\|[ \t]*[-_][-_][-_]+$\\|"
798                                    paragraph-separate))
799   (make-local-variable 'message-reply-headers)
800   (setq message-reply-headers nil)
801   (make-local-variable 'message-newsreader)
802   (make-local-variable 'message-mailer)
803   (make-local-variable 'message-post-method)
804   (make-local-variable 'message-sent-message-via)
805   (setq message-sent-message-via nil)
806   (make-local-variable 'message-checksum)
807   (setq message-checksum nil)
808   (when (fboundp 'mail-hist-define-keys)
809     (mail-hist-define-keys))
810   (when (string-match "XEmacs\\|Lucid" emacs-version)
811     (message-setup-toolbar))
812   (easy-menu-add message-mode-menu message-mode-map)
813   ;; Allow mail alias things.
814   (if (fboundp 'mail-abbrevs-setup)
815       (mail-abbrevs-setup)
816     (funcall (intern "mail-aliases-setup")))
817   (run-hooks 'text-mode-hook 'message-mode-hook))
818
819 \f
820
821 ;;;
822 ;;; Message mode commands
823 ;;;
824
825 ;;; Movement commands
826
827 (defun message-goto-to ()
828   "Move point to the To header."
829   (interactive)
830   (message-position-on-field "To"))
831
832 (defun message-goto-subject ()
833   "Move point to the Subject header."
834   (interactive)
835   (message-position-on-field "Subject"))
836
837 (defun message-goto-cc ()
838   "Move point to the Cc header."
839   (interactive)
840   (message-position-on-field "Cc" "To"))
841
842 (defun message-goto-bcc ()
843   "Move point to the Bcc  header."
844   (interactive)
845   (message-position-on-field "Bcc" "Cc" "To"))
846
847 (defun message-goto-fcc ()
848   "Move point to the Fcc header."
849   (interactive)
850   (message-position-on-field "Fcc" "To" "Newsgroups"))
851
852 (defun message-goto-reply-to ()
853   "Move point to the Reply-To header."
854   (interactive)
855   (message-position-on-field "Reply-To" "Subject"))
856
857 (defun message-goto-newsgroups ()
858   "Move point to the Newsgroups header."
859   (interactive)
860   (message-position-on-field "Newsgroups"))
861
862 (defun message-goto-distribution ()
863   "Move point to the Distribution header."
864   (interactive)
865   (message-position-on-field "Distribution"))
866
867 (defun message-goto-followup-to ()
868   "Move point to the Followup-To header."
869   (interactive)
870   (message-position-on-field "Followup-To" "Newsgroups"))
871
872 (defun message-goto-keywords ()
873   "Move point to the Keywords header."
874   (interactive)
875   (message-position-on-field "Keywords" "Subject"))
876
877 (defun message-goto-summary ()
878   "Move point to the Summary header."
879   (interactive)
880   (message-position-on-field "Summary" "Subject"))
881
882 (defun message-goto-body ()
883   "Move point to the beginning of the message body."
884   (interactive)
885   (goto-char (point-min))
886   (search-forward (concat "\n" mail-header-separator "\n") nil t))
887
888 (defun message-goto-signature ()
889   "Move point to the beginning of the message signature."
890   (interactive)
891   (goto-char (point-min))
892   (or (re-search-forward message-signature-separator nil t)
893       (goto-char (point-max))))
894
895 \f
896
897 (defun message-insert-to ()
898   "Insert a To header that points to the author of the article being replied to."
899   (interactive)
900   (when (message-position-on-field "To")
901     (insert ", "))
902   (insert (or (message-fetch-reply-field "reply-to")
903               (message-fetch-reply-field "from") "")))
904
905 (defun message-insert-newsgroups ()
906   "Insert the Newsgroups header from the article being replied to."
907   (interactive)
908   (when (message-position-on-field "Newsgroups")
909     (insert ","))
910   (insert (or (message-fetch-reply-field "newsgroups") "")))
911
912 \f
913
914 ;;; Various commands
915
916 (defun message-insert-signature (&optional force)
917   "Insert a signature.  See documentation for the `message-signature' variable."
918   (interactive (list t))
919   (let* ((signature 
920           (cond ((and (null message-signature)
921                       force)
922                  t)
923                 ((message-functionp message-signature)
924                  (funcall message-signature))
925                 ((listp message-signature)
926                  (eval message-signature))
927                 (t message-signature)))
928          (signature
929           (cond ((stringp signature)
930                  signature)
931                 ((and (eq t signature)
932                       message-signature-file
933                       (file-exists-p message-signature-file))
934                  signature))))
935     (when signature
936       ;; Remove blank lines at the end of the message.
937       (goto-char (point-max))
938       (skip-chars-backward " \t\n")
939       (end-of-line)
940       (delete-region (point) (point-max))
941       ;; Insert the signature.
942       (insert "\n\n-- \n")
943       (if (eq signature t)
944           (insert-file-contents message-signature-file)
945         (insert signature))
946       (goto-char (point-max))
947       (or (bolp) (insert "\n")))))
948
949 (defvar message-caesar-translation-table nil)
950
951 (defun message-caesar-region (b e &optional n)
952   "Caesar rotation of region by N, default 13, for decrypting netnews."
953   (interactive
954    (list
955     (min (point) (or (mark t) (point)))
956     (max (point) (or (mark t) (point)))
957     (when current-prefix-arg
958       (prefix-numeric-value current-prefix-arg))))
959
960   (setq n (if (numberp n) (mod n 26) 13)) ;canonize N
961   (unless (or (zerop n)                 ; no action needed for a rot of 0
962               (= b e))                  ; no region to rotate
963     ;; We build the table, if necessary.
964     (when (or (not message-caesar-translation-table)
965               (/= (aref message-caesar-translation-table ?a) (+ ?a n)))
966       (let ((i -1) 
967             (table (make-string 256 0)))
968         (while (< (incf i) 256)
969           (aset table i i))
970         (setq table
971               (concat
972                (substring table 0 ?A)
973                (substring table (+ ?A n) (+ ?A n (- 26 n)))
974                (substring table ?A (+ ?A n))
975                (substring table (+ ?A 26) ?a)
976                (substring table (+ ?a n) (+ ?a n (- 26 n)))
977                (substring table ?a (+ ?a n))
978                (substring table (+ ?a 26) 255)))
979         (setq message-caesar-translation-table table)))
980     ;; Then we translate the region.  Do it this way to retain 
981     ;; text properties.
982     (while (< b e)
983       (subst-char-in-region 
984        b (1+ b) (char-after b)
985        (aref message-caesar-translation-table (char-after b)))
986       (incf b))))
987
988 (defun message-caesar-buffer-body (&optional rotnum)
989   "Caesar rotates all letters in the current buffer by 13 places.
990 Used to encode/decode possibly offensive messages (commonly in net.jokes).
991 With prefix arg, specifies the number of places to rotate each letter forward.
992 Mail and USENET news headers are not rotated."
993   (interactive (if current-prefix-arg
994                    (list (prefix-numeric-value current-prefix-arg))
995                  (list nil)))
996   (save-excursion
997     (save-restriction
998       (when (message-goto-body)
999         (narrow-to-region (point) (point-max)))
1000       (message-caesar-region (point-min) (point-max) rotnum))))
1001
1002 (defun message-rename-buffer (&optional enter-string)
1003   "Rename the *message* buffer to \"*message* RECIPIENT\".  
1004 If the function is run with a prefix, it will ask for a new buffer
1005 name, rather than giving an automatic name."
1006   (interactive "Pbuffer name: ")
1007   (save-excursion
1008     (save-restriction
1009       (goto-char (point-min))
1010       (narrow-to-region (point) 
1011                         (search-forward mail-header-separator nil 'end))
1012       (let* ((mail-to (if (message-news-p) (message-fetch-field "Newsgroups")
1013                         (message-fetch-field "To")))
1014              (mail-trimmed-to
1015               (if (string-match "," mail-to)
1016                   (concat (substring mail-to 0 (match-beginning 0)) ", ...")
1017                 mail-to))
1018              (name-default (concat "*message* " mail-trimmed-to))
1019              (name (if enter-string
1020                        (read-string "New buffer name: " name-default)
1021                      name-default)))
1022         (rename-buffer name t)))))
1023
1024 (defun message-fill-yanked-message (&optional justifyp)
1025   "Fill the paragraphs of a message yanked into this one.
1026 Numeric argument means justify as well."
1027   (interactive "P")
1028   (save-excursion
1029     (goto-char (point-min))
1030     (search-forward (concat "\n" mail-header-separator "\n") nil t)
1031     (let ((fill-prefix message-yank-prefix))
1032       (fill-individual-paragraphs (point) (point-max) justifyp t))))
1033
1034 (defun message-indent-citation ()
1035   "Modify text just inserted from a message to be cited.
1036 The inserted text should be the region.
1037 When this function returns, the region is again around the modified text.
1038
1039 Normally, indent each nonblank line `message-indentation-spaces' spaces.
1040 However, if `message-yank-prefix' is non-nil, insert that prefix on each line."
1041   (let ((start (point)))
1042     ;; Remove unwanted headers.
1043     (when message-ignored-cited-headers
1044       (save-restriction
1045         (narrow-to-region 
1046          (goto-char start)
1047          (if (search-forward "\n\n" nil t)
1048              (1- (point))
1049            (point)))
1050         (message-remove-header message-ignored-cited-headers t)))
1051     ;; Do the indentation.
1052     (if (null message-yank-prefix)
1053         (indent-rigidly start (mark t) message-indentation-spaces)
1054       (save-excursion
1055         (goto-char start)
1056         (while (< (point) (mark t))
1057           (insert message-yank-prefix)
1058           (forward-line 1)))
1059       (goto-char start))))
1060
1061 (defun message-yank-original (&optional arg)
1062   "Insert the message being replied to, if any.
1063 Puts point before the text and mark after.
1064 Normally indents each nonblank line ARG spaces (default 3).  However,
1065 if `message-yank-prefix' is non-nil, insert that prefix on each line.
1066
1067 Just \\[universal-argument] as argument means don't indent, insert no
1068 prefix, and don't delete any headers."
1069   (interactive "P")
1070   (let ((modified (buffer-modified-p)))
1071     (when (and message-reply-buffer
1072                message-cite-function)
1073       (delete-windows-on message-reply-buffer t)
1074       (insert-buffer message-reply-buffer)
1075       (funcall message-cite-function)
1076       (exchange-point-and-mark)
1077       (unless (bolp)
1078         (insert ?\n))
1079       (unless modified
1080         (setq message-checksum (message-checksum))))))
1081
1082 (defun message-cite-original ()    
1083   (let ((start (point))
1084         (functions 
1085          (when message-indent-citation-function
1086            (if (listp message-indent-citation-function)
1087                message-indent-citation-function
1088              (list message-indent-citation-function)))))
1089     (goto-char start)
1090     (while functions
1091       (funcall (pop functions)))
1092     (when message-citation-line-function
1093       (unless (bolp)
1094         (insert "\n"))
1095       (funcall message-citation-line-function))))
1096
1097 (defun message-insert-citation-line ()
1098   "Function that inserts a simple citation line."
1099   (when message-reply-headers
1100     (insert (mail-header-from message-reply-headers) " writes:\n\n")))
1101
1102 (defun message-position-on-field (header &rest afters)
1103   (let ((case-fold-search t))
1104     (save-restriction
1105       (narrow-to-region
1106        (goto-char (point-min))
1107        (progn
1108          (re-search-forward 
1109           (concat "^" (regexp-quote mail-header-separator) "$"))
1110          (match-beginning 0)))
1111       (goto-char (point-min))
1112       (if (re-search-forward (concat "^" (regexp-quote header) ":") nil t)
1113           (progn
1114             (re-search-forward "^[^ \t]" nil 'move)
1115             (beginning-of-line)
1116             (skip-chars-backward "\n")
1117             t)
1118         (while (and afters
1119                     (not (re-search-forward 
1120                           (concat "^" (regexp-quote (car afters)) ":")
1121                           nil t)))
1122           (pop afters))
1123         (when afters
1124           (re-search-forward "^[^ \t]" nil 'move)
1125           (beginning-of-line))
1126         (insert header ": \n")
1127         (forward-char -1)
1128         nil))))
1129
1130 (defun message-remove-signature ()
1131   "Remove the signature from the text between point and mark.
1132 The text will also be indented the normal way."
1133   (save-excursion
1134     (let ((start (point))
1135           mark)
1136     (if (not (re-search-forward message-signature-separator (mark t) t))
1137         ;; No signature here, so we just indent the cited text.
1138         (message-indent-citation)
1139       ;; Find the last non-empty line.
1140       (forward-line -1)
1141       (while (looking-at "[ \t]*$")
1142         (forward-line -1))
1143       (forward-line 1)
1144       (setq mark (set-marker (make-marker) (point)))
1145       (goto-char start)
1146       (message-indent-citation)
1147       ;; Enable undoing the deletion.
1148       (undo-boundary)
1149       (delete-region mark (mark t))
1150       (set-marker mark nil)))))
1151
1152 \f
1153
1154 ;;;
1155 ;;; Sending messages
1156 ;;;
1157
1158 (defun message-send-and-exit (&optional arg)
1159   "Send message like `message-send', then, if no errors, exit from mail buffer."
1160   (interactive "P")
1161   (let ((buf (current-buffer))
1162         (actions message-exit-actions))
1163     (when (and (message-send arg)
1164                (buffer-name buf))
1165       (if message-kill-buffer-on-exit
1166           (kill-buffer buf)
1167         (bury-buffer buf)
1168         (when (eq buf (current-buffer))
1169           (message-bury buf)))
1170       (message-do-actions actions))))
1171
1172 (defun message-dont-send ()
1173   "Don't send the message you have been editing."
1174   (interactive)
1175   (message-bury (current-buffer))
1176   (message-do-actions message-postpone-actions))
1177
1178 (defun message-kill-buffer ()
1179   "Kill the current buffer."
1180   (interactive)
1181   (let ((actions message-kill-actions))
1182     (kill-buffer (current-buffer))
1183     (message-do-actions actions)))
1184
1185 (defun message-bury (buffer)
1186   "Bury this mail buffer."
1187   (let ((newbuf (other-buffer buffer)))
1188     (bury-buffer buffer)
1189     (if (and (fboundp 'frame-parameters)
1190              (cdr (assq 'dedicated (frame-parameters)))
1191              (not (null (delq (selected-frame) (visible-frame-list)))))
1192         (delete-frame (selected-frame))
1193       (switch-to-buffer newbuf))))
1194
1195 (defun message-send (&optional arg)
1196   "Send the message in the current buffer.
1197 If `message-interactive' is non-nil, wait for success indication
1198 or error messages, and inform user.
1199 Otherwise any failure is reported in a message back to
1200 the user from the mailer."
1201   (interactive "P")
1202   (when (if buffer-file-name
1203             (y-or-n-p (format "Send buffer contents as %s message? "
1204                               (if (message-mail-p)
1205                                   (if (message-news-p) "mail and news" "mail")
1206                                 "news")))
1207           (or (buffer-modified-p)
1208               (y-or-n-p "No changes in the buffer; really send? ")))
1209     ;; Make it possible to undo the coming changes.
1210     (undo-boundary)
1211     (message-fix-before-sending)
1212     (run-hooks 'message-send-hook)
1213     (message "Sending...")
1214     (when (and (or (not (message-news-p))
1215                    (and (or (not (memq 'news message-sent-message-via))
1216                             (y-or-n-p
1217                              "Already sent message via news; resend? "))
1218                         (funcall message-send-news-function arg)))
1219                (or (not (message-mail-p))
1220                    (and (or (not (memq 'mail message-sent-message-via))
1221                             (y-or-n-p
1222                              "Already sent message via mail; resend? "))
1223                         (message-send-mail arg))))
1224       (message-do-fcc)
1225       (when (fboundp 'mail-hist-put-headers-into-history)
1226         (mail-hist-put-headers-into-history))
1227       (run-hooks 'message-sent-hook)
1228       (message "Sending...done")
1229       ;; If buffer has no file, mark it as unmodified and delete autosave.
1230       (unless buffer-file-name
1231         (set-buffer-modified-p nil)
1232         (delete-auto-save-file-if-necessary t))
1233       ;; Delete other mail buffers and stuff.
1234       (message-do-send-housekeeping)
1235       (message-do-actions message-send-actions)
1236       ;; Return success.
1237       t)))
1238
1239 (defun message-fix-before-sending ()
1240   "Do various things to make the message nice before sending it."
1241   ;; Make sure there's a newline at the end of the message.
1242   (goto-char (point-max))
1243   (unless (bolp)
1244     (insert "\n")))
1245
1246 (defun message-add-action (action &rest types)
1247   "Add ACTION to be performed when doing an exit of type TYPES."
1248   (let (var)
1249     (while types
1250       (set (setq var (intern (format "message-%s-actions" (pop types))))
1251            (nconc (symbol-value var) (list action))))))
1252
1253 (defun message-do-actions (actions)
1254   "Perform all actions in ACTIONS."
1255   ;; Now perform actions on successful sending.
1256   (while actions
1257     (condition-case nil
1258         (cond 
1259          ;; A simple function.
1260          ((message-functionp (car actions))
1261           (funcall (car actions)))
1262          ;; Something to be evaled.
1263          (t
1264           (eval (car actions))))
1265       (error))
1266     (pop actions)))
1267
1268 (defun message-send-mail (&optional arg)
1269   (require 'mail-utils)
1270   (let ((tembuf (generate-new-buffer " message temp"))
1271         (case-fold-search nil)
1272         (news (message-news-p))
1273         (mailbuf (current-buffer)))
1274     (save-restriction
1275       (message-narrow-to-headers)
1276       ;; Insert some headers.
1277       (let ((message-deletable-headers
1278              (if news nil message-deletable-headers)))
1279         (message-generate-headers message-required-mail-headers))
1280       ;; Let the user do all of the above.
1281       (run-hooks 'message-header-hook))
1282     (unwind-protect
1283         (save-excursion
1284           (set-buffer tembuf)
1285           (erase-buffer)
1286           (insert-buffer-substring mailbuf)
1287           ;; Remove some headers.
1288           (save-restriction
1289             (message-narrow-to-headers)
1290             ;; Remove some headers.
1291             (message-remove-header message-ignored-mail-headers t))
1292           (goto-char (point-max))
1293           ;; require one newline at the end.
1294           (or (= (preceding-char) ?\n)
1295               (insert ?\n))
1296           (when (and news
1297                      (or (message-fetch-field "cc")
1298                          (message-fetch-field "to")))
1299             (message-insert-courtesy-copy))
1300           (funcall message-send-mail-function))
1301       (kill-buffer tembuf))
1302     (set-buffer mailbuf)
1303     (push 'mail message-sent-message-via)))
1304
1305 (defun message-send-mail-with-sendmail ()
1306   "Send off the prepared buffer with sendmail."
1307   (let ((errbuf (if message-interactive
1308                     (generate-new-buffer " sendmail errors")
1309                   0))
1310         resend-to-addresses delimline)
1311     (let ((case-fold-search t))
1312       (save-restriction
1313         (message-narrow-to-headers)
1314         (setq resend-to-addresses (message-fetch-field "resent-to")))
1315       ;; Change header-delimiter to be what sendmail expects.
1316       (goto-char (point-min))
1317       (re-search-forward
1318        (concat "^" (regexp-quote mail-header-separator) "\n"))
1319       (replace-match "\n")
1320       (backward-char 1)
1321       (setq delimline (point-marker))
1322       ;; Insert an extra newline if we need it to work around
1323       ;; Sun's bug that swallows newlines.
1324       (goto-char (1+ delimline))
1325       (when (eval message-mailer-swallows-blank-line)
1326         (newline))
1327       (when message-interactive
1328         (save-excursion
1329           (set-buffer errbuf)
1330           (erase-buffer))))
1331     (let ((default-directory "/"))
1332       (apply 'call-process-region
1333              (append (list (point-min) (point-max)
1334                            (if (boundp 'sendmail-program)
1335                                sendmail-program
1336                              "/usr/lib/sendmail")
1337                            nil errbuf nil "-oi")
1338                      ;; Always specify who from,
1339                      ;; since some systems have broken sendmails.
1340                      (list "-f" (user-login-name))
1341                      ;; These mean "report errors by mail"
1342                      ;; and "deliver in background".
1343                      (if (null message-interactive) '("-oem" "-odb"))
1344                      ;; Get the addresses from the message
1345                      ;; unless this is a resend.
1346                      ;; We must not do that for a resend
1347                      ;; because we would find the original addresses.
1348                      ;; For a resend, include the specific addresses.
1349                      (if resend-to-addresses
1350                          (list resend-to-addresses)
1351                        '("-t")))))
1352     (when message-interactive
1353       (save-excursion
1354         (set-buffer errbuf)
1355         (goto-char (point-min))
1356         (while (re-search-forward "\n\n* *" nil t)
1357           (replace-match "; "))
1358         (if (not (zerop (buffer-size)))
1359             (error "Sending...failed to %s"
1360                    (buffer-substring (point-min) (point-max)))))
1361       (when (bufferp errbuf)
1362         (kill-buffer errbuf)))))
1363
1364 (defun message-send-mail-with-mh ()
1365   "Send the prepared message buffer with mh."
1366   (let ((mh-previous-window-config nil)
1367         (name (make-temp-name
1368                (concat (file-name-as-directory message-autosave-directory)
1369                        "msg."))))
1370     (setq buffer-file-name name)
1371     (mh-send-letter)
1372     (condition-case ()
1373         (delete-file name)
1374       (error nil))))
1375
1376 (defun message-send-news (&optional arg)
1377   (let ((tembuf (generate-new-buffer " *message temp*"))
1378         (case-fold-search nil)
1379         (method (if (message-functionp message-post-method)
1380                     (funcall message-post-method arg)
1381                   message-post-method))
1382         (messbuf (current-buffer))
1383         result)
1384     (save-restriction
1385       (message-narrow-to-headers)
1386       ;; Insert some headers.
1387       (message-generate-headers message-required-news-headers)
1388       ;; Let the user do all of the above.
1389       (run-hooks 'message-header-hook))
1390     (when (message-check-news-syntax)
1391       (unwind-protect
1392           (save-excursion
1393             (set-buffer tembuf)
1394             (buffer-disable-undo (current-buffer))
1395             (erase-buffer) 
1396             (insert-buffer-substring messbuf)
1397             ;; Remove some headers.
1398             (save-restriction
1399               (message-narrow-to-headers)
1400               ;; Remove some headers.
1401               (message-remove-header message-ignored-news-headers t))
1402             (goto-char (point-max))
1403             ;; require one newline at the end.
1404             (or (= (preceding-char) ?\n)
1405                 (insert ?\n))
1406             (let ((case-fold-search t))
1407               ;; Remove the delimeter.
1408               (goto-char (point-min))
1409               (re-search-forward
1410                (concat "^" (regexp-quote mail-header-separator) "\n"))
1411               (replace-match "\n")
1412               (backward-char 1))
1413             (require (car method))
1414             (funcall (intern (format "%s-open-server" (car method)))
1415                      (cadr method) (cddr method))
1416             (setq result
1417                   (funcall (intern (format "%s-request-post" (car method))))))
1418         (kill-buffer tembuf))
1419       (set-buffer messbuf)
1420       (if result
1421           (push 'news message-sent-message-via)
1422         (message "Couldn't send message via news: %s"
1423                  (nnheader-get-report (car method)))
1424         nil))))
1425
1426 ;;;
1427 ;;; Header generation & syntax checking.
1428 ;;;
1429
1430 (defun message-check-news-syntax ()
1431   "Check the syntax of the message."
1432   (and 
1433    ;; We narrow to the headers and check them first.
1434    (save-excursion
1435      (save-restriction
1436        (message-narrow-to-headers)
1437        (and 
1438         ;; Check for commands in Subject.
1439         (or 
1440          (message-check-element 'subject-cmsg)
1441          (save-excursion
1442            (if (string-match "^cmsg " (message-fetch-field "subject"))
1443                (y-or-n-p
1444                 "The control code \"cmsg \" is in the subject. Really post? ")
1445              t)))
1446         ;; Check for multiple identical headers.
1447         (or (message-check-element 'multiple-headers)
1448             (save-excursion
1449               (let (found)
1450                 (while (and (not found) 
1451                             (re-search-forward "^[^ \t:]+: " nil t))
1452                   (save-excursion
1453                     (or (re-search-forward 
1454                          (concat "^" (setq found
1455                                            (buffer-substring 
1456                                             (match-beginning 0) 
1457                                             (- (match-end 0) 2))))
1458                          nil t)
1459                         (setq found nil))))
1460                 (if found
1461                     (y-or-n-p 
1462                      (format "Multiple %s headers. Really post? " found))
1463                   t))))
1464         ;; Check for Version and Sendsys.
1465         (or (message-check-element 'sendsys)
1466             (save-excursion
1467               (if (re-search-forward "^Sendsys:\\|^Version:" nil t)
1468                   (y-or-n-p
1469                    (format "The article contains a %s command. Really post? "
1470                            (buffer-substring (match-beginning 0) 
1471                                              (1- (match-end 0)))))
1472                 t)))
1473         ;; See whether we can shorten Followup-To.
1474         (or (message-check-element 'shorten-followup-to)
1475             (let ((newsgroups (message-fetch-field "newsgroups"))
1476                   (followup-to (message-fetch-field "followup-to"))
1477                   to)
1478               (when (and newsgroups (string-match "," newsgroups)
1479                          (not followup-to)
1480                          (not
1481                           (zerop
1482                            (length
1483                             (setq to (completing-read 
1484                                       "Followups to: (default all groups) " 
1485                                       (mapcar (lambda (g) (list g))
1486                                               (cons "poster" 
1487                                                     (message-tokenize-header 
1488                                                      newsgroups)))))))))
1489                 (goto-char (point-min))
1490                 (insert "Followup-To: " to "\n"))
1491               t))
1492
1493         ;; Check for Approved.
1494         (or (message-check-element 'approved)
1495             (save-excursion
1496               (if (re-search-forward "^Approved:" nil t)
1497                   (y-or-n-p
1498                    "The article contains an Approved header. Really post? ")
1499                 t)))
1500         ;; Check the Message-Id header.
1501         (or (message-check-element 'message-id)
1502             (save-excursion
1503               (let* ((case-fold-search t)
1504                      (message-id (message-fetch-field "message-id")))
1505                 (or (not message-id)
1506                     (and (string-match "@" message-id)
1507                          (string-match "@[^\\.]*\\." message-id))
1508                     (y-or-n-p
1509                      (format 
1510                       "The Message-ID looks strange: \"%s\". Really post? "
1511                       message-id))))))
1512         ;; Check the Subject header.
1513         (or 
1514          (message-check-element 'subject)
1515          (save-excursion
1516            (let* ((case-fold-search t)
1517                   (subject (message-fetch-field "subject")))
1518              (or
1519               (and subject
1520                    (not (string-match "\\`[ \t]*\\'" subject)))
1521               (progn
1522                 (message 
1523                  "The subject field is empty or missing.  Posting is denied.")
1524                 nil)))))
1525         ;; Check the Newsgroups & Followup-To headers.
1526         (or
1527          (message-check-element 'existing-newsgroups)
1528          (let* ((case-fold-search t)
1529                 (newsgroups (message-fetch-field "newsgroups"))
1530                 (followup-to (message-fetch-field "followup-to"))
1531                 (groups (message-tokenize-header
1532                          (if followup-to
1533                              (concat newsgroups "," followup-to)
1534                            newsgroups)))
1535                 (hashtb (and (boundp 'gnus-active-hashtb)
1536                              gnus-active-hashtb))
1537                 errors)
1538            (if (not hashtb)
1539                t
1540              (while groups
1541                (unless (boundp (intern (car groups) hashtb))
1542                  (push (car groups) errors))
1543                (pop groups))
1544              (if (not errors)
1545                  t
1546                (y-or-n-p
1547                 (format
1548                  "Really post to %s unknown group%s: %s "
1549                  (if (= (length errors) 1) "this" "these")
1550                  (if (= (length errors) 1) "" "s")
1551                  (mapconcat 'identity errors ", ")))))))
1552         ;; Check the Newsgroups & Followup-To headers for syntax errors.
1553         (or
1554          (message-check-element 'valid-newsgroups)
1555          (let ((case-fold-search t)
1556                (headers '("Newsgroups" "Followup-To"))
1557                header error)
1558            (while (and headers (not error))
1559              (when (setq header (mail-fetch-field (car headers)))
1560                (if (or
1561                     (not (string-match
1562                           "\\`\\([-.a-zA-Z0-9]+\\)?\\(,[-.a-zA-Z0-9]+\\)*\\'"
1563                           header))
1564                     (memq 
1565                      nil (mapcar 
1566                           (lambda (g)
1567                             (not (string-match "\\.\\'\\|\\.\\." g)))
1568                           (message-tokenize-header header ","))))
1569                    (setq error t)))
1570              (unless error
1571                (pop headers)))
1572            (if (not error)
1573                t
1574              (y-or-n-p
1575               (format "The %s header looks odd: \"%s\".  Really post? "
1576                       (car headers) header)))))
1577         ;; Check the From header.
1578         (or 
1579          (message-check-element 'from)
1580          (save-excursion
1581            (let* ((case-fold-search t)
1582                   (from (message-fetch-field "from")))
1583              (cond
1584               ((not from)
1585                (message "There is no From line.  Posting is denied.")
1586                nil)
1587               ((not (string-match "@[^\\.]*\\." from))
1588                (message
1589                 "Denied posting -- the From looks strange: \"%s\"." from)
1590                nil)
1591               ((string-match "@[^@]*@" from)
1592                (message 
1593                 "Denied posting -- two \"@\"'s in the From header: %s." from)
1594                nil)
1595               ((string-match "(.*).*(.*)" from)
1596                (message
1597                 "Denied posting -- the From header looks strange: \"%s\"." 
1598                 from)
1599                nil)
1600               (t t))))))))
1601    ;; Check for long lines.
1602    (or (message-check-element 'long-lines)
1603        (save-excursion
1604          (goto-char (point-min))
1605          (re-search-forward
1606           (concat "^" (regexp-quote mail-header-separator) "$"))
1607          (while (and
1608                  (progn
1609                    (end-of-line)
1610                    (< (current-column) 80))
1611                  (zerop (forward-line 1))))
1612          (or (bolp)
1613              (eobp)
1614              (y-or-n-p
1615               "You have lines longer than 79 characters.  Really post? "))))
1616    ;; Check whether the article is empty.
1617    (or (message-check-element 'empty)
1618        (save-excursion
1619          (goto-char (point-min))
1620          (re-search-forward
1621           (concat "^" (regexp-quote mail-header-separator) "$"))
1622          (forward-line 1)
1623          (let ((b (point)))
1624            (or (re-search-forward message-signature-separator nil t)
1625                (goto-char (point-max)))
1626            (beginning-of-line)
1627            (or (re-search-backward "[^ \n\t]" b t)
1628                (y-or-n-p "Empty article.  Really post? ")))))
1629    ;; Check for control characters.
1630    (or (message-check-element 'control-chars)
1631        (save-excursion
1632          (if (re-search-forward "[\000-\007\013\015-\037\200-\237]" nil t)
1633              (y-or-n-p 
1634               "The article contains control characters. Really post? ")
1635            t)))
1636    ;; Check excessive size.
1637    (or (message-check-element 'size)
1638        (if (> (buffer-size) 60000)
1639            (y-or-n-p
1640             (format "The article is %d octets long. Really post? "
1641                     (buffer-size)))
1642          t))
1643    ;; Check whether any new text has been added.
1644    (or (message-check-element 'new-text)
1645        (not message-checksum)
1646        (not (eq (message-checksum) message-checksum))
1647        (y-or-n-p
1648         "It looks like no new text has been added.  Really post? "))
1649    ;; Check the length of the signature.
1650    (or
1651     (message-check-element 'signature)
1652     (progn
1653       (goto-char (point-max))
1654       (if (or (not (re-search-backward "^-- $" nil t))
1655               (search-forward message-forward-end-separator nil t))
1656           t
1657         (if (> (count-lines (point) (point-max)) 5)
1658             (y-or-n-p
1659              (format
1660               "Your .sig is %d lines; it should be max 4.  Really post? "
1661               (count-lines (point) (point-max))))
1662           t))))))
1663
1664 (defun message-check-element (type)
1665   "Returns non-nil if this type is not to be checked."
1666   (if (eq message-syntax-checks 'dont-check-for-anything-just-trust-me)
1667       t
1668     (let ((able (assq type message-syntax-checks)))
1669       (and (consp able)
1670            (eq (cdr able) 'disabled)))))
1671
1672 (defun message-checksum ()
1673   "Return a \"checksum\" for the current buffer."
1674   (let ((sum 0))
1675     (save-excursion
1676       (goto-char (point-min))
1677       (re-search-forward
1678        (concat "^" (regexp-quote mail-header-separator) "$"))
1679       (while (not (eobp))
1680         (setq sum (logxor sum (following-char)))
1681         (forward-char 1)))
1682     sum))
1683
1684 (defun message-do-fcc ()
1685   "Process Fcc headers in the current buffer."
1686   (let ((case-fold-search t)
1687         (buf (current-buffer))
1688         list file)
1689     (save-excursion
1690       (set-buffer (get-buffer-create " *message temp*"))
1691       (buffer-disable-undo (current-buffer))
1692       (erase-buffer)
1693       (insert-buffer-substring buf)
1694       (save-restriction
1695         (message-narrow-to-headers)
1696         (while (setq file (message-fetch-field "fcc"))
1697           (push file list)
1698           (message-remove-header "fcc" nil t)))
1699       (goto-char (point-min))
1700       (re-search-forward (concat "^" (regexp-quote mail-header-separator) "$"))
1701       (replace-match "" t t)
1702       ;; Process FCC operations.
1703       (while list
1704         (setq file (pop list))
1705         (if (string-match "^[ \t]*|[ \t]*\\(.*\\)[ \t]*$" file)
1706             ;; Pipe the article to the program in question.
1707             (call-process-region (point-min) (point-max) shell-file-name
1708                                  nil nil nil "-c" (match-string 1 file))
1709           ;; Save the article.
1710           (setq file (expand-file-name file))
1711           (unless (file-exists-p (file-name-directory file))
1712             (make-directory (file-name-directory file) t))
1713           (if (and message-fcc-handler-function
1714                    (not (eq message-fcc-handler-function 'rmail-output)))
1715               (funcall message-fcc-handler-function file)
1716             (if (and (file-readable-p file) (mail-file-babyl-p file))
1717                 (rmail-output file 1)
1718               (let ((mail-use-rfc822 t))
1719                 (rmail-output file 1 t t))))))
1720       (kill-buffer (current-buffer)))))
1721
1722 (defun message-cleanup-headers ()
1723   "Do various automatic cleanups of the headers."
1724   ;; Remove empty lines in the header.
1725   (save-restriction
1726     (message-narrow-to-headers)
1727     (while (re-search-forward "^[ \t]*\n" nil t)
1728       (replace-match "" t t)))
1729
1730   ;; Correct Newsgroups and Followup-To headers: change sequence of
1731   ;; spaces to comma and eliminate spaces around commas.  Eliminate
1732   ;; embedded line breaks.
1733   (goto-char (point-min))
1734   (while (re-search-forward "^\\(Newsgroups\\|Followup-To\\): +" nil t)
1735     (save-restriction
1736       (narrow-to-region
1737        (point)
1738        (if (re-search-forward "^[^ \t]" nil t)
1739            (match-beginning 0)
1740          (forward-line 1)
1741          (point)))
1742       (goto-char (point-min))
1743       (while (re-search-forward "\n[ \t]+" nil t)
1744         (replace-match " " t t))        ;No line breaks (too confusing)
1745       (goto-char (point-min))
1746       (while (re-search-forward "[ \t\n]*,[ \t\n]*\\|[ \t]+" nil t)
1747         (replace-match "," t t))
1748       (goto-char (point-min))
1749       ;; Remove trailing commas.
1750       (when (re-search-forward ",+$" nil t)
1751         (replace-match "" t t)))))
1752
1753 (defun message-make-date ()
1754   "Make a valid data header."
1755   (let ((now (current-time)))
1756     (timezone-make-date-arpa-standard 
1757      (current-time-string now) (current-time-zone now))))
1758
1759 (defun message-make-message-id ()
1760   "Make a unique Message-ID."
1761   (concat "<" (message-unique-id) 
1762           (let ((psubject (save-excursion (message-fetch-field "subject"))))
1763             (if (and message-reply-headers
1764                      (mail-header-references message-reply-headers)
1765                      (mail-header-subject message-reply-headers)
1766                      psubject
1767                      (mail-header-subject message-reply-headers)
1768                      (not (string= 
1769                            (message-strip-subject-re
1770                             (mail-header-subject message-reply-headers))
1771                            (message-strip-subject-re psubject))))
1772                 "_-_" ""))
1773           "@" (message-make-fqdm) ">"))
1774
1775 (defvar message-unique-id-char nil)
1776
1777 ;; If you ever change this function, make sure the new version
1778 ;; cannot generate IDs that the old version could.
1779 ;; You might for example insert a "." somewhere (not next to another dot
1780 ;; or string boundary), or modify the "fsf" string.
1781 (defun message-unique-id ()
1782   ;; Don't use microseconds from (current-time), they may be unsupported.
1783   ;; Instead we use this randomly inited counter.
1784   (setq message-unique-id-char
1785         (% (1+ (or message-unique-id-char (logand (random t) (1- (lsh 1 20)))))
1786            ;; (current-time) returns 16-bit ints,
1787            ;; and 2^16*25 just fits into 4 digits i base 36.
1788            (* 25 25)))
1789   (let ((tm (current-time)))
1790     (concat
1791      (if (memq system-type '(ms-dos emx vax-vms))
1792          (let ((user (downcase (user-login-name))))
1793            (while (string-match "[^a-z0-9_]" user)
1794              (aset user (match-beginning 0) ?_))
1795            user)
1796        (message-number-base36 (user-uid) -1))
1797      (message-number-base36 (+ (car   tm) 
1798                                (lsh (% message-unique-id-char 25) 16)) 4)
1799      (message-number-base36 (+ (nth 1 tm)
1800                                (lsh (/ message-unique-id-char 25) 16)) 4)
1801      ;; Append the newsreader name, because while the generated
1802      ;; ID is unique to this newsreader, other newsreaders might
1803      ;; otherwise generate the same ID via another algorithm.
1804      ".fsf")))
1805
1806 (defun message-number-base36 (num len)
1807   (if (if (< len 0) (<= num 0) (= len 0))
1808       ""
1809     (concat (message-number-base36 (/ num 36) (1- len))
1810             (char-to-string (aref "zyxwvutsrqponmlkjihgfedcba9876543210"
1811                                   (% num 36))))))
1812
1813 (defun message-make-organization ()
1814   "Make an Organization header."
1815   (let* ((organization 
1816           (or (getenv "ORGANIZATION")
1817               (when message-user-organization
1818                 (if (message-functionp message-user-organization)
1819                     (funcall message-user-organization)
1820                   message-user-organization)))))
1821     (save-excursion
1822       (message-set-work-buffer)
1823       (cond ((stringp organization)
1824              (insert organization))
1825             ((and (eq t organization)
1826                   message-user-organization-file
1827                   (file-exists-p message-user-organization-file))
1828              (insert-file-contents message-user-organization-file)))
1829       (goto-char (point-min))
1830       (while (re-search-forward "[\t\n]+" nil t)
1831         (replace-match "" t t))
1832       (unless (zerop (buffer-size))
1833         (buffer-string)))))
1834
1835 (defun message-make-lines ()
1836   "Count the number of lines and return numeric string."
1837   (save-excursion
1838     (save-restriction
1839       (widen)
1840       (goto-char (point-min))
1841       (re-search-forward 
1842        (concat "^" (regexp-quote mail-header-separator) "$"))
1843       (forward-line 1)
1844       (int-to-string (count-lines (point) (point-max))))))
1845
1846 (defun message-make-in-reply-to ()
1847   "Return the In-Reply-To header for this message."
1848   (when message-reply-headers
1849     (let ((from (mail-header-from message-reply-headers))
1850           (date (mail-header-date message-reply-headers)))
1851       (when from
1852         (let ((stop-pos 
1853                (string-match "  *at \\|  *@ \\| *(\\| *<" from)))
1854           (concat (if stop-pos (substring from 0 stop-pos) from)
1855                   "'s message of " 
1856                   (if (or (not date) (string= date ""))
1857                       "(unknown date)" date)))))))
1858
1859 (defun message-make-distribution ()
1860   "Make a Distribution header."
1861   (let ((orig-distribution (message-fetch-reply-field "distribution")))
1862     (cond ((message-functionp message-distribution-function)
1863            (funcall message-distribution-function))
1864           (t orig-distribution))))
1865
1866 (defun message-make-expires ()
1867   "Return an Expires header based on `message-expires'."
1868   (let ((current (current-time))
1869         (future (* 1.0 message-expires 60 60 24)))
1870     ;; Add the future to current.
1871     (setcar current (+ (car current) (round (/ future (expt 2 16)))))
1872     (setcar (cdr current) (+ (nth 1 current) (% (round future) (expt 2 16))))
1873     ;; Return the date in the future in UT.
1874     (timezone-make-date-arpa-standard 
1875      (current-time-string current) (current-time-zone current) '(0 "UT"))))
1876
1877 (defun message-make-path ()
1878   "Return uucp path."
1879   (let ((login-name (user-login-name)))
1880     (cond ((null message-user-path)
1881            (concat (system-name) "!" login-name))
1882           ((stringp message-user-path)
1883            ;; Support GENERICPATH.  Suggested by vixie@decwrl.dec.com.
1884            (concat message-user-path "!" login-name))
1885           (t login-name))))
1886
1887 (defun message-make-from ()
1888   "Make a From header."
1889   (let* ((login (message-make-address))
1890          (fullname 
1891           (or (and (boundp 'user-full-name)
1892                    user-full-name)
1893               (user-full-name))))
1894     (when (string= fullname "&")
1895       (setq fullname (user-login-name)))
1896     (save-excursion
1897       (message-set-work-buffer)
1898       (cond 
1899        ((or (null message-from-style)
1900             (equal fullname ""))
1901         (insert login))
1902        ((or (eq message-from-style 'angles)
1903             (and (not (eq message-from-style 'parens))
1904                  ;; Use angles if no quoting is needed, or if parens would
1905                  ;; need quoting too.
1906                  (or (not (string-match "[^- !#-'*+/-9=?A-Z^-~]" fullname))
1907                      (let ((tmp (concat fullname nil)))
1908                        (while (string-match "([^()]*)" tmp)
1909                          (aset tmp (match-beginning 0) ?-)
1910                          (aset tmp (1- (match-end 0)) ?-))
1911                        (string-match "[\\()]" tmp)))))
1912         (insert fullname)
1913         (goto-char (point-min))
1914         ;; Look for a character that cannot appear unquoted
1915         ;; according to RFC 822.
1916         (when (re-search-forward "[^- !#-'*+/-9=?A-Z^-~]" nil 1)
1917           ;; Quote fullname, escaping specials.
1918           (goto-char (point-min))
1919           (insert "\"")
1920           (while (re-search-forward "[\"\\]" nil 1)
1921             (replace-match "\\\\\\&" t))
1922           (insert "\""))
1923         (insert " <" login ">"))
1924        (t                               ; 'parens or default
1925         (insert login " (")
1926         (let ((fullname-start (point)))
1927           (insert fullname)
1928           (goto-char fullname-start)
1929           ;; RFC 822 says \ and nonmatching parentheses
1930           ;; must be escaped in comments.
1931           ;; Escape every instance of ()\ ...
1932           (while (re-search-forward "[()\\]" nil 1)
1933             (replace-match "\\\\\\&" t))
1934           ;; ... then undo escaping of matching parentheses,
1935           ;; including matching nested parentheses.
1936           (goto-char fullname-start)
1937           (while (re-search-forward 
1938                   "\\(\\=\\|[^\\]\\(\\\\\\\\\\)*\\)\\\\(\\(\\([^\\]\\|\\\\\\\\\\)*\\)\\\\)"
1939                     nil 1)
1940             (replace-match "\\1(\\3)" t)
1941             (goto-char fullname-start)))
1942         (insert ")")))
1943       (buffer-string))))
1944
1945 (defun message-make-sender ()
1946   "Return the \"real\" user address.
1947 This function tries to ignore all user modifications, and 
1948 give as trustworthy answer as possible."
1949   (concat (user-login-name) "@" (system-name)))
1950
1951 (defun message-make-address ()
1952   "Make the address of the user."
1953   (or (message-user-mail-address)
1954       (concat (user-login-name) "@" (message-make-domain))))
1955
1956 (defun message-user-mail-address ()
1957   "Return the pertinent part of `user-mail-address'."
1958   (when user-mail-address
1959     (nth 1 (mail-extract-address-components user-mail-address))))
1960
1961 (defun message-make-fqdm ()
1962   "Return user's fully qualified domain name."
1963   (let ((system-name (system-name)))
1964     (cond 
1965      ((string-match "[^.]\\.[^.]" system-name)
1966       ;; `system-name' returned the right result.
1967       system-name)
1968      ;; We try `user-mail-address' as a backup.
1969      ((string-match "@\\(.*\\)\\'" (message-user-mail-address))
1970       (match-string 1 user-mail-address))
1971      ;; Try `mail-host-address'.
1972      ((and (boundp 'mail-host-address)
1973            mail-host-address)
1974       mail-host-address)
1975      ;; Default to this bogus thing.
1976      (t
1977       (concat system-name ".i-have-a-misconfigured-system-so-shoot-me")))))
1978
1979 (defun message-make-host-name ()
1980   "Return the name of the host."
1981   (let ((fqdm (message-make-fqdm)))
1982     (string-match "^[^.]+\\." fqdm)
1983     (substring fqdm 0 (1- (match-end 0)))))
1984
1985 (defun message-make-domain ()
1986   "Return the domain name."
1987   (or mail-host-address
1988       (message-make-fqdm)))
1989
1990 (defun message-generate-headers (headers)
1991   "Prepare article HEADERS.
1992 Headers already prepared in the buffer are not modified."
1993   (save-restriction
1994     (message-narrow-to-headers)
1995     (let* ((Date (message-make-date))
1996            (Message-ID (message-make-message-id))
1997            (Organization (message-make-organization))
1998            (From (message-make-from))
1999            (Path (message-make-path))
2000            (Subject nil)
2001            (Newsgroups nil)
2002            (In-Reply-To (message-make-in-reply-to))
2003            (To nil)
2004            (Distribution (message-make-distribution))
2005            (Lines (message-make-lines))
2006            (X-Newsreader message-newsreader)
2007            (X-Mailer (and (not (message-fetch-field "X-Newsreader"))
2008                           message-mailer))
2009            (Expires (message-make-expires))
2010            (case-fold-search t)
2011            header value elem)
2012       ;; First we remove any old generated headers.
2013       (let ((headers message-deletable-headers))
2014         (while headers
2015           (goto-char (point-min))
2016           (and (re-search-forward 
2017                 (concat "^" (symbol-name (car headers)) ": *") nil t)
2018                (get-text-property (1+ (match-beginning 0)) 'message-deletable)
2019                (message-delete-line))
2020           (pop headers)))
2021       ;; Go through all the required headers and see if they are in the
2022       ;; articles already. If they are not, or are empty, they are
2023       ;; inserted automatically - except for Subject, Newsgroups and
2024       ;; Distribution. 
2025       (while headers
2026         (goto-char (point-min))
2027         (setq elem (pop headers))
2028         (if (consp elem)
2029             (if (eq (car elem) 'optional)
2030                 (setq header (cdr elem))
2031               (setq header (car elem)))
2032           (setq header elem))
2033         (when (or (not (re-search-forward 
2034                         (concat "^" (downcase (symbol-name header)) ":") 
2035                         nil t))
2036                   (progn
2037                     ;; The header was found. We insert a space after the
2038                     ;; colon, if there is none.
2039                     (if (/= (following-char) ? ) (insert " ") (forward-char 1))
2040                     ;; Find out whether the header is empty...
2041                     (looking-at "[ \t]*$")))
2042           ;; So we find out what value we should insert.
2043           (setq value
2044                 (cond 
2045                  ((and (consp elem) (eq (car elem) 'optional))
2046                   ;; This is an optional header.  If the cdr of this
2047                   ;; is something that is nil, then we do not insert
2048                   ;; this header.
2049                   (setq header (cdr elem))
2050                   (or (and (fboundp (cdr elem)) (funcall (cdr elem)))
2051                       (and (boundp (cdr elem)) (symbol-value (cdr elem)))))
2052                  ((consp elem)
2053                   ;; The element is a cons.  Either the cdr is a
2054                   ;; string to be inserted verbatim, or it is a
2055                   ;; function, and we insert the value returned from
2056                   ;; this function.
2057                   (or (and (stringp (cdr elem)) (cdr elem))
2058                       (and (fboundp (cdr elem)) (funcall (cdr elem)))))
2059                  ((and (boundp header) (symbol-value header))
2060                   ;; The element is a symbol.  We insert the value
2061                   ;; of this symbol, if any.
2062                   (symbol-value header))
2063                  (t
2064                   ;; We couldn't generate a value for this header,
2065                   ;; so we just ask the user.
2066                   (read-from-minibuffer
2067                    (format "Empty header for %s; enter value: " header)))))
2068           ;; Finally insert the header.
2069           (when (and value 
2070                      (not (equal value "")))
2071             (save-excursion
2072               (if (bolp)
2073                   (progn
2074                     ;; This header didn't exist, so we insert it.
2075                     (goto-char (point-max))
2076                     (insert (symbol-name header) ": " value "\n")
2077                     (forward-line -1))
2078                 ;; The value of this header was empty, so we clear
2079                 ;; totally and insert the new value.
2080                 (delete-region (point) (message-point-at-eol))
2081                 (insert value))
2082               ;; Add the deletable property to the headers that require it.
2083               (and (memq header message-deletable-headers)
2084                    (progn (beginning-of-line) (looking-at "[^:]+: "))
2085                    (add-text-properties 
2086                     (point) (match-end 0)
2087                     '(message-deletable t face italic) (current-buffer)))))))
2088       ;; Insert new Sender if the From is strange. 
2089       (let ((from (message-fetch-field "from"))
2090             (sender (message-fetch-field "sender"))
2091             (secure-sender (message-make-sender)))
2092         (when (and from 
2093                    (not (message-check-element 'sender))
2094                    (not (string=
2095                          (downcase
2096                           (cadr (mail-extract-address-components from)))
2097                          (downcase secure-sender)))
2098                    (or (null sender)
2099                        (not 
2100                         (string=
2101                          (downcase
2102                           (cadr (mail-extract-address-components sender)))
2103                          (downcase secure-sender)))))
2104           (goto-char (point-min))    
2105           ;; Rename any old Sender headers to Original-Sender.
2106           (when (re-search-forward "^Sender:" nil t)
2107             (beginning-of-line)
2108             (insert "Original-")
2109             (beginning-of-line))
2110           (insert "Sender: " secure-sender "\n"))))))
2111
2112 (defun message-insert-courtesy-copy ()
2113   "Insert a courtesy message in mail copies of combined messages."
2114   (save-excursion
2115     (save-restriction
2116       (message-narrow-to-headers)
2117       (let ((newsgroups (message-fetch-field "newsgroups")))
2118         (when newsgroups
2119           (goto-char (point-max))
2120           (insert "Posted-To: " newsgroups "\n"))))
2121     (forward-line 1)
2122     (insert message-courtesy-message)))
2123     
2124 ;;;
2125 ;;; Setting up a message buffer
2126 ;;;
2127
2128 (defun message-fill-header (header value)
2129   (let ((begin (point))
2130         (fill-column 78)
2131         (fill-prefix "\t"))
2132     (insert (capitalize (symbol-name header))
2133             ": "
2134             (if (consp value) (car value) value)
2135             "\n")
2136     (save-restriction
2137       (narrow-to-region begin (point))
2138       (fill-region-as-paragraph begin (point))
2139       ;; Tapdance around looong Message-IDs.
2140       (forward-line -1)
2141       (when (looking-at "[ \t]*$")
2142         (message-delete-line))
2143       (goto-char begin)
2144       (re-search-forward ":" nil t)
2145       (when (looking-at "\n[ \t]+")
2146         (replace-match " " t t))
2147       (goto-char (point-max)))))
2148
2149 (defun message-position-point ()
2150   "Move point to where the user probably wants to find it."
2151   (message-narrow-to-headers)
2152   (cond 
2153    ((re-search-forward "^[^:]+:[ \t]*$" nil t)
2154     (search-backward ":" )
2155     (widen)
2156     (forward-char 1)
2157     (if (= (following-char) ? )
2158         (forward-char 1)
2159       (insert " ")))
2160    (t
2161     (goto-char (point-max))
2162     (widen)
2163     (forward-line 1)
2164     (unless (looking-at "$")
2165       (forward-line 2)))
2166    (sit-for 0)))
2167
2168 (defun message-buffer-name (type &optional to group)
2169   "Return a new (unique) buffer name based on TYPE and TO."
2170   (if message-generate-new-buffers
2171       (generate-new-buffer-name
2172        (concat "*" type
2173                (if (or to group)
2174                    (concat " to "
2175                            (or (car (mail-extract-address-components to))
2176                                to)
2177                            (if group (concat " on " group) ""))
2178                  "")
2179                "*"))
2180     (format "*%s message*" type)))
2181
2182 (defun message-pop-to-buffer (name)
2183   "Pop to buffer NAME, and warn if it already exists and is modified."
2184   (let ((buffer (get-buffer name)))
2185     (if (and buffer
2186              (buffer-name buffer))
2187         (progn
2188           (set-buffer (pop-to-buffer buffer))
2189           (when (and (buffer-modified-p)
2190                      (not (y-or-n-p
2191                            "Message already being composed; erase? ")))
2192             (error "Message being composed")))
2193       (set-buffer (pop-to-buffer name))))
2194   (erase-buffer)
2195   (message-mode))
2196
2197 (defun message-do-send-housekeeping ()
2198   "Kill old message buffers."
2199   ;; We might have sent this buffer already.  Delete it from the
2200   ;; list of buffers.
2201   (setq message-buffer-list (delq (current-buffer) message-buffer-list))
2202   (when (and message-max-buffers
2203              (>= (length message-buffer-list) message-max-buffers))
2204     ;; Kill the oldest buffer -- unless it has been changed.
2205     (let ((buffer (pop message-buffer-list)))
2206       (when (and (buffer-name buffer)
2207                  (not (buffer-modified-p buffer)))
2208         (kill-buffer buffer))))
2209   ;; Rename the buffer.
2210   (when (string-match "\\`\\*" (buffer-name))
2211     (rename-buffer 
2212      (concat "*sent " (substring (buffer-name) (match-end 0))) t))
2213   ;; Push the current buffer onto the list.
2214   (when message-max-buffers
2215     (setq message-buffer-list 
2216           (nconc message-buffer-list (list (current-buffer))))))
2217
2218 (defun message-setup (headers &optional replybuffer actions)
2219   (when actions
2220     (setq message-send-actions actions))
2221   (setq message-reply-buffer replybuffer)
2222   (goto-char (point-min))
2223   ;; Insert all the headers.
2224   (mail-header-format 
2225    (let ((h headers)
2226          (alist message-header-format-alist))
2227      (while h
2228        (unless (assq (caar h) message-header-format-alist)
2229          (push (list (caar h)) alist))
2230        (pop h))
2231      alist)
2232    headers)
2233   (forward-line -1)
2234   (when message-default-headers
2235     (insert message-default-headers))
2236   (put-text-property
2237    (point)
2238    (progn
2239      (insert mail-header-separator "\n")
2240      (point))
2241    'read-only nil)
2242   (forward-line -1)
2243   (when (message-news-p)
2244     (when message-default-news-headers
2245       (insert message-default-news-headers))
2246     (when message-generate-headers-first
2247       (message-generate-headers
2248        (delq 'Lines
2249              (delq 'Subject
2250                    (copy-sequence message-required-news-headers))))))
2251   (when (message-mail-p)
2252     (when message-default-mail-headers
2253       (insert message-default-mail-headers))
2254     (when message-generate-headers-first
2255       (message-generate-headers
2256        (delq 'Lines
2257              (delq 'Subject
2258                    (copy-sequence message-required-mail-headers))))))
2259   (message-insert-signature)
2260   (message-set-auto-save-file-name)
2261   (save-restriction
2262     (message-narrow-to-headers)
2263     (run-hooks 'message-header-setup-hook))
2264   (set-buffer-modified-p nil)
2265   (run-hooks 'message-setup-hook)
2266   (message-position-point)
2267   (undo-boundary))
2268
2269 (defun message-set-auto-save-file-name ()
2270   "Associate the message buffer with a file in the drafts directory."
2271   (when message-autosave-directory
2272     (unless (file-exists-p message-autosave-directory)
2273       (make-directory message-autosave-directory t))
2274     (let ((name (make-temp-name
2275                  (concat (file-name-as-directory message-autosave-directory)
2276                          "msg."))))
2277       (setq buffer-auto-save-file-name
2278             (save-excursion
2279               (prog1
2280                   (progn
2281                     (set-buffer (get-buffer-create " *draft tmp*"))
2282                     (setq buffer-file-name name)
2283                     (make-auto-save-file-name))
2284                 (kill-buffer (current-buffer)))))
2285       (clear-visited-file-modtime))))
2286
2287 \f
2288
2289 ;;;
2290 ;;; Commands for interfacing with message
2291 ;;;
2292
2293 ;;;###autoload
2294 (defun message-mail (&optional to subject)
2295   "Start editing a mail message to be sent."
2296   (interactive)
2297   (message-pop-to-buffer (message-buffer-name "mail" to))
2298   (message-setup `((To . ,(or to "")) (Subject . ,(or subject "")))))
2299
2300 ;;;###autoload
2301 (defun message-news (&optional newsgroups subject)
2302   "Start editing a news article to be sent."
2303   (interactive)
2304   (message-pop-to-buffer (message-buffer-name "news" nil newsgroups))
2305   (message-setup `((Newsgroups . ,(or newsgroups "")) 
2306                    (Subject . ,(or subject "")))))
2307
2308 ;;;###autoload
2309 (defun message-reply (&optional to-address wide ignore-reply-to)
2310   "Start editing a reply to the article in the current buffer."
2311   (interactive)
2312   (let ((cur (current-buffer))
2313         from subject date reply-to to cc
2314         references message-id follow-to 
2315         mct never-mct gnus-warning)
2316     (save-restriction
2317       (narrow-to-region
2318        (goto-char (point-min))
2319        (if (search-forward "\n\n" nil t)
2320            (1- (point))
2321          (point-max)))
2322       ;; Allow customizations to have their say.
2323       (if (not wide)
2324           ;; This is a regular reply.
2325           (if (message-functionp message-reply-to-function)
2326               (setq follow-to (funcall message-reply-to-function)))
2327         ;; This is a followup.
2328         (if (message-functionp message-wide-reply-to-function)
2329             (save-excursion
2330               (setq follow-to
2331                     (funcall message-wide-reply-to-function)))))
2332       ;; Find all relevant headers we need.
2333       (setq from (message-fetch-field "from")
2334             date (message-fetch-field "date") 
2335             subject (or (message-fetch-field "subject") "none")
2336             to (message-fetch-field "to")
2337             cc (message-fetch-field "cc")
2338             mct (message-fetch-field "mail-copies-to")
2339             reply-to (unless ignore-reply-to (message-fetch-field "reply-to"))
2340             references (message-fetch-field "references")
2341             message-id (message-fetch-field "message-id"))
2342       ;; Remove any (buggy) Re:'s that are present and make a
2343       ;; proper one.
2344       (when (string-match "^[ \t]*[Rr][Ee]:[ \t]*" subject)
2345         (setq subject (substring subject (match-end 0))))
2346       (setq subject (concat "Re: " subject))
2347
2348       (when (and (setq gnus-warning (message-fetch-field "gnus-warning"))
2349                  (string-match "<[^>]+>" gnus-warning))
2350         (setq message-id (match-string 0 gnus-warning)))
2351             
2352       ;; Handle special values of Mail-Copies-To.
2353       (when mct
2354         (cond ((equal (downcase mct) "never")
2355                (setq never-mct t)
2356                (setq mct nil))
2357               ((equal (downcase mct) "always")
2358                (setq mct (or reply-to from)))))
2359
2360       (unless follow-to
2361         (if (or (not wide)
2362                 to-address)
2363             (setq follow-to (list (cons 'To (or to-address reply-to from))))
2364           (let (ccalist)
2365             (save-excursion
2366               (message-set-work-buffer)
2367               (unless never-mct
2368                 (insert (or reply-to from "")))
2369               (insert 
2370                (if (bolp) "" ", ") (or to "")
2371                (if mct (concat (if (bolp) "" ", ") mct) "")
2372                (if cc (concat (if (bolp) "" ", ") cc) ""))
2373               ;; Remove addresses that match `rmail-dont-reply-to-names'. 
2374               (insert (prog1 (rmail-dont-reply-to (buffer-string))
2375                         (erase-buffer)))
2376               (goto-char (point-min))
2377               (setq ccalist
2378                     (mapcar
2379                      (lambda (addr)
2380                        (cons (mail-strip-quoted-names addr) addr))
2381                      (nreverse (mail-parse-comma-list))))
2382               (let ((s ccalist))
2383                 (while s
2384                   (setq ccalist (delq (assoc (car (pop s)) s) ccalist)))))
2385             (setq follow-to (list (cons 'To (cdr (pop ccalist)))))
2386             (when ccalist
2387               (push (cons 'Cc
2388                           (mapconcat (lambda (addr) (cdr addr)) ccalist ", "))
2389                     follow-to)))))
2390       (widen))
2391
2392     (message-pop-to-buffer (message-buffer-name "reply" from))
2393
2394     (setq message-reply-headers
2395           (vector 0 subject from date message-id references 0 0 ""))
2396
2397     (message-setup
2398      `((Subject . ,subject)
2399        ,@follow-to 
2400        ,@(if (or references message-id)
2401              `((References . ,(concat (or references "") (and references " ")
2402                                       (or message-id ""))))
2403            nil))
2404      cur)))
2405
2406 ;;;###autoload
2407 (defun message-wide-reply (&optional to-address)
2408   (interactive)
2409   (message-reply to-address t))
2410
2411 ;;;###autoload
2412 (defun message-followup ()
2413   (interactive)
2414   (let ((cur (current-buffer))
2415         from subject date reply-to mct
2416         references message-id follow-to 
2417         followup-to distribution newsgroups gnus-warning)
2418     (save-restriction
2419       (narrow-to-region
2420        (goto-char (point-min))
2421        (if (search-forward "\n\n" nil t)
2422            (1- (point))
2423          (point-max)))
2424       (when (message-functionp message-followup-to-function)
2425         (setq follow-to
2426               (funcall message-followup-to-function)))
2427       (setq from (message-fetch-field "from")
2428             date (message-fetch-field "date") 
2429             subject (or (message-fetch-field "subject") "none")
2430             references (message-fetch-field "references")
2431             message-id (message-fetch-field "message-id")
2432             followup-to (message-fetch-field "followup-to")
2433             newsgroups (message-fetch-field "newsgroups")
2434             reply-to (message-fetch-field "reply-to")
2435             distribution (message-fetch-field "distribution")
2436             mct (message-fetch-field "mail-copies-to"))
2437       (when (and (setq gnus-warning (message-fetch-field "gnus-warning"))
2438                  (string-match "<[^>]+>" gnus-warning))
2439         (setq message-id (match-string 0 gnus-warning)))
2440       ;; Remove bogus distribution.
2441       (and (stringp distribution)
2442            (string-match "world" distribution)
2443            (setq distribution nil))
2444       ;; Remove any (buggy) Re:'s that are present and make a
2445       ;; proper one.
2446       (when (string-match "^[ \t]*[Rr][Ee]:[ \t]*" subject)
2447         (setq subject (substring subject (match-end 0))))
2448       (setq subject (concat "Re: " subject))
2449       (widen))
2450
2451     (message-pop-to-buffer (message-buffer-name "followup" from newsgroups))
2452
2453     (message-setup
2454      `((Subject . ,subject)
2455        ,@(cond 
2456           (follow-to follow-to)
2457           ((and followup-to message-use-followup-to)
2458            (list
2459             (cond 
2460              ((equal (downcase followup-to) "poster")
2461               (if (or (eq message-use-followup-to 'use)
2462                       (message-y-or-n-p "Obey Followup-To: poster? " t "\
2463 You should normally obey the Followup-To: header.
2464
2465 `Followup-To: poster' sends your response via e-mail instead of news.
2466
2467 A typical situation where `Followup-To: poster' is used is when the poster
2468 does not read the newsgroup, so he wouldn't see any replies sent to it."))
2469                   (cons 'To (or reply-to from ""))
2470                 (cons 'Newsgroups newsgroups)))
2471              (t
2472               (if (or (equal followup-to newsgroups)
2473                       (not (eq message-use-followup-to 'ask))
2474                       (message-y-or-n-p
2475                        (concat "Obey Followup-To: " followup-to "? ") t "\
2476 You should normally obey the Followup-To: header.
2477
2478         `Followup-To: " followup-to "'
2479 directs your response to " (if (string-match "," followup-to)
2480                                "the specified newsgroups"
2481                              "that newsgroup only") ".
2482
2483 If a message is posted to several newsgroups, Followup-To is often
2484 used to direct the following discussion to one newsgroup only,
2485 because discussions that are spread over several newsgroup tend to
2486 be fragmented and very difficult to follow.
2487
2488 Also, some source/announcment newsgroups are not indented for discussion;
2489 responses here are directed to other newsgroups."))
2490                   (cons 'Newsgroups followup-to)
2491                 (cons 'Newsgroups newsgroups))))))
2492           (t
2493            `((Newsgroups . ,newsgroups))))
2494        ,@(and distribution (list (cons 'Distribution distribution)))
2495        (References . ,(concat (or references "") (and references " ")
2496                               (or message-id "")))
2497        ,@(when (and mct
2498                     (not (equal (downcase mct) "never")))
2499            (list (cons 'Cc (if (equal (downcase mct) "always")
2500                                (or reply-to from "")
2501                              mct)))))
2502
2503      cur)
2504
2505     (setq message-reply-headers
2506           (vector 0 subject from date message-id references 0 0 ""))))
2507
2508
2509 ;;;###autoload
2510 (defun message-cancel-news ()
2511   "Cancel an article you posted."
2512   (interactive)
2513   (unless (message-news-p)
2514     (error "This is not a news article; canceling is impossible"))
2515   (when (yes-or-no-p "Do you really want to cancel this article? ")
2516     (let (from newsgroups message-id distribution buf)
2517       (save-excursion
2518         ;; Get header info. from original article.
2519         (save-restriction
2520           (message-narrow-to-head)
2521           (setq from (message-fetch-field "from")
2522                 newsgroups (message-fetch-field "newsgroups")
2523                 message-id (message-fetch-field "message-id")
2524                 distribution (message-fetch-field "distribution")))
2525         ;; Make sure that this article was written by the user.
2526         (unless (string-equal
2527                  (downcase (cadr (mail-extract-address-components from)))
2528                  (downcase (message-make-address)))
2529           (error "This article is not yours"))
2530         ;; Make control message.
2531         (setq buf (set-buffer (get-buffer-create " *message cancel*")))
2532         (buffer-disable-undo (current-buffer))
2533         (erase-buffer)
2534         (insert "Newsgroups: " newsgroups "\n"
2535                 "From: " (message-make-from) "\n"
2536                 "Subject: cmsg cancel " message-id "\n"
2537                 "Control: cancel " message-id "\n"
2538                 (if distribution
2539                     (concat "Distribution: " distribution "\n")
2540                   "")
2541                 mail-header-separator "\n"
2542                 "This is a cancel message from " from ".\n")
2543         (message "Canceling your article...")
2544         (let ((message-syntax-checks 'dont-check-for-anything-just-trust-me))
2545           (funcall message-send-news-function))
2546         (message "Canceling your article...done")
2547         (kill-buffer buf)))))
2548
2549 ;;;###autoload
2550 (defun message-supersede ()
2551   "Start composing a message to supersede the current message.
2552 This is done simply by taking the old article and adding a Supersedes
2553 header line with the old Message-ID."
2554   (interactive)
2555   (let ((cur (current-buffer)))
2556     ;; Check whether the user owns the article that is to be superseded. 
2557     (unless (string-equal
2558              (downcase (cadr (mail-extract-address-components
2559                               (message-fetch-field "from"))))
2560              (downcase (message-make-address)))
2561       (error "This article is not yours"))
2562     ;; Get a normal message buffer.
2563     (message-pop-to-buffer (message-buffer-name "supersede"))
2564     (insert-buffer-substring cur)
2565     (message-narrow-to-head)
2566     ;; Remove unwanted headers.
2567     (when message-ignored-supersedes-headers
2568       (message-remove-header message-ignored-supersedes-headers t))
2569     (goto-char (point-min))
2570     (if (not (re-search-forward "^Message-ID: " nil t))
2571         (error "No Message-ID in this article")
2572       (replace-match "Supersedes: " t t))
2573     (goto-char (point-max))
2574     (insert mail-header-separator)
2575     (widen)
2576     (forward-line 1)))
2577
2578 ;;;###autoload
2579 (defun message-recover ()
2580   "Reread contents of current buffer from its last auto-save file."
2581   (interactive)
2582   (let ((file-name (make-auto-save-file-name)))
2583     (cond ((save-window-excursion
2584              (if (not (eq system-type 'vax-vms))
2585                  (with-output-to-temp-buffer "*Directory*"
2586                    (buffer-disable-undo standard-output)
2587                    (let ((default-directory "/"))
2588                      (call-process
2589                       "ls" nil standard-output nil "-l" file-name))))
2590              (yes-or-no-p (format "Recover auto save file %s? " file-name)))
2591            (let ((buffer-read-only nil))
2592              (erase-buffer)
2593              (insert-file-contents file-name nil)))
2594           (t (error "message-recover cancelled")))))
2595
2596 ;;; Forwarding messages.
2597
2598 (defun message-make-forward-subject ()
2599   "Return a Subject header suitable for the message in the current buffer."
2600   (concat "[" (or (message-fetch-field (if (message-news-p) "newsgroups" "from"))
2601                   "(nowhere)")
2602           "] " (or (message-fetch-field "Subject") "")))
2603
2604 ;;;###autoload
2605 (defun message-forward (&optional news)
2606   "Forward the current message via mail.  
2607 Optional NEWS will use news to forward instead of mail."
2608   (interactive "P")
2609   (let ((cur (current-buffer))
2610         (subject (message-make-forward-subject)))
2611     (if news (message-news nil subject) (message-mail nil subject))
2612     ;; Put point where we want it before inserting the forwarded
2613     ;; message. 
2614     (if message-signature-before-forwarded-message
2615         (goto-char (point-max))
2616       (message-goto-body))
2617     ;; Make sure we're at the start of the line.
2618     (unless (eolp)
2619       (insert "\n"))
2620     ;; Narrow to the area we are to insert.
2621     (narrow-to-region (point) (point))
2622     ;; Insert the separators and the forwarded buffer.
2623     (insert message-forward-start-separator)
2624     (insert-buffer-substring cur)
2625     (goto-char (point-max))
2626     (insert message-forward-end-separator)
2627     (set-text-properties (point-min) (point-max) nil)
2628     ;; Remove all unwanted headers.
2629     (goto-char (point-min))
2630     (forward-line 1)
2631     (narrow-to-region (point) (if (search-forward "\n\n" nil t)
2632                                   (1- (point))
2633                                 (point)))
2634     (goto-char (point-min))
2635     (message-remove-header message-included-forward-headers t nil t)
2636     (widen)
2637     (message-position-point)))
2638
2639 ;;;###autoload
2640 (defun message-resend (address)
2641   "Resend the current article to ADDRESS."
2642   (interactive "sResend message to: ")
2643   (save-excursion
2644     (let ((cur (current-buffer))
2645           beg)
2646       ;; We first set up a normal mail buffer.
2647       (set-buffer (get-buffer-create " *message resend*"))
2648       (buffer-disable-undo (current-buffer))
2649       (erase-buffer)
2650       (message-setup `((To . ,address)))
2651       ;; Insert our usual headers.
2652       (message-generate-headers '(From Date To))
2653       (message-narrow-to-headers)
2654       ;; Rename them all to "Resent-*".
2655       (while (re-search-forward "^[A-Za-z]" nil t)
2656         (forward-char -1)
2657         (insert "Resent-"))
2658       (widen)
2659       (forward-line)
2660       (delete-region (point) (point-max))
2661       (setq beg (point))
2662       ;; Insert the message to be resent.
2663       (insert-buffer-substring cur)
2664       (goto-char (point-min))
2665       (search-forward "\n\n")
2666       (forward-char -1)
2667       (save-restriction
2668         (narrow-to-region beg (point))
2669         (message-remove-header message-ignored-resent-headers t)
2670         (goto-char (point-max)))
2671       (insert mail-header-separator)
2672       ;; Rename all old ("Also-")Resent headers.
2673       (while (re-search-backward "^\\(Also-\\)?Resent-" beg t)
2674         (beginning-of-line)
2675         (insert "Also-"))
2676       ;; Send it.
2677       (message-send-mail)
2678       (kill-buffer (current-buffer)))))
2679
2680 ;;;###autoload
2681 (defun message-bounce ()
2682   "Re-mail the current message.
2683 This only makes sense if the current message is a bounce message than
2684 contains some mail you have written which has been bounced back to
2685 you."
2686   (interactive)
2687   (let ((cur (current-buffer))
2688         boundary)
2689     (message-pop-to-buffer (message-buffer-name "bounce"))
2690     (insert-buffer-substring cur)
2691     (undo-boundary)
2692     (message-narrow-to-head)
2693     (if (and (message-fetch-field "Mime-Version")
2694              (setq boundary (message-fetch-field "Content-Type")))
2695         (if (string-match "boundary=\"\\([^\"]+\\)\"" boundary)
2696             (setq boundary (concat (match-string 1 boundary) " *\n"
2697                                    "Content-Type: message/rfc822"))
2698           (setq boundary nil)))
2699     (widen)
2700     (goto-char (point-min))
2701     (search-forward "\n\n" nil t)
2702     (or (and boundary
2703              (re-search-forward boundary nil t)
2704              (forward-line 2))
2705         (and (re-search-forward message-unsent-separator nil t)
2706              (forward-line 1))
2707         (and (search-forward "\n\n" nil t)
2708              (re-search-forward "^Return-Path:.*\n" nil t)))
2709     ;; We remove everything before the bounced mail.
2710     (delete-region 
2711      (point-min)
2712      (if (re-search-forward "[^ \t]*:" nil t)
2713          (match-beginning 0)
2714        (point)))
2715     (save-restriction
2716       (message-narrow-to-head)
2717       (message-remove-header message-ignored-bounced-headers t)
2718       (goto-char (point-max))
2719       (insert mail-header-separator))
2720     (message-position-point)))
2721
2722 ;;;
2723 ;;; Interactive entry points for new message buffers.
2724 ;;;
2725
2726 ;;;###autoload
2727 (defun message-mail-other-window (&optional to subject)
2728   "Like `message-mail' command, but display mail buffer in another window."
2729   (interactive)
2730   (let ((pop-up-windows t)
2731         (special-display-buffer-names nil)
2732         (special-display-regexps nil)
2733         (same-window-buffer-names nil)
2734         (same-window-regexps nil))
2735     (message-pop-to-buffer (message-buffer-name "mail" to)))
2736   (message-setup `((To . ,(or to "")) (Subject . ,(or subject "")))))
2737
2738 ;;;###autoload
2739 (defun message-mail-other-frame (&optional to subject)
2740   "Like `message-mail' command, but display mail buffer in another frame."
2741   (interactive)
2742   (let ((pop-up-frames t)
2743         (special-display-buffer-names nil)
2744         (special-display-regexps nil)
2745         (same-window-buffer-names nil)
2746         (same-window-regexps nil))
2747     (message-pop-to-buffer (message-buffer-name "mail" to)))
2748   (message-setup `((To . ,(or to "")) (Subject . ,(or subject "")))))
2749
2750 ;;;###autoload
2751 (defun message-news-other-window (&optional newsgroups subject)
2752   "Start editing a news article to be sent."
2753   (interactive)
2754   (let ((pop-up-windows t)
2755         (special-display-buffer-names nil)
2756         (special-display-regexps nil)
2757         (same-window-buffer-names nil)
2758         (same-window-regexps nil))
2759     (message-pop-to-buffer (message-buffer-name "news" nil newsgroups)))
2760   (message-setup `((Newsgroups . ,(or newsgroups "")) 
2761                    (Subject . ,(or subject "")))))
2762
2763 ;;;###autoload
2764 (defun message-news-other-frame (&optional newsgroups subject)
2765   "Start editing a news article to be sent."
2766   (interactive)
2767   (let ((pop-up-frames t)
2768         (special-display-buffer-names nil)
2769         (special-display-regexps nil)
2770         (same-window-buffer-names nil)
2771         (same-window-regexps nil))
2772     (message-pop-to-buffer (message-buffer-name "news" nil newsgroups)))
2773   (message-setup `((Newsgroups . ,(or newsgroups "")) 
2774                    (Subject . ,(or subject "")))))
2775
2776 ;;; underline.el
2777
2778 ;; This code should be moved to underline.el (from which it is stolen). 
2779
2780 ;;;###autoload
2781 (defun bold-region (start end)
2782   "Bold all nonblank characters in the region.
2783 Works by overstriking characters.
2784 Called from program, takes two arguments START and END
2785 which specify the range to operate on."
2786   (interactive "r")
2787   (save-excursion
2788    (let ((end1 (make-marker)))
2789      (move-marker end1 (max start end))
2790      (goto-char (min start end))
2791      (while (< (point) end1)
2792        (or (looking-at "[_\^@- ]")
2793            (insert (following-char) "\b"))
2794        (forward-char 1)))))
2795
2796 ;;;###autoload
2797 (defun unbold-region (start end)
2798   "Remove all boldness (overstruck characters) in the region.
2799 Called from program, takes two arguments START and END
2800 which specify the range to operate on."
2801   (interactive "r")
2802   (save-excursion
2803    (let ((end1 (make-marker)))
2804      (move-marker end1 (max start end))
2805      (goto-char (min start end)) 
2806      (while (re-search-forward "\b" end1 t)
2807        (if (eq (following-char) (char-after (- (point) 2)))
2808            (delete-char -2))))))
2809
2810 ;; Support for toolbar
2811 (when (string-match "XEmacs\\|Lucid" emacs-version)
2812   (require 'messagexmas))
2813
2814 ;;; Group name completion.
2815
2816 (defvar message-newgroups-header-regexp
2817   "^\\(Newsgroups\\|Followup-To\\|Posted-To\\):"
2818   "Regexp that match headers that lists groups.")
2819
2820 (defun message-tab ()
2821   "Expand group names in Newsgroups and Followup-To headers.
2822 Do a `tab-to-tab-stop' if not in those headers."
2823   (interactive)
2824   (if (let ((mail-abbrev-mode-regexp message-newgroups-header-regexp))
2825         (mail-abbrev-in-expansion-header-p))
2826       (message-expand-group)
2827     (tab-to-tab-stop)))
2828
2829 (defvar gnus-active-hashtb)
2830 (defun message-expand-group ()
2831   (let* ((b (save-excursion (skip-chars-backward "^, :\t\n") (point)))
2832          (completion-ignore-case t)
2833          (string (buffer-substring b (point)))
2834          (hashtb (and (boundp 'gnus-active-hashtb) gnus-active-hashtb))
2835          (completions (all-completions string hashtb))
2836          (cur (current-buffer))
2837          comp)
2838     (delete-region b (point))
2839     (cond 
2840      ((= (length completions) 1)
2841       (if (string= (car completions) string)
2842           (progn
2843             (insert string)
2844             (message "Only matching group"))
2845         (insert (car completions))))
2846      ((and (setq comp (try-completion string hashtb))
2847            (not (string= comp string)))
2848       (insert comp))
2849      (t
2850       (insert string)
2851       (if (not comp)
2852           (message "No matching groups")
2853         (pop-to-buffer "*Completions*")
2854         (buffer-disable-undo (current-buffer))
2855         (let ((buffer-read-only nil))
2856           (erase-buffer)
2857           (let ((standard-output (current-buffer)))
2858             (display-completion-list (sort completions 'string<)))
2859           (goto-char (point-min))
2860           (pop-to-buffer cur)))))))
2861
2862 ;;; Help stuff.
2863
2864 (defmacro message-y-or-n-p (question show &rest text)
2865   "Ask QUESTION, displaying the rest of the arguments in a temporary buffer."
2866   `(message-talkative-question 'y-or-n-p ,question ,show ,@text))
2867
2868 (defun message-talkative-question (ask question show &rest text)
2869   "Call FUNCTION with argument QUESTION, displaying the rest of the arguments in a temporary buffer if SHOW.  
2870 The following arguments may contain lists of values."
2871   (if (and show
2872            (setq text (message-flatten-list text)))
2873       (save-window-excursion
2874         (save-excursion
2875           (with-output-to-temp-buffer " *MESSAGE information message*"
2876             (set-buffer " *MESSAGE information message*")
2877             (mapcar 'princ text)
2878             (goto-char (point-min))))
2879         (funcall ask question))
2880     (funcall ask question)))
2881
2882 (defun message-flatten-list (&rest list)
2883   (message-flatten-list-1 list))
2884
2885 (defun message-flatten-list-1 (list)
2886   (cond ((consp list) 
2887          (apply 'append (mapcar 'message-flatten-list-1 list)))
2888         (list
2889          (list list))))
2890
2891 (provide 'message)
2892
2893 ;;; message.el ends here