Merge emacs-21_0_90-emh-1_13_0-0.
[elisp/emh.git] / emh-comp.el
1 ;;; emh-comp.el --- emh functions for composing messages
2
3 ;; Copyright (C) 1993,94,95,96,97,98,99,2000 Free Software Foundation, Inc.
4
5 ;; Author: MORIOKA Tomohiko <tomo@m17n.org>
6 ;;         OKABE Yasuo <okabe@kudpc.kyoto-u.ac.jp>
7 ;; Created: 1996/2/29 (separated from tm-mh-e.el)
8 ;;      Renamed: 1997/2/21 from tmh-comp.el
9 ;; Keywords: mail composing, MH, MIME, mail
10
11 ;; This file is part of emh.
12
13 ;; This program is free software; you can redistribute it and/or
14 ;; modify it under the terms of the GNU General Public License as
15 ;; published by the Free Software Foundation; either version 2, or (at
16 ;; your option) any later version.
17
18 ;; This program is distributed in the hope that it will be useful, but
19 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21 ;; General Public License for more details.
22
23 ;; You should have received a copy of the GNU General Public License
24 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
25 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
26 ;; Boston, MA 02111-1307, USA.
27
28 ;;; Code:
29
30 (require 'mh-comp)
31 (require 'mime-edit)
32
33 ;; Avoid byte compile warnings.
34 ;; (defvar gnus-article-buffer)
35 ;; (defvar gnus-article-copy)
36 ;; (defvar gnus-original-article-buffer)
37 ;; (eval-when-compile
38 ;;   (fset 'gnus-copy-article-buffer 'ignore)
39 ;;   )
40
41
42 ;;; @ variable
43 ;;;
44
45 (defvar emh-forwcomps "forwcomps"
46   "Name of file to be used as a skeleton for forwarding messages.
47 Default is \"forwcomps\".  If not a complete path name, the file
48 is searched for first in the user's MH directory, then in the
49 system MH lib directory.")
50
51 ;; (defvar emh-message-yank-function 'mh-yank-cur-msg)
52
53
54 ;;; @ for tm-edit
55 ;;;
56
57 (defun emh::make-message (folder number)
58   (vector folder number)
59   )
60
61 (defun emh::message/folder (message)
62   (elt message 0)
63   )
64
65 (defun emh::message/number (message)
66   (elt message 1)
67   )
68
69 (defun emh::message/file-name (message)
70   (expand-file-name
71    (emh::message/number message)
72    (mh-expand-file-name (emh::message/folder message))
73    ))
74
75 ;;; modified by OKABE Yasuo <okabe@kudpc.kyoto-u.ac.jp>
76 ;;;     1995/11/14 (cf. [tm-ja:1096])
77 (defun emh-prompt-for-message (prompt folder &optional default)
78   (let* ((files
79           (directory-files (mh-expand-file-name folder) nil "^[0-9]+$")
80           )
81          (folder-buf (get-buffer folder))
82          (default
83            (if folder-buf
84                (save-excursion
85                  (set-buffer folder-buf)
86                  (let* ((show-buffer (get-buffer mh-show-buffer))
87                         (show-buffer-file-name
88                          (buffer-file-name show-buffer)))
89                    (if show-buffer-file-name
90                        (file-name-nondirectory show-buffer-file-name)))))))
91     (if (or (null default)
92             (not (string-match "^[0-9]+$" default)))
93         (setq default
94               (if (and (string= folder mh-sent-from-folder)
95                        mh-sent-from-msg)
96                   (int-to-string mh-sent-from-msg)
97                 (save-excursion
98                   (let (cur-msg)
99                     (if (and
100                          (= 0 (mh-exec-cmd-quiet nil "pick" folder "cur"))
101                          (set-buffer mh-temp-buffer)
102                          (setq cur-msg (buffer-string))
103                          (string-match "^[0-9]+$" cur-msg))
104                         (substring cur-msg 0 (match-end 0))
105                       (car files)))))))
106     (completing-read prompt
107                      (let ((i 0))
108                        (mapcar (function
109                                 (lambda (file)
110                                   (setq i (+ i 1))
111                                   (list file i)
112                                   ))
113                                files)
114                        ) nil nil default)
115     ))
116
117 ;;; modified by OKABE Yasuo <okabe@kudpc.kyoto-u.ac.jp>
118 ;;;     1995/11/14 (cf. [tm-ja:1096])
119 (defun emh-query-message (&optional message)
120   (let (folder number)
121     (if message
122         (progn
123           (setq folder (emh::message/folder message))
124           (setq number (emh::message/number message))
125           ))
126     (or (stringp folder)
127         (setq folder (mh-prompt-for-folder
128                       "Message from"
129                       (if (and (stringp mh-sent-from-folder)
130                                (string-match "^\\+" mh-sent-from-folder))
131                           mh-sent-from-folder "+inbox")
132                       nil)))
133     (setq number
134           (if (numberp number)
135               (number-to-string number)
136             (emh-prompt-for-message "Message number: " folder)
137             ))
138     (emh::make-message folder number)
139     ))
140
141 (defun emh-insert-message (&optional message)
142   ;; always ignores message
143   ;; (let ((article-buffer
144   ;;        (if (not (and (stringp mh-sent-from-folder)
145   ;;                      (numberp mh-sent-from-msg)
146   ;;                      ))
147   ;;            (cond ((and (boundp 'gnus-original-article-buffer)
148   ;;                        (bufferp mh-sent-from-folder)
149   ;;                        (get-buffer gnus-original-article-buffer)
150   ;;                        )
151   ;;                   gnus-original-article-buffer)
152   ;;                  ((and (boundp 'gnus-article-buffer)
153   ;;                        (get-buffer gnus-article-buffer)
154   ;;                        (bufferp mh-sent-from-folder)
155   ;;                        )
156   ;;                   (save-excursion
157   ;;                     (set-buffer gnus-article-buffer)
158   ;;                     (if (eq major-mode 'mime-view-mode)
159   ;;                         mime-raw-buffer
160   ;;                       (current-buffer)
161   ;;                       )))
162   ;;                  ))))
163   (if (null article-buffer)
164       (emh-insert-mail
165        (emh::make-message mh-sent-from-folder mh-sent-from-msg))
166     ;; (insert-buffer article-buffer)
167     ;; (mime-edit-inserted-message-filter)
168     ;; )
169     ))
170
171 (defun emh-insert-mail (&optional message)
172   (save-excursion
173     (save-restriction
174       (let ((message-file
175              (emh::message/file-name (emh-query-message message))))
176         (narrow-to-region (point) (point))
177         (insert-file-contents message-file)
178         (push-mark (point-max))
179         (mime-edit-inserted-message-filter)
180     ))))
181
182 (set-alist 'mime-edit-message-inserter-alist
183            'mh-letter-mode (function emh-insert-message))
184 (set-alist 'mime-edit-mail-inserter-alist
185            'mh-letter-mode (function emh-insert-mail))
186 (set-alist 'mime-edit-mail-inserter-alist
187            'news-reply-mode (function emh-insert-mail))
188 (set-alist
189  'mime-edit-split-message-sender-alist
190  'mh-letter-mode
191  (function
192   (lambda (&optional arg)
193     (interactive "P")
194     (write-region (point-min) (point-max)
195                   mime-edit-draft-file-name nil 'no-message)
196     (cond (arg
197            (pop-to-buffer "MH mail delivery")
198            (erase-buffer)
199            (mh-exec-cmd-output mh-send-prog t "-watch" "-nopush"
200                                "-nodraftfolder"
201                                mh-send-args
202                                mime-edit-draft-file-name)
203            (goto-char (point-max))      ; show the interesting part
204            (recenter -1)
205            (sit-for 1))
206           (t
207            (apply 'mh-exec-cmd-quiet t mh-send-prog 
208                   (mh-list-to-string
209                    (list "-nopush" "-nodraftfolder"
210                          "-noverbose" "-nowatch"
211                          mh-send-args mime-edit-draft-file-name)))))
212     )))
213
214
215 ;;; @ commands using tm-edit features
216 ;;;
217
218 (defun emh-edit-again (msg)
219   "Clean-up a draft or a message previously sent and make it resendable.
220 Default is the current message.
221 The variable mh-new-draft-cleaned-headers specifies the headers to remove.
222 See also documentation for `\\[mh-send]' function."
223   (interactive (list (mh-get-msg-num t)))
224   (catch 'tag
225     (let* ((from-folder mh-current-folder)
226            (config (current-window-configuration))
227            (draft
228             (cond ((and mh-draft-folder (equal from-folder mh-draft-folder))
229                    (let ((name (format "draft-%d" msg)))
230                      (if (get-buffer name)
231                          (throw 'tag (pop-to-buffer name))
232                        )
233                      (let ((filename
234                             (mh-msg-filename msg mh-draft-folder)
235                             ))
236                        (set-buffer (get-buffer-create name))
237                        (as-binary-input-file (insert-file-contents filename))
238                        (setq buffer-file-name filename)
239                        )
240                      (pop-to-buffer name)
241                      (if (re-search-forward "^-+$" nil t)
242                          (replace-match "")
243                          )
244                      name))
245                   (t
246                    (let ((flag enable-multibyte-characters))
247                      (prog1
248                          (as-binary-input-file
249                           (mh-read-draft "clean-up"
250                                          (mh-msg-filename msg) nil))
251                        (set-buffer-multibyte flag)
252                        ))
253                    ))))
254       (goto-char (point-min))
255       (mh-clean-msg-header (point-min) mh-new-draft-cleaned-headers nil)
256       (let ((cs (detect-coding-region (point-min)(point-max))))
257         (set-buffer-file-coding-system
258          (if (listp cs)
259              (car cs)
260            cs)))
261       (save-buffer)
262       (mime-edit-again nil 'no-separator 'not-turn-on)
263       (goto-char (point-min))
264       (mh-compose-and-send-mail draft "" from-folder nil nil nil nil nil nil
265                                 config)
266       )))
267
268 ;;; by OKABE Yasuo <okabe@kudpc.kyoto-u.ac.jp>
269 ;;;     1996/2/29 (cf. [tm-ja:1643])
270 (defun emh-extract-rejected-mail (msg)
271   "Extract a letter returned by the mail system and make it re-editable.
272 Default is the current message.  The variable mh-new-draft-cleaned-headers
273 gives the headers to clean out of the original message."
274   (interactive (list (mh-get-msg-num t)))
275   (let ((from-folder mh-current-folder)
276         (config (current-window-configuration))
277         (draft (mh-read-draft "extraction" (mh-msg-filename msg) nil)))
278     (setq buffer-read-only nil)
279     (goto-char (point-min))
280     (cond 
281      ((and
282        (re-search-forward
283         (concat "^\\($\\|[Cc]ontent-[Tt]ype:[ \t]+multipart/\\)") nil t)
284        (not (bolp))
285        (re-search-forward "boundary=\"\\([^\"]+\\)\"" nil t))
286       (let ((case-fold-search t)
287             (boundary (buffer-substring (match-beginning 1) (match-end 1))))
288         (cond
289          ((re-search-forward
290            (concat "^--" boundary "\n"
291                    "content-type:[ \t]+"
292                    "\\(message/rfc822\\|text/rfc822-headers\\)\n"
293                    "\\(.+\n\\)*\n") nil t)
294           (delete-region (point-min) (point))
295           (mh-clean-msg-header (point-min) mh-new-draft-cleaned-headers nil)
296           (search-forward
297            (concat "\n--" boundary "--\n") nil t)
298           (delete-region (match-beginning 0) (point-max)))
299          (t
300           (message "Seems no message/rfc822 part.")))))
301      ((re-search-forward mh-rejected-letter-start nil t)
302       (skip-chars-forward " \t\n")
303       (delete-region (point-min) (point))
304       (mh-clean-msg-header (point-min) mh-new-draft-cleaned-headers nil))
305      (t
306       (message "Does not appear to be a rejected letter.")))
307     (goto-char (point-min))
308     (if (re-search-forward "^-+$" nil t)
309         (replace-match "")
310       )
311     (mime-edit-again nil t t)
312     (goto-char (point-min))
313     (set-buffer-modified-p nil)
314     (mh-compose-and-send-mail draft "" from-folder msg
315                               (mh-get-header-field "To:")
316                               (mh-get-header-field "From:")
317                               (mh-get-header-field "Cc:")
318                               nil nil config)))
319
320 ;;; by OKABE Yasuo <okabe@kudpc.kyoto-u.ac.jp>
321 ;;;     1995/11/14 (cf. [tm-ja:1099])
322 (defun emh-forward (to cc &optional msg-or-seq)
323   "Forward a message or message sequence as MIME message/rfc822.
324 Defaults to displayed message. If optional prefix argument provided,
325 then prompt for the message sequence. See also documentation for
326 `\\[mh-send]' function."
327   (interactive (list (mh-read-address "To: ")
328                      (mh-read-address "Cc: ")
329                      (if current-prefix-arg
330                          (mh-read-seq-default "Forward" t)
331                        (mh-get-msg-num t)
332                        )))
333   (or msg-or-seq
334       (setq msg-or-seq (mh-get-msg-num t)))
335   (let* ((folder mh-current-folder)
336          (config (current-window-configuration))
337          ;; uses "draft" for compatibility with forw.
338          ;; forw always leaves file in "draft" since it doesn't have -draft
339          (draft-name (expand-file-name "draft" mh-user-path))
340          (draft (cond ((or (not (file-exists-p draft-name))
341                            (y-or-n-p "The file `draft' exists.  Discard it? "))
342                        (mh-exec-cmd "comp"
343                                     "-noedit" "-nowhatnowproc"
344                                     "-form" emh-forwcomps
345                                     "-nodraftfolder")
346                        (prog1
347                            (mh-read-draft "" draft-name t)
348                          (mh-insert-fields "To:" to "Cc:" cc)
349                          (set-buffer-modified-p nil)))
350                       (t
351                        (mh-read-draft "" draft-name nil)))))
352     (let ((msubtype "digest")
353           orig-from orig-subject multipart-flag
354           (tag-regexp
355            (concat "^"
356                    (regexp-quote (mime-make-tag "message" "rfc822"))))
357           )
358       (goto-char (point-min))
359       (save-excursion
360         (save-restriction
361           (goto-char (point-max))
362           (if (not (bolp)) (insert "\n"))
363           (let ((beg (point)))
364             (narrow-to-region beg beg)
365             (mh-exec-cmd-output "pick" nil folder msg-or-seq)
366             (if (> (count-lines (point) (point-max)) 1)
367                 (setq multipart-flag t)
368               )
369             (while (re-search-forward "^\\([0-9]+\\)\n" nil t)
370               (let ((forw-msg
371                      (buffer-substring (match-beginning 1) (match-end 1)))
372                     (beg (match-beginning 0))
373                     (end (match-end 0))
374                     )
375                 (save-restriction
376                   (narrow-to-region beg end)
377                   ;; modified for Emacs 18
378                   (delete-region beg end)
379                   (insert-file-contents
380                    (mh-expand-file-name forw-msg
381                                         (mh-expand-file-name folder))
382                    )
383                   (save-excursion
384                     (push-mark (point-max))
385                     (mime-edit-inserted-message-filter))
386                   (goto-char (point-max))
387                   )
388                 (save-excursion
389                   (goto-char beg)
390                   (mime-edit-insert-tag "message" "rfc822")
391                   )))
392             (delete-region (point) (point-max))
393             (if multipart-flag
394                 (mime-edit-enclose-digest-region beg (point))
395               ))))
396       (re-search-forward tag-regexp)
397       (forward-line 1)
398       (save-restriction
399         (narrow-to-region (point) (point-max))
400         (setq orig-from (eword-decode-string
401                          (mh-get-header-field "From:")))
402         (setq orig-subject (eword-decode-string
403                             (mh-get-header-field "Subject:")))
404         )
405       (let ((forw-subject
406              (mh-forwarded-letter-subject orig-from orig-subject)))
407         (mh-insert-fields "Subject:" forw-subject)
408         (goto-char (point-min))
409         (re-search-forward tag-regexp)
410         (forward-line -1)
411         (delete-other-windows)
412         (if (numberp msg-or-seq)
413             (mh-add-msgs-to-seq msg-or-seq 'forwarded t)
414           (mh-add-msgs-to-seq (mh-seq-to-msgs msg-or-seq) 'forwarded t))
415         (mh-compose-and-send-mail draft "" folder msg-or-seq
416                                   to forw-subject cc
417                                   mh-note-forw "Forwarded:"
418                                   config)))))
419
420 (cond ((not (featurep 'mh-utils))
421        (defun emh::insert-letter (folder number verbatim)
422          (mh-insert-letter verbatim folder number)
423          )
424        )
425       ((and (boundp 'mh-e-version)
426             (string-lessp mh-e-version "5"))
427        (defun emh::insert-letter (folder number verbatim)
428          (mh-insert-letter number folder verbatim)
429          )
430        )
431       (t
432        (defalias 'emh::insert-letter 'mh-insert-letter)
433        ))
434
435 (defun emh-insert-letter (verbatim)
436   "Interface to mh-insert-letter."
437   (interactive "P")
438   (let*
439       ((folder (mh-prompt-for-folder
440                 "Message from"
441                 (if (and (stringp mh-sent-from-folder)
442                          (string-match "^\\+" mh-sent-from-folder))
443                     mh-sent-from-folder "+inbox")
444                 nil))
445        (number (emh-prompt-for-message "Message number: " folder)))
446     (emh::insert-letter folder number verbatim)))
447
448 ;; (defun emh-yank-cur-msg-with-no-filter ()
449 ;;   "Insert the current message into the draft buffer.
450 ;; This function makes new show-buffer from article-buffer to disable
451 ;; variable `mime-preview-text/plain-hook'. If you don't want to use text
452 ;; filters for replying message, please set it to
453 ;; `emh-message-yank-function'.
454 ;; Prefix each non-blank line in the message with the string in
455 ;; `mh-ins-buf-prefix'. The entire message will be inserted if
456 ;; `mh-yank-from-start-of-msg' is non-nil. If this variable is nil, the
457 ;; portion of the message following the point will be yanked.  If
458 ;; `mh-delete-yanked-msg-window' is non-nil, any window displaying the
459 ;; yanked message will be deleted."
460 ;;   (interactive)
461 ;;   (if (and mh-sent-from-folder mh-sent-from-msg)
462 ;;       (let ((to-point (point))
463 ;;             (to-buffer (current-buffer)))
464 ;;         (set-buffer mh-sent-from-folder)
465 ;;         (if mh-delete-yanked-msg-window
466 ;;             (delete-windows-on mh-show-buffer))
467 ;;         (set-buffer mh-show-buffer)     ; Find displayed message
468 ;;         (let ((mh-ins-str
469 ;;                (if mime-raw-buffer
470 ;;                    (let (mime-display-text/plain-hook buf)
471 ;;                      (prog1
472 ;;                          (save-window-excursion
473 ;;                            (set-buffer mime-raw-buffer)
474 ;;                            (setq buf (mime-view-mode))
475 ;;                            (buffer-string)
476 ;;                            )
477 ;;                        (kill-buffer buf)
478 ;;                        ))
479 ;;                  (buffer-string)
480 ;;                  )))
481 ;;           (set-buffer to-buffer)
482 ;;           (save-restriction
483 ;;             (narrow-to-region to-point to-point)
484 ;;             (push-mark)
485 ;;             (insert mh-ins-str)
486 ;;             (mh-insert-prefix-string mh-ins-buf-prefix)
487 ;;             (insert "\n"))))
488 ;;     (error "There is no current message")))
489
490 ;; (defun emh-yank-current-message ()
491 ;;   "Insert the current message into the draft buffer.
492 ;; It uses variable `emh-message-yank-function'
493 ;; to select message yanking function."
494 ;;   (interactive)
495 ;;   (let ((mh-sent-from-folder mh-sent-from-folder)
496 ;;         (mh-sent-from-msg mh-sent-from-msg))
497 ;;     (if (and (not (stringp mh-sent-from-folder))
498 ;;              (boundp 'gnus-article-buffer)
499 ;;              (get-buffer gnus-article-buffer)
500 ;;              (bufferp mh-sent-from-folder)
501 ;;              ) ; might be called from GNUS
502 ;;         (if (boundp 'gnus-article-copy) ; might be sgnus
503 ;;             (save-excursion
504 ;;               (gnus-copy-article-buffer)
505 ;;               (setq mh-sent-from-folder gnus-article-copy)
506 ;;               (set-buffer mh-sent-from-folder)
507 ;;               (setq mh-show-buffer gnus-article-copy)
508 ;;               )
509 ;;           (save-excursion
510 ;;             (setq mh-sent-from-folder gnus-article-buffer)
511 ;;             (set-buffer gnus-article-buffer)
512 ;;             (setq mh-show-buffer (current-buffer))
513 ;;             )))
514 ;;     (funcall emh-message-yank-function)
515 ;;     ))
516
517 ;; (substitute-key-definition
518 ;;  'mh-yank-cur-msg 'emh-yank-current-message mh-letter-mode-map)
519 ;; (substitute-key-definition
520 ;;  'mh-insert-letter 'emh-insert-letter mh-letter-mode-map)
521
522
523 ;;; @ end
524 ;;;
525
526 (provide 'emh-comp)
527 (require 'emh)
528
529 ;;; emh-comp.el ends here