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