mh-comp.el 44 KB

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