Merge from emacs--rel--22
[bpt/emacs.git] / lisp / mh-e / mh-comp.el
1 ;;; mh-comp.el --- MH-E functions for composing and sending messages
2
3 ;; Copyright (C) 1993, 1995, 1997,
4 ;; 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
5
6 ;; Author: Bill Wohler <wohler@newt.com>
7 ;; Maintainer: Bill Wohler <wohler@newt.com>
8 ;; Keywords: mail
9 ;; See: mh-e.el
10
11 ;; This file is part of GNU Emacs.
12
13 ;; GNU Emacs is free software; you can redistribute it and/or modify
14 ;; it under the terms of the GNU General Public License as published by
15 ;; the Free Software Foundation; either version 3, or (at your option)
16 ;; any later version.
17
18 ;; GNU Emacs is distributed in the hope that it will be useful,
19 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 ;; GNU General Public License for more details.
22
23 ;; You should have received a copy of the GNU General Public License
24 ;; along with GNU Emacs; see the file COPYING. If not, write to the
25 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
26 ;; Boston, MA 02110-1301, USA.
27
28 ;;; Commentary:
29
30 ;; This file includes the functions in the MH-Folder maps that get us
31 ;; into MH-Letter mode, as well the functions in the MH-Letter mode
32 ;; that are used to send the mail. Other that those, functions that
33 ;; are needed in mh-letter.el should be found there.
34
35 ;;; Change Log:
36
37 ;;; Code:
38
39 (require 'mh-e)
40 (require 'mh-gnus) ;needed because mh-gnus.el not compiled
41 (require 'mh-scan)
42
43 (require 'sendmail)
44
45 (autoload 'easy-menu-add "easymenu")
46 (autoload 'mml-insert-tag "mml")
47
48 \f
49
50 ;;; Site Customization
51
52 (defvar mh-send-prog "send"
53 "Name of the MH send program.
54 Some sites need to change this because of a name conflict.")
55
56 (defvar mh-send-uses-spost-flag nil
57 "Non-nil means \"send\" uses \"spost\" to submit messages.
58
59 If the value of \"postproc:\" is \"spost\", you may need to set
60 this variable to t to tell MH-E to avoid using features of
61 \"post\" that are not supported by \"spost\". You'll know that
62 you'll need to do this if sending mail fails with an error of
63 \"spost: -msgid unknown\".")
64
65 (defvar mh-redist-background nil
66 "If non-nil redist will be done in background like send.
67 This allows transaction log to be visible if -watch, -verbose or
68 -snoop are used.")
69
70 \f
71
72 ;;; Variables
73
74 (defvar mh-comp-formfile "components"
75 "Name of file to be used as a skeleton for composing messages.
76
77 Default is \"components\".
78
79 If not an absolute file name, the file is searched for first in the
80 user's MH directory, then in the system MH lib directory.")
81
82 (defvar mh-repl-formfile "replcomps"
83 "Name of file to be used as a skeleton for replying to messages.
84
85 Default is \"replcomps\".
86
87 If not an absolute file name, the file is searched for first in the
88 user's MH directory, then in the system MH lib directory.")
89
90 (defvar mh-repl-group-formfile "replgroupcomps"
91 "Name of file to be used as a skeleton for replying to messages.
92
93 Default is \"replgroupcomps\".
94
95 This file is used to form replies to the sender and all recipients of
96 a message. Only used if `(mh-variant-p 'nmh)' is non-nil.
97 If not an absolute file name, the file is searched for first in the
98 user's MH directory, then in the system MH lib directory.")
99
100 (defvar mh-rejected-letter-start
101 (format "^%s$"
102 (regexp-opt
103 '("Content-Type: message/rfc822" ;MIME MDN
104 "------ This is a copy of the message, including all the headers. ------";from exim
105 "--- Below this line is a copy of the message."; from qmail
106 " ----- Unsent message follows -----" ;from sendmail V5
107 " --------Unsent Message below:" ; from sendmail at BU
108 " ----- Original message follows -----" ;from sendmail V8
109 "------- Unsent Draft" ;from MH itself
110 "---------- Original Message ----------" ;from zmailer
111 " --- The unsent message follows ---" ;from AIX mail system
112 " Your message follows:" ;from MMDF-II
113 "Content-Description: Returned Content" ;1993 KJ sendmail
114 ))))
115
116 (defvar mh-new-draft-cleaned-headers
117 "^Date:\\|^Received:\\|^Message-Id:\\|^From:\\|^Sender:\\|^Errors-To:\\|^Delivery-Date:\\|^Return-Path:"
118 "Regexp of header lines to remove before offering a message as a new draft\\<mh-folder-mode-map>.
119 Used by the \\[mh-edit-again] and \\[mh-extract-rejected-mail] commands.")
120
121 (defvar mh-letter-mode-syntax-table nil
122 "Syntax table used by MH-E while in MH-Letter mode.")
123
124 (if mh-letter-mode-syntax-table
125 ()
126 (setq mh-letter-mode-syntax-table
127 (make-syntax-table text-mode-syntax-table))
128 (modify-syntax-entry ?% "." mh-letter-mode-syntax-table))
129
130 (defvar mh-send-args ""
131 "Extra args to pass to \"send\" command.")
132
133 (defvar mh-annotate-char nil
134 "Character to use to annotate `mh-sent-from-msg'.")
135
136 (defvar mh-annotate-field nil
137 "Field name for message annotation.")
138
139 (defvar mh-annotate-list nil
140 "Messages annotated, either a sequence name or a list of message numbers.
141 This variable can be used by `mh-annotate-msg-hook'.")
142
143 (defvar mh-insert-auto-fields-done-local nil
144 "Buffer-local variable set when `mh-insert-auto-fields' called successfully.")
145 (make-variable-buffer-local 'mh-insert-auto-fields-done-local)
146
147 \f
148
149 ;;; MH-E Entry Points
150
151 ;;;###autoload
152 (defun mh-smail ()
153 "Compose a message with the MH mail system.
154 See `mh-send' for more details on composing mail."
155 (interactive)
156 (mh-find-path)
157 (call-interactively 'mh-send))
158
159 ;;;###autoload
160 (defun mh-smail-other-window ()
161 "Compose a message with the MH mail system in other window.
162 See `mh-send' for more details on composing mail."
163 (interactive)
164 (mh-find-path)
165 (call-interactively 'mh-send-other-window))
166
167 (defun mh-send-other-window (to cc subject)
168 "Compose a message in another window.
169
170 See `mh-send' for more information and a description of how the
171 TO, CC, and SUBJECT arguments are used."
172 (interactive (list
173 (mh-interactive-read-address "To: ")
174 (mh-interactive-read-address "Cc: ")
175 (mh-interactive-read-string "Subject: ")))
176 (let ((pop-up-windows t))
177 (mh-send-sub to cc subject (current-window-configuration))))
178
179 (defvar mh-error-if-no-draft nil) ;raise error over using old draft
180
181 ;;;###autoload
182 (defun mh-smail-batch (&optional to subject other-headers &rest ignored)
183 "Compose a message with the MH mail system.
184
185 This function does not prompt the user for any header fields, and
186 thus is suitable for use by programs that want to create a mail
187 buffer. Users should use \\[mh-smail] to compose mail.
188
189 Optional arguments for setting certain fields include TO,
190 SUBJECT, and OTHER-HEADERS. Additional arguments are IGNORED.
191
192 This function remains for Emacs 21 compatibility. New
193 applications should use `mh-user-agent-compose'."
194 (mh-find-path)
195 (let ((mh-error-if-no-draft t))
196 (mh-send (or to "") "" (or subject ""))))
197
198 ;;;###autoload
199 (define-mail-user-agent 'mh-e-user-agent
200 'mh-user-agent-compose 'mh-send-letter 'mh-fully-kill-draft
201 'mh-before-send-letter-hook)
202
203 ;;;###autoload
204 (defun mh-user-agent-compose (&optional to subject other-headers continue
205 switch-function yank-action
206 send-actions)
207 "Set up mail composition draft with the MH mail system.
208 This is the `mail-user-agent' entry point to MH-E. This function
209 conforms to the contract specified by `define-mail-user-agent'
210 which means that this function should accept the same arguments
211 as `compose-mail'.
212
213 The optional arguments TO and SUBJECT specify recipients and the
214 initial Subject field, respectively.
215
216 OTHER-HEADERS is an alist specifying additional header fields.
217 Elements look like (HEADER . VALUE) where both HEADER and VALUE
218 are strings.
219
220 CONTINUE, SWITCH-FUNCTION, YANK-ACTION and SEND-ACTIONS are
221 ignored."
222 (mh-find-path)
223 (let ((mh-error-if-no-draft t))
224 (mh-send to "" subject)
225 (while other-headers
226 (mh-insert-fields (concat (car (car other-headers)) ":")
227 (cdr (car other-headers)))
228 (setq other-headers (cdr other-headers)))))
229
230 ;; Shush compiler.
231 (defvar sendmail-coding-system) ; XEmacs
232
233 ;;;###autoload
234 (defun mh-send-letter (&optional arg)
235 "Save draft and send message.
236
237 When you are all through editing a message, you send it with this
238 command. You can give a prefix argument ARG to monitor the first stage
239 of the delivery\; this output can be found in a buffer called \"*MH-E
240 Mail Delivery*\".
241
242 The hook `mh-before-send-letter-hook' is run at the beginning of
243 this command. For example, if you want to check your spelling in
244 your message before sending, add the function `ispell-message'.
245
246 Unless `mh-insert-auto-fields' had previously been called
247 manually, the function `mh-insert-auto-fields' is called to
248 insert fields based upon the recipients. If fields are added, you
249 are given a chance to see and to confirm these fields before the
250 message is actually sent. You can do away with this confirmation
251 by turning off the option `mh-auto-fields-prompt-flag'.
252
253 In case the MH \"send\" program is installed under a different name,
254 use `mh-send-prog' to tell MH-E the name."
255 (interactive "P")
256 (run-hooks 'mh-before-send-letter-hook)
257 (if (and (mh-insert-auto-fields t)
258 mh-auto-fields-prompt-flag
259 (goto-char (point-min)))
260 (if (not (y-or-n-p "Auto fields inserted, send? "))
261 (error "Send aborted")))
262 (cond ((mh-mh-directive-present-p)
263 (mh-mh-to-mime))
264 ((or (mh-mml-tag-present-p) (not (mh-ascii-buffer-p)))
265 (mh-mml-to-mime)))
266 (save-buffer)
267 (message "Sending...")
268 (let ((draft-buffer (current-buffer))
269 (file-name buffer-file-name)
270 (config mh-previous-window-config)
271 (coding-system-for-write
272 (if (and (local-variable-p 'buffer-file-coding-system
273 (current-buffer)) ;XEmacs needs two args
274 ;; We're not sure why, but buffer-file-coding-system
275 ;; tends to get set to undecided-unix.
276 (not (memq buffer-file-coding-system
277 '(undecided undecided-unix undecided-dos))))
278 buffer-file-coding-system
279 (or (and (boundp 'sendmail-coding-system) sendmail-coding-system)
280 (and (boundp 'default-buffer-file-coding-system )
281 default-buffer-file-coding-system)
282 'iso-latin-1))))
283 ;; Older versions of spost do not support -msgid and -mime.
284 (unless mh-send-uses-spost-flag
285 ;; Adding a Message-ID field looks good, makes it easier to search for
286 ;; message in your +outbox, and best of all doesn't break threading for
287 ;; the recipient if you reply to a message in your +outbox.
288 (setq mh-send-args (concat "-msgid " mh-send-args))
289 ;; The default BCC encapsulation will make a MIME message unreadable.
290 ;; With nmh use the -mime arg to prevent this.
291 (if (and (mh-variant-p 'nmh)
292 (mh-goto-header-field "Bcc:")
293 (mh-goto-header-field "Content-Type:"))
294 (setq mh-send-args (concat "-mime " mh-send-args))))
295 (cond (arg
296 (pop-to-buffer mh-mail-delivery-buffer)
297 (erase-buffer)
298 (mh-exec-cmd-output mh-send-prog t
299 "-nodraftfolder" "-watch" "-nopush"
300 (split-string mh-send-args) file-name)
301 (goto-char (point-max)) ; show the interesting part
302 (recenter -1)
303 (set-buffer draft-buffer)) ; for annotation below
304 (t
305 (mh-exec-cmd-daemon mh-send-prog nil
306 "-nodraftfolder" "-noverbose"
307 (split-string mh-send-args) file-name)))
308 (if mh-annotate-char
309 (mh-annotate-msg mh-sent-from-msg
310 mh-sent-from-folder
311 mh-annotate-char
312 "-component" mh-annotate-field
313 "-text" (format "\"%s %s\""
314 (mh-get-header-field "To:")
315 (mh-get-header-field "Cc:"))))
316
317 (cond ((or (not arg)
318 (y-or-n-p "Kill draft buffer? "))
319 (kill-buffer draft-buffer)
320 (if config
321 (set-window-configuration config))))
322 (if arg
323 (message "Sending...done")
324 (message "Sending...backgrounded"))))
325
326 ;;;###autoload
327 (defun mh-fully-kill-draft ()
328 "Quit editing and delete draft message.
329
330 If for some reason you are not happy with the draft, you can use
331 this command to kill the draft buffer and delete the draft
332 message. Use the command \\[kill-buffer] if you don't want to
333 delete the draft message."
334 (interactive)
335 (if (y-or-n-p "Kill draft message? ")
336 (let ((config mh-previous-window-config))
337 (if (file-exists-p buffer-file-name)
338 (delete-file buffer-file-name))
339 (set-buffer-modified-p nil)
340 (kill-buffer (buffer-name))
341 (message "")
342 (if config
343 (set-window-configuration config)))
344 (error "Message not killed")))
345
346 \f
347
348 ;;; MH-Folder Commands
349
350 ;; Alphabetical.
351
352 ;;;###mh-autoload
353 (defun mh-edit-again (message)
354 "Edit a MESSAGE to send it again.
355
356 If you don't complete a draft for one reason or another, and if
357 the draft buffer is no longer available, you can pick your draft
358 up again with this command. If you don't use a draft folder, your
359 last \"draft\" file will be used. If you use draft folders,
360 you'll need to visit the draft folder with \"\\[mh-visit-folder]
361 drafts <RET>\", use \\[mh-next-undeleted-msg] to move to the
362 appropriate message, and then use \\[mh-edit-again] to prepare
363 the message for editing.
364
365 This command can also be used to take messages that were sent to
366 you and to send them to more people.
367
368 Don't use this command to re-edit a message from a Mailer-Daemon
369 who complained that your mail wasn't posted for some reason or
370 another (see `mh-extract-rejected-mail').
371
372 The default message is the current message.
373
374 See also `mh-send'."
375 (interactive (list (mh-get-msg-num t)))
376 (let* ((from-folder mh-current-folder)
377 (config (current-window-configuration))
378 (draft
379 (cond ((and mh-draft-folder (equal from-folder mh-draft-folder))
380 (pop-to-buffer (find-file-noselect (mh-msg-filename message))
381 t)
382 (rename-buffer (format "draft-%d" message))
383 ;; Make buffer writable...
384 (setq buffer-read-only nil)
385 ;; If buffer was being used to display the message reinsert
386 ;; from file...
387 (when (eq major-mode 'mh-show-mode)
388 (erase-buffer)
389 (insert-file-contents buffer-file-name))
390 (buffer-name))
391 (t
392 (mh-read-draft "clean-up" (mh-msg-filename message) nil)))))
393 (mh-clean-msg-header (point-min) mh-new-draft-cleaned-headers nil)
394 (mh-insert-header-separator)
395 (goto-char (point-min))
396 (save-buffer)
397 (mh-compose-and-send-mail draft "" from-folder nil nil nil nil nil nil
398 config)
399 (mh-letter-mode-message)
400 (mh-letter-adjust-point)))
401
402 ;;;###mh-autoload
403 (defun mh-extract-rejected-mail (message)
404 "Edit a MESSAGE that was returned by the mail system.
405
406 This command prepares the message for editing by removing the
407 Mailer-Daemon envelope and unneeded header fields. Fix whatever
408 addressing problem you had, and send the message again with
409 \\[mh-send-letter].
410
411 The default message is the current message.
412
413 See also `mh-send'."
414 (interactive (list (mh-get-msg-num t)))
415 (let ((from-folder mh-current-folder)
416 (config (current-window-configuration))
417 (draft (mh-read-draft "extraction" (mh-msg-filename message) nil)))
418 (goto-char (point-min))
419 (cond ((re-search-forward mh-rejected-letter-start nil t)
420 (skip-chars-forward " \t\n")
421 (delete-region (point-min) (point))
422 (mh-clean-msg-header (point-min) mh-new-draft-cleaned-headers nil))
423 (t
424 (message "Does not appear to be a rejected letter")))
425 (mh-insert-header-separator)
426 (goto-char (point-min))
427 (save-buffer)
428 (mh-compose-and-send-mail draft "" from-folder message
429 (mh-get-header-field "To:")
430 (mh-get-header-field "From:")
431 (mh-get-header-field "Cc:")
432 nil nil config)
433 (mh-letter-mode-message)))
434
435 ;;;###mh-autoload
436 (defun mh-forward (to cc &optional range)
437 "Forward message.
438
439 You are prompted for the TO and CC recipients. You are given a
440 draft to edit that looks like it would if you had run the MH
441 command \"forw\". You can then add some text.
442
443 You can forward several messages by using a RANGE. All of the
444 messages in the range are inserted into your draft. Check the
445 documentation of `mh-interactive-range' to see how RANGE is read
446 in interactive use.
447
448 The hook `mh-forward-hook' is called on the draft.
449
450 See also `mh-compose-forward-as-mime-flag',
451 `mh-forward-subject-format', and `mh-send'."
452 (interactive (list (mh-interactive-read-address "To: ")
453 (mh-interactive-read-address "Cc: ")
454 (mh-interactive-range "Forward")))
455 (let* ((folder mh-current-folder)
456 (msgs (mh-range-to-msg-list range))
457 (config (current-window-configuration))
458 (fwd-msg-file (mh-msg-filename (car msgs) folder))
459 ;; forw always leaves file in "draft" since it doesn't have -draft
460 (draft-name (expand-file-name "draft" mh-user-path))
461 (draft (cond ((or (not (file-exists-p draft-name))
462 (y-or-n-p "The file draft exists; discard it? "))
463 (mh-exec-cmd "forw" "-build"
464 (if (and (mh-variant-p 'nmh)
465 mh-compose-forward-as-mime-flag)
466 "-mime")
467 mh-current-folder
468 (mh-coalesce-msg-list msgs))
469 (prog1
470 (mh-read-draft "" draft-name t)
471 (mh-insert-fields "To:" to "Cc:" cc)
472 (save-buffer)))
473 (t
474 (mh-read-draft "" draft-name nil)))))
475 (let (orig-from
476 orig-subject)
477 (save-excursion
478 (set-buffer (get-buffer-create mh-temp-buffer))
479 (erase-buffer)
480 (insert-file-contents fwd-msg-file)
481 (setq orig-from (mh-get-header-field "From:"))
482 (setq orig-subject (mh-get-header-field "Subject:")))
483 (let ((forw-subject
484 (mh-forwarded-letter-subject orig-from orig-subject)))
485 (mh-insert-fields "Subject:" forw-subject)
486 (goto-char (point-min))
487 ;; If using MML, translate MH-style directive
488 (if (equal mh-compose-insertion 'mml)
489 (save-excursion
490 (goto-char (mh-mail-header-end))
491 (while
492 (re-search-forward
493 "^#forw \\[\\([^]]+\\)\\] \\(+\\S-+\\) \\(.*\\)$"
494 (point-max) t)
495 (let ((description (if (equal (match-string 1)
496 "forwarded messages")
497 "forwarded message %d"
498 (match-string 1)))
499 (msgs (split-string (match-string 3)))
500 (i 0))
501 (beginning-of-line)
502 (delete-region (point) (progn (forward-line 1) (point)))
503 (dolist (msg msgs)
504 (setq i (1+ i))
505 (mh-mml-forward-message (format description i)
506 folder msg)
507 ;; Was inserted before us, move to end of file to preserve order
508 (goto-char (point-max)))))))
509 ;; Postition just before forwarded message
510 (if (re-search-forward "^------- Forwarded Message" nil t)
511 (forward-line -1)
512 (goto-char (mh-mail-header-end))
513 (forward-line 1))
514 (delete-other-windows)
515 (mh-add-msgs-to-seq msgs 'forwarded t)
516 (mh-compose-and-send-mail draft "" folder msgs
517 to forw-subject cc
518 mh-note-forw "Forwarded:"
519 config)
520 (mh-letter-mode-message)
521 (mh-letter-adjust-point)
522 (run-hooks 'mh-forward-hook)))))
523
524 (defun mh-forwarded-letter-subject (from subject)
525 "Return a Subject suitable for a forwarded message.
526 Original message has headers FROM and SUBJECT."
527 (let ((addr-start (string-match "<" from))
528 (comment (string-match "(" from)))
529 (cond ((and addr-start (> addr-start 0))
530 ;; Full Name <luser@host>
531 (setq from (substring from 0 (1- addr-start))))
532 (comment
533 ;; luser@host (Full Name)
534 (setq from (substring from (1+ comment) (1- (length from)))))))
535 (format mh-forward-subject-format from subject))
536
537 ;;;###mh-autoload
538 (defun mh-redistribute (to cc &optional message)
539 "Redistribute a message.
540
541 This command is similar in function to forwarding mail, but it
542 does not allow you to edit the message, nor does it add your name
543 to the \"From\" header field. It appears to the recipient as if
544 the message had come from the original sender. When you run this
545 command, you are prompted for the TO and CC recipients. The
546 default MESSAGE is the current message.
547
548 Also investigate the command \\[mh-edit-again] for another way to
549 redistribute messages.
550
551 See also `mh-redist-full-contents-flag'."
552 (interactive (list (mh-read-address "Redist-To: ")
553 (mh-read-address "Redist-Cc: ")
554 (mh-get-msg-num t)))
555 (or message
556 (setq message (mh-get-msg-num t)))
557 (save-window-excursion
558 (let ((folder mh-current-folder)
559 (draft (mh-read-draft "redistribution"
560 (if mh-redist-full-contents-flag
561 (mh-msg-filename message)
562 nil)
563 nil)))
564 (mh-goto-header-end 0)
565 (insert "Resent-To: " to "\n")
566 (if (not (equal cc "")) (insert "Resent-cc: " cc "\n"))
567 (mh-clean-msg-header
568 (point-min)
569 "^Message-Id:\\|^Received:\\|^Return-Path:\\|^Sender:\\|^Date:\\|^From:"
570 nil)
571 (save-buffer)
572 (message "Redistributing...")
573 (let ((env "mhdist=1"))
574 ;; Setup environment...
575 (setq env (concat env " mhaltmsg="
576 (if mh-redist-full-contents-flag
577 buffer-file-name
578 (mh-msg-filename message folder))))
579 (unless mh-redist-full-contents-flag
580 (setq env (concat env " mhannotate=1")))
581 ;; Redistribute...
582 (if mh-redist-background
583 (mh-exec-cmd-env-daemon env mh-send-prog nil buffer-file-name)
584 (mh-exec-cmd-error env mh-send-prog "-push" buffer-file-name))
585 ;; Annotate...
586 (mh-annotate-msg message folder mh-note-dist
587 "-component" "Resent:"
588 "-text" (format "\"%s %s\"" to cc)))
589 (kill-buffer draft)
590 (message "Redistributing...done"))))
591
592 ;;;###mh-autoload
593 (defun mh-reply (message &optional reply-to includep)
594 "Reply to a MESSAGE.
595
596 When you reply to a message, you are first prompted with \"Reply
597 to whom?\" (unless the optional argument REPLY-TO is provided).
598 You have several choices here.
599
600 Response Reply Goes To
601
602 from The person who sent the message. This is the
603 default, so <RET> is sufficient.
604
605 to Replies to the sender, plus all recipients in the
606 \"To:\" header field.
607
608 all cc Forms a reply to the addresses in the
609 \"Mail-Followup-To:\" header field if one
610 exists; otherwise forms a reply to the sender,
611 plus all recipients.
612
613 Depending on your answer, \"repl\" is given a different argument
614 to form your reply. Specifically, a choice of \"from\" or none at
615 all runs \"repl -nocc all\", and a choice of \"to\" runs \"repl
616 -cc to\". Finally, either \"cc\" or \"all\" runs \"repl -cc all
617 -nocc me\".
618
619 Two windows are then created. One window contains the message to
620 which you are replying in an MH-Show buffer. Your draft, in
621 MH-Letter mode (*note `mh-letter-mode'), is in the other window.
622 If the reply draft was not one that you expected, check the
623 things that affect the behavior of \"repl\" which include the
624 \"repl:\" profile component and the \"replcomps\" and
625 \"replgroupcomps\" files.
626
627 If you supply a prefix argument INCLUDEP, the message you are
628 replying to is inserted in your reply after having first been run
629 through \"mhl\" with the format file \"mhl.reply\".
630
631 Alternatively, you can customize the option `mh-yank-behavior'
632 and choose one of its \"Automatically\" variants to do the same
633 thing. If you do so, the prefix argument has no effect.
634
635 Another way to include the message automatically in your draft is
636 to use \"repl: -filter repl.filter\" in your MH profile.
637
638 If you wish to customize the header or other parts of the reply
639 draft, please see \"repl\" and \"mh-format\".
640
641 See also `mh-reply-show-message-flag',
642 `mh-reply-default-reply-to', and `mh-send'."
643 (interactive (list
644 (mh-get-msg-num t)
645 (let ((minibuffer-help-form
646 "from => Sender only\nto => Sender and primary recipients\ncc or all => Sender and all recipients"))
647 (or mh-reply-default-reply-to
648 (completing-read "Reply to whom (default from): "
649 '(("from") ("to") ("cc") ("all"))
650 nil
651 t)))
652 current-prefix-arg))
653 (let* ((folder mh-current-folder)
654 (show-buffer mh-show-buffer)
655 (config (current-window-configuration))
656 (group-reply (or (equal reply-to "cc") (equal reply-to "all")))
657 (form-file (cond ((and (mh-variant-p 'nmh 'mu-mh) group-reply
658 (stringp mh-repl-group-formfile))
659 mh-repl-group-formfile)
660 ((stringp mh-repl-formfile) mh-repl-formfile)
661 (t nil))))
662 (message "Composing a reply...")
663 (mh-exec-cmd "repl" "-build" "-noquery" "-nodraftfolder"
664 (if form-file
665 (list "-form" form-file))
666 mh-current-folder message
667 (cond ((or (equal reply-to "from") (equal reply-to ""))
668 '("-nocc" "all"))
669 ((equal reply-to "to")
670 '("-cc" "to"))
671 (group-reply (if (mh-variant-p 'nmh 'mu-mh)
672 '("-group" "-nocc" "me")
673 '("-cc" "all" "-nocc" "me"))))
674 (cond ((or (eq mh-yank-behavior 'autosupercite)
675 (eq mh-yank-behavior 'autoattrib))
676 '("-noformat"))
677 (includep '("-filter" "mhl.reply"))
678 (t '())))
679 (let ((draft (mh-read-draft "reply"
680 (expand-file-name "reply" mh-user-path)
681 t)))
682 (delete-other-windows)
683 (save-buffer)
684
685 (let ((to (mh-get-header-field "To:"))
686 (subject (mh-get-header-field "Subject:"))
687 (cc (mh-get-header-field "Cc:")))
688 (goto-char (point-min))
689 (mh-goto-header-end 1)
690 (or includep
691 (not mh-reply-show-message-flag)
692 (mh-in-show-buffer (show-buffer)
693 (mh-display-msg message folder)))
694 (mh-add-msgs-to-seq message 'answered t)
695 (message "Composing a reply...done")
696 (mh-compose-and-send-mail draft "" folder message to subject cc
697 mh-note-repl "Replied:" config))
698 (when (and (or (eq 'autosupercite mh-yank-behavior)
699 (eq 'autoattrib mh-yank-behavior))
700 (eq (mh-show-buffer-message-number) mh-sent-from-msg))
701 (undo-boundary)
702 (mh-yank-cur-msg))
703 (mh-letter-mode-message))))
704
705 ;;;###mh-autoload
706 (defun mh-send (to cc subject)
707 "Compose a message.
708
709 Your letter appears in an Emacs buffer whose mode is
710 MH-Letter (see `mh-letter-mode').
711
712 The arguments TO, CC, and SUBJECT can be used to prefill the
713 draft fields or suppress the prompts if `mh-compose-prompt-flag'
714 is on. They are also passed to the function set in the option
715 `mh-compose-letter-function'.
716
717 See also `mh-insert-x-mailer-flag' and `mh-letter-mode-hook'.
718
719 Outside of an MH-Folder buffer (`mh-folder-mode'), you must call
720 either \\[mh-smail] or \\[mh-smail-other-window] to compose a new
721 message."
722 (interactive (list
723 (mh-interactive-read-address "To: ")
724 (mh-interactive-read-address "Cc: ")
725 (mh-interactive-read-string "Subject: ")))
726 (let ((config (current-window-configuration)))
727 (delete-other-windows)
728 (mh-send-sub to cc subject config)))
729
730 \f
731
732 ;;; Support Routines
733
734 (defun mh-interactive-read-address (prompt)
735 "Read an address.
736 If `mh-compose-prompt-flag' is non-nil, then read an address with
737 PROMPT.
738 Otherwise return the empty string."
739 (if mh-compose-prompt-flag (mh-read-address prompt) ""))
740
741 (defun mh-interactive-read-string (prompt)
742 "Read a string.
743 If `mh-compose-prompt-flag' is non-nil, then read a string with
744 PROMPT.
745 Otherwise return the empty string."
746 (if mh-compose-prompt-flag (read-string prompt) ""))
747
748 ;;;###mh-autoload
749 (defun mh-show-buffer-message-number (&optional buffer)
750 "Message number of displayed message in corresponding show buffer.
751
752 Return nil if show buffer not displayed.
753 If in `mh-letter-mode', don't display the message number being replied
754 to, but rather the message number of the show buffer associated with
755 our originating folder buffer.
756 Optional argument BUFFER can be used to specify the buffer."
757 (save-excursion
758 (if buffer
759 (set-buffer buffer))
760 (cond ((eq major-mode 'mh-show-mode)
761 (let ((number-start (mh-search-from-end ?/ buffer-file-name)))
762 (string-to-number (substring buffer-file-name
763 (1+ number-start)))))
764 ((and (eq major-mode 'mh-folder-mode)
765 mh-show-buffer
766 (get-buffer mh-show-buffer))
767 (mh-show-buffer-message-number mh-show-buffer))
768 ((and (eq major-mode 'mh-letter-mode)
769 mh-sent-from-folder
770 (get-buffer mh-sent-from-folder))
771 (mh-show-buffer-message-number mh-sent-from-folder))
772 (t
773 nil))))
774
775 (defun mh-send-sub (to cc subject config)
776 "Do the real work of composing and sending a letter.
777 Expects the TO, CC, and SUBJECT fields as arguments.
778 CONFIG is the window configuration before sending mail."
779 (let ((folder mh-current-folder)
780 (msg-num (mh-get-msg-num nil)))
781 (message "Composing a message...")
782 (let ((draft (mh-read-draft
783 "message"
784 (let (components)
785 (cond
786 ((file-exists-p
787 (setq components
788 (expand-file-name mh-comp-formfile mh-user-path)))
789 components)
790 ((file-exists-p
791 (setq components
792 (expand-file-name mh-comp-formfile mh-lib)))
793 components)
794 (t
795 (error "Can't find %s in %s or %s"
796 mh-comp-formfile mh-user-path mh-lib))))
797 nil)))
798 (mh-insert-fields "To:" to "Subject:" subject "Cc:" cc)
799 (goto-char (point-max))
800 (mh-compose-and-send-mail draft "" folder msg-num
801 to subject cc
802 nil nil config)
803 (mh-letter-mode-message)
804 (mh-letter-adjust-point))))
805
806 (defun mh-read-draft (use initial-contents delete-contents-file)
807 "Read draft file into a draft buffer and make that buffer the current one.
808
809 USE is a message used for prompting about the intended use of the
810 message.
811 INITIAL-CONTENTS is filename that is read into an empty buffer, or nil
812 if buffer should not be modified. Delete the initial-contents file if
813 DELETE-CONTENTS-FILE flag is set.
814 Returns the draft folder's name.
815 If the draft folder facility is enabled in ~/.mh_profile, a new buffer
816 is used each time and saved in the draft folder. The draft file can
817 then be reused."
818 (cond (mh-draft-folder
819 (let ((orig-default-dir default-directory)
820 (draft-file-name (mh-new-draft-name)))
821 (pop-to-buffer (generate-new-buffer
822 (format "draft-%s"
823 (file-name-nondirectory draft-file-name))))
824 (condition-case ()
825 (insert-file-contents draft-file-name t)
826 (file-error))
827 (setq default-directory orig-default-dir)))
828 (t
829 (let ((draft-name (expand-file-name "draft" mh-user-path)))
830 (pop-to-buffer "draft") ; Create if necessary
831 (if (buffer-modified-p)
832 (if (y-or-n-p "Draft has been modified; kill anyway? ")
833 (set-buffer-modified-p nil)
834 (error "Draft preserved")))
835 (setq buffer-file-name draft-name)
836 (clear-visited-file-modtime)
837 (unlock-buffer)
838 (cond ((and (file-exists-p draft-name)
839 (not (equal draft-name initial-contents)))
840 (insert-file-contents draft-name)
841 (delete-file draft-name))))))
842 (cond ((and initial-contents
843 (or (zerop (buffer-size))
844 (if (y-or-n-p
845 (format "A draft exists. Use for %s? " use))
846 (if mh-error-if-no-draft
847 (error "A prior draft exists"))
848 t)))
849 (erase-buffer)
850 (insert-file-contents initial-contents)
851 (if delete-contents-file (delete-file initial-contents))))
852 (auto-save-mode 1)
853 (if mh-draft-folder
854 (save-buffer)) ; Do not reuse draft name
855 (buffer-name))
856
857 (defun mh-new-draft-name ()
858 "Return the pathname of folder for draft messages."
859 (save-excursion
860 (mh-exec-cmd-quiet t "mhpath" mh-draft-folder "new")
861 (buffer-substring (point-min) (1- (point-max)))))
862
863 (defun mh-insert-fields (&rest name-values)
864 "Insert the NAME-VALUES pairs in the current buffer.
865 If the field exists, append the value to it.
866 Do not insert any pairs whose value is the empty string."
867 (let ((case-fold-search t))
868 (while name-values
869 (let ((field-name (car name-values))
870 (value (car (cdr name-values))))
871 (if (not (string-match "^.*:$" field-name))
872 (setq field-name (concat field-name ":")))
873 (cond ((or (null value)
874 (equal value ""))
875 nil)
876 ((mh-position-on-field field-name)
877 (insert " " (or value "")))
878 (t
879 (insert field-name " " value "\n")))
880 (setq name-values (cdr (cdr name-values)))))))
881
882 (defun mh-compose-and-send-mail (draft send-args
883 sent-from-folder sent-from-msg
884 to subject cc
885 annotate-char annotate-field
886 config)
887 "Edit and compose a draft message in buffer DRAFT and send or save it.
888 SEND-ARGS is the argument passed to the send command.
889 SENT-FROM-FOLDER is buffer containing scan listing of current folder,
890 or nil if none exists.
891 SENT-FROM-MSG is the message number or sequence name or nil.
892 The TO, SUBJECT, and CC fields are passed to the
893 `mh-compose-letter-function'.
894 If ANNOTATE-CHAR is non-null, it is used to notate the scan listing of
895 the message. In that case, the ANNOTATE-FIELD is used to build a
896 string for `mh-annotate-msg'.
897 CONFIG is the window configuration to restore after sending the
898 letter."
899 (pop-to-buffer draft)
900 (mh-letter-mode)
901
902 ;; Insert identity.
903 (mh-insert-identity mh-identity-default t)
904 (mh-identity-make-menu)
905 (mh-identity-add-menu)
906
907 ;; Insert extra fields.
908 (mh-insert-x-mailer)
909 (mh-insert-x-face)
910
911 (mh-letter-hide-all-skipped-fields)
912
913 (setq mh-sent-from-folder sent-from-folder)
914 (setq mh-sent-from-msg sent-from-msg)
915 (setq mh-send-args send-args)
916 (setq mh-annotate-char annotate-char)
917 (setq mh-annotate-field annotate-field)
918 (setq mh-previous-window-config config)
919 (setq mode-line-buffer-identification (list " {%b}"))
920 (mh-logo-display)
921 (mh-make-local-hook 'kill-buffer-hook)
922 (add-hook 'kill-buffer-hook 'mh-tidy-draft-buffer nil t)
923 (run-hook-with-args 'mh-compose-letter-function to subject cc))
924
925 (defun mh-insert-x-mailer ()
926 "Append an X-Mailer field to the header.
927 The versions of MH-E, Emacs, and MH are shown."
928 ;; Lazily initialize mh-x-mailer-string.
929 (when (and mh-insert-x-mailer-flag (null mh-x-mailer-string))
930 (setq mh-x-mailer-string
931 (format "MH-E %s; %s; %sEmacs %s"
932 mh-version mh-variant-in-use
933 (if (featurep 'xemacs) "X" "GNU ")
934 (cond ((not (featurep 'xemacs))
935 (string-match "[0-9]+\\.[0-9]+\\(\\.[0-9]+\\)?"
936 emacs-version)
937 (match-string 0 emacs-version))
938 ((string-match "[0-9.]*\\( +\([ a-z]+[0-9]+\)\\)?"
939 emacs-version)
940 (match-string 0 emacs-version))
941 (t (format "%s.%s" emacs-major-version
942 emacs-minor-version))))))
943 ;; Insert X-Mailer, but only if it doesn't already exist.
944 (save-excursion
945 (when (and mh-insert-x-mailer-flag
946 (null (mh-goto-header-field "X-Mailer")))
947 (mh-insert-fields "X-Mailer:" mh-x-mailer-string))))
948
949 (defun mh-insert-x-face ()
950 "Append X-Face, Face or X-Image-URL field to header.
951 If the field already exists, this function does nothing."
952 (when (and (file-exists-p mh-x-face-file)
953 (file-readable-p mh-x-face-file))
954 (save-excursion
955 (unless (or (mh-position-on-field "X-Face")
956 (mh-position-on-field "Face")
957 (mh-position-on-field "X-Image-URL"))
958 (save-excursion
959 (goto-char (+ (point) (cadr (insert-file-contents mh-x-face-file))))
960 (if (not (looking-at "^"))
961 (insert "\n")))
962 (unless (looking-at "\\(X-Face\\|Face\\|X-Image-URL\\): ")
963 (insert "X-Face: "))))))
964
965 (defun mh-tidy-draft-buffer ()
966 "Run when a draft buffer is destroyed."
967 (let ((buffer (get-buffer mh-recipients-buffer)))
968 (if buffer
969 (kill-buffer buffer))))
970
971 (defun mh-letter-mode-message ()
972 "Display a help message for users of `mh-letter-mode'.
973 This should be the last function called when composing the draft."
974 (message "%s" (substitute-command-keys
975 (concat "Type \\[mh-send-letter] to send message, "
976 "\\[mh-help] for help"))))
977
978 (defun mh-letter-adjust-point ()
979 "Move cursor to first header field if are using the no prompt mode."
980 (unless mh-compose-prompt-flag
981 (goto-char (point-max))
982 (mh-letter-next-header-field)))
983
984 (defun mh-annotate-msg (msg folder note &rest args)
985 "Mark MSG in FOLDER with character NOTE and annotate message with ARGS.
986 MSG can be a message number, a list of message numbers, or a sequence.
987 The hook `mh-annotate-msg-hook' is run after annotating; see its
988 documentation for variables it can use."
989 (apply 'mh-exec-cmd "anno" folder
990 (if (listp msg) (append msg args) (cons msg args)))
991 (save-excursion
992 (cond ((get-buffer folder) ; Buffer may be deleted
993 (set-buffer folder)
994 (mh-iterate-on-range nil msg
995 (mh-notate nil note
996 (+ mh-cmd-note mh-scan-field-destination-offset))))))
997 (let ((mh-current-folder folder)
998 ;; mh-annotate-list is a sequence name or a list of message numbers
999 (mh-annotate-list (if (numberp msg) (list msg) msg)))
1000 (run-hooks 'mh-annotate-msg-hook)))
1001
1002 (defun mh-insert-header-separator ()
1003 "Insert `mh-mail-header-separator', if absent."
1004 (save-excursion
1005 (goto-char (point-min))
1006 (rfc822-goto-eoh)
1007 (if (looking-at "$")
1008 (insert mh-mail-header-separator))))
1009
1010 ;;;###mh-autoload
1011 (defun mh-insert-auto-fields (&optional non-interactive)
1012 "Insert custom fields if recipient is found in `mh-auto-fields-list'.
1013
1014 Once the header contains one or more recipients, you may run this
1015 command to insert these fields manually. However, if you use this
1016 command, the automatic insertion when the message is sent is
1017 disabled.
1018
1019 In a program, set buffer-local `mh-insert-auto-fields-done-local'
1020 if header fields were added. If NON-INTERACTIVE is non-nil,
1021 perform actions quietly and only if
1022 `mh-insert-auto-fields-done-local' is nil. Return t if fields
1023 added; otherwise return nil."
1024 (interactive)
1025 (when (or (not non-interactive)
1026 (not mh-insert-auto-fields-done-local))
1027 (save-excursion
1028 (when (and (or (mh-goto-header-field "To:")
1029 (mh-goto-header-field "cc:")))
1030 (let ((list mh-auto-fields-list)
1031 (fields-inserted nil))
1032 (while list
1033 (let ((regexp (nth 0 (car list)))
1034 (entries (nth 1 (car list))))
1035 (when (mh-regexp-in-field-p regexp "To:" "cc:")
1036 (setq mh-insert-auto-fields-done-local t)
1037 (setq fields-inserted t)
1038 (if (not non-interactive)
1039 (message "Fields for %s added" regexp))
1040 (let ((entry-list entries))
1041 (while entry-list
1042 (let ((field (caar entry-list))
1043 (value (cdar entry-list)))
1044 (cond
1045 ((equal ":identity" field)
1046 (when
1047 ;;(and (not mh-identity-local)
1048 ;; Bug 1204506. But do we need to be able
1049 ;; to set an identity manually that won't be
1050 ;; overridden by mh-insert-auto-fields?
1051 (assoc value mh-identity-list)
1052 ;;)
1053 (mh-insert-identity value)))
1054 (t
1055 (mh-modify-header-field field value
1056 (equal field "From")))))
1057 (setq entry-list (cdr entry-list))))))
1058 (setq list (cdr list)))
1059 fields-inserted)))))
1060
1061 (defun mh-modify-header-field (field value &optional overwrite-flag)
1062 "To header FIELD add VALUE.
1063 If OVERWRITE-FLAG is non-nil then the old value, if present, is
1064 discarded."
1065 (cond ((and overwrite-flag
1066 (mh-goto-header-field (concat field ":")))
1067 (insert " " value)
1068 (delete-region (point) (mh-line-end-position)))
1069 ((and (not overwrite-flag)
1070 (mh-regexp-in-field-p (concat "\\b" value "\\b") field))
1071 ;; Already there, do nothing.
1072 )
1073 ((and (not overwrite-flag)
1074 (mh-goto-header-field (concat field ":")))
1075 (insert " " value ","))
1076 (t
1077 (mh-goto-header-end 0)
1078 (insert field ": " value "\n"))))
1079
1080 (defun mh-regexp-in-field-p (regexp &rest fields)
1081 "Non-nil means REGEXP was found in FIELDS."
1082 (save-excursion
1083 (let ((search-result nil)
1084 (field))
1085 (while fields
1086 (setq field (car fields))
1087 (if (and (mh-goto-header-field field)
1088 (re-search-forward
1089 regexp (save-excursion (mh-header-field-end)(point)) t))
1090 (setq fields nil
1091 search-result t)
1092 (setq fields (cdr fields))))
1093 search-result)))
1094
1095 (defun mh-ascii-buffer-p ()
1096 "Check if current buffer is entirely composed of ASCII.
1097 The function doesn't work for XEmacs since `find-charset-region'
1098 doesn't exist there."
1099 (loop for charset in (mh-funcall-if-exists
1100 find-charset-region (point-min) (point-max))
1101 unless (eq charset 'ascii) return nil
1102 finally return t))
1103
1104 (provide 'mh-comp)
1105
1106 ;; Local Variables:
1107 ;; indent-tabs-mode: nil
1108 ;; sentence-end-double-space: nil
1109 ;; End:
1110
1111 ;; arch-tag: 62865511-e610-4923-b0b5-f45a8ab70a34
1112 ;;; mh-comp.el ends here