5b2c9e92c896bacd4de5fac8d4b4689f30c95a30
[elisp/wanderlust.git] / wl / wl-draft.el
1 ;;; wl-draft.el --- Message draft mode for Wanderlust.
2
3 ;; Copyright (C) 1998,1999,2000 Yuuichi Teranishi <teranisi@gohome.org>
4 ;; Copyright (C) 1998,1999,2000 Masahiro MURATA <muse@ba2.so-net.ne.jp>
5
6 ;; Author: Yuuichi Teranishi <teranisi@gohome.org>
7 ;;      Masahiro MURATA <muse@ba2.so-net.ne.jp>
8 ;; Keywords: mail, net news
9
10 ;; This file is part of Wanderlust (Yet Another Message Interface on Emacsen).
11
12 ;; This program is free software; you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation; either version 2, or (at your option)
15 ;; any later version.
16 ;;
17 ;; This program is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 ;; GNU General Public License for more details.
21 ;;
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
24 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25 ;; Boston, MA 02111-1307, USA.
26 ;;
27
28 ;;; Commentary:
29 ;;
30
31 ;;; Code:
32 ;;
33
34 (require 'sendmail)
35 (require 'wl-template)
36 (require 'emu)
37 (condition-case nil (require 'timezone) (error nil))
38 (require 'std11)
39 (require 'wl-vars)
40
41 (defvar x-face-add-x-face-version-header)
42 (defvar mail-reply-buffer)
43 (defvar mail-from-style)
44
45 (eval-when-compile
46   (require 'elmo-pop3)
47   (defalias-maybe 'x-face-insert 'ignore)
48   (defalias-maybe 'x-face-insert-version-header 'ignore)
49   (defalias-maybe 'wl-init 'ignore)
50   (defalias-maybe 'wl-draft-mode 'ignore))
51
52 (eval-and-compile
53   (autoload 'wl-addrmgr "wl-addrmgr"))
54
55 (defvar wl-draft-buf-name "Draft")
56 (defvar wl-draft-buffer-file-name nil)
57 (defvar wl-draft-field-completion-list nil)
58 (defvar wl-draft-verbose-send t)
59 (defvar wl-draft-verbose-msg nil)
60 (defvar wl-draft-queue-flushing nil)
61 (defvar wl-draft-config-variables nil)
62 (defvar wl-draft-config-exec-flag t)
63 (defvar wl-draft-buffer-cur-summary-buffer nil)
64 (defvar wl-draft-clone-local-variable-regexp "^\\(wl\\|mime\\)")
65 (defvar wl-draft-sendlog-filename "sendlog")
66 (defvar wl-draft-queue-save-filename "qinfo")
67 (defvar wl-draft-config-save-filename "config")
68 (defvar wl-draft-queue-flush-send-function 'wl-draft-dispatch-message)
69 (defvar wl-sent-message-via nil)
70 (defvar wl-sent-message-modified nil)
71 (defvar wl-sent-message-queued nil)
72 (defvar wl-draft-fcc-list nil)
73 (defvar wl-draft-reedit nil)
74 (defvar wl-draft-reply-buffer nil)
75 (defvar wl-draft-forward nil)
76 (defvar wl-draft-parent-folder nil)
77 (defvar wl-draft-doing-mime-bcc nil)
78
79 (defvar wl-draft-config-sub-func-alist
80   '((body               . wl-draft-config-sub-body)
81     (top                . wl-draft-config-sub-top)
82     (bottom             . wl-draft-config-sub-bottom)
83     (header             . wl-draft-config-sub-header)
84     (header-top         . wl-draft-config-sub-header-top)
85     (header-bottom      . wl-draft-config-sub-header)
86     (part-top           . wl-draft-config-sub-part-top)
87     (part-bottom        . wl-draft-config-sub-part-bottom)
88     (body-file          . wl-draft-config-sub-body-file)
89     (top-file           . wl-draft-config-sub-top-file)
90     (bottom-file        . wl-draft-config-sub-bottom-file)
91     (header-file        . wl-draft-config-sub-header-file)
92     (template           . wl-draft-config-sub-template)
93     (x-face             . wl-draft-config-sub-x-face)))
94
95 (make-variable-buffer-local 'wl-draft-buffer-file-name)
96 (make-variable-buffer-local 'wl-draft-buffer-cur-summary-buffer)
97 (make-variable-buffer-local 'wl-draft-config-variables)
98 (make-variable-buffer-local 'wl-draft-config-exec-flag)
99 (make-variable-buffer-local 'wl-sent-message-via)
100 (make-variable-buffer-local 'wl-sent-message-queued)
101 (make-variable-buffer-local 'wl-draft-fcc-list)
102 (make-variable-buffer-local 'wl-draft-reply-buffer)
103 (make-variable-buffer-local 'wl-draft-parent-folder)
104
105 (defsubst wl-smtp-password-key (user mechanism server)
106   (format "SMTP:%s/%s@%s"
107           user mechanism server))
108
109 (defmacro wl-smtp-extension-bind (&rest body)
110   (` (let* ((smtp-sasl-mechanisms
111              (if wl-smtp-authenticate-type
112                  (mapcar 'upcase
113                          (if (listp wl-smtp-authenticate-type)
114                              wl-smtp-authenticate-type
115                            (list wl-smtp-authenticate-type)))))
116             (smtp-use-sasl (and smtp-sasl-mechanisms t))
117             (smtp-use-starttls (eq wl-smtp-connection-type 'starttls))
118             smtp-sasl-user-name smtp-sasl-properties sasl-read-passphrase)
119        (if (and (string= (car smtp-sasl-mechanisms) "DIGEST-MD5")
120                 ;; sendmail bug?
121                 (string-match "^\\([^@]*\\)@\\([^@]*\\)"
122                               wl-smtp-posting-user))
123            (setq smtp-sasl-user-name (match-string 1 wl-smtp-posting-user)
124                  smtp-sasl-properties (list 'realm
125                                             (match-string 2 wl-smtp-posting-user)))
126          (setq smtp-sasl-user-name wl-smtp-posting-user
127                smtp-sasl-properties nil))
128        (setq sasl-read-passphrase
129              (function
130               (lambda (prompt)
131                 (elmo-get-passwd
132                  (wl-smtp-password-key
133                   smtp-sasl-user-name
134                   (car smtp-sasl-mechanisms)
135                   smtp-server)))))
136        (,@ body))))
137
138 (defun wl-draft-insert-date-field ()
139   "Insert Date field."
140   (insert "Date: " (wl-make-date-string) "\n"))
141
142 (defun wl-draft-insert-from-field ()
143   "Insert From field."
144   ;; Put the "From:" field in unless for some odd reason
145   ;; they put one in themselves.
146   (let* ((login (or user-mail-address (user-login-name)))
147          (fullname (user-full-name)))
148     (cond ((eq mail-from-style 'angles)
149            (insert "From: " fullname)
150            (let ((fullname-start (+ (point-min) (length "From: ")))
151                  (fullname-end (point-marker)))
152              (goto-char fullname-start)
153              ;; Look for a character that cannot appear unquoted
154              ;; according to RFC 822.
155              (if (re-search-forward "[^- !#-'*+/-9=?A-Z^-~]"
156                                     fullname-end 1)
157                  (progn
158                    ;; Quote fullname, escaping specials.
159                    (goto-char fullname-start)
160                    (insert "\"")
161                    (while (re-search-forward "[\"\\]"
162                                              fullname-end 1)
163                      (replace-match "\\\\\\&" t))
164                    (insert "\""))))
165            (insert " <" login ">\n"))
166           ((eq mail-from-style 'parens)
167            (insert "From: " login " (")
168            (let ((fullname-start (point)))
169              (insert fullname)
170              (let ((fullname-end (point-marker)))
171                (goto-char fullname-start)
172                ;; RFC 822 says \ and nonmatching parentheses
173                ;; must be escaped in comments.
174                ;; Escape every instance of ()\ ...
175                (while (re-search-forward "[()\\]" fullname-end 1)
176                  (replace-match "\\\\\\&" t))
177                ;; ... then undo escaping of matching parentheses,
178                ;; including matching nested parentheses.
179                (goto-char fullname-start)
180                (while (re-search-forward
181                        "\\(\\=\\|[^\\]\\(\\\\\\\\\\)*\\)\\\\(\\(\\([^\\]\\|\\\\\\\\\\)*\\)\\\\)"
182                        fullname-end 1)
183                  (replace-match "\\1(\\3)" t)
184                  (goto-char fullname-start))))
185            (insert ")\n"))
186           ((not mail-from-style)
187            (insert "From: " login "\n")))))
188
189 (defun wl-draft-insert-x-face-field ()
190   "Insert X-Face header."
191   (interactive)
192   (if (not (file-exists-p wl-x-face-file))
193       (error "File %s does not exist" wl-x-face-file)
194     (beginning-of-buffer)
195     (search-forward mail-header-separator nil t)
196     (beginning-of-line)
197     (wl-draft-insert-x-face-field-here)
198     (run-hooks 'wl-draft-insert-x-face-field-hook))) ; highlight it if you want.
199
200 (defun wl-draft-insert-x-face-field-here ()
201   "Insert X-Face field at point."
202   (let ((x-face-string (elmo-get-file-string wl-x-face-file)))
203     (when (string-match "^\\(X-Face:\\)?[ \t\n]*" x-face-string)
204       (setq x-face-string (substring x-face-string (match-end 0))))
205     (insert "X-Face: " x-face-string))
206   (when (not (= (preceding-char) ?\n))  ; for chomped (choped) x-face-string
207     (insert ?\n))
208   ;; Insert X-Face-Version: field
209   (when (and (fboundp 'x-face-insert-version-header)
210              (boundp 'x-face-add-x-face-version-header)
211              x-face-add-x-face-version-header)
212     (x-face-insert-version-header)))
213
214 (defun wl-draft-setup ()
215   (let ((field wl-draft-fields)
216         cl)
217     (while field
218       (setq cl (append cl
219                        (list (cons (concat (car field) " ")
220                                    (concat (car field) " ")))))
221       (setq field (cdr field)))
222     (setq cl
223           (cons (cons (concat wl-draft-mime-bcc-field-name  ": ")
224                       (concat wl-draft-mime-bcc-field-name  ": "))
225                 cl))
226     (setq wl-draft-field-completion-list cl)
227     (setq wl-address-complete-header-regexp
228           (wl-regexp-opt
229            (append wl-address-complete-header-list
230                    (list (concat wl-draft-mime-bcc-field-name  ":")))))))
231
232 (defun wl-draft-make-mail-followup-to (recipients)
233   (if (elmo-list-member
234        (or wl-user-mail-address-list
235            (list (wl-address-header-extract-address wl-from)))
236        recipients)
237       (let ((rlist (elmo-list-delete
238                     (or wl-user-mail-address-list
239                         (list (wl-address-header-extract-address wl-from)))
240                     (copy-sequence recipients))))
241         (if (elmo-list-member rlist (mapcar 'downcase
242                                             wl-subscribed-mailing-list))
243             rlist
244           (append rlist (list (wl-address-header-extract-address
245                                wl-from)))))
246     recipients))
247
248 (defun wl-draft-delete-myself-from-cc (to cc)
249   (let ((myself (or wl-user-mail-address-list
250                     (list (wl-address-header-extract-address wl-from)))))
251     (cond (wl-draft-always-delete-myself ; always-delete option
252            (elmo-list-delete myself cc))
253           ((elmo-list-member (append to cc) ; subscribed mailing-list
254                              (mapcar 'downcase wl-subscribed-mailing-list))
255            (elmo-list-delete myself cc))
256           (t cc))))
257
258 (defun wl-draft-forward (original-subject summary-buf)
259   (let (references parent-folder)
260     (with-current-buffer summary-buf
261       (setq parent-folder (wl-summary-buffer-folder-name)))
262     (with-current-buffer (wl-message-get-original-buffer)
263       (setq references (nconc
264                         (std11-field-bodies '("References" "In-Reply-To"))
265                         (list (std11-field-body "Message-Id"))))
266       (setq references (delq nil references)
267             references (mapconcat 'identity references " ")
268             references (wl-draft-parse-msg-id-list-string references)
269             references (wl-delete-duplicates references)
270             references (when references
271                          (mapconcat 'identity references "\n\t"))))
272     (wl-draft (list (cons 'To "")
273                     (cons 'Subject
274                           (concat wl-forward-subject-prefix original-subject))
275                     (cons 'References references))
276               nil nil nil nil parent-folder))
277   (goto-char (point-max))
278   (wl-draft-insert-message)
279   (mail-position-on-field "To"))
280
281 (defun wl-draft-strip-subject-re (subject)
282   "Remove \"Re:\" from subject lines. Shamelessly copied from Gnus."
283   (if (string-match wl-subject-prefix-regexp subject)
284       (substring subject (match-end 0))
285     subject))
286
287 (defun wl-draft-reply-list-symbol (with-arg)
288   "Return symbol `wl-draft-reply-*-argument-list' match condition.
289 Check WITH-ARG and From: field."
290   (if (wl-address-user-mail-address-p (or (elmo-field-body "From") ""))
291       (if with-arg
292           'wl-draft-reply-myself-with-argument-list
293         'wl-draft-reply-myself-without-argument-list)
294     (if with-arg
295         'wl-draft-reply-with-argument-list
296       'wl-draft-reply-without-argument-list)))
297
298 (defun wl-draft-reply (buf with-arg summary-buf)
299   "Reply to BUF buffer message.
300 Reply to author if WITH-ARG is non-nil."
301 ;;;(save-excursion
302   (let (r-list
303         to mail-followup-to cc subject in-reply-to references newsgroups
304         to-alist cc-alist decoder parent-folder)
305     (set-buffer summary-buf)
306     (setq parent-folder (wl-summary-buffer-folder-name))
307     (set-buffer buf)
308     (setq r-list (symbol-value (wl-draft-reply-list-symbol with-arg)))
309     (catch 'done
310       (while r-list
311         (when (let ((condition (car (car r-list))))
312                 (cond ((stringp condition)
313                        (std11-field-body condition))
314                       ((listp condition)
315                        (catch 'done
316                          (while condition
317                            (if (not (std11-field-body (car condition)))
318                                (throw 'done nil))
319                            (setq condition (cdr condition)))
320                          t))
321                       ((symbolp condition)
322                        (funcall condition))))
323           (let ((r-to-list (nth 0 (cdr (car r-list))))
324                 (r-cc-list (nth 1 (cdr (car r-list))))
325                 (r-ng-list (nth 2 (cdr (car r-list)))))
326             (when (and (member "Followup-To" r-ng-list)
327                        (string= (std11-field-body "Followup-To") "poster"))
328               (setq r-to-list (cons "From" r-to-list))
329               (setq r-ng-list (delete "Followup-To"
330                                       (copy-sequence r-ng-list))))
331             (if (and r-to-list (symbolp r-to-list))
332                 (setq to (wl-concat-list (funcall r-to-list) ","))
333               (setq to (wl-concat-list (cons to
334                                              (elmo-multiple-fields-body-list
335                                               r-to-list))
336                                        ",")))
337             (if (and r-cc-list (symbolp r-cc-list))
338                 (setq cc (wl-concat-list (funcall r-to-list) ","))
339               (setq cc (wl-concat-list (cons cc
340                                              (elmo-multiple-fields-body-list
341                                               r-cc-list))
342                                        ",")))
343             (if (and r-ng-list (symbolp r-ng-list))
344                 (setq newsgroups (wl-concat-list (funcall r-ng-list) ","))
345               (setq newsgroups (wl-concat-list (cons newsgroups
346                                                      (std11-field-bodies
347                                                       r-ng-list))
348                                                ","))))
349           (throw 'done nil))
350         (setq r-list (cdr r-list)))
351       (error "No match field: check your `%s'"
352              (symbol-name (wl-draft-reply-list-symbol with-arg))))
353     (setq subject (std11-field-body "Subject"))
354     (setq to (wl-parse-addresses to)
355           cc (wl-parse-addresses cc))
356     (with-temp-buffer                   ; to keep raw buffer unibyte.
357       (elmo-set-buffer-multibyte default-enable-multibyte-characters)
358       (setq decoder (mime-find-field-decoder 'Subject 'plain))
359       (setq subject (if (and subject decoder)
360                         (funcall decoder subject) subject))
361       (setq to-alist
362             (mapcar
363              (lambda (addr)
364                (setq decoder (mime-find-field-decoder 'To 'plain))
365                (cons (nth 1 (std11-extract-address-components addr))
366                      (if decoder (funcall decoder addr) addr)))
367              to))
368       (setq cc-alist
369             (mapcar
370              (lambda (addr)
371                (setq decoder (mime-find-field-decoder 'Cc 'plain))
372                (cons (nth 1 (std11-extract-address-components addr))
373                      (if decoder (funcall decoder addr) addr)))
374              cc)))
375     (and wl-reply-subject-prefix
376          (setq subject (concat wl-reply-subject-prefix
377                                (wl-draft-strip-subject-re
378                                 (or subject "")))))
379     (setq in-reply-to (std11-field-body "Message-Id"))
380     (setq references (nconc
381                       (std11-field-bodies '("References" "In-Reply-To"))
382                       (list in-reply-to)))
383     (setq to (delq nil (mapcar 'car to-alist)))
384     (setq cc (delq nil (mapcar 'car cc-alist)))
385     ;; if subscribed mailing list is contained in cc or to
386     ;; and myself is contained in cc,
387     ;; delete myself from cc.
388     (setq cc (wl-draft-delete-myself-from-cc to cc))
389     (when wl-insert-mail-followup-to
390       (setq mail-followup-to
391             (wl-draft-make-mail-followup-to (append to cc)))
392       (setq mail-followup-to (wl-delete-duplicates mail-followup-to nil t)))
393     (with-temp-buffer                   ; to keep raw buffer unibyte.
394       (elmo-set-buffer-multibyte default-enable-multibyte-characters)
395       (setq newsgroups (wl-parse newsgroups
396                                  "[ \t\f\r\n,]*\\([^ \t\f\r\n,]+\\)")
397             newsgroups (wl-delete-duplicates newsgroups)
398             newsgroups
399             (if newsgroups
400                 (mapconcat
401                  (lambda (grp)
402                    (setq decoder (mime-find-field-decoder 'Newsgroups 'plain))
403                    (if decoder (funcall decoder grp) grp))
404                  newsgroups ","))))
405     (setq to (wl-delete-duplicates to nil t))
406     (setq cc (wl-delete-duplicates
407               (append (wl-delete-duplicates cc nil t)
408                       to (copy-sequence to))
409               t t))
410     (and to (setq to (mapconcat
411                       '(lambda (addr)
412                          (if wl-draft-reply-use-address-with-full-name
413                              (or (cdr (assoc addr to-alist)) addr)
414                            addr))
415                       to ",\n\t")))
416     (and cc (setq cc (mapconcat
417                       '(lambda (addr)
418                          (if wl-draft-reply-use-address-with-full-name
419                              (or (cdr (assoc addr cc-alist)) addr)
420                            addr))
421                       cc ",\n\t")))
422     (and mail-followup-to
423          (setq mail-followup-to
424                (mapconcat
425                 '(lambda (addr)
426                    (if wl-draft-reply-use-address-with-full-name
427                        (or (cdr (assoc addr (append to-alist cc-alist))) addr)
428                      addr))
429                 mail-followup-to ",\n\t")))
430     (and (null to) (setq to cc cc nil))
431     (setq references (delq nil references)
432           references (mapconcat 'identity references " ")
433           references (wl-draft-parse-msg-id-list-string references)
434           references (wl-delete-duplicates references)
435           references (if references
436                          (mapconcat 'identity references "\n\t")))
437     (wl-draft (list (cons 'To to)
438                     (cons 'Cc cc)
439                     (cons 'Newsgroups newsgroups)
440                     (cons 'Subject subject)
441                     (cons 'In-Reply-To in-reply-to)
442                     (cons 'References references)
443                     (cons 'Mail-Followup-To mail-followup-to))
444               nil nil nil nil parent-folder)
445     (setq wl-draft-reply-buffer buf))
446   (run-hooks 'wl-reply-hook))
447
448 (defun wl-draft-add-references ()
449   (wl-draft-add-in-reply-to "References"))
450
451 (defun wl-draft-add-in-reply-to (&optional alt-field)
452   (let* ((mes-id (save-excursion
453                    (set-buffer mail-reply-buffer)
454                    (std11-field-body "message-id")))
455          (field (or alt-field "In-Reply-To"))
456          (ref (std11-field-body field))
457          (ref-list nil) (st nil))
458     (when (and mes-id ref)
459       (while (string-match "<[^>]+>" ref st)
460         (setq ref-list
461               (cons (substring ref (match-beginning 0) (setq st (match-end 0)))
462                     ref-list)))
463       (when (and ref-list
464                  (member mes-id ref-list))
465         (setq mes-id nil)))
466     (when mes-id
467       (save-excursion
468         (when (mail-position-on-field field)
469           (forward-line)
470           (while (looking-at "^[ \t]")
471             (forward-line))
472           (setq mes-id (concat "\t" mes-id "\n")))
473         (insert mes-id))
474       t)))
475
476 (defun wl-draft-yank-from-mail-reply-buffer (decode-it
477                                              &optional ignored-fields)
478   (interactive)
479   (save-restriction
480     (narrow-to-region (point)(point))
481     (insert
482      (with-current-buffer mail-reply-buffer
483        (when decode-it
484          (decode-mime-charset-region (point-min) (point-max)
485                                      wl-mime-charset))
486        (buffer-substring-no-properties
487         (point-min) (point-max))))
488     (when ignored-fields
489       (goto-char (point-min))
490       (wl-draft-delete-fields ignored-fields))
491     (goto-char (point-max))
492     (push-mark (point) nil t)
493     (goto-char (point-min)))
494   (let ((beg (point)))
495     (cond (mail-citation-hook (run-hooks 'mail-citation-hook))
496           (mail-yank-hooks (run-hooks 'mail-yank-hooks))
497           (wl-draft-cite-function (funcall wl-draft-cite-function))) ; default cite
498     (run-hooks 'wl-draft-cited-hook)
499     (when (if wl-draft-add-references
500               (wl-draft-add-references)
501             (if wl-draft-add-in-reply-to
502                 (wl-draft-add-in-reply-to)))
503       (wl-highlight-headers 'for-draft)) ; highlight when added References:
504     (when wl-highlight-body-too
505       (wl-highlight-body-region beg (point-max)))))
506
507 (defun wl-draft-confirm ()
508   "Confirm send message."
509   (interactive)
510   (y-or-n-p (format "Send current draft as %s? "
511                     (cond ((and (wl-message-mail-p) (wl-message-news-p))
512                            "Mail and News")
513                           ((wl-message-mail-p) "Mail")
514                           ((wl-message-news-p) "News")))))
515
516 (defun wl-message-news-p ()
517   "If exist valid Newsgroups field, return non-nil."
518   (std11-field-body "Newsgroups"))
519
520 (defun wl-message-field-exists-p (field)
521   "If FIELD exist and FIELD value is not empty, return non-nil."
522   (let ((value (std11-field-body field)))
523     (and value
524          (not (string= value "")))))
525
526 (defun wl-message-mail-p ()
527   "If exist To, Cc or Bcc field, return non-nil."
528   (or (wl-message-field-exists-p "To")
529       (wl-message-field-exists-p "Resent-to")
530       (wl-message-field-exists-p "Cc")
531       (wl-message-field-exists-p "Bcc")
532       (wl-message-field-exists-p wl-draft-mime-bcc-field-name)
533 ;;; This may be needed..
534 ;;;   (wl-message-field-exists-p "Fcc")
535       ))
536
537 (defun wl-draft-open-file (&optional file)
538   "Open FILE for edit."
539   (interactive)
540 ;;;(interactive "*fFile to edit: ")
541   (wl-draft-edit-string (elmo-get-file-string
542                          (or file
543                              (read-file-name "File to edit: "
544                                              (or wl-temporary-file-directory
545                                                  "~/"))))))
546
547 (defun wl-draft-edit-string (string)
548   (let ((cur-buf (current-buffer))
549         (tmp-buf (get-buffer-create " *wl-draft-edit-string*"))
550         to subject in-reply-to cc references newsgroups mail-followup-to
551         content-type content-transfer-encoding from
552         body-beg)
553     (set-buffer tmp-buf)
554     (erase-buffer)
555     (insert string)
556     (setq to (std11-field-body "To"))
557     (setq to (and to
558                   (eword-decode-string
559                    (decode-mime-charset-string
560                     to
561                     wl-mime-charset))))
562     (setq subject (std11-field-body "Subject"))
563     (setq subject (and subject
564                        (eword-decode-string
565                         (decode-mime-charset-string
566                          subject
567                          wl-mime-charset))))
568     (setq from (std11-field-body "From")
569           from (and from
570                     (eword-decode-string
571                      (decode-mime-charset-string
572                       from
573                       wl-mime-charset))))
574     (setq in-reply-to (std11-field-body "In-Reply-To"))
575     (setq cc (std11-field-body "Cc"))
576     (setq cc (and cc
577                   (eword-decode-string
578                    (decode-mime-charset-string
579                     cc
580                     wl-mime-charset))))
581     (setq references (std11-field-body "References"))
582     (setq newsgroups (std11-field-body "Newsgroups"))
583     (setq mail-followup-to (std11-field-body "Mail-Followup-To"))
584     (setq content-type (std11-field-body "Content-Type"))
585     (setq content-transfer-encoding (std11-field-body "Content-Transfer-Encoding"))
586     (goto-char (point-min))
587     (or (re-search-forward "\n\n" nil t)
588         (search-forward (concat mail-header-separator "\n") nil t))
589     (unwind-protect
590         (set-buffer
591          (wl-draft (list
592                     (cons 'From
593                           (if (member
594                                (nth 1 (std11-extract-address-components from))
595                                wl-user-mail-address-list)
596                               from))
597                     (cons 'To to)
598                     (cons 'Cc cc)
599                     (cons 'Subject subject)
600                     (cons 'Newsgroups newsgroups)
601                     (cons 'Mail-Followup-To mail-followup-to)
602                     (cons 'In-Reply-To in-reply-to)
603                     (cons 'References references))
604                    content-type content-transfer-encoding
605                    (buffer-substring (point) (point-max))
606                    'edit-again))
607       (and to (mail-position-on-field "To"))
608       (delete-other-windows)
609       (kill-buffer tmp-buf)))
610   (run-hooks 'wl-draft-reedit-hook))
611
612 (defun wl-draft-insert-current-message (dummy)
613   (interactive)
614   (let (original-buffer
615         mail-reply-buffer
616         mail-citation-hook mail-yank-hooks
617         wl-draft-add-references wl-draft-add-in-reply-to
618         wl-draft-cite-function)
619     (with-current-buffer wl-draft-buffer-cur-summary-buffer
620       (with-current-buffer wl-message-buffer
621         (setq original-buffer (wl-message-get-original-buffer))
622         (if (zerop
623              (with-current-buffer original-buffer
624                (buffer-size)))
625             (error "No current message"))))
626     (setq mail-reply-buffer original-buffer)
627     (wl-draft-yank-from-mail-reply-buffer
628      nil
629      wl-ignored-forwarded-headers)))
630
631 (defun wl-draft-insert-get-message (dummy)
632   (let ((fld (completing-read
633               "Folder name: "
634               (if (memq 'read-folder wl-use-folder-petname)
635                   (wl-folder-get-entity-with-petname)
636                 wl-folder-entity-hashtb)
637               nil nil wl-default-spec
638               'wl-read-folder-hist))
639         (number (call-interactively
640                  (function (lambda (num)
641                              (interactive "nNumber: ")
642                              num))))
643         (mail-reply-buffer (get-buffer-create "*wl-draft-insert-get-message*"))
644         mail-citation-hook mail-yank-hooks
645         wl-draft-cite-function)
646     (unwind-protect
647         (progn
648           (elmo-message-fetch (wl-folder-get-elmo-folder fld)
649                               number
650                               ;; No cache.
651                               (elmo-make-fetch-strategy 'entire)
652                               nil mail-reply-buffer)
653           (wl-draft-yank-from-mail-reply-buffer nil))
654       (kill-buffer mail-reply-buffer))))
655
656 ;;
657 ;; default body citation func
658 ;;
659 (defun wl-default-draft-cite ()
660   (let ((mail-yank-ignored-headers "[^:]+:")
661         (mail-yank-prefix "> ")
662         (summary-buf wl-current-summary-buffer)
663         (message-buf (get-buffer (wl-current-message-buffer)))
664         from date cite-title num entity)
665     (if (and summary-buf
666              (buffer-live-p summary-buf)
667              message-buf
668              (buffer-live-p message-buf))
669         (progn
670           (with-current-buffer summary-buf
671             (let ((elmo-mime-charset wl-summary-buffer-mime-charset))
672               (setq num (save-excursion
673                           (set-buffer message-buf)
674                           wl-message-buffer-cur-number))
675               (setq entity (elmo-msgdb-overview-get-entity
676                             num (wl-summary-buffer-msgdb)))
677               (setq date (elmo-msgdb-overview-entity-get-date entity))
678               (setq from (elmo-msgdb-overview-entity-get-from entity))))
679           (setq cite-title (format "At %s,\n%s wrote:"
680                                    (or date "some time ago")
681                                    (if wl-default-draft-cite-decorate-author
682                                        (funcall wl-summary-from-function
683                                                 (or from "you"))
684                                      (or from "you"))))))
685     (and cite-title
686          (insert cite-title "\n"))
687     (mail-indent-citation)))
688
689 (defvar wl-draft-buffer nil "Draft buffer to yank content")
690 (defun wl-draft-yank-to-draft-buffer (buffer)
691   "Yank BUFFER content to `wl-draft-buffer'."
692   (set-buffer wl-draft-buffer)
693   (let ((mail-reply-buffer buffer))
694     (wl-draft-yank-from-mail-reply-buffer nil)
695     (kill-buffer buffer)))
696
697 (defun wl-draft-yank-original (&optional arg)
698   "Yank original message."
699   (interactive "P")
700   (if arg
701       (let (buf mail-reply-buffer)
702         (elmo-set-work-buf
703          (yank)
704          (setq buf (current-buffer)))
705         (setq mail-reply-buffer buf)
706         (wl-draft-yank-from-mail-reply-buffer nil))
707     (wl-draft-yank-current-message-entity)))
708
709 (defun wl-draft-hide (editing-buffer)
710   "Hide the editing draft buffer if possible."
711   (when (and editing-buffer
712              (buffer-live-p editing-buffer))
713     (set-buffer editing-buffer)
714     (let ((sum-buf wl-draft-buffer-cur-summary-buffer)
715           fld-buf sum-win fld-win)
716       (if (and wl-draft-use-frame
717                (> (length (visible-frame-list)) 1))
718           ;; hide draft frame
719           (delete-frame)
720         ;; hide draft window
721         (or (one-window-p)
722             (delete-window)))
723       ;; stay folder window if required
724       (when wl-stay-folder-window
725         (if (setq fld-buf (get-buffer wl-folder-buffer-name))
726             (if (setq fld-win (get-buffer-window fld-buf))
727                 (select-window fld-win)
728               (if wl-draft-resume-folder-window ;; resume folder window
729                   (switch-to-buffer fld-buf)))))
730       (if (buffer-live-p sum-buf)
731           (if (setq sum-win (get-buffer-window sum-buf t))
732               ;; if Summary is on the frame, select it.
733               (select-window sum-win)
734             ;; if summary is not on the frame, switch to it.
735             (if (and wl-stay-folder-window
736                      (or wl-draft-resume-folder-window fld-win))
737                 (wl-folder-select-buffer sum-buf)
738               (switch-to-buffer sum-buf)))))))
739
740 (defun wl-draft-delete (editing-buffer)
741   "kill the editing draft buffer and delete the file corresponds to it."
742   (save-excursion
743     (when editing-buffer
744       (set-buffer editing-buffer)
745       (if wl-draft-buffer-file-name
746           (progn
747             (if (file-exists-p wl-draft-buffer-file-name)
748                 (delete-file wl-draft-buffer-file-name))
749             (let ((msg (and wl-draft-buffer-file-name
750                             (string-match "[0-9]+$" wl-draft-buffer-file-name)
751                             (string-to-int
752                              (match-string 0 wl-draft-buffer-file-name)))))
753               (wl-draft-config-info-operation msg 'delete))))
754       (set-buffer-modified-p nil)               ; force kill
755       (kill-buffer editing-buffer))))
756
757 (defun wl-draft-kill (&optional force-kill)
758   "Kill current draft buffer and quit editing."
759   (interactive "P")
760   (save-excursion
761     (when (and (or (eq major-mode 'wl-draft-mode)
762                    (eq major-mode 'mail-mode))
763                (or force-kill
764                    (y-or-n-p "Kill Current Draft? ")))
765       (let ((cur-buf (current-buffer)))
766         (wl-draft-hide cur-buf)
767         (wl-draft-delete cur-buf)))
768     (message "")))
769
770 (defun wl-draft-fcc ()
771   "Add a new Fcc field, with file name completion."
772   (interactive)
773   (or (mail-position-on-field "fcc" t)  ;Put new field after exiting Fcc.
774       (mail-position-on-field "to"))
775   (insert "\nFcc: "))
776
777 ;; Imported from message.el.
778 (defun wl-draft-elide-region (b e)
779   "Elide the text in the region.
780 An ellipsis (from `wl-draft-elide-ellipsis') will be inserted where the
781 text was killed."
782   (interactive "r")
783   (kill-region b e)
784   (insert wl-draft-elide-ellipsis))
785
786 ;; Imported from message.el.
787 (defun wl-draft-beginning-of-line (&optional n)
788   "Move point to beginning of header value or to beginning of line."
789   (interactive "p")
790   (let ((zrs 'zmacs-region-stays))
791     (when (and (interactive-p) (boundp zrs))
792       (set zrs t)))
793   (if (wl-draft-point-in-header-p)
794       (let* ((here (point))
795              (bol (progn (beginning-of-line n) (point)))
796              (eol (line-end-position))
797              (eoh (and (looking-at "[^ \t]")
798                        (re-search-forward ": *" eol t))))
799         (if (and eoh (or (> here eoh) (= here bol)))
800             (goto-char eoh)
801           (goto-char bol)))
802     (beginning-of-line n)))
803
804 (defun wl-draft-point-in-header-p ()
805   "Return t if point is in the header."
806   (save-excursion
807     (let ((p (point)))
808       (goto-char (point-min))
809       (not (re-search-forward
810             (concat "^" (regexp-quote mail-header-separator) "\n")
811             p t)))))
812
813 ;; function for wl-sent-message-via
814
815 (defmacro wl-draft-sent-message-p (type)
816   (` (eq (nth 1 (assq (, type) wl-sent-message-via)) 'sent)))
817
818 (defmacro wl-draft-set-sent-message (type result &optional server-port)
819   (` (let ((element (assq (, type) wl-sent-message-via)))
820        (if element
821            (unless (eq (nth 1 element) (, result))
822              (setcdr element (list (, result) (, server-port)))
823              (setq wl-sent-message-modified t))
824          (push (list (, type) (, result) (, server-port)) wl-sent-message-via)
825          (setq wl-sent-message-modified t)))))
826
827 (defun wl-draft-sent-message-results ()
828   (let ((results wl-sent-message-via)
829         unplugged-via sent-via)
830     (while results
831       (cond ((eq (nth 1 (car results)) 'unplugged)
832              (push (caar results) unplugged-via))
833             ((eq (nth 1 (car results)) 'sent)
834              (push (caar results) sent-via)))
835       (setq results (cdr results)))
836     (list unplugged-via sent-via)))
837
838 (defun wl-draft-write-sendlog (status proto server to id)
839   "Write send log file, if `wl-draft-sendlog' is non-nil."
840   (when wl-draft-sendlog
841     (with-temp-buffer
842       (let* ((filename (expand-file-name wl-draft-sendlog-filename
843                                          elmo-msgdb-directory))
844              (filesize (nth 7 (file-attributes filename)))
845              (server (if server (concat " server=" server) ""))
846              (to (if to (cond
847                          ((memq proto '(fcc queue))
848                           (format " folder=\"%s\"" to))
849                          ((eq proto 'nntp)
850                           (format " ng=<%s>" to))
851                          (t
852                           (concat " to="
853                                   (mapconcat
854                                    'identity
855                                    (mapcar '(lambda(x) (format "<%s>" x)) to)
856                                    ","))))
857                    ""))
858              (id (if id (concat " id=" id) ""))
859              (time (format-time-string "%Y/%m/%d %T")))
860         (insert (format "%s proto=%s stat=%s%s%s%s\n"
861                         time proto status server to id))
862         (if (and wl-draft-sendlog-max-size filesize
863                  (> filesize wl-draft-sendlog-max-size))
864             (rename-file filename (concat filename ".old") t))
865         (if (file-writable-p filename)
866             (write-region-as-binary (point-min) (point-max)
867                                     filename t 'no-msg)
868           (message "%s is not writable." filename))))))
869
870 (defun wl-draft-get-header-delimiter (&optional delete)
871   ;; If DELETE is non-nil, replace the header delimiter with a blank line
872   (let (delimline)
873     (goto-char (point-min))
874     (when (re-search-forward
875            (concat "^" (regexp-quote mail-header-separator) "$\\|^$") nil t)
876       (replace-match "")
877       (if delete
878           (forward-char -1))
879       (setq delimline (point-marker)))
880     delimline))
881
882 (defun wl-draft-send-mail-with-qmail ()
883   "Pass the prepared message buffer to qmail-inject.
884 Refer to the documentation for the variable `send-mail-function'
885 to find out how to use this."
886   (if (and wl-draft-qmail-send-plugged
887            (not (elmo-plugged-p)))
888       (wl-draft-set-sent-message 'mail 'unplugged)
889     ;; send the message
890     (run-hooks 'wl-mail-send-pre-hook) ;; X-PGP-Sig, Cancel-Lock
891     (let ((id (std11-field-body "Message-ID"))
892           (to (std11-field-body "To")))
893       (case
894           (as-binary-process
895            (apply
896             'call-process-region 1 (point-max) wl-qmail-inject-program
897             nil nil nil
898             wl-qmail-inject-args))
899         ;; qmail-inject doesn't say anything on it's stdout/stderr,
900         ;; we have to look at the retval instead
901         (0   (progn
902                (wl-draft-set-sent-message 'mail 'sent)
903                (wl-draft-write-sendlog 'ok 'qmail nil (list to) id)))
904         (1   (error "`qmail-inject' reported permanent failure"))
905         (111 (error "`qmail-inject' reported transient failure"))
906         ;; should never happen
907         (t   (error "`qmail-inject' reported unknown failure"))))))
908
909 (defun wl-draft-parse-msg-id-list-string (string)
910   "Get msg-id list from STRING."
911   (let (msg-id-list)
912     (dolist (parsed-id (std11-parse-msg-ids-string string))
913       (when (eq (car parsed-id) 'msg-id)
914         (setq msg-id-list (cons (std11-msg-id-string parsed-id)
915                                 msg-id-list))))
916     (nreverse msg-id-list)))
917
918 (defun wl-draft-eword-encode-address-list (string &optional column)
919   "Encode header field STRING as list of address, and return the result.
920 Cause an error when STRING contains invalid address.
921 Optional argument COLUMN is start-position of the field."
922   (car (eword-encode-rword-list
923         (or column eword-encode-default-start-column)
924         (eword-encode-addresses-to-rword-list
925          (wl-draft-std11-parse-addresses (std11-lexical-analyze string))))))
926
927 (defun wl-draft-std11-parse-addresses (lal)
928   (let ((ret (std11-parse-address lal)))
929     (when (and (not (and (eq (length lal) 1)
930                          (eq (car (car lal)) 'spaces)))
931                (null ret))
932       (error "Error while parsing address"))
933     (if ret
934         (let ((dest (list (car ret))))
935           (setq lal (cdr ret))
936           (while (and (setq ret (std11-parse-ascii-token lal))
937                       (string-equal (cdr (assq 'specials (car ret))) ",")
938                       (setq ret (std11-parse-address (cdr ret)))
939                       )
940             (setq dest (cons (car ret) dest))
941             (setq lal (cdr ret)))
942           (while (eq 'spaces (car (car lal)))
943             (setq lal (cdr lal)))
944           (if lal (error "Error while parsing address"))
945           (nreverse dest)))))
946
947 (defun wl-draft-parse-mailbox-list (field &optional remove-group-list)
948   "Get mailbox list of FIELD from current buffer.
949 The buffer is expected to be narrowed to just the headers of the message.
950 If optional argument REMOVE-GROUP-LIST is non-nil, remove group list content
951 from current buffer."
952   (save-excursion
953     (let ((case-fold-search t)
954           (inhibit-read-only t)
955           addresses address
956           mailbox-list beg seq has-group-list)
957       (goto-char (point-min))
958       (while (re-search-forward (concat "^" (regexp-quote field) "[\t ]*:")
959                                 nil t)
960         (setq beg (point))
961         (re-search-forward "^[^ \t]" nil 'move)
962         (beginning-of-line)
963         (skip-chars-backward "\n")
964         (setq seq (std11-lexical-analyze
965                    (buffer-substring-no-properties beg (point))))
966         (setq addresses (wl-draft-std11-parse-addresses seq))
967         (while addresses
968           (cond ((eq (car (car addresses)) 'group)
969                  (setq has-group-list t)
970                  (setq mailbox-list
971                        (nconc mailbox-list
972                               (mapcar
973                                'std11-address-string
974                                (nth 2 (car addresses))))))
975                 ((eq (car (car addresses)) 'mailbox)
976                  (setq address (nth 1 (car addresses)))
977                  (setq mailbox-list
978                        (nconc mailbox-list
979                               (list
980                                (std11-addr-to-string
981                                 (if (eq (car address) 'phrase-route-addr)
982                                     (nth 2 address)
983                                   (cdr address))))))))
984           (setq addresses (cdr addresses)))
985         (when (and remove-group-list has-group-list)
986           (delete-region beg (point))
987           (insert (wl-address-string-without-group-list-contents seq))))
988       mailbox-list)))
989
990 (defun wl-draft-deduce-address-list (buffer header-start header-end)
991   "Get address list suitable for smtp RCPT TO:<address>.
992 Group list content is removed if `wl-draft-remove-group-list-contents' is
993 non-nil."
994   (let ((fields (if (and wl-draft-doing-mime-bcc
995                          wl-draft-disable-bcc-for-mime-bcc)
996                     '("to" "cc")
997                   '("to" "cc" "bcc")))
998         (resent-fields '("resent-to" "resent-cc" "resent-bcc"))
999         (case-fold-search t)
1000         addrs recipients)
1001     (save-excursion
1002       (save-restriction
1003         (narrow-to-region header-start header-end)
1004         (goto-char (point-min))
1005         (save-excursion
1006           (if (re-search-forward "^resent-to[\t ]*:" nil t)
1007               (setq fields resent-fields)))
1008         (while fields
1009           (setq recipients
1010                 (nconc recipients
1011                        (wl-draft-parse-mailbox-list
1012                         (car fields)
1013                         wl-draft-remove-group-list-contents)))
1014           (setq fields (cdr fields)))
1015         recipients))))
1016
1017 ;;
1018 ;; from Semi-gnus
1019 ;;
1020 (defun wl-draft-send-mail-with-smtp ()
1021   "Send the prepared message buffer with SMTP."
1022   (require 'smtp)
1023   (let* ((errbuf (if mail-interactive
1024                      (generate-new-buffer " smtp errors")
1025                    0))
1026          (case-fold-search t)
1027          (default-case-fold-search t)
1028          (sender (or wl-envelope-from
1029                      (wl-address-header-extract-address wl-from)))
1030          (delimline (save-excursion
1031                       (goto-char (point-min))
1032                       (re-search-forward
1033                        (concat "^" (regexp-quote mail-header-separator)
1034                                "$\\|^$") nil t)
1035                       (point-marker)))
1036          (smtp-server
1037           (or wl-smtp-posting-server smtp-server "localhost"))
1038          (smtp-service (or wl-smtp-posting-port smtp-service))
1039          (smtp-local-domain (or smtp-local-domain wl-local-domain))
1040          (id (std11-field-body "message-id"))
1041          recipients)
1042     (if (not (elmo-plugged-p smtp-server smtp-service))
1043         (wl-draft-set-sent-message 'mail 'unplugged
1044                                    (cons smtp-server smtp-service))
1045       (unwind-protect
1046           (save-excursion
1047             ;; Instead of `smtp-deduce-address-list'.
1048             (setq recipients (wl-draft-deduce-address-list
1049                               (current-buffer) (point-min) delimline))
1050             (unless recipients (error "No recipients"))
1051             ;; Insert an extra newline if we need it to work around
1052             ;; Sun's bug that swallows newlines.
1053             (goto-char (1+ delimline))
1054             (if (eval mail-mailer-swallows-blank-line)
1055                 (newline))
1056             (run-hooks 'wl-mail-send-pre-hook) ;; X-PGP-Sig, Cancel-Lock
1057             (if mail-interactive
1058                 (save-excursion
1059                   (set-buffer errbuf)
1060                   (erase-buffer)))
1061             (wl-draft-delete-field "bcc" delimline)
1062             (wl-draft-delete-field "resent-bcc" delimline)
1063             (let (process-connection-type)
1064               (as-binary-process
1065                (when recipients
1066                  (wl-smtp-extension-bind
1067                   (condition-case err
1068                       (smtp-send-buffer sender recipients (current-buffer))
1069                     (error
1070                      (wl-draft-write-sendlog 'failed 'smtp smtp-server
1071                                              recipients id)
1072                      (if (and (eq (car err) 'smtp-response-error)
1073                               (/= (nth 1 err) 334))
1074                          (elmo-remove-passwd
1075                           (wl-smtp-password-key
1076                            smtp-sasl-user-name
1077                            (car smtp-sasl-mechanisms)
1078                            smtp-server)))
1079                      (signal (car err) (cdr err)))))
1080                  (wl-draft-set-sent-message 'mail 'sent)
1081                  (wl-draft-write-sendlog
1082                   'ok 'smtp smtp-server recipients id)))))
1083         (if (bufferp errbuf)
1084             (kill-buffer errbuf))))))
1085
1086 (defun wl-draft-send-mail-with-pop-before-smtp ()
1087   "Send the prepared message buffer with POP-before-SMTP."
1088   (require 'elmo-pop3)
1089   (let ((session
1090          (luna-make-entity
1091           'elmo-pop3-folder
1092           :user   (or wl-pop-before-smtp-user
1093                       elmo-pop3-default-user)
1094           :server (or wl-pop-before-smtp-server
1095                       elmo-pop3-default-server)
1096           :port   (or wl-pop-before-smtp-port
1097                       elmo-pop3-default-port)
1098           :auth   (or wl-pop-before-smtp-authenticate-type
1099                       elmo-pop3-default-authenticate-type)
1100           :stream-type (or wl-pop-before-smtp-stream-type
1101                            elmo-pop3-default-stream-type))))
1102     (condition-case error
1103         (progn
1104           (elmo-pop3-get-session session)
1105           (when session (elmo-network-close-session session)))
1106       (error
1107        (elmo-network-close-session session)
1108        (unless (string= (nth 1 error) "Unplugged")
1109          (signal (car error)(cdr error))))))
1110   (wl-draft-send-mail-with-smtp))
1111
1112 (defun wl-draft-insert-required-fields (&optional force-msgid)
1113   "Insert Message-ID, Date, and From field.
1114 If FORCE-MSGID, insert message-id regardless of `wl-insert-message-id'."
1115   ;; Insert Message-Id field...
1116   (goto-char (point-min))
1117   (when (and (or force-msgid
1118                  wl-insert-message-id)
1119              (not (re-search-forward "^Message-ID[ \t]*:" nil t)))
1120     (insert (concat "Message-ID: "
1121                     (funcall wl-message-id-function)
1122                     "\n")))
1123   ;; Insert date field.
1124   (goto-char (point-min))
1125   (or (re-search-forward "^Date[ \t]*:" nil t)
1126       (wl-draft-insert-date-field))
1127   ;; Insert from field.
1128   (goto-char (point-min))
1129   (or (re-search-forward "^From[ \t]*:" nil t)
1130       (wl-draft-insert-from-field)))
1131
1132 (defun wl-draft-normal-send-func (editing-buffer kill-when-done)
1133   "Send the message in the current buffer."
1134   (save-restriction
1135     (std11-narrow-to-header mail-header-separator)
1136     (wl-draft-insert-required-fields)
1137     ;; Delete null fields.
1138     (goto-char (point-min))
1139     (while (re-search-forward "^[^ \t\n:]+:[ \t]*\n" nil t)
1140       (replace-match ""))
1141     ;; ignore any blank lines in the header
1142     (while (re-search-forward "\n\n\n*" nil t)
1143       (replace-match "\n")))
1144 ;;;  (run-hooks 'wl-mail-send-pre-hook) ;; X-PGP-Sig, Cancel-Lock
1145   (wl-draft-dispatch-message)
1146   (when kill-when-done
1147     ;; hide editing-buffer.
1148     (wl-draft-hide editing-buffer)
1149     ;; delete editing-buffer and its file.
1150     (wl-draft-delete editing-buffer)))
1151
1152 (defun wl-draft-dispatch-message (&optional mes-string)
1153   "Send the message in the current buffer.  Not modified the header fields."
1154   (let (delimline mime-bcc)
1155     (if (and wl-draft-verbose-send mes-string)
1156         (message mes-string))
1157     ;; get fcc folders.
1158     (setq delimline (wl-draft-get-header-delimiter t))
1159     (unless wl-draft-fcc-list
1160       (setq wl-draft-fcc-list (wl-draft-get-fcc-list delimline)))
1161     ;;
1162     (setq wl-sent-message-modified nil)
1163     (unwind-protect
1164         (progn
1165           (if (and (wl-message-mail-p)
1166                    (not (wl-draft-sent-message-p 'mail)))
1167               (if (or (not (or wl-draft-force-queuing
1168                                wl-draft-force-queuing-mail))
1169                       (memq 'mail wl-sent-message-queued))
1170                   (progn
1171                     (setq mime-bcc (wl-draft-mime-bcc-field))
1172                     (funcall wl-draft-send-mail-function)
1173                     (when (not (zerop (length mime-bcc)))
1174                       (wl-draft-do-mime-bcc mime-bcc)))
1175                 (push 'mail wl-sent-message-queued)
1176                 (wl-draft-set-sent-message 'mail 'unplugged)))
1177           (if (and (wl-message-news-p)
1178                    (not (wl-draft-sent-message-p 'news))
1179                    (not (wl-message-field-exists-p "Resent-to")))
1180               (if (or (not (or wl-draft-force-queuing
1181                                wl-draft-force-queuing-news))
1182                       (memq 'news wl-sent-message-queued))
1183                   (funcall wl-draft-send-news-function)
1184                 (push 'news wl-sent-message-queued)
1185                 (wl-draft-set-sent-message 'news 'unplugged))))
1186       (let* ((status (wl-draft-sent-message-results))
1187              (unplugged-via (car status))
1188              (sent-via (nth 1 status)))
1189         ;; If one sent, process fcc folder.
1190         (if (and sent-via wl-draft-fcc-list)
1191             (progn
1192               (wl-draft-do-fcc (wl-draft-get-header-delimiter)
1193                                wl-draft-fcc-list)
1194               (setq wl-draft-fcc-list nil)))
1195         (if wl-draft-use-cache
1196             (let ((id (std11-field-body "Message-ID"))
1197                   (elmo-enable-disconnected-operation t))
1198               (elmo-file-cache-save (elmo-file-cache-get-path id)
1199                                     nil)))
1200         ;; If one unplugged, append queue.
1201         (when (and unplugged-via
1202                    wl-sent-message-modified)
1203           (if wl-draft-enable-queuing
1204               (progn
1205                 (wl-draft-queue-append wl-sent-message-via)
1206                 (setq wl-sent-message-modified 'requeue))
1207             (error "Unplugged")))
1208         (when wl-draft-verbose-send
1209           (if (and unplugged-via sent-via);; combined message
1210               (progn
1211                 (setq wl-draft-verbose-msg
1212                       (format "Sending%s and Queuing%s..."
1213                               sent-via unplugged-via))
1214                 (message (concat wl-draft-verbose-msg "done")))
1215             (if mes-string
1216                 (message (concat mes-string
1217                                  (if sent-via "done" "failed")))))))))
1218   (not wl-sent-message-modified)) ;; return value
1219
1220 (defun wl-draft-raw-send (&optional kill-when-done force-pre-hook mes-string)
1221   "Force send current buffer as raw message."
1222   (interactive)
1223   (save-excursion
1224     (let (wl-interactive-send
1225 ;;;       wl-draft-verbose-send
1226           (wl-mail-send-pre-hook (and force-pre-hook wl-mail-send-pre-hook))
1227           (wl-news-send-pre-hook (and force-pre-hook wl-news-send-pre-hook))
1228           mail-send-hook
1229           mail-send-actions)
1230       (wl-draft-send kill-when-done mes-string))))
1231
1232 (defun wl-draft-clone-local-variables ()
1233   (let ((locals (buffer-local-variables))
1234         result)
1235     (while locals
1236       (when (and (consp (car locals))
1237                  (car (car locals))
1238                  (string-match wl-draft-clone-local-variable-regexp
1239                                (symbol-name (car (car locals)))))
1240         (wl-append result (list (car (car locals)))))
1241       (setq locals (cdr locals)))
1242     result))
1243
1244 (defun wl-draft-send (&optional kill-when-done mes-string)
1245   "Send current draft message.
1246 If KILL-WHEN-DONE is non-nil, current draft buffer is killed"
1247   (interactive)
1248   ;; Don't call this explicitly.
1249   ;; Added to 'wl-draft-send-hook (by teranisi)
1250   ;; (wl-draft-config-exec)
1251   (run-hooks 'wl-draft-send-hook)
1252   (when (or (not wl-interactive-send)
1253             (y-or-n-p "Do you really want to send current draft? "))
1254     (let ((send-mail-function 'wl-draft-raw-send)
1255           (editing-buffer (current-buffer))
1256           (sending-buffer (wl-draft-generate-clone-buffer
1257                            " *wl-draft-sending-buffer*"
1258                            (append wl-draft-config-variables
1259                                    (wl-draft-clone-local-variables))))
1260           (wl-draft-verbose-msg nil)
1261           err)
1262       (unwind-protect
1263           (save-excursion (set-buffer sending-buffer)
1264             (if (and (not (wl-message-mail-p))
1265                      (not (wl-message-news-p)))
1266                 (error "No recipient is specified"))
1267             (expand-abbrev) ; for mail-abbrevs
1268             (let ((mime-header-encode-method-alist
1269                    (append
1270                     '((wl-draft-eword-encode-address-list
1271                        .  (To Cc Bcc Resent-To Resent-Cc Bcc Resent-Bcc)))
1272                     (if (boundp 'mime-header-encode-method-alist)
1273                         (symbol-value 'mime-header-encode-method-alist)))))
1274               (run-hooks 'mail-send-hook) ; translate buffer
1275               )
1276             ;;
1277             (if wl-draft-verbose-send
1278                 (message (or mes-string "Sending...")))
1279             (funcall wl-draft-send-function editing-buffer kill-when-done)
1280             ;; Now perform actions on successful sending.
1281             (while mail-send-actions
1282               (condition-case ()
1283                   (apply (car (car mail-send-actions))
1284                          (cdr (car mail-send-actions)))
1285                 (error))
1286               (setq mail-send-actions (cdr mail-send-actions)))
1287             (if wl-draft-verbose-send
1288                 (message (concat (or wl-draft-verbose-msg
1289                                      mes-string "Sending...")
1290                                  "done"))))
1291         ;; kill sending buffer, anyway.
1292         (and (buffer-live-p sending-buffer)
1293              (kill-buffer sending-buffer))))))
1294
1295 (defun wl-draft-mime-bcc-field ()
1296   "Return the MIME-Bcc field body. The field is deleted."
1297   (prog1 (std11-field-body wl-draft-mime-bcc-field-name)
1298     (wl-draft-delete-field wl-draft-mime-bcc-field-name)))
1299
1300 (defun wl-draft-do-mime-bcc (field-body)
1301   "Send MIME-Bcc (Encapsulated blind carbon copy)."
1302   (let ((orig-from (mime-decode-field-body (std11-field-body "from")
1303                                            'From))
1304         (orig-subj (mime-decode-field-body (or (std11-field-body "subject")
1305                                                "")
1306                                            'Subject))
1307         (recipients (wl-parse-addresses field-body))
1308         (draft-buffer (current-buffer))
1309         wl-draft-use-frame)
1310     (save-window-excursion
1311       (when (and (not wl-draft-doing-mime-bcc) ; To avoid infinite loop.
1312                  (not (zerop (length field-body))))
1313         (let ((wl-draft-doing-mime-bcc t))
1314           (dolist (recipient recipients)
1315             (wl-draft-create-buffer)
1316             (wl-draft-create-contents
1317              (append `((From . ,orig-from)
1318                        (To . ,recipient)
1319                        (Subject . ,(concat "A blind carbon copy ("
1320                                            orig-subj
1321                                            ")")))
1322                      (wl-draft-default-headers)))
1323             (wl-draft-insert-mail-header-separator)
1324             (wl-draft-prepare-edit)
1325             (goto-char (point-max))
1326             (insert (or wl-draft-mime-bcc-body
1327                         "This is a blind carbon copy.")
1328                     "\n")
1329             (mime-edit-insert-tag "message" "rfc822")
1330             (insert-buffer draft-buffer)
1331             (let (wl-interactive-send)
1332               (wl-draft-send 'kill-when-done))))))))
1333
1334 ;; Derived from `message-save-drafts' in T-gnus.
1335 (defun wl-draft-save ()
1336   "Save current draft."
1337   (interactive)
1338   (if (buffer-modified-p)
1339       (progn
1340         (message "Saving %s..." wl-draft-buffer-file-name)
1341         (let ((msg (buffer-substring-no-properties (point-min) (point-max))))
1342           (with-temp-file wl-draft-buffer-file-name
1343             (insert msg)
1344             ;; If no header separator, insert it.
1345             (save-excursion
1346               (goto-char (point-min))
1347               (unless (re-search-forward
1348                        (concat "^" (regexp-quote mail-header-separator) "$")
1349                        nil t)
1350                 (goto-char (point-min))
1351                 (if (re-search-forward "\n\n" nil t)
1352                     (replace-match (concat "\n" mail-header-separator "\n"))
1353                   (goto-char (point-max))
1354                   (insert (if (eq (char-before) ?\n) "" "\n")
1355                           mail-header-separator "\n"))))
1356             (let ((mime-header-encode-method-alist
1357                    '((eword-encode-unstructured-field-body))))
1358               (mime-edit-translate-buffer))
1359             (wl-draft-get-header-delimiter t)))
1360         (set-buffer-modified-p nil)
1361         (wl-draft-config-info-operation
1362          (and (string-match "[0-9]+$" wl-draft-buffer-file-name)
1363               (string-to-int
1364                (match-string 0 wl-draft-buffer-file-name)))
1365          'save)
1366         (message "Saving %s...done" wl-draft-buffer-file-name))
1367     (message "(No changes need to be saved)")))
1368
1369 (defun wl-draft-mimic-kill-buffer ()
1370   "Kill the current (draft) buffer with query."
1371   (interactive)
1372   (let ((bufname (read-buffer (format "Kill buffer: (default %s) "
1373                                       (buffer-name))))
1374         wl-draft-use-frame)
1375     (if (or (not bufname)
1376             (string-equal bufname "")
1377             (string-equal bufname (buffer-name)))
1378         (let ((bufname (current-buffer)))
1379           (when (or (not (buffer-modified-p))
1380                     (yes-or-no-p
1381                      (format "Buffer %s modified; kill anyway? " bufname)))
1382             (set-buffer-modified-p nil)
1383             (wl-draft-hide bufname)
1384             (kill-buffer bufname)))
1385       (kill-buffer bufname))))
1386
1387 (defun wl-draft-save-and-exit ()
1388   "Save current draft and exit current draft mode."
1389   (interactive)
1390   (wl-draft-save)
1391   (let ((editing-buffer (current-buffer)))
1392     (wl-draft-hide editing-buffer)
1393     (kill-buffer editing-buffer)))
1394
1395 (defun wl-draft-send-and-exit ()
1396   "Send current draft message and kill it."
1397   (interactive)
1398   (wl-draft-send t))
1399
1400 (defun wl-draft-send-from-toolbar ()
1401   (interactive)
1402   (let ((wl-interactive-send t))
1403     (wl-draft-send-and-exit)))
1404
1405 (defun wl-draft-delete-field (field &optional delimline replace)
1406   (wl-draft-delete-fields (regexp-quote field) delimline replace))
1407
1408 (defun wl-draft-delete-fields (field &optional delimline replace)
1409   (save-restriction
1410     (unless delimline
1411       (goto-char (point-min))
1412       (if (search-forward "\n\n" nil t)
1413           (setq delimline (point))
1414         (setq delimline (point-max))))
1415     (narrow-to-region (point-min) delimline)
1416     (goto-char (point-min))
1417     (let ((regexp (concat "^" field ":"))
1418           (case-fold-search t))
1419       (while (not (eobp))
1420         (if (looking-at regexp)
1421             (progn
1422               (delete-region
1423                (point)
1424                (progn
1425                  (forward-line 1)
1426                  (if (re-search-forward "^[^ \t]" nil t)
1427                      (goto-char (match-beginning 0))
1428                    (point-max))))
1429               (if replace
1430                   (insert (concat field ": " replace "\n"))))
1431           (forward-line 1)
1432           (if (re-search-forward "^[^ \t]" nil t)
1433               (goto-char (match-beginning 0))
1434             (point-max)))))))
1435
1436 (defun wl-draft-get-fcc-list (header-end)
1437   (if (and wl-draft-doing-mime-bcc
1438            wl-draft-disable-fcc-for-mime-bcc)
1439       (progn
1440         (wl-draft-delete-field "fcc")
1441         nil)
1442     (let (fcc-list
1443           (case-fold-search t))
1444       (or (markerp header-end) (error "HEADER-END must be a marker"))
1445       (save-excursion
1446         (goto-char (point-min))
1447         (while (re-search-forward "^Fcc:[ \t]*" header-end t)
1448           (setq fcc-list
1449                 (cons (buffer-substring-no-properties
1450                        (point)
1451                        (progn
1452                          (end-of-line)
1453                          (skip-chars-backward " \t")
1454                          (point)))
1455                       fcc-list))
1456           (save-match-data
1457             (wl-folder-confirm-existence
1458              (wl-folder-get-elmo-folder (eword-decode-string (car fcc-list)))))
1459           (delete-region (match-beginning 0)
1460                          (progn (forward-line 1) (point)))))
1461       fcc-list)))
1462
1463 (defun wl-draft-do-fcc (header-end &optional fcc-list)
1464   (let ((send-mail-buffer (current-buffer))
1465         (tembuf (generate-new-buffer " fcc output"))
1466         (case-fold-search t)
1467         beg end)
1468     (or (markerp header-end) (error "HEADER-END must be a marker"))
1469     (save-excursion
1470       (unless fcc-list
1471         (setq fcc-list (wl-draft-get-fcc-list header-end)))
1472       (set-buffer tembuf)
1473       (erase-buffer)
1474       ;; insert just the headers to avoid moving the gap more than
1475       ;; necessary (the message body could be arbitrarily huge.)
1476       (insert-buffer-substring send-mail-buffer 1 header-end)
1477       (wl-draft-insert-required-fields t)
1478       (goto-char (point-max))
1479       (insert-buffer-substring send-mail-buffer header-end)
1480       (let ((id (std11-field-body "Message-ID"))
1481             (elmo-enable-disconnected-operation t)
1482             cache-saved)
1483         (while fcc-list
1484           (unless (or cache-saved
1485                       (elmo-folder-plugged-p
1486                        (wl-folder-get-elmo-folder (car fcc-list))))
1487             (elmo-file-cache-save id nil) ;; for disconnected operation
1488             (setq cache-saved t))
1489           (if (elmo-folder-append-buffer
1490                (wl-folder-get-elmo-folder
1491                 (eword-decode-string (car fcc-list)))
1492                (not wl-fcc-force-as-read))
1493               (wl-draft-write-sendlog 'ok 'fcc nil (car fcc-list) id)
1494             (wl-draft-write-sendlog 'failed 'fcc nil (car fcc-list) id))
1495           (setq fcc-list (cdr fcc-list)))))
1496     (kill-buffer tembuf)))
1497
1498 (defun wl-draft-on-field-p ()
1499   (if (< (point)
1500          (save-excursion
1501            (goto-char (point-min))
1502            (search-forward (concat "\n" mail-header-separator "\n") nil 0)
1503            (point)))
1504       (if (bolp)
1505           (if (bobp)
1506               t
1507             (save-excursion
1508               (forward-line -1)
1509               (if (or (looking-at ".*,[ \t]?$")
1510                       (looking-at "^[^ \t]+:[ \t]+.*:$")); group list name
1511                   nil t)))
1512         (let ((pos (point)))
1513           (save-excursion
1514             (beginning-of-line)
1515             (if (looking-at "^[ \t]")
1516                 nil
1517               (if (re-search-forward ":" pos t) nil t)))))))
1518
1519 (defun wl-draft-random-alphabet ()
1520   (let ((alphabet '(?A ?B ?C ?D ?E ?F ?G ?H ?I ?J ?K ?L ?M ?N ?O ?P ?Q ?R ?S ?T ?U ?V ?W ?X ?Y ?Z)))
1521     (nth (abs (% (random) 26)) alphabet)))
1522
1523 ;;;;;;;;;;;;;;;;
1524 ;;;###autoload
1525 (defun wl-draft (&optional header-alist
1526                            content-type content-transfer-encoding
1527                            body edit-again
1528                            parent-folder)
1529   "Write and send mail/news message with Wanderlust."
1530   (interactive)
1531   (require 'wl)
1532   (unless wl-init
1533     (wl-load-profile)
1534     (wl-folder-init)
1535     (elmo-init)
1536     (wl-plugged-init t))
1537   (let (wl-demo)
1538     (wl-init)) ; returns immediately if already initialized.
1539
1540
1541   (let (buf-name header-alist-internal)
1542     (setq buf-name
1543           (wl-draft-create-buffer
1544            (or
1545             (eq this-command 'wl-draft)
1546             (eq this-command 'wl-summary-write)
1547             (eq this-command 'wl-summary-write-current-folder)
1548             (eq this-command 'wl-folder-write-current-folder))
1549            parent-folder))
1550
1551     (unless (cdr (assq 'From header-alist))
1552       (setq header-alist
1553             (append (list (cons 'From wl-from)) header-alist)))
1554     (unless (cdr (assq 'To header-alist))
1555       (let ((to))
1556         (when (setq to (and
1557                         (interactive-p)
1558                         ""))
1559           (if (assq 'To header-alist)
1560               (setcdr (assq 'To header-alist) to)
1561             (setq header-alist
1562                   (append header-alist
1563                           (list (cons 'To to))))))))
1564     (unless (cdr (assq 'Subject header-alist))
1565       (if (assq 'Subject header-alist)
1566           (setcdr (assq 'Subject header-alist) "")
1567         (setq header-alist
1568               (append header-alist (list (cons 'Subject ""))))))
1569     (setq header-alist (append header-alist
1570                                (wl-draft-default-headers)
1571                                wl-draft-additional-header-alist
1572                                (if body (list "" (cons 'Body body)))))
1573     (wl-draft-create-contents header-alist)
1574     (if edit-again
1575         (wl-draft-decode-body
1576          content-type content-transfer-encoding))
1577     (wl-draft-insert-mail-header-separator)
1578     (wl-draft-prepare-edit)
1579     (if (interactive-p)
1580         (run-hooks 'wl-mail-setup-hook))
1581     (goto-char (point-min))
1582     (wl-user-agent-compose-internal) ;; user-agent
1583     (cond ((and
1584             (interactive-p)
1585             (string= (cdr (assq 'To header-alist)) ""))
1586            (mail-position-on-field "To"))
1587           (t
1588            (goto-char (point-max))))
1589     buf-name))
1590
1591 (defun wl-draft-create-buffer (&optional full parent-folder)
1592   (let* ((draft-folder (wl-folder-get-elmo-folder wl-draft-folder))
1593          (parent-folder (or parent-folder (wl-summary-buffer-folder-name)))
1594          (summary-buf (wl-summary-get-buffer parent-folder))
1595         buf-name file-name num change-major-mode-hook)
1596     (if (not (elmo-folder-message-file-p draft-folder))
1597         (error "%s folder cannot be used for draft folder" wl-draft-folder))
1598     (setq num (elmo-max-of-list
1599                (or (elmo-folder-list-messages draft-folder) '(0))))
1600     (setq num (+ 1 num))
1601     ;; To get unused buffer name.
1602     (while (get-buffer (concat wl-draft-folder "/" (int-to-string num)))
1603       (setq num (+ 1 num)))
1604     (setq buf-name (find-file-noselect
1605                     (setq file-name
1606                           (elmo-message-file-name
1607                            (wl-folder-get-elmo-folder wl-draft-folder)
1608                            num))))
1609     (if wl-draft-use-frame
1610         (switch-to-buffer-other-frame buf-name)
1611       (switch-to-buffer buf-name))
1612     (set-buffer buf-name)
1613     (if (not (string-match (regexp-quote wl-draft-folder)
1614                            (buffer-name)))
1615         (rename-buffer (concat wl-draft-folder "/" (int-to-string num))))
1616     (if (or (eq wl-draft-reply-buffer-style 'full)
1617             full)
1618         (delete-other-windows))
1619     (auto-save-mode -1)
1620     (wl-draft-mode)
1621     (make-local-variable 'truncate-partial-width-windows)
1622     (setq truncate-partial-width-windows nil)
1623     (setq truncate-lines wl-draft-truncate-lines)
1624     (setq wl-sent-message-via nil)
1625     (setq wl-sent-message-queued nil)
1626     (setq wl-draft-buffer-file-name file-name)
1627     (setq wl-draft-config-exec-flag t)
1628     (setq wl-draft-parent-folder parent-folder)
1629     (or (eq this-command 'wl-folder-write-current-folder)
1630         (setq wl-draft-buffer-cur-summary-buffer summary-buf))
1631     buf-name))
1632
1633 (defun wl-draft-create-contents (header-alist)
1634   "header-alist' sample
1635 '(function  ;; funcall
1636   string    ;; insert string
1637   (symbol . string)    ;;  insert symbol-value: string
1638   (symbol . function)  ;;  (funcall) and if it returns string,
1639                        ;;  insert symbol-value: string
1640   (symbol . nil)       ;;  do nothing
1641   nil                  ;;  do nothing
1642   )
1643 "
1644   (unless (eq major-mode 'wl-draft-mode)
1645     (error "wl-draft-create-header must be use in wl-draft-mode."))
1646   (let ((halist header-alist)
1647         field value)
1648     (while halist
1649       (cond
1650        ;; function
1651        ((functionp (car halist)) (funcall (car halist)))
1652        ;; string
1653        ((stringp (car halist)) (insert (car halist) "\n"))
1654        ;; cons
1655        ((consp (car halist))
1656         (setq field (car (car halist)))
1657         (setq value (cdr (car halist)))
1658         (cond
1659          ((symbolp field)
1660           (cond
1661            ((eq field 'Body) ; body
1662             (insert value))
1663            ((stringp value) (insert (symbol-name field) ": " value "\n"))
1664            ((functionp value)
1665             (let ((value-return (funcall value)))
1666               (when (stringp value-return)
1667                 (insert (symbol-name field) ": " value-return "\n"))))
1668            ((not value))
1669            (t
1670             (debug))))
1671          ;;
1672          ((not field))
1673          (t
1674           (debug))
1675          )))
1676       (setq halist (cdr halist)))))
1677
1678 (defun wl-draft-prepare-edit ()
1679   (unless (eq major-mode 'wl-draft-mode)
1680     (error "wl-draft-create-header must be use in wl-draft-mode."))
1681   (let (change-major-mode-hook)
1682     (wl-draft-editor-mode)
1683     (add-hook 'local-write-file-hooks 'wl-draft-save)
1684     (wl-draft-overload-functions)
1685     (wl-highlight-headers 'for-draft)
1686     (wl-draft-save)
1687     (clear-visited-file-modtime)))
1688
1689 (defun wl-draft-decode-header ()
1690   (save-excursion
1691     (std11-narrow-to-header)
1692     (wl-draft-decode-message-in-buffer)
1693     (widen)))
1694
1695 (defun wl-draft-decode-body (&optional content-type content-transfer-encoding)
1696   (let ((content-type
1697          (or content-type
1698                 (std11-field-body "content-type")))
1699         (content-transfer-encoding
1700          (or content-transfer-encoding
1701              (std11-field-body "content-transfer-encoding")))
1702         delimline)
1703     (save-excursion
1704       (std11-narrow-to-header)
1705       (wl-draft-delete-field "content-type")
1706       (wl-draft-delete-field "content-transfer-encoding")
1707       (goto-char (point-max))
1708       (setq delimline (point-marker))
1709       (widen)
1710       (narrow-to-region delimline (point-max))
1711       (goto-char (point-min))
1712       (when content-type
1713         (insert "Content-type: " content-type "\n"))
1714       (when content-transfer-encoding
1715         (insert "Content-Transfer-Encoding: " content-transfer-encoding "\n"))
1716       (wl-draft-decode-message-in-buffer)
1717       (goto-char (point-min))
1718       (unless (re-search-forward "^$" (point-at-eol) t)
1719         (insert "\n"))
1720       (widen)
1721       delimline)))
1722
1723 ;;; subroutine for wl-draft-create-contents
1724 ;;; must be used in wl-draft-mode
1725 (defun wl-draft-check-new-line ()
1726   (if (not (= (preceding-char) ?\n))
1727       (insert ?\n)))
1728
1729 (defsubst wl-draft-trim-ccs (cc)
1730   (let ((field
1731          (if (functionp cc)
1732              (funcall cc)
1733            cc)))
1734     (if (and field
1735              (null (and wl-draft-delete-myself-from-bcc-fcc
1736                         (elmo-list-member
1737                          (mapcar 'wl-address-header-extract-address
1738                                  (append
1739                                   (wl-parse-addresses (std11-field-body "To"))
1740                                   (wl-parse-addresses (std11-field-body "Cc"))))
1741                          (mapcar 'downcase wl-subscribed-mailing-list)))))
1742         field
1743       nil)))
1744
1745 (defsubst wl-draft-default-headers ()
1746   (list
1747    (cons 'Mail-Reply-To (and wl-insert-mail-reply-to
1748                              (wl-address-header-extract-address
1749                               wl-from)))
1750    (cons 'User-Agent wl-generate-mailer-string-function)
1751    (cons 'Reply-To mail-default-reply-to)
1752    (cons 'Bcc (function
1753                (lambda ()
1754                  (wl-draft-trim-ccs
1755                   (or wl-bcc (and mail-self-blind (user-login-name)))))))
1756    (cons 'Fcc (function
1757                (lambda ()
1758                  (wl-draft-trim-ccs wl-fcc))))
1759    (cons 'Organization wl-organization)
1760    (and wl-auto-insert-x-face
1761         (file-exists-p wl-x-face-file)
1762         'wl-draft-insert-x-face-field-here) ;; allow nil
1763    mail-default-headers
1764    ;; check \n at th end of line for `mail-default-headers'
1765    'wl-draft-check-new-line
1766    ))
1767
1768 (defun wl-draft-insert-mail-header-separator (&optional delimline)
1769   (save-excursion
1770     (if delimline
1771         (goto-char delimline)
1772       (goto-char (point-min))
1773       (if (search-forward "\n\n" nil t)
1774           (delete-backward-char 1)
1775         (goto-char (point-max))))
1776     (wl-draft-check-new-line)
1777     (put-text-property (point)
1778                        (progn
1779                          (insert mail-header-separator "\n")
1780                          (1- (point)))
1781                        'category 'mail-header-separator)))
1782
1783 ;;;;;;;;;;;;;;;;
1784
1785 (defun wl-draft-elmo-nntp-send ()
1786   (let ((elmo-nntp-post-pre-hook wl-news-send-pre-hook)
1787         (elmo-nntp-default-user
1788          (or wl-nntp-posting-user elmo-nntp-default-user))
1789         (elmo-nntp-default-server
1790          (or wl-nntp-posting-server elmo-nntp-default-server))
1791         (elmo-nntp-default-port
1792          (or wl-nntp-posting-port elmo-nntp-default-port))
1793         (elmo-nntp-default-stream-type
1794          (or wl-nntp-posting-stream-type elmo-nntp-default-stream-type))
1795         (elmo-nntp-default-function wl-nntp-posting-function)
1796         condition)
1797     (if (setq condition (cdr (elmo-string-matched-assoc
1798                               (std11-field-body "Newsgroups")
1799                               wl-nntp-posting-config-alist)))
1800         (if (stringp condition)
1801             (setq elmo-nntp-default-server condition)
1802           (while (car condition)
1803             (set (intern (format "elmo-nntp-default-%s"
1804                                  (symbol-name (caar condition))))
1805                  (cdar condition))
1806             (setq condition (cdr condition)))))
1807     (unless elmo-nntp-default-function
1808       (error "wl-draft-nntp-send: posting-function is nil."))
1809     (if (not (elmo-plugged-p elmo-nntp-default-server elmo-nntp-default-port))
1810         (wl-draft-set-sent-message 'news 'unplugged
1811                                    (cons elmo-nntp-default-server
1812                                          elmo-nntp-default-port))
1813       (funcall elmo-nntp-default-function
1814                elmo-nntp-default-server (current-buffer))
1815       (wl-draft-set-sent-message 'news 'sent)
1816       (wl-draft-write-sendlog 'ok 'nntp elmo-nntp-default-server
1817                               (std11-field-body "Newsgroups")
1818                               (std11-field-body "Message-ID")))))
1819
1820 (defun wl-draft-generate-clone-buffer (name &optional local-variables)
1821   "Generate clone of current buffer named NAME."
1822   (let ((editing-buffer (current-buffer)))
1823     (save-excursion
1824       (set-buffer (generate-new-buffer name))
1825       (erase-buffer)
1826       (wl-draft-mode)
1827       (wl-draft-editor-mode)
1828       (insert-buffer editing-buffer)
1829       (message "")
1830       (while local-variables
1831         (make-local-variable (car local-variables))
1832         (set (car local-variables)
1833              (save-excursion
1834                (set-buffer editing-buffer)
1835                (symbol-value (car local-variables))))
1836         (setq local-variables (cdr local-variables)))
1837       (current-buffer))))
1838
1839 (defun wl-draft-reedit (number)
1840   (let ((draft-folder (wl-folder-get-elmo-folder wl-draft-folder))
1841         (wl-draft-reedit t)
1842         buffer file-name change-major-mode-hook)
1843     (setq file-name (elmo-message-file-name draft-folder number))
1844     (unless (file-exists-p file-name)
1845       (error "File %s does not exist" file-name))
1846     (if (setq buffer (get-buffer
1847                       (concat wl-draft-folder "/"
1848                               (number-to-string number))))
1849         (progn
1850           (if wl-draft-use-frame
1851               (switch-to-buffer-other-frame buffer)
1852             (switch-to-buffer buffer))
1853           (set-buffer buffer))
1854       (setq buffer (get-buffer-create (number-to-string number)))
1855       (if wl-draft-use-frame
1856           (switch-to-buffer-other-frame buffer)
1857         (switch-to-buffer buffer))
1858       (set-buffer buffer)
1859       (insert-file-contents-as-binary file-name)
1860       (let((mime-edit-again-ignored-field-regexp
1861             "^\\(Content-.*\\|Mime-Version\\):"))
1862         (wl-draft-decode-message-in-buffer))
1863       (wl-draft-insert-mail-header-separator)
1864       (if (not (string-match (regexp-quote wl-draft-folder)
1865                              (buffer-name)))
1866           (rename-buffer (concat wl-draft-folder "/" (buffer-name))))
1867       (auto-save-mode -1)
1868       (wl-draft-mode)
1869       (setq buffer-file-name file-name)
1870       (make-local-variable 'truncate-partial-width-windows)
1871       (setq truncate-partial-width-windows nil)
1872       (setq truncate-lines wl-draft-truncate-lines)
1873       (setq wl-sent-message-via nil)
1874       (setq wl-sent-message-queued nil)
1875       (setq wl-draft-buffer-file-name file-name)
1876       (wl-draft-config-info-operation number 'load)
1877       (goto-char (point-min))
1878       (wl-draft-overload-functions)
1879       (wl-draft-editor-mode)
1880       (add-hook 'local-write-file-hooks 'wl-draft-save)
1881       (wl-highlight-headers 'for-draft)
1882       (run-hooks 'wl-draft-reedit-hook)
1883       (goto-char (point-max))
1884       buffer)))
1885
1886 (defmacro wl-draft-body-goto-top ()
1887   (` (progn
1888        (goto-char (point-min))
1889        (if (re-search-forward mail-header-separator nil t)
1890            (forward-char 1)
1891          (goto-char (point-max))))))
1892
1893 (defmacro wl-draft-body-goto-bottom ()
1894   (` (goto-char (point-max))))
1895
1896 (defmacro wl-draft-config-body-goto-header ()
1897   (` (progn
1898        (goto-char (point-min))
1899        (if (re-search-forward mail-header-separator nil t)
1900            (beginning-of-line)
1901          (goto-char (point-max))))))
1902
1903 (defun wl-draft-config-sub-body (content)
1904   (wl-draft-body-goto-top)
1905   (delete-region (point) (point-max))
1906   (if content (insert (eval content))))
1907
1908 (defun wl-draft-config-sub-top (content)
1909   (wl-draft-body-goto-top)
1910   (if content (insert (eval content))))
1911
1912 (defun wl-draft-config-sub-bottom (content)
1913   (wl-draft-body-goto-bottom)
1914   (if content (insert (eval content))))
1915
1916 (defun wl-draft-config-sub-header (content)
1917   (wl-draft-config-body-goto-header)
1918   (if content (insert (concat (eval content) "\n"))))
1919
1920 (defun wl-draft-config-sub-header-top (content)
1921   (goto-char (point-min))
1922   (if content (insert (concat (eval content) "\n"))))
1923
1924 (defun wl-draft-config-sub-part-top (content)
1925   (goto-char (mime-edit-content-beginning))
1926   (if content (insert (concat (eval content) "\n"))))
1927
1928 (defun wl-draft-config-sub-part-bottom (content)
1929   (goto-char (mime-edit-content-end))
1930   (if content (insert (concat (eval content) "\n"))))
1931
1932 (defsubst wl-draft-config-sub-file (content)
1933   (let ((coding-system-for-read wl-cs-autoconv)
1934         (file (expand-file-name (eval content))))
1935     (if (file-exists-p file)
1936         (insert-file-contents file)
1937       (error "%s: no exists file" file))))
1938
1939 (defun wl-draft-config-sub-body-file (content)
1940   (wl-draft-body-goto-top)
1941   (delete-region (point) (point-max))
1942   (wl-draft-config-sub-file content))
1943
1944 (defun wl-draft-config-sub-top-file (content)
1945   (wl-draft-body-goto-top)
1946   (wl-draft-config-sub-file content))
1947
1948 (defun wl-draft-config-sub-bottom-file (content)
1949   (wl-draft-body-goto-bottom)
1950   (wl-draft-config-sub-file content))
1951
1952 (defun wl-draft-config-sub-header-file (content)
1953   (wl-draft-config-body-goto-header)
1954   (wl-draft-config-sub-file content))
1955
1956 (defun wl-draft-config-sub-template (content)
1957   (setq wl-draft-config-variables
1958         (wl-template-insert (eval content))))
1959
1960 (defun wl-draft-config-sub-x-face (content)
1961   (if (and (string-match "\\.xbm\\(\\.gz\\)?$" content)
1962            (fboundp 'x-face-insert)) ; x-face.el is installed.
1963       (x-face-insert content)
1964     (wl-draft-replace-field "X-Face" (elmo-get-file-string content t) t)))
1965
1966 (defsubst wl-draft-config-sub-func (field content)
1967   (let (func)
1968     (if (setq func (assq field wl-draft-config-sub-func-alist))
1969         (let (wl-draft-config-variables)
1970           (funcall (cdr func) content)
1971           ;; for wl-draft-config-sub-template
1972           (cons t wl-draft-config-variables)))))
1973
1974 (defsubst wl-draft-config-exec-sub (clist)
1975   (let (config local-variables)
1976     (while clist
1977       (setq config (car clist))
1978       (cond
1979        ((functionp config)
1980         (funcall config))
1981        ((consp config)
1982         (let ((field (car config))
1983               (content (cdr config))
1984               ret-val)
1985           (cond
1986            ((stringp field)
1987             (wl-draft-replace-field field (eval content) t))
1988            ((setq ret-val (wl-draft-config-sub-func field content))
1989             (if (cdr ret-val) ;; for wl-draft-config-sub-template
1990                 (wl-append local-variables (cdr ret-val))))
1991            ((boundp field) ;; variable
1992             (make-local-variable field)
1993             (set field (eval content))
1994             (wl-append local-variables (list field)))
1995            (t
1996             (error "%s: not variable" field)))))
1997        (t
1998         (error "%s: not supported type" config)))
1999       (setq clist (cdr clist)))
2000     local-variables))
2001
2002 (defun wl-draft-prepared-config-exec (&optional config-alist reply-buf)
2003   "Change headers in draft preparation time."
2004   (interactive)
2005   (unless wl-draft-reedit
2006     (let ((config-alist
2007            (or config-alist
2008                (and (boundp 'wl-draft-prepared-config-alist)
2009                     wl-draft-prepared-config-alist)     ;; For compatible.
2010                wl-draft-config-alist)))
2011       (if config-alist
2012           (wl-draft-config-exec config-alist reply-buf)))))
2013
2014 (defun wl-draft-config-exec (&optional config-alist reply-buf)
2015   "Change headers according to the value of `wl-draft-config-alist'.
2016 Automatically applied in draft sending time."
2017   (interactive)
2018   (let ((case-fold-search t)
2019         (alist (or config-alist wl-draft-config-alist))
2020         (reply-buf (or reply-buf (and (buffer-live-p wl-draft-reply-buffer)
2021                                       wl-draft-reply-buffer)))
2022         (local-variables wl-draft-config-variables)
2023         key clist found)
2024     (when (and (or (interactive-p)
2025                    wl-draft-config-exec-flag)
2026                alist)
2027       (save-excursion
2028         (catch 'done
2029           (while alist
2030             (setq key (caar alist)
2031                   clist (cdar alist))
2032             (cond
2033              ((eq key 'reply)
2034               (when (and
2035                      reply-buf
2036                      (save-excursion
2037                        (set-buffer reply-buf)
2038                        (save-restriction
2039                          (std11-narrow-to-header)
2040                          (goto-char (point-min))
2041                          (re-search-forward (car clist) nil t))))
2042                 (wl-draft-config-exec-sub (cdr clist))
2043                 (setq found t)))
2044              ((stringp key)
2045               (when (save-restriction
2046                       (std11-narrow-to-header mail-header-separator)
2047                       (goto-char (point-min))
2048                       (re-search-forward key nil t))
2049                 (wl-append local-variables
2050                            (wl-draft-config-exec-sub clist))
2051                 (setq found t)))
2052              ((eval key)
2053               (wl-append local-variables
2054                          (wl-draft-config-exec-sub clist))
2055               (setq found t)))
2056             (if (and found wl-draft-config-matchone)
2057                 (throw 'done t))
2058             (setq alist (cdr alist))))
2059         (if found
2060             (setq wl-draft-config-exec-flag nil))
2061         (run-hooks 'wl-draft-config-exec-hook)
2062         (put-text-property (point-min)(point-max) 'face nil)
2063         (wl-highlight-message (point-min)(point-max) t)
2064         (setq wl-draft-config-variables
2065               (elmo-uniq-list local-variables))))))
2066
2067 (defun wl-draft-replace-field (field content &optional add)
2068   (save-excursion
2069     (save-restriction
2070       (let ((case-fold-search t)
2071             (inhibit-read-only t) ;; added by teranisi.
2072             beg)
2073         (std11-narrow-to-header mail-header-separator)
2074         (goto-char (point-min))
2075         (if (re-search-forward (concat "^" (regexp-quote field) ":") nil t)
2076             (if content
2077                 ;; replace field
2078                 (progn
2079                   (setq beg (point))
2080                   (re-search-forward "^[^ \t]" nil 'move)
2081                   (beginning-of-line)
2082                   (skip-chars-backward "\n")
2083                   (delete-region beg (point))
2084                   (insert " " content))
2085               ;; delete field
2086               (save-excursion
2087                 (beginning-of-line)
2088                 (setq beg (point)))
2089               (re-search-forward "^[^ \t]" nil 'move)
2090               (beginning-of-line)
2091               (delete-region beg (point)))
2092           (when (and add content)
2093             ;; add field
2094             (goto-char (point-max))
2095             (insert (concat field ": " content "\n"))))))))
2096
2097 (defun wl-draft-config-info-operation (msg operation)
2098   (let* ((msgdb-dir (elmo-folder-msgdb-path (wl-folder-get-elmo-folder
2099                                              wl-draft-folder)))
2100          (filename
2101           (expand-file-name
2102            (format "%s-%d" wl-draft-config-save-filename msg)
2103            msgdb-dir))
2104          element alist variable)
2105     (cond
2106      ((eq operation 'save)
2107       (let ((variables (elmo-uniq-list wl-draft-config-variables)))
2108         (while (setq variable (pop variables))
2109           (when (boundp variable)
2110             (wl-append alist
2111                        (list (cons variable (eval variable))))))
2112         (elmo-object-save filename alist)))
2113      ((eq operation 'load)
2114       (setq alist (elmo-object-load filename))
2115       (while (setq element (pop alist))
2116         (set (make-local-variable (car element)) (cdr element))
2117         (wl-append wl-draft-config-variables (list (car element)))))
2118      ((eq operation 'delete)
2119       (if (file-exists-p filename)
2120           (delete-file filename))))))
2121
2122 (defun wl-draft-queue-info-operation (msg operation
2123                                           &optional add-sent-message-via)
2124   (let* ((msgdb-dir (elmo-folder-msgdb-path
2125                      (wl-folder-get-elmo-folder wl-queue-folder)))
2126          (filename
2127           (expand-file-name
2128            (format "%s-%d" wl-draft-queue-save-filename msg)
2129            msgdb-dir))
2130          element alist variable)
2131     (cond
2132      ((eq operation 'save)
2133       (let ((variables (elmo-uniq-list
2134                         (append wl-draft-queue-save-variables
2135                                 wl-draft-config-variables
2136                                 (list 'wl-draft-fcc-list)))))
2137         (if add-sent-message-via
2138             (progn
2139               (push 'wl-sent-message-queued variables)
2140               (push 'wl-sent-message-via variables)))
2141         (while (setq variable (pop variables))
2142           (when (boundp variable)
2143             (wl-append alist
2144                        (list (cons variable (eval variable))))))
2145         (elmo-object-save filename alist)))
2146      ((eq operation 'load)
2147       (setq alist (elmo-object-load filename))
2148       (while (setq element (pop alist))
2149         (set (make-local-variable (car element)) (cdr element))))
2150      ((eq operation 'get-sent-via)
2151       (setq alist (elmo-object-load filename))
2152       (cdr (assq 'wl-sent-message-via alist)))
2153      ((eq operation 'delete)
2154       (if (file-exists-p filename)
2155           (delete-file filename))))))
2156
2157 (defun wl-draft-queue-append (wl-sent-message-via)
2158   (if wl-draft-verbose-send
2159       (message "Queuing..."))
2160   (let ((send-buffer (current-buffer))
2161         (folder (wl-folder-get-elmo-folder wl-queue-folder))
2162         (message-id (std11-field-body "Message-ID")))
2163     (if (elmo-folder-append-buffer folder t)
2164         (progn
2165           (wl-draft-queue-info-operation
2166            (car (elmo-folder-status folder))
2167            'save wl-sent-message-via)
2168           (wl-draft-write-sendlog 'ok 'queue nil wl-queue-folder message-id)
2169           (when wl-draft-verbose-send
2170             (setq wl-draft-verbose-msg "Queuing...")
2171             (message "Queuing...done")))
2172       (wl-draft-write-sendlog 'failed 'queue nil wl-queue-folder message-id)
2173       (error "Queuing failed"))))
2174
2175 (defun wl-draft-queue-flush ()
2176   "Flush draft queue."
2177   (interactive)
2178   (let* ((queue-folder (wl-folder-get-elmo-folder wl-queue-folder))
2179          (msgs2 (progn
2180                   (elmo-folder-open-internal queue-folder)
2181                   (elmo-folder-list-messages queue-folder)))
2182          (i 0)
2183          (performed 0)
2184          (wl-draft-queue-flushing t)
2185          msgs failure len buffer msgid sent-via)
2186     ;; get plugged send message
2187     (while msgs2
2188       (setq sent-via (wl-draft-queue-info-operation (car msgs2) 'get-sent-via))
2189       (catch 'found
2190         (while sent-via
2191           (when (and (eq (nth 1 (car sent-via)) 'unplugged)
2192                      (or (not (nth 2 (car sent-via)))
2193                          (elmo-plugged-p
2194                           (car (nth 2 (car sent-via)))
2195                           (cdr (nth 2 (car sent-via))))))
2196             (wl-append msgs (list (car msgs2)))
2197             (throw 'found t))
2198           (setq sent-via (cdr sent-via))))
2199       (setq msgs2 (cdr msgs2)))
2200     (when (> (setq len (length msgs)) 0)
2201       (if (elmo-y-or-n-p (format
2202                           "%d message(s) are in the sending queue.  Send now? "
2203                           len)
2204                          (not elmo-dop-flush-confirm) t)
2205           (progn
2206             (save-excursion
2207               (setq buffer (get-buffer-create " *wl-draft-queue-flush*"))
2208               (set-buffer buffer)
2209               (while msgs
2210                 ;; reset buffer local variables
2211                 (kill-all-local-variables)
2212                 (erase-buffer)
2213                 (setq i (+ 1 i)
2214                       failure nil)
2215                 (setq wl-sent-message-via nil)
2216                 (wl-draft-queue-info-operation (car msgs) 'load)
2217                 (elmo-message-fetch queue-folder
2218                                     (car msgs)
2219                                     (elmo-make-fetch-strategy 'entire)
2220                                     nil (current-buffer))
2221                 (condition-case err
2222                     (setq failure (funcall
2223                                    wl-draft-queue-flush-send-function
2224                                    (format "Sending (%d/%d)..." i len)))
2225 ;;;               (wl-draft-raw-send nil nil
2226 ;;;                                  (format "Sending (%d/%d)..." i len))
2227                   (error
2228                    (elmo-display-error err t)
2229                    (setq failure t))
2230                   (quit
2231                    (setq failure t)))
2232                 (if (eq wl-sent-message-modified 'requeue)
2233                     (progn
2234                       (elmo-folder-delete-messages
2235                        queue-folder (cons (car msgs) nil))
2236                       (wl-draft-queue-info-operation (car msgs) 'delete))
2237                   (unless failure
2238                     (elmo-folder-delete-messages
2239                      queue-folder (cons (car msgs) nil))
2240                     (wl-draft-queue-info-operation (car msgs) 'delete)
2241                     (setq performed (+ 1 performed))))
2242                 (setq msgs (cdr msgs)))
2243               (kill-buffer buffer)
2244               (message "%d message(s) are sent." performed)))
2245         (message "%d message(s) are remained to be sent." len))
2246       (elmo-folder-close queue-folder)
2247       len)))
2248
2249 (defun wl-jump-to-draft-buffer (&optional arg)
2250   "Jump to the draft if exists."
2251   (interactive "P")
2252   (if arg
2253       (wl-jump-to-draft-folder)
2254     (let ((draft-bufs (wl-collect-draft))
2255           buf)
2256       (cond
2257        ((null draft-bufs)
2258         (message "No draft buffer exist."))
2259        (t
2260         (setq draft-bufs
2261               (sort (mapcar 'buffer-name draft-bufs)
2262                     (function (lambda (a b)
2263                                 (not (string< a b))))))
2264         (if (setq buf (cdr (member (buffer-name)
2265                                    draft-bufs)))
2266             (setq buf (car buf))
2267           (setq buf (car draft-bufs)))
2268         (switch-to-buffer buf))))))
2269
2270 (defun wl-jump-to-draft-folder ()
2271   (let ((msgs (reverse (elmo-folder-list-messages (wl-folder-get-elmo-folder
2272                                                    wl-draft-folder))))
2273         (mybuf (buffer-name))
2274         msg buf)
2275     (if (not msgs)
2276         (message "No draft message exist.")
2277       (if (string-match (concat "^" wl-draft-folder "/") mybuf)
2278           (setq msg (cadr (memq
2279                            (string-to-int (substring mybuf (match-end 0)))
2280                            msgs))))
2281       (or msg (setq msg (car msgs)))
2282       (if (setq buf (get-buffer (format "%s/%d" wl-draft-folder msg)))
2283           (switch-to-buffer buf)
2284         (wl-draft-reedit msg)))))
2285
2286 (defun wl-draft-highlight-and-recenter (&optional n)
2287   (interactive "P")
2288   (if wl-highlight-body-too
2289       (let ((beg (point-min))
2290             (end (point-max)))
2291         (put-text-property beg end 'face nil)
2292         (wl-highlight-message beg end t)))
2293   (recenter n))
2294
2295 ;;;; user-agent support by Sen Nagata
2296
2297 ;; this appears to be necessarily global...
2298 (defvar wl-user-agent-compose-p nil)
2299 (defvar wl-user-agent-headers-and-body-alist nil)
2300
2301 ;; this should be a generic function for mail-mode -- i wish there was
2302 ;; something like it in sendmail.el
2303 (defun wl-user-agent-insert-header (header-name header-value)
2304   "Insert HEADER-NAME w/ value HEADER-VALUE into a message."
2305   ;; it seems like overriding existing headers is acceptable -- should
2306   ;; we provide an option?
2307
2308   ;; plan was: unfold header (might be folded), remove existing value, insert
2309   ;;           new value
2310   ;; wl doesn't seem to fold header lines yet anyway :-)
2311
2312   (let ((kill-whole-line t)
2313         end-of-line)
2314     (mail-position-on-field (capitalize header-name))
2315     (setq end-of-line (point))
2316     (beginning-of-line)
2317     (re-search-forward ":" end-of-line)
2318     (insert (concat " " header-value "\n"))
2319     (kill-line)))
2320
2321 ;; this should be a generic function for mail-mode -- i wish there was
2322 ;; something like it in sendmail.el
2323 ;;
2324 ;; ** haven't dealt w/ case where the body is already set **
2325 (defun wl-user-agent-insert-body (body-text)
2326   "Insert a body of text, BODY-TEXT, into a message."
2327   ;; code defensively... :-P
2328   (goto-char (point-min))
2329   (search-forward mail-header-separator)
2330   (forward-line 1)
2331   (insert body-text))
2332
2333 ;;;###autoload
2334 (defun wl-user-agent-compose (&optional to subject other-headers continue
2335                                         switch-function yank-action
2336                                         send-actions)
2337   "Support the `compose-mail' interface for wl.
2338 Only support for TO, SUBJECT, and OTHER-HEADERS has been implemented.
2339 Support for CONTINUE, YANK-ACTION, and SEND-ACTIONS has not
2340 been implemented yet.  Partial support for SWITCH-FUNCTION now supported."
2341
2342   (unless (featurep 'wl)
2343     (require 'wl))
2344   ;; protect these -- to and subject get bound at some point, so it looks
2345   ;; to be necessary to protect the values used w/in
2346   (let ((wl-user-agent-headers-and-body-alist other-headers)
2347         (wl-draft-use-frame (eq switch-function 'switch-to-buffer-other-frame))
2348         (wl-draft-reply-buffer-style 'split))
2349     (when (eq switch-function 'switch-to-buffer-other-window)
2350       (when (one-window-p t)
2351         (if (window-minibuffer-p) (other-window 1))
2352         (split-window))
2353       (other-window 1))
2354     (if to
2355         (if (wl-string-match-assoc "to" wl-user-agent-headers-and-body-alist
2356                                    'ignore-case)
2357             (setcdr
2358              (wl-string-match-assoc "to" wl-user-agent-headers-and-body-alist
2359                                     'ignore-case)
2360              to)
2361           (setq wl-user-agent-headers-and-body-alist
2362                 (cons (cons "to" to)
2363                       wl-user-agent-headers-and-body-alist))))
2364     (if subject
2365         (if (wl-string-match-assoc "subject"
2366                                    wl-user-agent-headers-and-body-alist
2367                                    'ignore-case)
2368             (setcdr
2369              (wl-string-match-assoc "subject"
2370                                     wl-user-agent-headers-and-body-alist
2371                                     'ignore-case)
2372              subject)
2373           (setq wl-user-agent-headers-and-body-alist
2374                 (cons (cons "subject" subject)
2375                       wl-user-agent-headers-and-body-alist))))
2376     ;; i think this is what we want to use...
2377     (unwind-protect
2378         (progn
2379           ;; tell the hook-function to do its stuff
2380           (setq wl-user-agent-compose-p t)
2381           ;; because to get the hooks working, wl-draft has to think it has
2382           ;; been called interactively
2383           (call-interactively 'wl-draft))
2384       (setq wl-user-agent-compose-p nil))))
2385
2386 (defun wl-user-agent-compose-internal ()
2387   "Manipulate headers and/or a body of a draft message."
2388   ;; being called from wl-user-agent-compose?
2389   (if wl-user-agent-compose-p
2390       (progn
2391         ;; insert headers
2392         (let ((headers wl-user-agent-headers-and-body-alist)
2393               (case-fold-search t))
2394           (while headers
2395             ;; skip body
2396             (if (not (string-match "^body$" (car (car headers))))
2397                 (wl-user-agent-insert-header
2398                  (car (car headers)) (cdr (car headers)))
2399               t)
2400             (setq headers (cdr headers))))
2401         ;; highlight headers (from wl-draft in wl-draft.el)
2402         (wl-highlight-headers 'for-draft)
2403         ;; insert body
2404         (if (wl-string-match-assoc "body" wl-user-agent-headers-and-body-alist
2405                                    'ignore-case)
2406             (wl-user-agent-insert-body
2407              (cdr (wl-string-match-assoc
2408                    "body"
2409                    wl-user-agent-headers-and-body-alist 'ignore-case)))))
2410     t))
2411
2412 (require 'product)
2413 (product-provide (provide 'wl-draft) (require 'wl-version))
2414
2415 ;;; wl-draft.el ends here