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