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