Merge from emacs-24; up to 2012-12-29T12:57:49Z!fgallina@gnu.org
[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
195eea0f 3;; Copyright (C) 1993, 1995, 1997, 2000-2013 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 124(defvar mh-regexp-in-field-syntax-table nil
1b0b1855 125 "Specify a syntax table for `mh-regexp-in-field-p' to use.")
855c6482
JH
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 430 ;; Merge in components
1b0b1855
BW
431 (mh-mapc
432 (function
433 (lambda (header-field)
434 (let ((field (car header-field))
435 (value (cdr header-field))
436 (case-fold-search t))
437 (cond
438 ;; Address field
439 ((string-match field "^To$\\|^Cc$\\|^From$")
440 (cond
441 ((not (mh-goto-header-field (concat field ":")))
442 ;; Header field does not exist, add it
443 (mh-goto-header-end 0)
444 (insert field ": " value "\n"))
445 ((string-equal value "")
446 ;; Header field already exists and no value
447 )
448 (t
449 ;; Header field exists and we have a value
450 (let (address mailbox (alias (mh-alias-expand value)))
451 (and alias
452 (setq address (ietf-drums-parse-address alias))
453 (setq mailbox (car address)))
454 ;; XXX - Need to parse all addresses out of field
455 (if (and
456 (not (mh-regexp-in-field-p
457 (concat "\\b" (regexp-quote value) "\\b") field))
458 mailbox
459 (not (mh-regexp-in-field-p
460 (concat "\\b" (regexp-quote mailbox) "\\b") field)))
461 (insert " " value ","))
462 ))))
463 ((string-match field "^Fcc$")
464 ;; Folder reference
465 (mh-modify-header-field field value))
466 ;; Text field, that's an easy case
467 (t
468 (mh-modify-header-field field value))))))
469 (mh-components-to-list (mh-find-components)))
c26cf6c8 470 (goto-char (point-min))
283b03f4 471 (save-buffer)
1b0b1855
BW
472 (mh-compose-and-send-mail
473 draft "" from-folder nil nil nil nil nil nil config)
a66894d8
BW
474 (mh-letter-mode-message)
475 (mh-letter-adjust-point)))
c26cf6c8 476
855c6482
JH
477(defun mh-extract-header-field ()
478 "Extract field name and field value from the field at point.
479Returns a list of field name and value (which may be null)."
480 (let ((end (save-excursion (mh-header-field-end)
481 (point))))
482 (if (looking-at mh-letter-header-field-regexp)
483 (save-excursion
484 (goto-char (match-end 1))
485 (forward-char 1)
486 (skip-chars-forward " \t")
487 (cons (match-string-no-properties 1) (buffer-substring-no-properties (point) end))))))
488
489
490(defun mh-components-to-list (components)
1b0b1855 491 "Convert the COMPONENTS file to a list of field names and values."
855c6482
JH
492 (with-current-buffer (get-buffer-create mh-temp-buffer)
493 (erase-buffer)
494 (insert-file-contents components)
495 (goto-char (point-min))
496 (let
497 ((header-fields nil))
498 (while (mh-in-header-p)
499 (setq header-fields (append header-fields (list (mh-extract-header-field))))
500 (mh-header-field-end)
501 (forward-char 1)
502 )
503 header-fields)))
504
c3d9274a 505;;;###mh-autoload
b2064e08
BW
506(defun mh-extract-rejected-mail (message)
507 "Edit a MESSAGE that was returned by the mail system.
508
2dcf34f9
BW
509This command prepares the message for editing by removing the
510Mailer-Daemon envelope and unneeded header fields. Fix whatever
511addressing problem you had, and send the message again with
512\\[mh-send-letter].
b2064e08
BW
513
514The default message is the current message.
f0d73c14
BW
515
516See also `mh-send'."
c26cf6c8
RS
517 (interactive (list (mh-get-msg-num t)))
518 (let ((from-folder mh-current-folder)
c3d9274a 519 (config (current-window-configuration))
b2064e08 520 (draft (mh-read-draft "extraction" (mh-msg-filename message) nil)))
c26cf6c8
RS
521 (goto-char (point-min))
522 (cond ((re-search-forward mh-rejected-letter-start nil t)
c3d9274a
BW
523 (skip-chars-forward " \t\n")
524 (delete-region (point-min) (point))
525 (mh-clean-msg-header (point-min) mh-new-draft-cleaned-headers nil))
526 (t
f0d73c14 527 (message "Does not appear to be a rejected letter")))
a1b4049d 528 (mh-insert-header-separator)
c26cf6c8 529 (goto-char (point-min))
283b03f4 530 (save-buffer)
b2064e08 531 (mh-compose-and-send-mail draft "" from-folder message
c3d9274a
BW
532 (mh-get-header-field "To:")
533 (mh-get-header-field "From:")
534 (mh-get-header-field "Cc:")
535 nil nil config)
bdcfe844 536 (mh-letter-mode-message)))
c26cf6c8 537
c3d9274a 538;;;###mh-autoload
a66894d8 539(defun mh-forward (to cc &optional range)
2be362c2 540 "Forward message.
a66894d8 541
2dcf34f9
BW
542You are prompted for the TO and CC recipients. You are given a
543draft to edit that looks like it would if you had run the MH
544command \"forw\". You can then add some text.
bdcfe844 545
2dcf34f9
BW
546You can forward several messages by using a RANGE. All of the
547messages in the range are inserted into your draft. Check the
548documentation of `mh-interactive-range' to see how RANGE is read
549in interactive use.
b2064e08 550
d1699462
BW
551The hook `mh-forward-hook' is called on the draft.
552
553See also `mh-compose-forward-as-mime-flag',
554`mh-forward-subject-format', and `mh-send'."
a66894d8
BW
555 (interactive (list (mh-interactive-read-address "To: ")
556 (mh-interactive-read-address "Cc: ")
557 (mh-interactive-range "Forward")))
c26cf6c8 558 (let* ((folder mh-current-folder)
a66894d8 559 (msgs (mh-range-to-msg-list range))
c3d9274a
BW
560 (config (current-window-configuration))
561 (fwd-msg-file (mh-msg-filename (car msgs) folder))
562 ;; forw always leaves file in "draft" since it doesn't have -draft
563 (draft-name (expand-file-name "draft" mh-user-path))
564 (draft (cond ((or (not (file-exists-p draft-name))
00b6a079 565 (y-or-n-p "The file draft exists; discard it? "))
f0d73c14
BW
566 (mh-exec-cmd "forw" "-build"
567 (if (and (mh-variant-p 'nmh)
568 mh-compose-forward-as-mime-flag)
569 "-mime")
924df208
BW
570 mh-current-folder
571 (mh-coalesce-msg-list msgs))
c3d9274a
BW
572 (prog1
573 (mh-read-draft "" draft-name t)
574 (mh-insert-fields "To:" to "Cc:" cc)
575 (save-buffer)))
576 (t
577 (mh-read-draft "" draft-name nil)))))
847b8219 578 (let (orig-from
c3d9274a 579 orig-subject)
b5553d47 580 (with-current-buffer (get-buffer-create mh-temp-buffer)
c3d9274a
BW
581 (erase-buffer)
582 (insert-file-contents fwd-msg-file)
583 (setq orig-from (mh-get-header-field "From:"))
584 (setq orig-subject (mh-get-header-field "Subject:")))
c26cf6c8 585 (let ((forw-subject
924df208 586 (mh-forwarded-letter-subject orig-from orig-subject)))
c3d9274a
BW
587 (mh-insert-fields "Subject:" forw-subject)
588 (goto-char (point-min))
ce974958
JH
589 ;; Set the local value of mh-mail-header-separator according to what is
590 ;; present in the buffer...
591 (set (make-local-variable 'mh-mail-header-separator)
592 (save-excursion
593 (goto-char (mh-mail-header-end))
594 (buffer-substring-no-properties (point) (mh-line-end-position))))
595 (set (make-local-variable 'mail-header-separator) mh-mail-header-separator) ;override sendmail.el
0c47b17c
BW
596 ;; If using MML, translate MH-style directive
597 (if (equal mh-compose-insertion 'mml)
c3d9274a 598 (save-excursion
a66894d8 599 (goto-char (mh-mail-header-end))
c3d9274a
BW
600 (while
601 (re-search-forward
602 "^#forw \\[\\([^]]+\\)\\] \\(+\\S-+\\) \\(.*\\)$"
603 (point-max) t)
604 (let ((description (if (equal (match-string 1)
605 "forwarded messages")
606 "forwarded message %d"
607 (match-string 1)))
608 (msgs (split-string (match-string 3)))
609 (i 0))
610 (beginning-of-line)
611 (delete-region (point) (progn (forward-line 1) (point)))
612 (dolist (msg msgs)
613 (setq i (1+ i))
614 (mh-mml-forward-message (format description i)
d5926104
JH
615 folder msg)
616 ;; Was inserted before us, move to end of file to preserve order
617 (goto-char (point-max)))))))
09e80d9f 618 ;; Position just before forwarded message.
c3d9274a
BW
619 (if (re-search-forward "^------- Forwarded Message" nil t)
620 (forward-line -1)
a66894d8 621 (goto-char (mh-mail-header-end))
c3d9274a
BW
622 (forward-line 1))
623 (delete-other-windows)
624 (mh-add-msgs-to-seq msgs 'forwarded t)
924df208 625 (mh-compose-and-send-mail draft "" folder msgs
c3d9274a
BW
626 to forw-subject cc
627 mh-note-forw "Forwarded:"
628 config)
a66894d8 629 (mh-letter-mode-message)
f0d73c14
BW
630 (mh-letter-adjust-point)
631 (run-hooks 'mh-forward-hook)))))
c26cf6c8
RS
632
633(defun mh-forwarded-letter-subject (from subject)
bdcfe844
BW
634 "Return a Subject suitable for a forwarded message.
635Original message has headers FROM and SUBJECT."
c26cf6c8 636 (let ((addr-start (string-match "<" from))
c3d9274a 637 (comment (string-match "(" from)))
c26cf6c8 638 (cond ((and addr-start (> addr-start 0))
c3d9274a
BW
639 ;; Full Name <luser@host>
640 (setq from (substring from 0 (1- addr-start))))
641 (comment
642 ;; luser@host (Full Name)
643 (setq from (substring from (1+ comment) (1- (length from)))))))
c26cf6c8
RS
644 (format mh-forward-subject-format from subject))
645
b2064e08
BW
646;;;###mh-autoload
647(defun mh-redistribute (to cc &optional message)
648 "Redistribute a message.
847b8219 649
2dcf34f9
BW
650This command is similar in function to forwarding mail, but it
651does not allow you to edit the message, nor does it add your name
652to the \"From\" header field. It appears to the recipient as if
653the message had come from the original sender. When you run this
654command, you are prompted for the TO and CC recipients. The
655default MESSAGE is the current message.
c26cf6c8 656
af435184 657Also investigate the command \\[mh-edit-again] for another way to
2dcf34f9 658redistribute messages.
b2064e08 659
d0f1af4d
BW
660See also `mh-redist-full-contents-flag'.
661
662The hook `mh-annotate-msg-hook' is run after annotating the
663message and scan line."
c26cf6c8 664 (interactive (list (mh-read-address "Redist-To: ")
c3d9274a
BW
665 (mh-read-address "Redist-Cc: ")
666 (mh-get-msg-num t)))
b2064e08
BW
667 (or message
668 (setq message (mh-get-msg-num t)))
c26cf6c8
RS
669 (save-window-excursion
670 (let ((folder mh-current-folder)
c3d9274a 671 (draft (mh-read-draft "redistribution"
b2064e08
BW
672 (if mh-redist-full-contents-flag
673 (mh-msg-filename message)
c3d9274a
BW
674 nil)
675 nil)))
c26cf6c8
RS
676 (mh-goto-header-end 0)
677 (insert "Resent-To: " to "\n")
678 (if (not (equal cc "")) (insert "Resent-cc: " cc "\n"))
924df208
BW
679 (mh-clean-msg-header
680 (point-min)
681 "^Message-Id:\\|^Received:\\|^Return-Path:\\|^Sender:\\|^Date:\\|^From:"
682 nil)
c26cf6c8
RS
683 (save-buffer)
684 (message "Redistributing...")
924df208
BW
685 (let ((env "mhdist=1"))
686 ;; Setup environment...
b2064e08
BW
687 (setq env (concat env " mhaltmsg="
688 (if mh-redist-full-contents-flag
689 buffer-file-name
690 (mh-msg-filename message folder))))
691 (unless mh-redist-full-contents-flag
924df208
BW
692 (setq env (concat env " mhannotate=1")))
693 ;; Redistribute...
694 (if mh-redist-background
695 (mh-exec-cmd-env-daemon env mh-send-prog nil buffer-file-name)
696 (mh-exec-cmd-error env mh-send-prog "-push" buffer-file-name))
697 ;; Annotate...
b2064e08 698 (mh-annotate-msg message folder mh-note-dist
924df208
BW
699 "-component" "Resent:"
700 "-text" (format "\"%s %s\"" to cc)))
c26cf6c8
RS
701 (kill-buffer draft)
702 (message "Redistributing...done"))))
703
c3d9274a 704;;;###mh-autoload
bdcfe844 705(defun mh-reply (message &optional reply-to includep)
b2064e08 706 "Reply to a MESSAGE.
f0d73c14 707
2dcf34f9
BW
708When you reply to a message, you are first prompted with \"Reply
709to whom?\" (unless the optional argument REPLY-TO is provided).
710You have several choices here.
b2064e08
BW
711
712 Response Reply Goes To
713
72cf2f2e 714 from The person who sent the message. This is the
2dcf34f9 715 default, so <RET> is sufficient.
b2064e08
BW
716
717 to Replies to the sender, plus all recipients in the
718 \"To:\" header field.
719
72cf2f2e
BW
720 all cc Forms a reply to the addresses in the
721 \"Mail-Followup-To:\" header field if one
722 exists; otherwise forms a reply to the sender,
723 plus all recipients.
b2064e08 724
2dcf34f9
BW
725Depending on your answer, \"repl\" is given a different argument
726to form your reply. Specifically, a choice of \"from\" or none at
727all runs \"repl -nocc all\", and a choice of \"to\" runs \"repl
728-cc to\". Finally, either \"cc\" or \"all\" runs \"repl -cc all
729-nocc me\".
b2064e08 730
2dcf34f9
BW
731Two windows are then created. One window contains the message to
732which you are replying in an MH-Show buffer. Your draft, in
72cf2f2e
BW
733MH-Letter mode (*note `mh-letter-mode'), is in the other window.
734If the reply draft was not one that you expected, check the
735things that affect the behavior of \"repl\" which include the
736\"repl:\" profile component and the \"replcomps\" and
737\"replgroupcomps\" files.
b2064e08 738
2dcf34f9
BW
739If you supply a prefix argument INCLUDEP, the message you are
740replying to is inserted in your reply after having first been run
741through \"mhl\" with the format file \"mhl.reply\".
b2064e08 742
2dcf34f9
BW
743Alternatively, you can customize the option `mh-yank-behavior'
744and choose one of its \"Automatically\" variants to do the same
745thing. If you do so, the prefix argument has no effect.
b2064e08 746
2dcf34f9
BW
747Another way to include the message automatically in your draft is
748to use \"repl: -filter repl.filter\" in your MH profile.
b2064e08 749
2dcf34f9
BW
750If you wish to customize the header or other parts of the reply
751draft, please see \"repl\" and \"mh-format\".
b2064e08 752
2dcf34f9
BW
753See also `mh-reply-show-message-flag',
754`mh-reply-default-reply-to', and `mh-send'."
bdcfe844
BW
755 (interactive (list
756 (mh-get-msg-num t)
757 (let ((minibuffer-help-form
758 "from => Sender only\nto => Sender and primary recipients\ncc or all => Sender and all recipients"))
759 (or mh-reply-default-reply-to
078cb314 760 (completing-read "Reply to whom (default from): "
bdcfe844
BW
761 '(("from") ("to") ("cc") ("all"))
762 nil
763 t)))
764 current-prefix-arg))
765 (let* ((folder mh-current-folder)
766 (show-buffer mh-show-buffer)
767 (config (current-window-configuration))
768 (group-reply (or (equal reply-to "cc") (equal reply-to "all")))
d1c1d7c1 769 (form-file (cond ((and (mh-variant-p 'nmh 'gnu-mh) group-reply
bdcfe844
BW
770 (stringp mh-repl-group-formfile))
771 mh-repl-group-formfile)
772 ((stringp mh-repl-formfile) mh-repl-formfile)
773 (t nil))))
774 (message "Composing a reply...")
775 (mh-exec-cmd "repl" "-build" "-noquery" "-nodraftfolder"
776 (if form-file
777 (list "-form" form-file))
778 mh-current-folder message
779 (cond ((or (equal reply-to "from") (equal reply-to ""))
780 '("-nocc" "all"))
781 ((equal reply-to "to")
782 '("-cc" "to"))
d1c1d7c1 783 (group-reply (if (mh-variant-p 'nmh 'gnu-mh)
bdcfe844
BW
784 '("-group" "-nocc" "me")
785 '("-cc" "all" "-nocc" "me"))))
0c47b17c
BW
786 (cond ((or (eq mh-yank-behavior 'autosupercite)
787 (eq mh-yank-behavior 'autoattrib))
c3d9274a
BW
788 '("-noformat"))
789 (includep '("-filter" "mhl.reply"))
790 (t '())))
bdcfe844
BW
791 (let ((draft (mh-read-draft "reply"
792 (expand-file-name "reply" mh-user-path)
793 t)))
794 (delete-other-windows)
795 (save-buffer)
a1506d29 796
bdcfe844
BW
797 (let ((to (mh-get-header-field "To:"))
798 (subject (mh-get-header-field "Subject:"))
799 (cc (mh-get-header-field "Cc:")))
800 (goto-char (point-min))
801 (mh-goto-header-end 1)
802 (or includep
803 (not mh-reply-show-message-flag)
804 (mh-in-show-buffer (show-buffer)
805 (mh-display-msg message folder)))
806 (mh-add-msgs-to-seq message 'answered t)
807 (message "Composing a reply...done")
808 (mh-compose-and-send-mail draft "" folder message to subject cc
809 mh-note-repl "Replied:" config))
0c47b17c
BW
810 (when (and (or (eq 'autosupercite mh-yank-behavior)
811 (eq 'autoattrib mh-yank-behavior))
bdcfe844
BW
812 (eq (mh-show-buffer-message-number) mh-sent-from-msg))
813 (undo-boundary)
814 (mh-yank-cur-msg))
815 (mh-letter-mode-message))))
c26cf6c8 816
c3d9274a 817;;;###mh-autoload
c26cf6c8 818(defun mh-send (to cc subject)
b2064e08
BW
819 "Compose a message.
820
2dcf34f9
BW
821Your letter appears in an Emacs buffer whose mode is
822MH-Letter (see `mh-letter-mode').
b2064e08 823
2dcf34f9
BW
824The arguments TO, CC, and SUBJECT can be used to prefill the
825draft fields or suppress the prompts if `mh-compose-prompt-flag'
826is on. They are also passed to the function set in the option
827`mh-compose-letter-function'.
b2064e08
BW
828
829See also `mh-insert-x-mailer-flag' and `mh-letter-mode-hook'.
830
2dcf34f9
BW
831Outside of an MH-Folder buffer (`mh-folder-mode'), you must call
832either \\[mh-smail] or \\[mh-smail-other-window] to compose a new
833message."
c26cf6c8 834 (interactive (list
a66894d8
BW
835 (mh-interactive-read-address "To: ")
836 (mh-interactive-read-address "Cc: ")
837 (mh-interactive-read-string "Subject: ")))
c26cf6c8
RS
838 (let ((config (current-window-configuration)))
839 (delete-other-windows)
840 (mh-send-sub to cc subject config)))
841
dda00b2c
BW
842\f
843
844;;; Support Routines
845
846(defun mh-interactive-read-address (prompt)
847 "Read an address.
848If `mh-compose-prompt-flag' is non-nil, then read an address with
849PROMPT.
850Otherwise return the empty string."
851 (if mh-compose-prompt-flag (mh-read-address prompt) ""))
852
853(defun mh-interactive-read-string (prompt)
854 "Read a string.
855If `mh-compose-prompt-flag' is non-nil, then read a string with
856PROMPT.
857Otherwise return the empty string."
858 (if mh-compose-prompt-flag (read-string prompt) ""))
859
c3d9274a 860;;;###mh-autoload
dda00b2c
BW
861(defun mh-show-buffer-message-number (&optional buffer)
862 "Message number of displayed message in corresponding show buffer.
b2064e08 863
dda00b2c
BW
864Return nil if show buffer not displayed.
865If in `mh-letter-mode', don't display the message number being replied
866to, but rather the message number of the show buffer associated with
867our originating folder buffer.
868Optional argument BUFFER can be used to specify the buffer."
869 (save-excursion
870 (if buffer
871 (set-buffer buffer))
872 (cond ((eq major-mode 'mh-show-mode)
873 (let ((number-start (mh-search-from-end ?/ buffer-file-name)))
874 (string-to-number (substring buffer-file-name
875 (1+ number-start)))))
876 ((and (eq major-mode 'mh-folder-mode)
877 mh-show-buffer
878 (get-buffer mh-show-buffer))
879 (mh-show-buffer-message-number mh-show-buffer))
880 ((and (eq major-mode 'mh-letter-mode)
881 mh-sent-from-folder
882 (get-buffer mh-sent-from-folder))
883 (mh-show-buffer-message-number mh-sent-from-folder))
884 (t
885 nil))))
c26cf6c8 886
855c6482
JH
887(defun mh-find-components ()
888 "Return the path to the components file."
889 (let (components)
890 (cond
891 ((file-exists-p
892 (setq components
893 (expand-file-name mh-comp-formfile mh-user-path)))
894 components)
895 ((file-exists-p
896 (setq components
897 (expand-file-name mh-comp-formfile mh-lib)))
898 components)
899 (t
900 (error "Can't find %s in %s or %s"
901 mh-comp-formfile mh-user-path mh-lib)))))
fb9958d7 902
c26cf6c8 903(defun mh-send-sub (to cc subject config)
bdcfe844
BW
904 "Do the real work of composing and sending a letter.
905Expects the TO, CC, and SUBJECT fields as arguments.
906CONFIG is the window configuration before sending mail."
c26cf6c8 907 (let ((folder mh-current-folder)
c3d9274a 908 (msg-num (mh-get-msg-num nil)))
c26cf6c8
RS
909 (message "Composing a message...")
910 (let ((draft (mh-read-draft
c3d9274a 911 "message"
855c6482 912 (mh-find-components)
c3d9274a 913 nil)))
c26cf6c8
RS
914 (mh-insert-fields "To:" to "Subject:" subject "Cc:" cc)
915 (goto-char (point-max))
c26cf6c8 916 (mh-compose-and-send-mail draft "" folder msg-num
c3d9274a
BW
917 to subject cc
918 nil nil config)
a66894d8
BW
919 (mh-letter-mode-message)
920 (mh-letter-adjust-point))))
c26cf6c8
RS
921
922(defun mh-read-draft (use initial-contents delete-contents-file)
bdcfe844 923 "Read draft file into a draft buffer and make that buffer the current one.
2dcf34f9
BW
924
925USE is a message used for prompting about the intended use of the
926message.
bdcfe844 927INITIAL-CONTENTS is filename that is read into an empty buffer, or nil
2dcf34f9 928if buffer should not be modified. Delete the initial-contents file if
bdcfe844
BW
929DELETE-CONTENTS-FILE flag is set.
930Returns the draft folder's name.
2dcf34f9
BW
931If the draft folder facility is enabled in ~/.mh_profile, a new buffer
932is used each time and saved in the draft folder. The draft file can
933then be reused."
c26cf6c8 934 (cond (mh-draft-folder
c3d9274a
BW
935 (let ((orig-default-dir default-directory)
936 (draft-file-name (mh-new-draft-name)))
937 (pop-to-buffer (generate-new-buffer
938 (format "draft-%s"
939 (file-name-nondirectory draft-file-name))))
940 (condition-case ()
941 (insert-file-contents draft-file-name t)
942 (file-error))
943 (setq default-directory orig-default-dir)))
944 (t
945 (let ((draft-name (expand-file-name "draft" mh-user-path)))
946 (pop-to-buffer "draft") ; Create if necessary
947 (if (buffer-modified-p)
948 (if (y-or-n-p "Draft has been modified; kill anyway? ")
949 (set-buffer-modified-p nil)
950 (error "Draft preserved")))
951 (setq buffer-file-name draft-name)
952 (clear-visited-file-modtime)
953 (unlock-buffer)
954 (cond ((and (file-exists-p draft-name)
955 (not (equal draft-name initial-contents)))
956 (insert-file-contents draft-name)
957 (delete-file draft-name))))))
c26cf6c8 958 (cond ((and initial-contents
c3d9274a
BW
959 (or (zerop (buffer-size))
960 (if (y-or-n-p
961 (format "A draft exists. Use for %s? " use))
962 (if mh-error-if-no-draft
963 (error "A prior draft exists"))
964 t)))
965 (erase-buffer)
966 (insert-file-contents initial-contents)
967 (if delete-contents-file (delete-file initial-contents))))
c26cf6c8
RS
968 (auto-save-mode 1)
969 (if mh-draft-folder
c3d9274a 970 (save-buffer)) ; Do not reuse draft name
c26cf6c8
RS
971 (buffer-name))
972
c26cf6c8 973(defun mh-new-draft-name ()
bdcfe844 974 "Return the pathname of folder for draft messages."
c26cf6c8
RS
975 (save-excursion
976 (mh-exec-cmd-quiet t "mhpath" mh-draft-folder "new")
977 (buffer-substring (point-min) (1- (point-max)))))
978
c26cf6c8 979(defun mh-insert-fields (&rest name-values)
bdcfe844
BW
980 "Insert the NAME-VALUES pairs in the current buffer.
981If the field exists, append the value to it.
982Do not insert any pairs whose value is the empty string."
c26cf6c8
RS
983 (let ((case-fold-search t))
984 (while name-values
985 (let ((field-name (car name-values))
c3d9274a 986 (value (car (cdr name-values))))
f0d73c14
BW
987 (if (not (string-match "^.*:$" field-name))
988 (setq field-name (concat field-name ":")))
a2c30782
BW
989 (cond ((or (null value)
990 (equal value ""))
c3d9274a
BW
991 nil)
992 ((mh-position-on-field field-name)
993 (insert " " (or value "")))
994 (t
995 (insert field-name " " value "\n")))
996 (setq name-values (cdr (cdr name-values)))))))
c26cf6c8 997
dda00b2c
BW
998(defun mh-compose-and-send-mail (draft send-args
999 sent-from-folder sent-from-msg
1000 to subject cc
1001 annotate-char annotate-field
1002 config)
1003 "Edit and compose a draft message in buffer DRAFT and send or save it.
1004SEND-ARGS is the argument passed to the send command.
1005SENT-FROM-FOLDER is buffer containing scan listing of current folder,
1006or nil if none exists.
1007SENT-FROM-MSG is the message number or sequence name or nil.
1008The TO, SUBJECT, and CC fields are passed to the
1009`mh-compose-letter-function'.
1010If ANNOTATE-CHAR is non-null, it is used to notate the scan listing of
1011the message. In that case, the ANNOTATE-FIELD is used to build a
1012string for `mh-annotate-msg'.
1013CONFIG is the window configuration to restore after sending the
1014letter."
1015 (pop-to-buffer draft)
1016 (mh-letter-mode)
847b8219 1017
dda00b2c
BW
1018 ;; Insert identity.
1019 (mh-insert-identity mh-identity-default t)
1020 (mh-identity-make-menu)
1021 (mh-identity-add-menu)
c26cf6c8 1022
b59ee24d
PG
1023 ;; Cleanup possibly RFC2047 encoded subject header
1024 (mh-decode-message-subject)
1025
dda00b2c
BW
1026 ;; Insert extra fields.
1027 (mh-insert-x-mailer)
1028 (mh-insert-x-face)
c26cf6c8 1029
dda00b2c 1030 (mh-letter-hide-all-skipped-fields)
924df208 1031
dda00b2c
BW
1032 (setq mh-sent-from-folder sent-from-folder)
1033 (setq mh-sent-from-msg sent-from-msg)
1034 (setq mh-send-args send-args)
1035 (setq mh-annotate-char annotate-char)
1036 (setq mh-annotate-field annotate-field)
1037 (setq mh-previous-window-config config)
1038 (setq mode-line-buffer-identification (list " {%b}"))
1039 (mh-logo-display)
1040 (mh-make-local-hook 'kill-buffer-hook)
1041 (add-hook 'kill-buffer-hook 'mh-tidy-draft-buffer nil t)
16b9a476 1042 (run-hook-with-args 'mh-compose-letter-function to subject cc))
bdcfe844 1043
a1b4049d 1044(defun mh-insert-x-mailer ()
bdcfe844
BW
1045 "Append an X-Mailer field to the header.
1046The versions of MH-E, Emacs, and MH are shown."
a1b4049d 1047 ;; Lazily initialize mh-x-mailer-string.
a66894d8 1048 (when (and mh-insert-x-mailer-flag (null mh-x-mailer-string))
f0d73c14
BW
1049 (setq mh-x-mailer-string
1050 (format "MH-E %s; %s; %sEmacs %s"
1051 mh-version mh-variant-in-use
a3269bc4
DN
1052 (if (featurep 'xemacs) "X" "GNU ")
1053 (cond ((not (featurep 'xemacs))
d5468dff
BW
1054 (string-match "[0-9]+\\.[0-9]+\\(\\.[0-9]+\\)?"
1055 emacs-version)
1056 (match-string 0 emacs-version))
f0d73c14
BW
1057 ((string-match "[0-9.]*\\( +\([ a-z]+[0-9]+\)\\)?"
1058 emacs-version)
1059 (match-string 0 emacs-version))
1060 (t (format "%s.%s" emacs-major-version
1061 emacs-minor-version))))))
a1b4049d
BW
1062 ;; Insert X-Mailer, but only if it doesn't already exist.
1063 (save-excursion
a66894d8
BW
1064 (when (and mh-insert-x-mailer-flag
1065 (null (mh-goto-header-field "X-Mailer")))
c3d9274a 1066 (mh-insert-fields "X-Mailer:" mh-x-mailer-string))))
a1b4049d 1067
dda00b2c
BW
1068(defun mh-insert-x-face ()
1069 "Append X-Face, Face or X-Image-URL field to header.
1070If the field already exists, this function does nothing."
1071 (when (and (file-exists-p mh-x-face-file)
1072 (file-readable-p mh-x-face-file))
1073 (save-excursion
1074 (unless (or (mh-position-on-field "X-Face")
1075 (mh-position-on-field "Face")
1076 (mh-position-on-field "X-Image-URL"))
1077 (save-excursion
1078 (goto-char (+ (point) (cadr (insert-file-contents mh-x-face-file))))
1079 (if (not (looking-at "^"))
1080 (insert "\n")))
1081 (unless (looking-at "\\(X-Face\\|Face\\|X-Image-URL\\): ")
1082 (insert "X-Face: "))))))
1083
dda00b2c
BW
1084(defun mh-tidy-draft-buffer ()
1085 "Run when a draft buffer is destroyed."
1086 (let ((buffer (get-buffer mh-recipients-buffer)))
1087 (if buffer
1088 (kill-buffer buffer))))
1089
1090(defun mh-letter-mode-message ()
1091 "Display a help message for users of `mh-letter-mode'.
1092This should be the last function called when composing the draft."
1093 (message "%s" (substitute-command-keys
1094 (concat "Type \\[mh-send-letter] to send message, "
1095 "\\[mh-help] for help"))))
1096
1097(defun mh-letter-adjust-point ()
1098 "Move cursor to first header field if are using the no prompt mode."
1099 (unless mh-compose-prompt-flag
1100 (goto-char (point-max))
1101 (mh-letter-next-header-field)))
1102
aad5673d
SG
1103(defun mh-annotate-msg (msg folder note &rest args)
1104 "Mark MSG in FOLDER with character NOTE and annotate message with ARGS.
662c14da 1105MSG can be a message number, a list of message numbers, or a sequence.
aad5673d
SG
1106The hook `mh-annotate-msg-hook' is run after annotating; see its
1107documentation for variables it can use."
1108 (apply 'mh-exec-cmd "anno" folder
dda00b2c
BW
1109 (if (listp msg) (append msg args) (cons msg args)))
1110 (save-excursion
aad5673d
SG
1111 (cond ((get-buffer folder) ; Buffer may be deleted
1112 (set-buffer folder)
dda00b2c
BW
1113 (mh-iterate-on-range nil msg
1114 (mh-notate nil note
aad5673d
SG
1115 (+ mh-cmd-note mh-scan-field-destination-offset))))))
1116 (let ((mh-current-folder folder)
1117 ;; mh-annotate-list is a sequence name or a list of message numbers
1118 (mh-annotate-list (if (numberp msg) (list msg) msg)))
1119 (run-hooks 'mh-annotate-msg-hook)))
dda00b2c 1120
dda00b2c
BW
1121(defun mh-insert-header-separator ()
1122 "Insert `mh-mail-header-separator', if absent."
1123 (save-excursion
1124 (goto-char (point-min))
1125 (rfc822-goto-eoh)
1126 (if (looking-at "$")
1127 (insert mh-mail-header-separator))))
bdcfe844 1128
a66894d8
BW
1129;;;###mh-autoload
1130(defun mh-insert-auto-fields (&optional non-interactive)
3b463df0 1131 "Insert custom fields if recipient is found in `mh-auto-fields-list'.
a66894d8 1132
3fbc098d
BW
1133Once the header contains one or more recipients, you may run this
1134command to insert these fields manually. However, if you use this
1135command, the automatic insertion when the message is sent is
1136disabled.
f0d73c14 1137
3fbc098d
BW
1138In a program, set buffer-local `mh-insert-auto-fields-done-local'
1139if header fields were added. If NON-INTERACTIVE is non-nil,
1140perform actions quietly and only if
1141`mh-insert-auto-fields-done-local' is nil. Return t if fields
1142added; otherwise return nil."
a66894d8 1143 (interactive)
f0d73c14
BW
1144 (when (or (not non-interactive)
1145 (not mh-insert-auto-fields-done-local))
a66894d8 1146 (save-excursion
f0d73c14
BW
1147 (when (and (or (mh-goto-header-field "To:")
1148 (mh-goto-header-field "cc:")))
1149 (let ((list mh-auto-fields-list)
1150 (fields-inserted nil))
a66894d8
BW
1151 (while list
1152 (let ((regexp (nth 0 (car list)))
1153 (entries (nth 1 (car list))))
9f7e7195 1154 (when (mh-regexp-in-field-p regexp "To:" "cc:")
a66894d8 1155 (setq mh-insert-auto-fields-done-local t)
f0d73c14 1156 (setq fields-inserted t)
a66894d8 1157 (if (not non-interactive)
f0d73c14 1158 (message "Fields for %s added" regexp))
a66894d8
BW
1159 (let ((entry-list entries))
1160 (while entry-list
1161 (let ((field (caar entry-list))
1162 (value (cdar entry-list)))
1163 (cond
f0d73c14 1164 ((equal ":identity" field)
dda00b2c
BW
1165 (when
1166 ;;(and (not mh-identity-local)
a05fcb7d 1167 ;; Bug 1204506. But do we need to be able
dda00b2c
BW
1168 ;; to set an identity manually that won't be
1169 ;; overridden by mh-insert-auto-fields?
1170 (assoc value mh-identity-list)
1171 ;;)
a66894d8
BW
1172 (mh-insert-identity value)))
1173 (t
1174 (mh-modify-header-field field value
1175 (equal field "From")))))
1176 (setq entry-list (cdr entry-list))))))
f0d73c14
BW
1177 (setq list (cdr list)))
1178 fields-inserted)))))
924df208
BW
1179
1180(defun mh-modify-header-field (field value &optional overwrite-flag)
1181 "To header FIELD add VALUE.
2dcf34f9
BW
1182If OVERWRITE-FLAG is non-nil then the old value, if present, is
1183discarded."
a66894d8
BW
1184 (cond ((and overwrite-flag
1185 (mh-goto-header-field (concat field ":")))
1186 (insert " " value)
d5dc8c56 1187 (delete-region (point) (mh-line-end-position)))
a66894d8 1188 ((and (not overwrite-flag)
855c6482 1189 (mh-regexp-in-field-p (concat "\\b" (regexp-quote value) "\\b") field))
a66894d8
BW
1190 ;; Already there, do nothing.
1191 )
1192 ((and (not overwrite-flag)
1193 (mh-goto-header-field (concat field ":")))
1194 (insert " " value ","))
1195 (t
1196 (mh-goto-header-end 0)
1197 (insert field ": " value "\n"))))
1198
dda00b2c
BW
1199(defun mh-regexp-in-field-p (regexp &rest fields)
1200 "Non-nil means REGEXP was found in FIELDS."
855c6482
JH
1201 (let ((old-syntax-table (syntax-table)))
1202 (unwind-protect
1203 (save-excursion
1b0b1855 1204 (let ((search-result nil))
855c6482 1205 (while fields
195eea0f
GM
1206 (let* ((field (car fields))
1207 (syntax-table
1208 (or mh-regexp-in-field-syntax-table
1209 (let ((case-fold-search t))
1210 (cond
1211 ((string-match field "^To$\\|^[BD]?cc$\\|^From$")
1212 mh-addr-syntax-table)
1213 ((string-match field "^Fcc$")
1214 mh-fcc-syntax-table)
1215 (t
1216 (syntax-table)))
1217 ))))
855c6482
JH
1218 (if (and (mh-goto-header-field field)
1219 (set-syntax-table syntax-table)
1220 (re-search-forward
1221 regexp (save-excursion (mh-header-field-end)(point)) t))
1222 (setq fields nil
1223 search-result t)
1224 (setq fields (cdr fields)))
1225 (set-syntax-table old-syntax-table)))
1226 search-result))
1b0b1855 1227 (set-syntax-table old-syntax-table))))
f0d73c14
BW
1228
1229(defun mh-ascii-buffer-p ()
1230 "Check if current buffer is entirely composed of ASCII.
2dcf34f9
BW
1231The function doesn't work for XEmacs since `find-charset-region'
1232doesn't exist there."
f0d73c14
BW
1233 (loop for charset in (mh-funcall-if-exists
1234 find-charset-region (point-min) (point-max))
1235 unless (eq charset 'ascii) return nil
1236 finally return t))
c26cf6c8 1237
bdcfe844
BW
1238(provide 'mh-comp)
1239
cee9f5c6
BW
1240;; Local Variables:
1241;; indent-tabs-mode: nil
1242;; sentence-end-double-space: nil
1243;; End:
60370d40
PJ
1244
1245;;; mh-comp.el ends here