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