* wl-draft.el (wl-draft-save): Disable only
[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                     (copy-sequence mime-header-encode-method-alist))
1449                    (key
1450                     (assq 'eword-encode-address-list
1451                           mime-header-encode-method-alist)))
1452               (setq mime-header-encode-method-alist
1453                     (delq key mime-header-encode-method-alist))
1454               (mime-edit-translate-buffer))
1455             (wl-draft-get-header-delimiter t)
1456             (setq next-number
1457                   (elmo-folder-next-message-number (wl-draft-get-folder)))
1458             (elmo-folder-append-buffer (wl-draft-get-folder)))
1459           (elmo-folder-check (wl-draft-get-folder))
1460           (elmo-folder-commit (wl-draft-get-folder))
1461           (setq wl-draft-buffer-message-number next-number)
1462           (rename-buffer (format "%s/%d" wl-draft-folder next-number))
1463           (setq buffer-file-name (buffer-name))
1464           (set-buffer-modified-p nil)
1465           (wl-draft-config-info-operation wl-draft-buffer-message-number 'save)
1466           (message "Saving...done")))
1467     (message "(No changes need to be saved)")))
1468
1469 (defun wl-draft-mimic-kill-buffer ()
1470   "Kill the current (draft) buffer with query."
1471   (interactive)
1472   (let ((bufname (read-buffer (format "Kill buffer: (default %s) "
1473                                       (buffer-name))))
1474         wl-draft-use-frame)
1475     (if (or (not bufname)
1476             (string-equal bufname "")
1477             (string-equal bufname (buffer-name)))
1478         (let ((bufname (current-buffer)))
1479           (when (or (not (buffer-modified-p))
1480                     (yes-or-no-p
1481                      (format "Buffer %s modified; kill anyway? " bufname)))
1482             (set-buffer-modified-p nil)
1483             (wl-draft-hide bufname)
1484             (kill-buffer bufname)))
1485       (kill-buffer bufname))))
1486
1487 (defun wl-draft-save-and-exit ()
1488   "Save current draft and exit current draft mode."
1489   (interactive)
1490   (wl-draft-save)
1491   (let ((editing-buffer (current-buffer)))
1492     (wl-draft-hide editing-buffer)
1493     (kill-buffer editing-buffer)))
1494
1495 (defun wl-draft-send-and-exit ()
1496   "Send current draft message and kill it."
1497   (interactive)
1498   (wl-draft-send t))
1499
1500 (defun wl-draft-send-from-toolbar ()
1501   (interactive)
1502   (let ((wl-interactive-send t))
1503     (wl-draft-send-and-exit)))
1504
1505 (defun wl-draft-delete-field (field &optional delimline replace)
1506   (wl-draft-delete-fields (regexp-quote field) delimline replace))
1507
1508 (defun wl-draft-delete-fields (field &optional delimline replace)
1509   (save-restriction
1510     (unless delimline
1511       (goto-char (point-min))
1512       (if (search-forward "\n\n" nil t)
1513           (setq delimline (point))
1514         (setq delimline (point-max))))
1515     (narrow-to-region (point-min) delimline)
1516     (goto-char (point-min))
1517     (let ((regexp (concat "^" field ":"))
1518           (case-fold-search t))
1519       (while (not (eobp))
1520         (if (looking-at regexp)
1521             (progn
1522               (delete-region
1523                (point)
1524                (progn
1525                  (forward-line 1)
1526                  (if (re-search-forward "^[^ \t]" nil t)
1527                      (goto-char (match-beginning 0))
1528                    (point-max))))
1529               (if replace
1530                   (insert (concat field ": " replace "\n"))))
1531           (forward-line 1)
1532           (if (re-search-forward "^[^ \t]" nil t)
1533               (goto-char (match-beginning 0))
1534             (point-max)))))))
1535
1536 (defun wl-draft-get-fcc-list (header-end)
1537   (if (and wl-draft-doing-mime-bcc
1538            wl-draft-disable-fcc-for-mime-bcc)
1539       (progn
1540         (wl-draft-delete-field "fcc")
1541         nil)
1542     (let (fcc-list
1543           (case-fold-search t))
1544       (or (markerp header-end) (error "HEADER-END must be a marker"))
1545       (save-excursion
1546         (goto-char (point-min))
1547         (while (re-search-forward "^Fcc:[ \t]*" header-end t)
1548           (save-match-data
1549             (setq fcc-list
1550                   (append fcc-list
1551                           (split-string
1552                            (buffer-substring-no-properties
1553                             (point)
1554                             (progn
1555                               (end-of-line)
1556                               (skip-chars-backward " \t")
1557                               (point)))
1558                            ",[ \t]*")))
1559             (dolist (folder fcc-list)
1560               (wl-folder-confirm-existence
1561                (wl-folder-get-elmo-folder (eword-decode-string folder)))))
1562           (delete-region (match-beginning 0)
1563                          (progn (forward-line 1) (point)))))
1564       fcc-list)))
1565
1566 (defcustom wl-draft-fcc-append-read-folder-history t
1567   "Non-nil to append fcc'ed folder to `wl-read-folder-history'."
1568   :type 'boolean
1569   :group 'wl-draft)
1570
1571 (defun wl-draft-do-fcc (header-end &optional fcc-list)
1572   (let ((send-mail-buffer (current-buffer))
1573         (tembuf (generate-new-buffer " fcc output"))
1574         (case-fold-search t)
1575         beg end)
1576     (or (markerp header-end) (error "HEADER-END must be a marker"))
1577     (save-excursion
1578       (unless fcc-list
1579         (setq fcc-list (wl-draft-get-fcc-list header-end)))
1580       (set-buffer tembuf)
1581       (erase-buffer)
1582       ;; insert just the headers to avoid moving the gap more than
1583       ;; necessary (the message body could be arbitrarily huge.)
1584       (insert-buffer-substring send-mail-buffer 1 header-end)
1585       (wl-draft-insert-required-fields t)
1586       (goto-char (point-max))
1587       (insert-buffer-substring send-mail-buffer header-end)
1588       (let ((id (std11-field-body "Message-ID"))
1589             (elmo-enable-disconnected-operation t)
1590             cache-saved)
1591         (while fcc-list
1592           (unless (or cache-saved
1593                       (elmo-folder-plugged-p
1594                        (wl-folder-get-elmo-folder (car fcc-list))))
1595             (elmo-file-cache-save id nil) ;; for disconnected operation
1596             (setq cache-saved t))
1597           (if (elmo-folder-append-buffer
1598                (wl-folder-get-elmo-folder
1599                 (eword-decode-string (car fcc-list)))
1600                (and wl-fcc-force-as-read '(read)))
1601               (wl-draft-write-sendlog 'ok 'fcc nil (car fcc-list) id)
1602             (wl-draft-write-sendlog 'failed 'fcc nil (car fcc-list) id))
1603           (if (and wl-draft-fcc-append-read-folder-history
1604                    (boundp 'wl-read-folder-history))
1605               (or (equal (car fcc-list) (car wl-read-folder-history))
1606                   (setq wl-read-folder-history
1607                         (append (list (car fcc-list)) wl-read-folder-history))))
1608           (setq fcc-list (cdr fcc-list)))))
1609     (kill-buffer tembuf)))
1610
1611 (defun wl-draft-on-field-p ()
1612   (if (< (point)
1613          (save-excursion
1614            (goto-char (point-min))
1615            (search-forward (concat "\n" mail-header-separator "\n") nil 0)
1616            (point)))
1617       (if (bolp)
1618           (if (bobp)
1619               t
1620             (save-excursion
1621               (forward-line -1)
1622               (if (or (looking-at ".*,[ \t]?$")
1623                       (looking-at "^[^ \t]+:[ \t]+.*:$")); group list name
1624                   nil t)))
1625         (let ((pos (point)))
1626           (save-excursion
1627             (beginning-of-line)
1628             (if (looking-at "^[ \t]")
1629                 nil
1630               (if (re-search-forward ":" pos t) nil t)))))))
1631
1632 ;;;;;;;;;;;;;;;;
1633 ;;;###autoload
1634 (defun wl-draft (&optional header-alist
1635                            content-type content-transfer-encoding
1636                            body edit-again
1637                            parent-folder)
1638   "Write and send mail/news message with Wanderlust."
1639   (interactive)
1640   (require 'wl)
1641   (unless wl-init
1642     (wl-load-profile)
1643     (wl-folder-init)
1644     (elmo-init)
1645     (wl-plugged-init t))
1646   (let (wl-demo)
1647     (wl-init)) ; returns immediately if already initialized.
1648
1649
1650   (let (buffer header-alist-internal)
1651     (setq buffer (wl-draft-create-buffer parent-folder))
1652     (unless (cdr (assq 'From header-alist))
1653       (setq header-alist
1654             (append (list (cons 'From wl-from)) header-alist)))
1655     (unless (cdr (assq 'To header-alist))
1656       (let ((to))
1657         (when (setq to (and
1658                         (interactive-p)
1659                         ""))
1660           (if (assq 'To header-alist)
1661               (setcdr (assq 'To header-alist) to)
1662             (setq header-alist
1663                   (append header-alist
1664                           (list (cons 'To to))))))))
1665     (unless (cdr (assq 'Subject header-alist))
1666       (if (assq 'Subject header-alist)
1667           (setcdr (assq 'Subject header-alist) "")
1668         (setq header-alist
1669               (append header-alist (list (cons 'Subject ""))))))
1670     (setq header-alist (append header-alist
1671                                (wl-draft-default-headers)
1672                                wl-draft-additional-header-alist
1673                                (if body (list "" (cons 'Body body)))))
1674     (wl-draft-create-contents header-alist)
1675     (if edit-again
1676         (wl-draft-decode-body
1677          content-type content-transfer-encoding))
1678     (wl-draft-insert-mail-header-separator)
1679     (wl-draft-prepare-edit)
1680     (if (interactive-p)
1681         (run-hooks 'wl-mail-setup-hook))
1682     (goto-char (point-min))
1683     (setq buffer-undo-list nil)
1684     (wl-user-agent-compose-internal) ;; user-agent
1685     (cond ((and
1686             (interactive-p)
1687             (string= (cdr (assq 'To header-alist)) ""))
1688            (mail-position-on-field "To"))
1689           (t
1690            (goto-char (point-max))))
1691     buffer))
1692
1693 (defun wl-draft-create-buffer (&optional parent-folder)
1694   (let* ((draft-folder (wl-draft-get-folder))
1695          (parent-folder (or parent-folder (wl-summary-buffer-folder-name)))
1696          (summary-buf (wl-summary-get-buffer parent-folder))
1697          (reply-or-forward
1698           (or (eq this-command 'wl-summary-reply)
1699               (eq this-command 'wl-summary-reply-with-citation)
1700               (eq this-command 'wl-summary-forward)
1701               (eq this-command 'wl-summary-target-mark-forward)
1702               (eq this-command 'wl-summary-target-mark-reply-with-citation)))
1703          (buffer (generate-new-buffer "*draft*")) ; Just for initial name.
1704          change-major-mode-hook)
1705     (set-buffer buffer)
1706     ;; switch-buffer according to draft buffer style.
1707     (if wl-draft-use-frame
1708         (switch-to-buffer-other-frame buffer)
1709       (if reply-or-forward
1710           (case wl-draft-reply-buffer-style
1711             (split
1712              (split-window-vertically)
1713              (other-window 1)
1714              (switch-to-buffer buffer))
1715             (keep
1716              (switch-to-buffer buffer))
1717             (full
1718              (delete-other-windows)
1719              (switch-to-buffer buffer))
1720             (t
1721              (if (functionp wl-draft-reply-buffer-style)
1722                  (funcall wl-draft-reply-buffer-style buffer)
1723                (error "Invalid value for wl-draft-reply-buffer-style"))))
1724         (case wl-draft-buffer-style
1725           (split
1726            (when (eq major-mode 'wl-summary-mode)
1727              (wl-summary-toggle-disp-msg 'off))
1728            (split-window-vertically)
1729            (other-window 1)
1730            (switch-to-buffer buffer))
1731           (keep
1732            (switch-to-buffer buffer))
1733           (full
1734            (delete-other-windows)
1735            (switch-to-buffer buffer))
1736           (t (if (functionp wl-draft-buffer-style)
1737                  (funcall wl-draft-buffer-style buffer)
1738                (error "Invalid value for wl-draft-buffer-style"))))))
1739     (auto-save-mode -1)
1740     (wl-draft-mode)
1741     (set-buffer-multibyte t)            ; draft buffer is always multibyte.
1742     (make-local-variable 'truncate-partial-width-windows)
1743     (setq truncate-partial-width-windows nil)
1744     (setq truncate-lines wl-draft-truncate-lines)
1745     (setq wl-sent-message-via nil)
1746     (setq wl-sent-message-queued nil)
1747     (setq wl-draft-config-exec-flag t)
1748     (setq wl-draft-parent-folder (or parent-folder ""))
1749     (or (eq this-command 'wl-folder-write-current-folder)
1750         (setq wl-draft-buffer-cur-summary-buffer summary-buf))
1751     buffer))
1752
1753 (defun wl-draft-create-contents (header-alist)
1754   "header-alist' sample
1755 '(function  ;; funcall
1756   string    ;; insert string
1757   (symbol . string)    ;;  insert symbol-value: string
1758   (symbol . function)  ;;  (funcall) and if it returns string,
1759                        ;;  insert symbol-value: string
1760   (symbol . nil)       ;;  do nothing
1761   nil                  ;;  do nothing
1762   )"
1763   (unless (eq major-mode 'wl-draft-mode)
1764     (error "`wl-draft-create-header' must be use in wl-draft-mode"))
1765   (let ((halist header-alist)
1766         field value)
1767     (while halist
1768       (cond
1769        ;; function
1770        ((functionp (car halist)) (funcall (car halist)))
1771        ;; string
1772        ((stringp (car halist)) (insert (car halist) "\n"))
1773        ;; cons
1774        ((consp (car halist))
1775         (setq field (car (car halist)))
1776         (setq value (cdr (car halist)))
1777         (cond
1778          ((symbolp field)
1779           (cond
1780            ((eq field 'Body) ; body
1781             (insert value))
1782            ((stringp value) (insert (symbol-name field) ": " value "\n"))
1783            ((functionp value)
1784             (let ((value-return (funcall value)))
1785               (when (stringp value-return)
1786                 (insert (symbol-name field) ": " value-return "\n"))))
1787            ((not value))
1788            (t
1789             (debug))))
1790          ;;
1791          ((not field))
1792          (t
1793           (debug))
1794          )))
1795       (setq halist (cdr halist)))))
1796
1797 (defun wl-draft-prepare-edit ()
1798   (unless (eq major-mode 'wl-draft-mode)
1799     (error "`wl-draft-create-header' must be use in wl-draft-mode"))
1800   (let (change-major-mode-hook)
1801     (wl-draft-editor-mode)
1802     (static-when (boundp 'auto-save-file-name-transforms)
1803       (make-local-variable 'auto-save-file-name-transforms)
1804       (setq auto-save-file-name-transforms
1805             (cons (list (concat (regexp-quote wl-draft-folder)
1806                                 "/\\([0-9]+\\)")
1807                         (concat (expand-file-name
1808                                  "auto-save-"
1809                                  (elmo-folder-msgdb-path
1810                                   (wl-draft-get-folder)))
1811                                 "\\1"))
1812                   auto-save-file-name-transforms)))
1813     (when wl-draft-write-file-function
1814       (add-hook 'local-write-file-hooks wl-draft-write-file-function))
1815     (wl-draft-overload-functions)
1816     (wl-highlight-headers 'for-draft)
1817     (wl-draft-save)
1818     (clear-visited-file-modtime)))
1819
1820 (defun wl-draft-decode-header ()
1821   (save-excursion
1822     (std11-narrow-to-header)
1823     (wl-draft-decode-message-in-buffer)
1824     (widen)))
1825
1826 (defun wl-draft-decode-body (&optional content-type content-transfer-encoding)
1827   (let ((content-type
1828          (or content-type
1829                 (std11-field-body "content-type")))
1830         (content-transfer-encoding
1831          (or content-transfer-encoding
1832              (std11-field-body "content-transfer-encoding")))
1833         delimline)
1834     (save-excursion
1835       (std11-narrow-to-header)
1836       (wl-draft-delete-field "content-type")
1837       (wl-draft-delete-field "content-transfer-encoding")
1838       (goto-char (point-max))
1839       (setq delimline (point-marker))
1840       (widen)
1841       (narrow-to-region delimline (point-max))
1842       (goto-char (point-min))
1843       (when content-type
1844         (insert "Content-type: " content-type "\n"))
1845       (when content-transfer-encoding
1846         (insert "Content-Transfer-Encoding: " content-transfer-encoding "\n"))
1847       (wl-draft-decode-message-in-buffer)
1848       (goto-char (point-min))
1849       (unless (re-search-forward "^$" (point-at-eol) t)
1850         (insert "\n"))
1851       (widen)
1852       delimline)))
1853
1854 ;;; subroutine for wl-draft-create-contents
1855 ;;; must be used in wl-draft-mode
1856 (defun wl-draft-check-new-line ()
1857   (if (not (= (preceding-char) ?\n))
1858       (insert ?\n)))
1859
1860 (defsubst wl-draft-trim-ccs (cc)
1861   (let ((field
1862          (if (functionp cc)
1863              (funcall cc)
1864            cc)))
1865     (if (and field
1866              (null (and wl-draft-delete-myself-from-bcc-fcc
1867                         (elmo-list-member
1868                          (mapcar 'wl-address-header-extract-address
1869                                  (append
1870                                   (wl-parse-addresses (std11-field-body "To"))
1871                                   (wl-parse-addresses (std11-field-body "Cc"))))
1872                          (mapcar 'downcase wl-subscribed-mailing-list)))))
1873         field
1874       nil)))
1875
1876 (defsubst wl-draft-default-headers ()
1877   (list
1878    (cons 'Mail-Reply-To (and wl-insert-mail-reply-to
1879                              (wl-address-header-extract-address
1880                               wl-from)))
1881    (cons 'User-Agent wl-generate-mailer-string-function)
1882    (cons 'Reply-To mail-default-reply-to)
1883    (cons 'Bcc (function
1884                (lambda ()
1885                  (wl-draft-trim-ccs
1886                   (or wl-bcc (and mail-self-blind (user-login-name)))))))
1887    (cons 'Fcc (function
1888                (lambda ()
1889                  (wl-draft-trim-ccs wl-fcc))))
1890    (cons 'Organization wl-organization)
1891    (and wl-auto-insert-x-face
1892         (file-exists-p wl-x-face-file)
1893         'wl-draft-insert-x-face-field-here) ;; allow nil
1894    mail-default-headers
1895    ;; check \n at th end of line for `mail-default-headers'
1896    'wl-draft-check-new-line
1897    ))
1898
1899 (defun wl-draft-insert-mail-header-separator (&optional delimline)
1900   (save-excursion
1901     (if delimline
1902         (goto-char delimline)
1903       (goto-char (point-min))
1904       (if (search-forward "\n\n" nil t)
1905           (delete-backward-char 1)
1906         (goto-char (point-max))))
1907     (wl-draft-check-new-line)
1908     (put-text-property (point)
1909                        (progn
1910                          (insert mail-header-separator "\n")
1911                          (1- (point)))
1912                        'category 'mail-header-separator)
1913     (point)))
1914
1915 ;;;;;;;;;;;;;;;;
1916
1917 (defun wl-draft-elmo-nntp-send ()
1918   (let ((elmo-nntp-post-pre-hook wl-news-send-pre-hook)
1919         (elmo-nntp-default-user
1920          (or wl-nntp-posting-user elmo-nntp-default-user))
1921         (elmo-nntp-default-server
1922          (or wl-nntp-posting-server elmo-nntp-default-server))
1923         (elmo-nntp-default-port
1924          (or wl-nntp-posting-port elmo-nntp-default-port))
1925         (elmo-nntp-default-stream-type
1926          (or wl-nntp-posting-stream-type elmo-nntp-default-stream-type))
1927         (elmo-nntp-default-function wl-nntp-posting-function)
1928         condition)
1929     (if (setq condition (cdr (elmo-string-matched-assoc
1930                               (std11-field-body "Newsgroups")
1931                               wl-nntp-posting-config-alist)))
1932         (if (stringp condition)
1933             (setq elmo-nntp-default-server condition)
1934           (while (car condition)
1935             (set (intern (format "elmo-nntp-default-%s"
1936                                  (symbol-name (caar condition))))
1937                  (cdar condition))
1938             (setq condition (cdr condition)))))
1939     (unless elmo-nntp-default-function
1940       (error "wl-draft-nntp-send: posting-function is nil"))
1941     (if (not (elmo-plugged-p elmo-nntp-default-server elmo-nntp-default-port))
1942         (wl-draft-set-sent-message 'news 'unplugged
1943                                    (cons elmo-nntp-default-server
1944                                          elmo-nntp-default-port))
1945       (funcall elmo-nntp-default-function
1946                elmo-nntp-default-server (current-buffer))
1947       (wl-draft-set-sent-message 'news 'sent)
1948       (wl-draft-write-sendlog 'ok 'nntp elmo-nntp-default-server
1949                               (std11-field-body "Newsgroups")
1950                               (std11-field-body "Message-ID")))))
1951
1952 (defun wl-draft-generate-clone-buffer (name &optional local-variables)
1953   "Generate clone of current buffer named NAME."
1954   (let ((editing-buffer (current-buffer)))
1955     (save-excursion
1956       (set-buffer (generate-new-buffer name))
1957       (erase-buffer)
1958       (wl-draft-mode)
1959       (wl-draft-editor-mode)
1960       (insert-buffer editing-buffer)
1961       (message "")
1962       (while local-variables
1963         (make-local-variable (car local-variables))
1964         (set (car local-variables)
1965              (save-excursion
1966                (set-buffer editing-buffer)
1967                (symbol-value (car local-variables))))
1968         (setq local-variables (cdr local-variables)))
1969       (current-buffer))))
1970
1971 (defun wl-draft-remove-text-plain-tag ()
1972   "Remove text/plain tag of mime-edit."
1973   (when (string= (mime-make-text-tag "plain")
1974                  (buffer-substring-no-properties (point-at-bol)(point-at-eol)))
1975     (delete-region (point-at-bol)(1+ (point-at-eol)))))
1976
1977 (defun wl-draft-reedit (number)
1978   (let ((draft-folder (wl-draft-get-folder))
1979         (wl-draft-reedit t)
1980         (num 0)
1981         buffer change-major-mode-hook body-top)
1982     (setq buffer (get-buffer-create (format "%s/%d" wl-draft-folder
1983                                             number)))
1984     (if wl-draft-use-frame
1985         (switch-to-buffer-other-frame buffer)
1986       (switch-to-buffer buffer))
1987     (set-buffer buffer)
1988     (elmo-message-fetch draft-folder number (elmo-make-fetch-strategy 'entire)
1989                         nil (current-buffer))
1990     (elmo-delete-cr-buffer)
1991     (let ((mime-edit-again-ignored-field-regexp
1992            "^\\(Content-.*\\|Mime-Version\\):"))
1993       (wl-draft-decode-message-in-buffer))
1994     (setq body-top (wl-draft-insert-mail-header-separator))
1995     (auto-save-mode -1)
1996     (wl-draft-mode)
1997     (make-local-variable 'truncate-partial-width-windows)
1998     (setq truncate-partial-width-windows nil)
1999     (setq truncate-lines wl-draft-truncate-lines)
2000     (setq wl-sent-message-via nil)
2001     (setq wl-sent-message-queued nil)
2002     (wl-draft-config-info-operation number 'load)
2003     (goto-char (point-min))
2004     (wl-draft-overload-functions)
2005     (wl-draft-editor-mode)
2006     (static-when (boundp 'auto-save-file-name-transforms)
2007       (make-local-variable 'auto-save-file-name-transforms)
2008       (setq auto-save-file-name-transforms
2009             (cons (list (concat (regexp-quote wl-draft-folder)
2010                                 "/\\([0-9]+\\)")
2011                         (concat (expand-file-name
2012                                  "auto-save-"
2013                                  (elmo-folder-msgdb-path
2014                                   (wl-draft-get-folder)))
2015                                 "\\1"))
2016                   auto-save-file-name-transforms)))
2017     (setq buffer-file-name (buffer-name)
2018           wl-draft-parent-folder ""
2019           wl-draft-buffer-message-number number)
2020     (when wl-draft-write-file-function
2021       (add-hook 'local-write-file-hooks wl-draft-write-file-function))
2022     (wl-highlight-headers 'for-draft)
2023     (goto-char body-top)
2024     (run-hooks 'wl-draft-reedit-hook)
2025     (goto-char (point-max))
2026     buffer))
2027
2028 (defmacro wl-draft-body-goto-top ()
2029   (` (progn
2030        (goto-char (point-min))
2031        (if (re-search-forward mail-header-separator nil t)
2032            (forward-char 1)
2033          (goto-char (point-max))))))
2034
2035 (defmacro wl-draft-body-goto-bottom ()
2036   (` (goto-char (point-max))))
2037
2038 (defmacro wl-draft-config-body-goto-header ()
2039   (` (progn
2040        (goto-char (point-min))
2041        (if (re-search-forward mail-header-separator nil t)
2042            (beginning-of-line)
2043          (goto-char (point-max))))))
2044
2045 (defsubst wl-draft-config-sub-eval-insert (content &optional newline)
2046   (let (content-value)
2047     (when (and content
2048                (stringp (setq content-value (eval content))))
2049       (insert content-value)
2050       (if newline (insert "\n")))))
2051
2052 (defun wl-draft-config-sub-body (content)
2053   (wl-draft-body-goto-top)
2054   (delete-region (point) (point-max))
2055   (wl-draft-config-sub-eval-insert content))
2056
2057 (defun wl-draft-config-sub-top (content)
2058   (wl-draft-body-goto-top)
2059   (wl-draft-config-sub-eval-insert content))
2060
2061 (defun wl-draft-config-sub-bottom (content)
2062   (wl-draft-body-goto-bottom)
2063   (wl-draft-config-sub-eval-insert content))
2064
2065 (defun wl-draft-config-sub-header (content)
2066   (wl-draft-config-body-goto-header)
2067   (wl-draft-config-sub-eval-insert content 'newline))
2068
2069 (defun wl-draft-config-sub-header-top (content)
2070   (goto-char (point-min))
2071   (wl-draft-config-sub-eval-insert content 'newline))
2072
2073 (defun wl-draft-config-sub-part-top (content)
2074   (goto-char (mime-edit-content-beginning))
2075   (wl-draft-config-sub-eval-insert content 'newline))
2076
2077 (defun wl-draft-config-sub-part-bottom (content)
2078   (goto-char (mime-edit-content-end))
2079   (wl-draft-config-sub-eval-insert content 'newline))
2080
2081 (defsubst wl-draft-config-sub-file (content)
2082   (let ((coding-system-for-read wl-cs-autoconv)
2083         (file (expand-file-name (eval content))))
2084     (if (file-exists-p file)
2085         (insert-file-contents file)
2086       (error "%s: no exists file" file))))
2087
2088 (defun wl-draft-config-sub-body-file (content)
2089   (wl-draft-body-goto-top)
2090   (delete-region (point) (point-max))
2091   (wl-draft-config-sub-file content))
2092
2093 (defun wl-draft-config-sub-top-file (content)
2094   (wl-draft-body-goto-top)
2095   (wl-draft-config-sub-file content))
2096
2097 (defun wl-draft-config-sub-bottom-file (content)
2098   (wl-draft-body-goto-bottom)
2099   (wl-draft-config-sub-file content))
2100
2101 (defun wl-draft-config-sub-header-file (content)
2102   (wl-draft-config-body-goto-header)
2103   (wl-draft-config-sub-file content))
2104
2105 (defun wl-draft-config-sub-template (content)
2106   (setq wl-draft-config-variables
2107         (wl-template-insert (eval content))))
2108
2109 (defun wl-draft-config-sub-x-face (content)
2110   (if (and (string-match "\\.xbm\\(\\.gz\\)?$" content)
2111            (fboundp 'x-face-insert)) ; x-face.el is installed.
2112       (x-face-insert content)
2113     (wl-draft-replace-field "X-Face" (elmo-get-file-string content t) t)))
2114
2115 (defsubst wl-draft-config-sub-func (field content)
2116   (let (func)
2117     (if (setq func (assq field wl-draft-config-sub-func-alist))
2118         (let (wl-draft-config-variables)
2119           (funcall (cdr func) content)
2120           ;; for wl-draft-config-sub-template
2121           (cons t wl-draft-config-variables)))))
2122
2123 (defsubst wl-draft-config-exec-sub (clist)
2124   (let (config local-variables)
2125     (while clist
2126       (setq config (car clist))
2127       (cond
2128        ((functionp config)
2129         (funcall config))
2130        ((consp config)
2131         (let ((field (car config))
2132               (content (cdr config))
2133               ret-val)
2134           (cond
2135            ((stringp field)
2136             (wl-draft-replace-field field (eval content) t))
2137            ((setq ret-val (wl-draft-config-sub-func field content))
2138             (if (cdr ret-val) ;; for wl-draft-config-sub-template
2139                 (wl-append local-variables (cdr ret-val))))
2140            ((boundp field) ;; variable
2141             (make-local-variable field)
2142             (set field (eval content))
2143             (wl-append local-variables (list field)))
2144            (t
2145             (error "%s: not variable" field)))))
2146        (t
2147         (error "%s: not supported type" config)))
2148       (setq clist (cdr clist)))
2149     local-variables))
2150
2151 (defun wl-draft-prepared-config-exec (&optional config-alist reply-buf)
2152   "Change headers in draft preparation time."
2153   (interactive)
2154   (unless wl-draft-reedit
2155     (let ((config-alist
2156            (or config-alist
2157                (and (boundp 'wl-draft-prepared-config-alist)
2158                     wl-draft-prepared-config-alist)     ;; For compatible.
2159                wl-draft-config-alist)))
2160       (if config-alist
2161           (wl-draft-config-exec config-alist reply-buf)))))
2162
2163 (defun wl-draft-config-exec (&optional config-alist reply-buf)
2164   "Change headers according to the value of `wl-draft-config-alist'.
2165 Automatically applied in draft sending time."
2166   (interactive)
2167   (let ((case-fold-search t)
2168         (alist (or config-alist wl-draft-config-alist))
2169         (reply-buf (or reply-buf (and (buffer-live-p wl-draft-reply-buffer)
2170                                       wl-draft-reply-buffer)))
2171         (local-variables wl-draft-config-variables)
2172         key clist found)
2173     (when (and (or (interactive-p)
2174                    wl-draft-config-exec-flag)
2175                alist)
2176       (save-excursion
2177         (catch 'done
2178           (while alist
2179             (setq key (caar alist)
2180                   clist (cdar alist))
2181             (cond
2182              ((eq key 'reply)
2183               (when (and
2184                      reply-buf
2185                      (save-excursion
2186                        (set-buffer reply-buf)
2187                        (save-restriction
2188                          (std11-narrow-to-header)
2189                          (goto-char (point-min))
2190                          (re-search-forward (car clist) nil t))))
2191                 (wl-draft-config-exec-sub (cdr clist))
2192                 (setq found t)))
2193              ((stringp key)
2194               (when (save-restriction
2195                       (std11-narrow-to-header mail-header-separator)
2196                       (goto-char (point-min))
2197                       (re-search-forward key nil t))
2198                 (wl-append local-variables
2199                            (wl-draft-config-exec-sub clist))
2200                 (setq found t)))
2201              ((eval key)
2202               (wl-append local-variables
2203                          (wl-draft-config-exec-sub clist))
2204               (setq found t)))
2205             (if (and found wl-draft-config-matchone)
2206                 (throw 'done t))
2207             (setq alist (cdr alist)))))
2208       (if found
2209           (setq wl-draft-config-exec-flag nil))
2210       (run-hooks 'wl-draft-config-exec-hook)
2211       (put-text-property (point-min)(point-max) 'face nil)
2212       (wl-highlight-message (point-min)(point-max) t)
2213       (setq wl-draft-config-variables
2214             (elmo-uniq-list local-variables)))))
2215
2216 (defun wl-draft-replace-field (field content &optional add)
2217   (save-excursion
2218     (save-restriction
2219       (let ((case-fold-search t)
2220             (inhibit-read-only t) ;; added by teranisi.
2221             beg)
2222         (std11-narrow-to-header mail-header-separator)
2223         (goto-char (point-min))
2224         (if (re-search-forward (concat "^" (regexp-quote field) ":") nil t)
2225             (if content
2226                 ;; replace field
2227                 (progn
2228                   (setq beg (point))
2229                   (re-search-forward "^[^ \t]" nil 'move)
2230                   (beginning-of-line)
2231                   (skip-chars-backward "\n")
2232                   (delete-region beg (point))
2233                   (insert " " content))
2234               ;; delete field
2235               (save-excursion
2236                 (beginning-of-line)
2237                 (setq beg (point)))
2238               (re-search-forward "^[^ \t]" nil 'move)
2239               (beginning-of-line)
2240               (delete-region beg (point)))
2241           (when (and add content)
2242             ;; add field
2243             (goto-char (point-max))
2244             (insert (concat field ": " content "\n"))))))))
2245
2246 (defun wl-draft-config-info-operation (msg operation)
2247   (let* ((msgdb-dir (elmo-folder-msgdb-path (wl-draft-get-folder)))
2248          (filename
2249           (expand-file-name
2250            (format "%s-%d" wl-draft-config-save-filename msg)
2251            msgdb-dir))
2252          element alist variable)
2253     (cond
2254      ((eq operation 'save)
2255       (let ((variables (elmo-uniq-list wl-draft-config-variables)))
2256         (while (setq variable (pop variables))
2257           (when (boundp variable)
2258             (wl-append alist
2259                        (list (cons variable (eval variable))))))
2260         (elmo-object-save filename alist)))
2261      ((eq operation 'load)
2262       (setq alist (elmo-object-load filename))
2263       (while (setq element (pop alist))
2264         (set (make-local-variable (car element)) (cdr element))
2265         (wl-append wl-draft-config-variables (list (car element)))))
2266      ((eq operation 'delete)
2267       (if (file-exists-p filename)
2268           (delete-file filename))))))
2269
2270 (defun wl-draft-queue-info-operation (msg operation
2271                                           &optional add-sent-message-via)
2272   (let* ((msgdb-dir (elmo-folder-msgdb-path
2273                      (wl-folder-get-elmo-folder wl-queue-folder)))
2274          (filename
2275           (expand-file-name
2276            (format "%s-%d" wl-draft-queue-save-filename msg)
2277            msgdb-dir))
2278          element alist variable)
2279     (cond
2280      ((eq operation 'save)
2281       (let ((variables (elmo-uniq-list
2282                         (append wl-draft-queue-save-variables
2283                                 wl-draft-config-variables
2284                                 (list 'wl-draft-fcc-list)))))
2285         (if add-sent-message-via
2286             (progn
2287               (push 'wl-sent-message-queued variables)
2288               (push 'wl-sent-message-via variables)))
2289         (while (setq variable (pop variables))
2290           (when (boundp variable)
2291             (wl-append alist
2292                        (list (cons variable (eval variable))))))
2293         (elmo-object-save filename alist)))
2294      ((eq operation 'load)
2295       (setq alist (elmo-object-load filename))
2296       (while (setq element (pop alist))
2297         (set (make-local-variable (car element)) (cdr element))))
2298      ((eq operation 'get-sent-via)
2299       (setq alist (elmo-object-load filename))
2300       (cdr (assq 'wl-sent-message-via alist)))
2301      ((eq operation 'delete)
2302       (if (file-exists-p filename)
2303           (delete-file filename))))))
2304
2305 (defun wl-draft-queue-append (wl-sent-message-via)
2306   (if wl-draft-verbose-send
2307       (message "Queuing..."))
2308   (let ((send-buffer (current-buffer))
2309         (folder (wl-folder-get-elmo-folder wl-queue-folder))
2310         (message-id (std11-field-body "Message-ID")))
2311     (if (elmo-folder-append-buffer folder)
2312         (progn
2313           (wl-draft-queue-info-operation
2314            (car (elmo-folder-status folder))
2315            'save wl-sent-message-via)
2316           (wl-draft-write-sendlog 'ok 'queue nil wl-queue-folder message-id)
2317           (when wl-draft-verbose-send
2318             (setq wl-draft-verbose-msg "Queuing...")
2319             (message "Queuing...done")))
2320       (wl-draft-write-sendlog 'failed 'queue nil wl-queue-folder message-id)
2321       (error "Queuing failed"))))
2322
2323 (defun wl-draft-queue-flush ()
2324   "Flush draft queue."
2325   (interactive)
2326   (let* ((queue-folder (wl-folder-get-elmo-folder wl-queue-folder))
2327          (msgs2 (progn
2328                   (elmo-folder-open-internal queue-folder)
2329                   (elmo-folder-list-messages queue-folder)))
2330          (i 0)
2331          (performed 0)
2332          (wl-draft-queue-flushing t)
2333          msgs failure len buffer msgid sent-via)
2334     ;; get plugged send message
2335     (while msgs2
2336       (setq sent-via (wl-draft-queue-info-operation (car msgs2) 'get-sent-via))
2337       (catch 'found
2338         (while sent-via
2339           (when (and (eq (nth 1 (car sent-via)) 'unplugged)
2340                      (or (not (nth 2 (car sent-via)))
2341                          (elmo-plugged-p
2342                           (car (nth 2 (car sent-via)))
2343                           (cdr (nth 2 (car sent-via))))))
2344             (wl-append msgs (list (car msgs2)))
2345             (throw 'found t))
2346           (setq sent-via (cdr sent-via))))
2347       (setq msgs2 (cdr msgs2)))
2348     (when (> (setq len (length msgs)) 0)
2349       (if (elmo-y-or-n-p (format
2350                           "%d message(s) are in the sending queue.  Send now? "
2351                           len)
2352                          (not elmo-dop-flush-confirm) t)
2353           (progn
2354             (save-excursion
2355               (setq buffer (get-buffer-create " *wl-draft-queue-flush*"))
2356               (set-buffer buffer)
2357               (while msgs
2358                 ;; reset buffer local variables
2359                 (kill-all-local-variables)
2360                 (erase-buffer)
2361                 (setq i (+ 1 i)
2362                       failure nil)
2363                 (setq wl-sent-message-via nil)
2364                 (wl-draft-queue-info-operation (car msgs) 'load)
2365                 (elmo-message-fetch queue-folder
2366                                     (car msgs)
2367                                     (elmo-make-fetch-strategy 'entire)
2368                                     nil (current-buffer))
2369                 (condition-case err
2370                     (setq failure (funcall
2371                                    wl-draft-queue-flush-send-function
2372                                    (format "Sending (%d/%d)..." i len)))
2373 ;;;               (wl-draft-raw-send nil nil
2374 ;;;                                  (format "Sending (%d/%d)..." i len))
2375                   (error
2376                    (elmo-display-error err t)
2377                    (setq failure t))
2378                   (quit
2379                    (setq failure t)))
2380                 (if (eq wl-sent-message-modified 'requeue)
2381                     (progn
2382                       (elmo-folder-delete-messages
2383                        queue-folder (cons (car msgs) nil))
2384                       (wl-draft-queue-info-operation (car msgs) 'delete))
2385                   (unless failure
2386                     (elmo-folder-delete-messages
2387                      queue-folder (cons (car msgs) nil))
2388                     (wl-draft-queue-info-operation (car msgs) 'delete)
2389                     (setq performed (+ 1 performed))))
2390                 (setq msgs (cdr msgs)))
2391               (kill-buffer buffer)
2392               (message "%d message(s) are sent." performed)))
2393         (message "%d message(s) are remained to be sent." len))
2394       (elmo-folder-close queue-folder)
2395       len)))
2396
2397 (defun wl-jump-to-draft-buffer (&optional arg)
2398   "Jump to the draft if exists."
2399   (interactive "P")
2400   (if arg
2401       (wl-jump-to-draft-folder)
2402     (let ((draft-bufs (wl-collect-draft))
2403           buf)
2404       (cond
2405        ((null draft-bufs)
2406         (message "No draft buffer exist."))
2407        (t
2408         (setq draft-bufs
2409               (sort (mapcar 'buffer-name draft-bufs)
2410                     (function (lambda (a b)
2411                                 (not (string< a b))))))
2412         (if (setq buf (cdr (member (buffer-name)
2413                                    draft-bufs)))
2414             (setq buf (car buf))
2415           (setq buf (car draft-bufs)))
2416         (switch-to-buffer buf))))))
2417
2418 (defun wl-jump-to-draft-folder ()
2419   (let ((msgs (reverse (elmo-folder-list-messages (wl-draft-get-folder))))
2420         (mybuf (buffer-name))
2421         msg buf)
2422     (if (not msgs)
2423         (message "No draft message exist.")
2424       (if (string-match (concat "^" wl-draft-folder "/") mybuf)
2425           (setq msg (cadr (memq
2426                            (string-to-int (substring mybuf (match-end 0)))
2427                            msgs))))
2428       (or msg (setq msg (car msgs)))
2429       (if (setq buf (get-buffer (format "%s/%d" wl-draft-folder msg)))
2430           (switch-to-buffer buf)
2431         (wl-draft-reedit msg)))))
2432
2433 (defun wl-draft-highlight-and-recenter (&optional n)
2434   (interactive "P")
2435   (when wl-highlight-body-too
2436     (let ((modified (buffer-modified-p)))
2437       (unwind-protect
2438           (progn
2439             (put-text-property (point-min) (point-max) 'face nil)
2440             (wl-highlight-message (point-min) (point-max) t))
2441         (set-buffer-modified-p modified))))
2442   (static-when (featurep 'xemacs)
2443     ;; Cope with one of many XEmacs bugs that `recenter' takes
2444     ;; a long time if there are a lot of invisible text lines.
2445     (redraw-frame))
2446   (recenter n))
2447
2448 ;; insert element from history
2449 (defvar wl-draft-current-history-position nil)
2450 (defvar wl-draft-history-backup-word "")
2451
2452 (defun wl-draft-previous-history-element (n)
2453   (interactive "p")
2454   (let (bol history beg end prev new)
2455     (when (and (not (wl-draft-on-field-p))
2456                (< (point)
2457                   (save-excursion
2458                     (goto-char (point-min))
2459                     (search-forward (concat "\n" mail-header-separator "\n") nil 0)
2460                     (point)))
2461                (save-excursion
2462                  (beginning-of-line)
2463                  (while (and (looking-at "^[ \t]")
2464                              (not (= (point) (point-min))))
2465                    (forward-line -1))
2466                  (cond
2467                   ((looking-at wl-folder-complete-header-regexp)
2468                    (and (boundp 'wl-read-folder-history)
2469                         (setq history wl-read-folder-history)))
2470                   ;; ((looking-at wl-address-complete-header-regexp)
2471                   ;;  (setq history .....))
2472                   (t
2473                    nil)))
2474                (eolp))
2475       (setq bol (save-excursion (beginning-of-line) (point)))
2476       (cond ((and (or (eq last-command 'wl-draft-previous-history-element)
2477                       (eq last-command 'wl-draft-next-history-element))
2478                   wl-draft-current-history-position)
2479              (setq end (point))
2480              (or (search-backward-regexp ",[ \t]*\\(.*\\)" bol t)
2481                  (search-backward-regexp "^[ \t]\\(.*\\)" bol t)
2482                  (search-backward-regexp "^[^ \t]*: \\(.*\\)" bol t))
2483              (setq prev (match-string 1))
2484              (goto-char (match-beginning 1))
2485              (setq beg (point))
2486              (if (cond ((< n 0)
2487                         (>= (+ n wl-draft-current-history-position) 0))
2488                        ((> n 0)
2489                         (<= (+ n wl-draft-current-history-position)
2490                             (length history))))
2491                  (progn
2492                    (setq wl-draft-current-history-position
2493                          (+ n wl-draft-current-history-position))
2494                    (setq new
2495                          (nth wl-draft-current-history-position
2496                               (append (list wl-draft-history-backup-word)
2497                                       history)))
2498                    (delete-region beg end)
2499                    (insert new))
2500                (goto-char end)
2501                (cond ((< n 0)
2502                       (message "End of history; no next item"))
2503                      ((> n 0)
2504                       (message "Beginning of history; no preceding item")))))
2505             ((and (> n 0)
2506                   (save-excursion
2507                     (or (search-backward-regexp ",[ \t]*\\(.*\\)" bol t)
2508                         (search-backward-regexp "^[ \t]\\(.*\\)" bol t)
2509                         (search-backward-regexp "^[^ \t]*: \\(.*\\)" bol t)))
2510                   (car history))
2511              (setq wl-draft-current-history-position 1)
2512              (setq wl-draft-history-backup-word (match-string 1))
2513              (delete-region (match-beginning 1) (match-end 1))
2514              (insert (car history)))
2515             (t
2516              (setq wl-draft-current-history-position nil))))))
2517
2518 (defun wl-draft-next-history-element (n)
2519   (interactive "p")
2520   (wl-draft-previous-history-element (- n)))
2521
2522 ;;;; user-agent support by Sen Nagata
2523
2524 ;; this appears to be necessarily global...
2525 (defvar wl-user-agent-compose-p nil)
2526 (defvar wl-user-agent-headers-and-body-alist nil)
2527
2528 ;; this should be a generic function for mail-mode -- i wish there was
2529 ;; something like it in sendmail.el
2530 (defun wl-user-agent-insert-header (header-name header-value)
2531   "Insert HEADER-NAME w/ value HEADER-VALUE into a message."
2532   ;; it seems like overriding existing headers is acceptable -- should
2533   ;; we provide an option?
2534
2535   ;; plan was: unfold header (might be folded), remove existing value, insert
2536   ;;           new value
2537   ;; wl doesn't seem to fold header lines yet anyway :-)
2538
2539   (let ((kill-whole-line t)
2540         end-of-line)
2541     (mail-position-on-field (capitalize header-name))
2542     (setq end-of-line (point))
2543     (beginning-of-line)
2544     (re-search-forward ":" end-of-line)
2545     (insert (concat " " header-value "\n"))
2546     (kill-line)))
2547
2548 ;; this should be a generic function for mail-mode -- i wish there was
2549 ;; something like it in sendmail.el
2550 ;;
2551 ;; ** haven't dealt w/ case where the body is already set **
2552 (defun wl-user-agent-insert-body (body-text)
2553   "Insert a body of text, BODY-TEXT, into a message."
2554   ;; code defensively... :-P
2555   (goto-char (point-min))
2556   (search-forward mail-header-separator)
2557   (forward-line 1)
2558   (insert body-text))
2559
2560 ;;;###autoload
2561 (defun wl-user-agent-compose (&optional to subject other-headers continue
2562                                         switch-function yank-action
2563                                         send-actions)
2564   "Support the `compose-mail' interface for wl.
2565 Only support for TO, SUBJECT, and OTHER-HEADERS has been implemented.
2566 Support for CONTINUE, YANK-ACTION, and SEND-ACTIONS has not
2567 been implemented yet.  Partial support for SWITCH-FUNCTION now supported."
2568
2569   (unless (featurep 'wl)
2570     (require 'wl))
2571   (or switch-function
2572       (setq switch-function 'keep))
2573   ;; protect these -- to and subject get bound at some point, so it looks
2574   ;; to be necessary to protect the values used w/in
2575   (let ((wl-user-agent-headers-and-body-alist other-headers)
2576         (wl-draft-use-frame (eq switch-function 'switch-to-buffer-other-frame))
2577         (wl-draft-buffer-style switch-function))
2578     (if to
2579         (if (wl-string-match-assoc "to" wl-user-agent-headers-and-body-alist
2580                                    'ignore-case)
2581             (setcdr
2582              (wl-string-match-assoc "to" wl-user-agent-headers-and-body-alist
2583                                     'ignore-case)
2584              to)
2585           (setq wl-user-agent-headers-and-body-alist
2586                 (cons (cons "to" to)
2587                       wl-user-agent-headers-and-body-alist))))
2588     (if subject
2589         (if (wl-string-match-assoc "subject"
2590                                    wl-user-agent-headers-and-body-alist
2591                                    'ignore-case)
2592             (setcdr
2593              (wl-string-match-assoc "subject"
2594                                     wl-user-agent-headers-and-body-alist
2595                                     'ignore-case)
2596              subject)
2597           (setq wl-user-agent-headers-and-body-alist
2598                 (cons (cons "subject" subject)
2599                       wl-user-agent-headers-and-body-alist))))
2600     ;; i think this is what we want to use...
2601     (unwind-protect
2602         (progn
2603           ;; tell the hook-function to do its stuff
2604           (setq wl-user-agent-compose-p t)
2605           ;; because to get the hooks working, wl-draft has to think it has
2606           ;; been called interactively
2607           (call-interactively 'wl-draft))
2608       (setq wl-user-agent-compose-p nil))))
2609
2610 (defun wl-user-agent-compose-internal ()
2611   "Manipulate headers and/or a body of a draft message."
2612   ;; being called from wl-user-agent-compose?
2613   (if wl-user-agent-compose-p
2614       (progn
2615         ;; insert headers
2616         (let ((headers wl-user-agent-headers-and-body-alist)
2617               (case-fold-search t))
2618           (while headers
2619             ;; skip body
2620             (if (not (string-match "^body$" (car (car headers))))
2621                 (wl-user-agent-insert-header
2622                  (car (car headers)) (cdr (car headers)))
2623               t)
2624             (setq headers (cdr headers))))
2625         ;; highlight headers (from wl-draft in wl-draft.el)
2626         (wl-highlight-headers 'for-draft)
2627         ;; insert body
2628         (if (wl-string-match-assoc "body" wl-user-agent-headers-and-body-alist
2629                                    'ignore-case)
2630             (wl-user-agent-insert-body
2631              (cdr (wl-string-match-assoc
2632                    "body"
2633                    wl-user-agent-headers-and-body-alist 'ignore-case)))))
2634     t))
2635
2636 (require 'product)
2637 (product-provide (provide 'wl-draft) (require 'wl-version))
2638
2639 ;;; wl-draft.el ends here