* wl-draft.el (wl-draft-send-mail-with-smtp): Fix logic
[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) 535))
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
1557            (or
1558             (eq this-command 'wl-draft)
1559             (eq this-command 'wl-summary-write)
1560             (eq this-command 'wl-summary-write-current-folder)
1561             (eq this-command 'wl-folder-write-current-folder))
1562            parent-folder))
1563
1564     (unless (cdr (assq 'From header-alist))
1565       (setq header-alist
1566             (append (list (cons 'From wl-from)) header-alist)))
1567     (unless (cdr (assq 'To header-alist))
1568       (let ((to))
1569         (when (setq to (and
1570                         (interactive-p)
1571                         ""))
1572           (if (assq 'To header-alist)
1573               (setcdr (assq 'To header-alist) to)
1574             (setq header-alist
1575                   (append header-alist
1576                           (list (cons 'To to))))))))
1577     (unless (cdr (assq 'Subject header-alist))
1578       (if (assq 'Subject header-alist)
1579           (setcdr (assq 'Subject header-alist) "")
1580         (setq header-alist
1581               (append header-alist (list (cons 'Subject ""))))))
1582     (setq header-alist (append header-alist
1583                                (wl-draft-default-headers)
1584                                wl-draft-additional-header-alist
1585                                (if body (list "" (cons 'Body body)))))
1586     (wl-draft-create-contents header-alist)
1587     (if edit-again
1588         (wl-draft-decode-body
1589          content-type content-transfer-encoding))
1590     (wl-draft-insert-mail-header-separator)
1591     (wl-draft-prepare-edit)
1592     (if (interactive-p)
1593         (run-hooks 'wl-mail-setup-hook))
1594     (goto-char (point-min))
1595     (wl-user-agent-compose-internal) ;; user-agent
1596     (cond ((and
1597             (interactive-p)
1598             (string= (cdr (assq 'To header-alist)) ""))
1599            (mail-position-on-field "To"))
1600           (t
1601            (goto-char (point-max))))
1602     buf-name))
1603
1604 (defun wl-draft-create-buffer (&optional full parent-folder)
1605   (let* ((draft-folder (wl-folder-get-elmo-folder wl-draft-folder))
1606          (parent-folder (or parent-folder (wl-summary-buffer-folder-name)))
1607          (summary-buf (wl-summary-get-buffer parent-folder))
1608         buf-name file-name num change-major-mode-hook)
1609     (if (not (elmo-folder-message-file-p draft-folder))
1610         (error "%s folder cannot be used for draft folder" wl-draft-folder))
1611     (setq num (elmo-max-of-list
1612                (or (elmo-folder-list-messages draft-folder) '(0))))
1613     (setq num (+ 1 num))
1614     ;; To get unused buffer name.
1615     (while (get-buffer (concat wl-draft-folder "/" (int-to-string num)))
1616       (setq num (+ 1 num)))
1617     (setq buf-name (find-file-noselect
1618                     (setq file-name
1619                           (elmo-message-file-name
1620                            (wl-folder-get-elmo-folder wl-draft-folder)
1621                            num))))
1622     (if wl-draft-use-frame
1623         (switch-to-buffer-other-frame buf-name)
1624       (switch-to-buffer buf-name))
1625     (set-buffer buf-name)
1626     (if (not (string-match (regexp-quote wl-draft-folder)
1627                            (buffer-name)))
1628         (rename-buffer (concat wl-draft-folder "/" (int-to-string num))))
1629     (if (or (eq wl-draft-reply-buffer-style 'full)
1630             full)
1631         (delete-other-windows))
1632     (auto-save-mode -1)
1633     (wl-draft-mode)
1634     (make-local-variable 'truncate-partial-width-windows)
1635     (setq truncate-partial-width-windows nil)
1636     (setq truncate-lines wl-draft-truncate-lines)
1637     (setq wl-sent-message-via nil)
1638     (setq wl-sent-message-queued nil)
1639     (setq wl-draft-buffer-file-name file-name)
1640     (setq wl-draft-config-exec-flag t)
1641     (setq wl-draft-parent-folder (or parent-folder ""))
1642     (or (eq this-command 'wl-folder-write-current-folder)
1643         (setq wl-draft-buffer-cur-summary-buffer summary-buf))
1644     buf-name))
1645
1646 (defun wl-draft-create-contents (header-alist)
1647   "header-alist' sample
1648 '(function  ;; funcall
1649   string    ;; insert string
1650   (symbol . string)    ;;  insert symbol-value: string
1651   (symbol . function)  ;;  (funcall) and if it returns string,
1652                        ;;  insert symbol-value: string
1653   (symbol . nil)       ;;  do nothing
1654   nil                  ;;  do nothing
1655   )
1656 "
1657   (unless (eq major-mode 'wl-draft-mode)
1658     (error "wl-draft-create-header must be use in wl-draft-mode."))
1659   (let ((halist header-alist)
1660         field value)
1661     (while halist
1662       (cond
1663        ;; function
1664        ((functionp (car halist)) (funcall (car halist)))
1665        ;; string
1666        ((stringp (car halist)) (insert (car halist) "\n"))
1667        ;; cons
1668        ((consp (car halist))
1669         (setq field (car (car halist)))
1670         (setq value (cdr (car halist)))
1671         (cond
1672          ((symbolp field)
1673           (cond
1674            ((eq field 'Body) ; body
1675             (insert value))
1676            ((stringp value) (insert (symbol-name field) ": " value "\n"))
1677            ((functionp value)
1678             (let ((value-return (funcall value)))
1679               (when (stringp value-return)
1680                 (insert (symbol-name field) ": " value-return "\n"))))
1681            ((not value))
1682            (t
1683             (debug))))
1684          ;;
1685          ((not field))
1686          (t
1687           (debug))
1688          )))
1689       (setq halist (cdr halist)))))
1690
1691 (defun wl-draft-prepare-edit ()
1692   (unless (eq major-mode 'wl-draft-mode)
1693     (error "wl-draft-create-header must be use in wl-draft-mode."))
1694   (let (change-major-mode-hook)
1695     (wl-draft-editor-mode)
1696     (add-hook 'local-write-file-hooks 'wl-draft-save)
1697     (wl-draft-overload-functions)
1698     (wl-highlight-headers 'for-draft)
1699     (wl-draft-save)
1700     (clear-visited-file-modtime)))
1701
1702 (defun wl-draft-decode-header ()
1703   (save-excursion
1704     (std11-narrow-to-header)
1705     (wl-draft-decode-message-in-buffer)
1706     (widen)))
1707
1708 (defun wl-draft-decode-body (&optional content-type content-transfer-encoding)
1709   (let ((content-type
1710          (or content-type
1711                 (std11-field-body "content-type")))
1712         (content-transfer-encoding
1713          (or content-transfer-encoding
1714              (std11-field-body "content-transfer-encoding")))
1715         delimline)
1716     (save-excursion
1717       (std11-narrow-to-header)
1718       (wl-draft-delete-field "content-type")
1719       (wl-draft-delete-field "content-transfer-encoding")
1720       (goto-char (point-max))
1721       (setq delimline (point-marker))
1722       (widen)
1723       (narrow-to-region delimline (point-max))
1724       (goto-char (point-min))
1725       (when content-type
1726         (insert "Content-type: " content-type "\n"))
1727       (when content-transfer-encoding
1728         (insert "Content-Transfer-Encoding: " content-transfer-encoding "\n"))
1729       (wl-draft-decode-message-in-buffer)
1730       (goto-char (point-min))
1731       (unless (re-search-forward "^$" (point-at-eol) t)
1732         (insert "\n"))
1733       (widen)
1734       delimline)))
1735
1736 ;;; subroutine for wl-draft-create-contents
1737 ;;; must be used in wl-draft-mode
1738 (defun wl-draft-check-new-line ()
1739   (if (not (= (preceding-char) ?\n))
1740       (insert ?\n)))
1741
1742 (defsubst wl-draft-trim-ccs (cc)
1743   (let ((field
1744          (if (functionp cc)
1745              (funcall cc)
1746            cc)))
1747     (if (and field
1748              (null (and wl-draft-delete-myself-from-bcc-fcc
1749                         (elmo-list-member
1750                          (mapcar 'wl-address-header-extract-address
1751                                  (append
1752                                   (wl-parse-addresses (std11-field-body "To"))
1753                                   (wl-parse-addresses (std11-field-body "Cc"))))
1754                          (mapcar 'downcase wl-subscribed-mailing-list)))))
1755         field
1756       nil)))
1757
1758 (defsubst wl-draft-default-headers ()
1759   (list
1760    (cons 'Mail-Reply-To (and wl-insert-mail-reply-to
1761                              (wl-address-header-extract-address
1762                               wl-from)))
1763    (cons 'User-Agent wl-generate-mailer-string-function)
1764    (cons 'Reply-To mail-default-reply-to)
1765    (cons 'Bcc (function
1766                (lambda ()
1767                  (wl-draft-trim-ccs
1768                   (or wl-bcc (and mail-self-blind (user-login-name)))))))
1769    (cons 'Fcc (function
1770                (lambda ()
1771                  (wl-draft-trim-ccs wl-fcc))))
1772    (cons 'Organization wl-organization)
1773    (and wl-auto-insert-x-face
1774         (file-exists-p wl-x-face-file)
1775         'wl-draft-insert-x-face-field-here) ;; allow nil
1776    mail-default-headers
1777    ;; check \n at th end of line for `mail-default-headers'
1778    'wl-draft-check-new-line
1779    ))
1780
1781 (defun wl-draft-insert-mail-header-separator (&optional delimline)
1782   (save-excursion
1783     (if delimline
1784         (goto-char delimline)
1785       (goto-char (point-min))
1786       (if (search-forward "\n\n" nil t)
1787           (delete-backward-char 1)
1788         (goto-char (point-max))))
1789     (wl-draft-check-new-line)
1790     (put-text-property (point)
1791                        (progn
1792                          (insert mail-header-separator "\n")
1793                          (1- (point)))
1794                        'category 'mail-header-separator)))
1795
1796 ;;;;;;;;;;;;;;;;
1797
1798 (defun wl-draft-elmo-nntp-send ()
1799   (let ((elmo-nntp-post-pre-hook wl-news-send-pre-hook)
1800         (elmo-nntp-default-user
1801          (or wl-nntp-posting-user elmo-nntp-default-user))
1802         (elmo-nntp-default-server
1803          (or wl-nntp-posting-server elmo-nntp-default-server))
1804         (elmo-nntp-default-port
1805          (or wl-nntp-posting-port elmo-nntp-default-port))
1806         (elmo-nntp-default-stream-type
1807          (or wl-nntp-posting-stream-type elmo-nntp-default-stream-type))
1808         (elmo-nntp-default-function wl-nntp-posting-function)
1809         condition)
1810     (if (setq condition (cdr (elmo-string-matched-assoc
1811                               (std11-field-body "Newsgroups")
1812                               wl-nntp-posting-config-alist)))
1813         (if (stringp condition)
1814             (setq elmo-nntp-default-server condition)
1815           (while (car condition)
1816             (set (intern (format "elmo-nntp-default-%s"
1817                                  (symbol-name (caar condition))))
1818                  (cdar condition))
1819             (setq condition (cdr condition)))))
1820     (unless elmo-nntp-default-function
1821       (error "wl-draft-nntp-send: posting-function is nil."))
1822     (if (not (elmo-plugged-p elmo-nntp-default-server elmo-nntp-default-port))
1823         (wl-draft-set-sent-message 'news 'unplugged
1824                                    (cons elmo-nntp-default-server
1825                                          elmo-nntp-default-port))
1826       (funcall elmo-nntp-default-function
1827                elmo-nntp-default-server (current-buffer))
1828       (wl-draft-set-sent-message 'news 'sent)
1829       (wl-draft-write-sendlog 'ok 'nntp elmo-nntp-default-server
1830                               (std11-field-body "Newsgroups")
1831                               (std11-field-body "Message-ID")))))
1832
1833 (defun wl-draft-generate-clone-buffer (name &optional local-variables)
1834   "Generate clone of current buffer named NAME."
1835   (let ((editing-buffer (current-buffer)))
1836     (save-excursion
1837       (set-buffer (generate-new-buffer name))
1838       (erase-buffer)
1839       (wl-draft-mode)
1840       (wl-draft-editor-mode)
1841       (insert-buffer editing-buffer)
1842       (message "")
1843       (while local-variables
1844         (make-local-variable (car local-variables))
1845         (set (car local-variables)
1846              (save-excursion
1847                (set-buffer editing-buffer)
1848                (symbol-value (car local-variables))))
1849         (setq local-variables (cdr local-variables)))
1850       (current-buffer))))
1851
1852 (defun wl-draft-reedit (number)
1853   (let ((draft-folder (wl-folder-get-elmo-folder wl-draft-folder))
1854         (wl-draft-reedit t)
1855         buffer file-name change-major-mode-hook)
1856     (setq file-name (elmo-message-file-name draft-folder number))
1857     (unless (file-exists-p file-name)
1858       (error "File %s does not exist" file-name))
1859     (if (setq buffer (get-buffer
1860                       (concat wl-draft-folder "/"
1861                               (number-to-string number))))
1862         (progn
1863           (if wl-draft-use-frame
1864               (switch-to-buffer-other-frame buffer)
1865             (switch-to-buffer buffer))
1866           (set-buffer buffer))
1867       (setq buffer (get-buffer-create (number-to-string number)))
1868       (if wl-draft-use-frame
1869           (switch-to-buffer-other-frame buffer)
1870         (switch-to-buffer buffer))
1871       (set-buffer buffer)
1872       (insert-file-contents-as-binary file-name)
1873       (let((mime-edit-again-ignored-field-regexp
1874             "^\\(Content-.*\\|Mime-Version\\):"))
1875 ;       (wl-draft-decode-message-in-buffer))
1876         ;;;; From gnus-article-mime-edit-article-setup in T-gnus
1877         ;;;; XXX: it is semi issue, perhaps [wl:10790]
1878         (let ((ofn (symbol-function 'mime-edit-decode-single-part-in-buffer)))
1879           (fset 'mime-edit-decode-single-part-in-buffer
1880                 (lambda (&rest args)
1881                   (unless (let ((content-type (car args)))
1882                             (eq 'text (mime-content-type-primary-type
1883                                        content-type)))
1884                     (setcar (cdr args) 'not-decode-text))
1885                   (apply ofn args)))
1886           (unwind-protect
1887               (wl-draft-decode-message-in-buffer)
1888             (fset 'mime-edit-decode-single-part-in-buffer ofn))))
1889       (wl-draft-insert-mail-header-separator)
1890       (if (not (string-match (regexp-quote wl-draft-folder)
1891                              (buffer-name)))
1892           (rename-buffer (concat wl-draft-folder "/" (buffer-name))))
1893       (auto-save-mode -1)
1894       (wl-draft-mode)
1895       (setq buffer-file-name file-name)
1896       (make-local-variable 'truncate-partial-width-windows)
1897       (setq truncate-partial-width-windows nil)
1898       (setq truncate-lines wl-draft-truncate-lines)
1899       (setq wl-sent-message-via nil)
1900       (setq wl-sent-message-queued nil)
1901       (setq wl-draft-buffer-file-name file-name)
1902       (wl-draft-config-info-operation number 'load)
1903       (goto-char (point-min))
1904       (wl-draft-overload-functions)
1905       (wl-draft-editor-mode)
1906       (add-hook 'local-write-file-hooks 'wl-draft-save)
1907       (wl-highlight-headers 'for-draft)
1908       (run-hooks 'wl-draft-reedit-hook)
1909       (goto-char (point-max))
1910       buffer)))
1911
1912 (defmacro wl-draft-body-goto-top ()
1913   (` (progn
1914        (goto-char (point-min))
1915        (if (re-search-forward mail-header-separator nil t)
1916            (forward-char 1)
1917          (goto-char (point-max))))))
1918
1919 (defmacro wl-draft-body-goto-bottom ()
1920   (` (goto-char (point-max))))
1921
1922 (defmacro wl-draft-config-body-goto-header ()
1923   (` (progn
1924        (goto-char (point-min))
1925        (if (re-search-forward mail-header-separator nil t)
1926            (beginning-of-line)
1927          (goto-char (point-max))))))
1928
1929 (defsubst wl-draft-config-sub-eval-insert (content &optional newline)
1930   (let (content-value)
1931     (when (and content
1932                (stringp (setq content-value (eval content))))
1933       (insert content-value)
1934       (if newline (insert "\n")))))
1935
1936 (defun wl-draft-config-sub-body (content)
1937   (wl-draft-body-goto-top)
1938   (delete-region (point) (point-max))
1939   (wl-draft-config-sub-eval-insert content))
1940
1941 (defun wl-draft-config-sub-top (content)
1942   (wl-draft-body-goto-top)
1943   (wl-draft-config-sub-eval-insert content))
1944
1945 (defun wl-draft-config-sub-bottom (content)
1946   (wl-draft-body-goto-bottom)
1947   (wl-draft-config-sub-eval-insert content))
1948
1949 (defun wl-draft-config-sub-header (content)
1950   (wl-draft-config-body-goto-header)
1951   (wl-draft-config-sub-eval-insert content 'newline))
1952
1953 (defun wl-draft-config-sub-header-top (content)
1954   (goto-char (point-min))
1955   (wl-draft-config-sub-eval-insert content 'newline))
1956
1957 (defun wl-draft-config-sub-part-top (content)
1958   (goto-char (mime-edit-content-beginning))
1959   (wl-draft-config-sub-eval-insert content 'newline))
1960
1961 (defun wl-draft-config-sub-part-bottom (content)
1962   (goto-char (mime-edit-content-end))
1963   (wl-draft-config-sub-eval-insert content 'newline))
1964
1965 (defsubst wl-draft-config-sub-file (content)
1966   (let ((coding-system-for-read wl-cs-autoconv)
1967         (file (expand-file-name (eval content))))
1968     (if (file-exists-p file)
1969         (insert-file-contents file)
1970       (error "%s: no exists file" file))))
1971
1972 (defun wl-draft-config-sub-body-file (content)
1973   (wl-draft-body-goto-top)
1974   (delete-region (point) (point-max))
1975   (wl-draft-config-sub-file content))
1976
1977 (defun wl-draft-config-sub-top-file (content)
1978   (wl-draft-body-goto-top)
1979   (wl-draft-config-sub-file content))
1980
1981 (defun wl-draft-config-sub-bottom-file (content)
1982   (wl-draft-body-goto-bottom)
1983   (wl-draft-config-sub-file content))
1984
1985 (defun wl-draft-config-sub-header-file (content)
1986   (wl-draft-config-body-goto-header)
1987   (wl-draft-config-sub-file content))
1988
1989 (defun wl-draft-config-sub-template (content)
1990   (setq wl-draft-config-variables
1991         (wl-template-insert (eval content))))
1992
1993 (defun wl-draft-config-sub-x-face (content)
1994   (if (and (string-match "\\.xbm\\(\\.gz\\)?$" content)
1995            (fboundp 'x-face-insert)) ; x-face.el is installed.
1996       (x-face-insert content)
1997     (wl-draft-replace-field "X-Face" (elmo-get-file-string content t) t)))
1998
1999 (defsubst wl-draft-config-sub-func (field content)
2000   (let (func)
2001     (if (setq func (assq field wl-draft-config-sub-func-alist))
2002         (let (wl-draft-config-variables)
2003           (funcall (cdr func) content)
2004           ;; for wl-draft-config-sub-template
2005           (cons t wl-draft-config-variables)))))
2006
2007 (defsubst wl-draft-config-exec-sub (clist)
2008   (let (config local-variables)
2009     (while clist
2010       (setq config (car clist))
2011       (cond
2012        ((functionp config)
2013         (funcall config))
2014        ((consp config)
2015         (let ((field (car config))
2016               (content (cdr config))
2017               ret-val)
2018           (cond
2019            ((stringp field)
2020             (wl-draft-replace-field field (eval content) t))
2021            ((setq ret-val (wl-draft-config-sub-func field content))
2022             (if (cdr ret-val) ;; for wl-draft-config-sub-template
2023                 (wl-append local-variables (cdr ret-val))))
2024            ((boundp field) ;; variable
2025             (make-local-variable field)
2026             (set field (eval content))
2027             (wl-append local-variables (list field)))
2028            (t
2029             (error "%s: not variable" field)))))
2030        (t
2031         (error "%s: not supported type" config)))
2032       (setq clist (cdr clist)))
2033     local-variables))
2034
2035 (defun wl-draft-prepared-config-exec (&optional config-alist reply-buf)
2036   "Change headers in draft preparation time."
2037   (interactive)
2038   (unless wl-draft-reedit
2039     (let ((config-alist
2040            (or config-alist
2041                (and (boundp 'wl-draft-prepared-config-alist)
2042                     wl-draft-prepared-config-alist)     ;; For compatible.
2043                wl-draft-config-alist)))
2044       (if config-alist
2045           (wl-draft-config-exec config-alist reply-buf)))))
2046
2047 (defun wl-draft-config-exec (&optional config-alist reply-buf)
2048   "Change headers according to the value of `wl-draft-config-alist'.
2049 Automatically applied in draft sending time."
2050   (interactive)
2051   (let ((case-fold-search t)
2052         (alist (or config-alist wl-draft-config-alist))
2053         (reply-buf (or reply-buf (and (buffer-live-p wl-draft-reply-buffer)
2054                                       wl-draft-reply-buffer)))
2055         (local-variables wl-draft-config-variables)
2056         key clist found)
2057     (when (and (or (interactive-p)
2058                    wl-draft-config-exec-flag)
2059                alist)
2060       (save-excursion
2061         (catch 'done
2062           (while alist
2063             (setq key (caar alist)
2064                   clist (cdar alist))
2065             (cond
2066              ((eq key 'reply)
2067               (when (and
2068                      reply-buf
2069                      (save-excursion
2070                        (set-buffer reply-buf)
2071                        (save-restriction
2072                          (std11-narrow-to-header)
2073                          (goto-char (point-min))
2074                          (re-search-forward (car clist) nil t))))
2075                 (wl-draft-config-exec-sub (cdr clist))
2076                 (setq found t)))
2077              ((stringp key)
2078               (when (save-restriction
2079                       (std11-narrow-to-header mail-header-separator)
2080                       (goto-char (point-min))
2081                       (re-search-forward key nil t))
2082                 (wl-append local-variables
2083                            (wl-draft-config-exec-sub clist))
2084                 (setq found t)))
2085              ((eval key)
2086               (wl-append local-variables
2087                          (wl-draft-config-exec-sub clist))
2088               (setq found t)))
2089             (if (and found wl-draft-config-matchone)
2090                 (throw 'done t))
2091             (setq alist (cdr alist))))
2092         (if found
2093             (setq wl-draft-config-exec-flag nil))
2094         (run-hooks 'wl-draft-config-exec-hook)
2095         (put-text-property (point-min)(point-max) 'face nil)
2096         (wl-highlight-message (point-min)(point-max) t)
2097         (setq wl-draft-config-variables
2098               (elmo-uniq-list local-variables))))))
2099
2100 (defun wl-draft-replace-field (field content &optional add)
2101   (save-excursion
2102     (save-restriction
2103       (let ((case-fold-search t)
2104             (inhibit-read-only t) ;; added by teranisi.
2105             beg)
2106         (std11-narrow-to-header mail-header-separator)
2107         (goto-char (point-min))
2108         (if (re-search-forward (concat "^" (regexp-quote field) ":") nil t)
2109             (if content
2110                 ;; replace field
2111                 (progn
2112                   (setq beg (point))
2113                   (re-search-forward "^[^ \t]" nil 'move)
2114                   (beginning-of-line)
2115                   (skip-chars-backward "\n")
2116                   (delete-region beg (point))
2117                   (insert " " content))
2118               ;; delete field
2119               (save-excursion
2120                 (beginning-of-line)
2121                 (setq beg (point)))
2122               (re-search-forward "^[^ \t]" nil 'move)
2123               (beginning-of-line)
2124               (delete-region beg (point)))
2125           (when (and add content)
2126             ;; add field
2127             (goto-char (point-max))
2128             (insert (concat field ": " content "\n"))))))))
2129
2130 (defun wl-draft-config-info-operation (msg operation)
2131   (let* ((msgdb-dir (elmo-folder-msgdb-path (wl-folder-get-elmo-folder
2132                                              wl-draft-folder)))
2133          (filename
2134           (expand-file-name
2135            (format "%s-%d" wl-draft-config-save-filename msg)
2136            msgdb-dir))
2137          element alist variable)
2138     (cond
2139      ((eq operation 'save)
2140       (let ((variables (elmo-uniq-list wl-draft-config-variables)))
2141         (while (setq variable (pop variables))
2142           (when (boundp variable)
2143             (wl-append alist
2144                        (list (cons variable (eval variable))))))
2145         (elmo-object-save filename alist)))
2146      ((eq operation 'load)
2147       (setq alist (elmo-object-load filename))
2148       (while (setq element (pop alist))
2149         (set (make-local-variable (car element)) (cdr element))
2150         (wl-append wl-draft-config-variables (list (car element)))))
2151      ((eq operation 'delete)
2152       (if (file-exists-p filename)
2153           (delete-file filename))))))
2154
2155 (defun wl-draft-queue-info-operation (msg operation
2156                                           &optional add-sent-message-via)
2157   (let* ((msgdb-dir (elmo-folder-msgdb-path
2158                      (wl-folder-get-elmo-folder wl-queue-folder)))
2159          (filename
2160           (expand-file-name
2161            (format "%s-%d" wl-draft-queue-save-filename msg)
2162            msgdb-dir))
2163          element alist variable)
2164     (cond
2165      ((eq operation 'save)
2166       (let ((variables (elmo-uniq-list
2167                         (append wl-draft-queue-save-variables
2168                                 wl-draft-config-variables
2169                                 (list 'wl-draft-fcc-list)))))
2170         (if add-sent-message-via
2171             (progn
2172               (push 'wl-sent-message-queued variables)
2173               (push 'wl-sent-message-via variables)))
2174         (while (setq variable (pop variables))
2175           (when (boundp variable)
2176             (wl-append alist
2177                        (list (cons variable (eval variable))))))
2178         (elmo-object-save filename alist)))
2179      ((eq operation 'load)
2180       (setq alist (elmo-object-load filename))
2181       (while (setq element (pop alist))
2182         (set (make-local-variable (car element)) (cdr element))))
2183      ((eq operation 'get-sent-via)
2184       (setq alist (elmo-object-load filename))
2185       (cdr (assq 'wl-sent-message-via alist)))
2186      ((eq operation 'delete)
2187       (if (file-exists-p filename)
2188           (delete-file filename))))))
2189
2190 (defun wl-draft-queue-append (wl-sent-message-via)
2191   (if wl-draft-verbose-send
2192       (message "Queuing..."))
2193   (let ((send-buffer (current-buffer))
2194         (folder (wl-folder-get-elmo-folder wl-queue-folder))
2195         (message-id (std11-field-body "Message-ID")))
2196     (if (elmo-folder-append-buffer folder t)
2197         (progn
2198           (wl-draft-queue-info-operation
2199            (car (elmo-folder-status folder))
2200            'save wl-sent-message-via)
2201           (wl-draft-write-sendlog 'ok 'queue nil wl-queue-folder message-id)
2202           (when wl-draft-verbose-send
2203             (setq wl-draft-verbose-msg "Queuing...")
2204             (message "Queuing...done")))
2205       (wl-draft-write-sendlog 'failed 'queue nil wl-queue-folder message-id)
2206       (error "Queuing failed"))))
2207
2208 (defun wl-draft-queue-flush ()
2209   "Flush draft queue."
2210   (interactive)
2211   (let* ((queue-folder (wl-folder-get-elmo-folder wl-queue-folder))
2212          (msgs2 (progn
2213                   (elmo-folder-open-internal queue-folder)
2214                   (elmo-folder-list-messages queue-folder)))
2215          (i 0)
2216          (performed 0)
2217          (wl-draft-queue-flushing t)
2218          msgs failure len buffer msgid sent-via)
2219     ;; get plugged send message
2220     (while msgs2
2221       (setq sent-via (wl-draft-queue-info-operation (car msgs2) 'get-sent-via))
2222       (catch 'found
2223         (while sent-via
2224           (when (and (eq (nth 1 (car sent-via)) 'unplugged)
2225                      (or (not (nth 2 (car sent-via)))
2226                          (elmo-plugged-p
2227                           (car (nth 2 (car sent-via)))
2228                           (cdr (nth 2 (car sent-via))))))
2229             (wl-append msgs (list (car msgs2)))
2230             (throw 'found t))
2231           (setq sent-via (cdr sent-via))))
2232       (setq msgs2 (cdr msgs2)))
2233     (when (> (setq len (length msgs)) 0)
2234       (if (elmo-y-or-n-p (format
2235                           "%d message(s) are in the sending queue.  Send now? "
2236                           len)
2237                          (not elmo-dop-flush-confirm) t)
2238           (progn
2239             (save-excursion
2240               (setq buffer (get-buffer-create " *wl-draft-queue-flush*"))
2241               (set-buffer buffer)
2242               (while msgs
2243                 ;; reset buffer local variables
2244                 (kill-all-local-variables)
2245                 (erase-buffer)
2246                 (setq i (+ 1 i)
2247                       failure nil)
2248                 (setq wl-sent-message-via nil)
2249                 (wl-draft-queue-info-operation (car msgs) 'load)
2250                 (elmo-message-fetch queue-folder
2251                                     (car msgs)
2252                                     (elmo-make-fetch-strategy 'entire)
2253                                     nil (current-buffer))
2254                 (condition-case err
2255                     (setq failure (funcall
2256                                    wl-draft-queue-flush-send-function
2257                                    (format "Sending (%d/%d)..." i len)))
2258 ;;;               (wl-draft-raw-send nil nil
2259 ;;;                                  (format "Sending (%d/%d)..." i len))
2260                   (error
2261                    (elmo-display-error err t)
2262                    (setq failure t))
2263                   (quit
2264                    (setq failure t)))
2265                 (if (eq wl-sent-message-modified 'requeue)
2266                     (progn
2267                       (elmo-folder-delete-messages
2268                        queue-folder (cons (car msgs) nil))
2269                       (wl-draft-queue-info-operation (car msgs) 'delete))
2270                   (unless failure
2271                     (elmo-folder-delete-messages
2272                      queue-folder (cons (car msgs) nil))
2273                     (wl-draft-queue-info-operation (car msgs) 'delete)
2274                     (setq performed (+ 1 performed))))
2275                 (setq msgs (cdr msgs)))
2276               (kill-buffer buffer)
2277               (message "%d message(s) are sent." performed)))
2278         (message "%d message(s) are remained to be sent." len))
2279       (elmo-folder-close queue-folder)
2280       len)))
2281
2282 (defun wl-jump-to-draft-buffer (&optional arg)
2283   "Jump to the draft if exists."
2284   (interactive "P")
2285   (if arg
2286       (wl-jump-to-draft-folder)
2287     (let ((draft-bufs (wl-collect-draft))
2288           buf)
2289       (cond
2290        ((null draft-bufs)
2291         (message "No draft buffer exist."))
2292        (t
2293         (setq draft-bufs
2294               (sort (mapcar 'buffer-name draft-bufs)
2295                     (function (lambda (a b)
2296                                 (not (string< a b))))))
2297         (if (setq buf (cdr (member (buffer-name)
2298                                    draft-bufs)))
2299             (setq buf (car buf))
2300           (setq buf (car draft-bufs)))
2301         (switch-to-buffer buf))))))
2302
2303 (defun wl-jump-to-draft-folder ()
2304   (let ((msgs (reverse (elmo-folder-list-messages (wl-folder-get-elmo-folder
2305                                                    wl-draft-folder))))
2306         (mybuf (buffer-name))
2307         msg buf)
2308     (if (not msgs)
2309         (message "No draft message exist.")
2310       (if (string-match (concat "^" wl-draft-folder "/") mybuf)
2311           (setq msg (cadr (memq
2312                            (string-to-int (substring mybuf (match-end 0)))
2313                            msgs))))
2314       (or msg (setq msg (car msgs)))
2315       (if (setq buf (get-buffer (format "%s/%d" wl-draft-folder msg)))
2316           (switch-to-buffer buf)
2317         (wl-draft-reedit msg)))))
2318
2319 (defun wl-draft-highlight-and-recenter (&optional n)
2320   (interactive "P")
2321   (when wl-highlight-body-too
2322     (let ((modified (buffer-modified-p)))
2323       (unwind-protect
2324           (progn
2325             (put-text-property (point-min) (point-max) 'face nil)
2326             (wl-highlight-message (point-min) (point-max) t))
2327         (set-buffer-modified-p modified))))
2328   (recenter n))
2329
2330 ;;;; user-agent support by Sen Nagata
2331
2332 ;; this appears to be necessarily global...
2333 (defvar wl-user-agent-compose-p nil)
2334 (defvar wl-user-agent-headers-and-body-alist nil)
2335
2336 ;; this should be a generic function for mail-mode -- i wish there was
2337 ;; something like it in sendmail.el
2338 (defun wl-user-agent-insert-header (header-name header-value)
2339   "Insert HEADER-NAME w/ value HEADER-VALUE into a message."
2340   ;; it seems like overriding existing headers is acceptable -- should
2341   ;; we provide an option?
2342
2343   ;; plan was: unfold header (might be folded), remove existing value, insert
2344   ;;           new value
2345   ;; wl doesn't seem to fold header lines yet anyway :-)
2346
2347   (let ((kill-whole-line t)
2348         end-of-line)
2349     (mail-position-on-field (capitalize header-name))
2350     (setq end-of-line (point))
2351     (beginning-of-line)
2352     (re-search-forward ":" end-of-line)
2353     (insert (concat " " header-value "\n"))
2354     (kill-line)))
2355
2356 ;; this should be a generic function for mail-mode -- i wish there was
2357 ;; something like it in sendmail.el
2358 ;;
2359 ;; ** haven't dealt w/ case where the body is already set **
2360 (defun wl-user-agent-insert-body (body-text)
2361   "Insert a body of text, BODY-TEXT, into a message."
2362   ;; code defensively... :-P
2363   (goto-char (point-min))
2364   (search-forward mail-header-separator)
2365   (forward-line 1)
2366   (insert body-text))
2367
2368 ;;;###autoload
2369 (defun wl-user-agent-compose (&optional to subject other-headers continue
2370                                         switch-function yank-action
2371                                         send-actions)
2372   "Support the `compose-mail' interface for wl.
2373 Only support for TO, SUBJECT, and OTHER-HEADERS has been implemented.
2374 Support for CONTINUE, YANK-ACTION, and SEND-ACTIONS has not
2375 been implemented yet.  Partial support for SWITCH-FUNCTION now supported."
2376
2377   (unless (featurep 'wl)
2378     (require 'wl))
2379   ;; protect these -- to and subject get bound at some point, so it looks
2380   ;; to be necessary to protect the values used w/in
2381   (let ((wl-user-agent-headers-and-body-alist other-headers)
2382         (wl-draft-use-frame (eq switch-function 'switch-to-buffer-other-frame))
2383         (wl-draft-reply-buffer-style 'split))
2384     (when (eq switch-function 'switch-to-buffer-other-window)
2385       (when (one-window-p t)
2386         (if (window-minibuffer-p) (other-window 1))
2387         (split-window))
2388       (other-window 1))
2389     (if to
2390         (if (wl-string-match-assoc "to" wl-user-agent-headers-and-body-alist
2391                                    'ignore-case)
2392             (setcdr
2393              (wl-string-match-assoc "to" wl-user-agent-headers-and-body-alist
2394                                     'ignore-case)
2395              to)
2396           (setq wl-user-agent-headers-and-body-alist
2397                 (cons (cons "to" to)
2398                       wl-user-agent-headers-and-body-alist))))
2399     (if subject
2400         (if (wl-string-match-assoc "subject"
2401                                    wl-user-agent-headers-and-body-alist
2402                                    'ignore-case)
2403             (setcdr
2404              (wl-string-match-assoc "subject"
2405                                     wl-user-agent-headers-and-body-alist
2406                                     'ignore-case)
2407              subject)
2408           (setq wl-user-agent-headers-and-body-alist
2409                 (cons (cons "subject" subject)
2410                       wl-user-agent-headers-and-body-alist))))
2411     ;; i think this is what we want to use...
2412     (unwind-protect
2413         (progn
2414           ;; tell the hook-function to do its stuff
2415           (setq wl-user-agent-compose-p t)
2416           ;; because to get the hooks working, wl-draft has to think it has
2417           ;; been called interactively
2418           (call-interactively 'wl-draft))
2419       (setq wl-user-agent-compose-p nil))))
2420
2421 (defun wl-user-agent-compose-internal ()
2422   "Manipulate headers and/or a body of a draft message."
2423   ;; being called from wl-user-agent-compose?
2424   (if wl-user-agent-compose-p
2425       (progn
2426         ;; insert headers
2427         (let ((headers wl-user-agent-headers-and-body-alist)
2428               (case-fold-search t))
2429           (while headers
2430             ;; skip body
2431             (if (not (string-match "^body$" (car (car headers))))
2432                 (wl-user-agent-insert-header
2433                  (car (car headers)) (cdr (car headers)))
2434               t)
2435             (setq headers (cdr headers))))
2436         ;; highlight headers (from wl-draft in wl-draft.el)
2437         (wl-highlight-headers 'for-draft)
2438         ;; insert body
2439         (if (wl-string-match-assoc "body" wl-user-agent-headers-and-body-alist
2440                                    'ignore-case)
2441             (wl-user-agent-insert-body
2442              (cdr (wl-string-match-assoc
2443                    "body"
2444                    wl-user-agent-headers-and-body-alist 'ignore-case)))))
2445     t))
2446
2447 (require 'product)
2448 (product-provide (provide 'wl-draft) (require 'wl-version))
2449
2450 ;;; wl-draft.el ends here