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