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