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