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