1 ;;; wl-draft.el -- Message draft mode for Wanderlust.
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>
6 ;; Author: Yuuichi Teranishi <teranisi@gohome.org>
7 ;; Masahiro MURATA <muse@ba2.so-net.ne.jp>
8 ;; Keywords: mail, net news
10 ;; This file is part of Wanderlust (Yet Another Message Interface on Emacsen).
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)
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.
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.
35 (require 'wl-template)
37 (condition-case nil (require 'timezone) (error nil))
41 (defvar x-face-add-x-face-version-header)
42 (defvar mail-reply-buffer)
43 (defvar mail-from-style)
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))
52 (defvar wl-draft-buf-name "Draft")
53 (defvar wl-caesar-region-func nil)
54 (defvar wl-draft-cite-func 'wl-default-draft-cite)
55 (defvar wl-draft-buffer-file-name nil)
56 (defvar wl-draft-field-completion-list nil)
57 (defvar wl-draft-verbose-send t)
58 (defvar wl-draft-verbose-msg nil)
59 (defvar wl-draft-queue-flushing nil)
60 (defvar wl-draft-config-variables nil)
61 (defvar wl-draft-config-exec-flag t)
62 (defvar wl-draft-buffer-cur-summary-buffer nil)
63 (defvar wl-draft-clone-local-variable-regexp "^\\(wl\\|mime\\)")
64 (defvar wl-draft-sendlog-filename "sendlog")
65 (defvar wl-draft-queue-save-filename "qinfo")
66 (defvar wl-draft-config-save-filename "config")
67 (defvar wl-draft-queue-flush-send-func 'wl-draft-dispatch-message)
68 (defvar wl-sent-message-via nil)
69 (defvar wl-sent-message-modified nil)
70 (defvar wl-draft-fcc-list nil)
71 (defvar wl-draft-reedit nil)
72 (defvar wl-draft-reply-buffer nil)
73 (defvar wl-draft-forward nil)
75 (defvar wl-draft-config-sub-func-alist
76 '((body . wl-draft-config-sub-body)
77 (top . wl-draft-config-sub-top)
78 (bottom . wl-draft-config-sub-bottom)
79 (header . wl-draft-config-sub-header)
80 (body-file . wl-draft-config-sub-body-file)
81 (top-file . wl-draft-config-sub-top-file)
82 (bottom-file . wl-draft-config-sub-bottom-file)
83 (header-file . wl-draft-config-sub-header-file)
84 (template . wl-draft-config-sub-template)
85 (x-face . wl-draft-config-sub-x-face)))
87 (make-variable-buffer-local 'wl-draft-buffer-file-name)
88 (make-variable-buffer-local 'wl-draft-buffer-cur-summary-buffer)
89 (make-variable-buffer-local 'wl-draft-config-variables)
90 (make-variable-buffer-local 'wl-draft-config-exec-flag)
91 (make-variable-buffer-local 'wl-sent-message-via)
92 (make-variable-buffer-local 'wl-draft-fcc-list)
93 (make-variable-buffer-local 'wl-draft-reply-buffer)
95 (defmacro wl-smtp-extension-bind (&rest body)
96 (` (let* ((smtp-sasl-mechanisms
97 (if wl-smtp-authenticate-type
99 (if (listp wl-smtp-authenticate-type)
100 wl-smtp-authenticate-type
101 (list wl-smtp-authenticate-type)))))
102 (smtp-use-sasl (and smtp-sasl-mechanisms t))
103 (smtp-use-starttls wl-smtp-connection-type)
104 smtp-sasl-user-name smtp-sasl-properties sasl-read-passphrase)
105 (if (and (string= (car smtp-sasl-mechanisms) "DIGEST-MD5")
107 (string-match "^\\([^@]*\\)@\\([^@]*\\)"
108 wl-smtp-posting-user))
109 (setq smtp-sasl-user-name (match-string 1 wl-smtp-posting-user)
110 smtp-sasl-properties (list 'realm
111 (match-string 2 wl-smtp-posting-user)))
112 (setq smtp-sasl-user-name wl-smtp-posting-user
113 smtp-sasl-properties nil))
114 (setq sasl-read-passphrase
123 (defun wl-draft-insert-date-field ()
125 (insert "Date: " (wl-make-date-string) "\n"))
127 (defun wl-draft-insert-from-field ()
129 ;; Put the "From:" field in unless for some odd reason
130 ;; they put one in themselves.
131 (let* ((login (or user-mail-address (user-login-name)))
132 (fullname (user-full-name)))
133 (cond ((eq mail-from-style 'angles)
134 (insert "From: " fullname)
135 (let ((fullname-start (+ (point-min) (length "From: ")))
136 (fullname-end (point-marker)))
137 (goto-char fullname-start)
138 ;; Look for a character that cannot appear unquoted
139 ;; according to RFC 822.
140 (if (re-search-forward "[^- !#-'*+/-9=?A-Z^-~]"
143 ;; Quote fullname, escaping specials.
144 (goto-char fullname-start)
146 (while (re-search-forward "[\"\\]"
148 (replace-match "\\\\\\&" t))
150 (insert " <" login ">\n"))
151 ((eq mail-from-style 'parens)
152 (insert "From: " login " (")
153 (let ((fullname-start (point)))
155 (let ((fullname-end (point-marker)))
156 (goto-char fullname-start)
157 ;; RFC 822 says \ and nonmatching parentheses
158 ;; must be escaped in comments.
159 ;; Escape every instance of ()\ ...
160 (while (re-search-forward "[()\\]" fullname-end 1)
161 (replace-match "\\\\\\&" t))
162 ;; ... then undo escaping of matching parentheses,
163 ;; including matching nested parentheses.
164 (goto-char fullname-start)
165 (while (re-search-forward
166 "\\(\\=\\|[^\\]\\(\\\\\\\\\\)*\\)\\\\(\\(\\([^\\]\\|\\\\\\\\\\)*\\)\\\\)"
168 (replace-match "\\1(\\3)" t)
169 (goto-char fullname-start))))
171 ((not mail-from-style)
172 (insert "From: " login "\n")))))
174 (defun wl-draft-insert-x-face-field ()
175 "Insert X-Face header."
177 (unless (file-exists-p wl-x-face-file)
178 (error "File %s does not exist" wl-x-face-file))
179 (beginning-of-buffer)
180 (search-forward mail-header-separator nil t)
182 (wl-draft-insert-x-face-field-here)
183 (run-hooks 'wl-draft-insert-x-face-field-hook)) ; highlight it if you want.
185 (defun wl-draft-insert-x-face-field-here ()
186 "Insert X-Face field at point."
187 (let ((x-face-string (elmo-get-file-string wl-x-face-file)))
188 (when (string-match "^[ \t]*" x-face-string)
189 (setq x-face-string (substring x-face-string (match-end 0))))
190 (insert "X-Face: " x-face-string))
191 (when (not (= (preceding-char) ?\n)) ; for chomped (choped) x-face-string
193 ;; Insert X-Face-Version: field
194 (when (and (fboundp 'x-face-insert-version-header)
195 (boundp 'x-face-add-x-face-version-header)
196 x-face-add-x-face-version-header)
197 (x-face-insert-version-header)))
199 (defun wl-draft-setup ()
200 (let ((field wl-draft-fields)
203 (setq ret-val (append ret-val
204 (list (cons (concat (car field) " ")
205 (concat (car field) " ")))))
206 (setq field (cdr field)))
207 (setq wl-draft-field-completion-list ret-val)))
209 (defun wl-draft-make-mail-followup-to (recipients)
210 (if (elmo-list-member
211 (or wl-user-mail-address-list
212 (list (wl-address-header-extract-address wl-from)))
214 (let ((rlist (elmo-list-delete
215 (or wl-user-mail-address-list
216 (list (wl-address-header-extract-address wl-from)))
217 (copy-sequence recipients))))
218 (if (elmo-list-member rlist (mapcar 'downcase
219 wl-subscribed-mailing-list))
221 (append rlist (list (wl-address-header-extract-address
225 (defun wl-draft-delete-myself-from-cc (to cc)
226 (let ((myself (or wl-user-mail-address-list
227 (list (wl-address-header-extract-address wl-from)))))
228 (cond (wl-draft-always-delete-myself ; always-delete option
229 (elmo-list-delete myself cc))
230 ((elmo-list-member (append to cc) ; subscribed mailing-list
231 (mapcar 'downcase wl-subscribed-mailing-list))
232 (elmo-list-delete myself cc))
235 (defun wl-draft-forward (original-subject summary-buf)
237 (with-current-buffer (wl-message-get-original-buffer)
238 (setq references (nconc
239 (std11-field-bodies '("References" "In-Reply-To"))
240 (list (std11-field-body "Message-Id"))))
241 (setq references (delq nil references)
242 references (mapconcat 'identity references " ")
243 references (wl-draft-parse-msg-id-list-string references)
244 references (wl-delete-duplicates references)
245 references (when references
246 (mapconcat 'identity references "\n\t"))))
247 (wl-draft "" (concat "Forward: " original-subject)
248 nil nil references nil nil nil nil nil nil summary-buf))
249 (goto-char (point-max))
250 (wl-draft-insert-message)
251 (mail-position-on-field "To"))
253 (defun wl-draft-strip-subject-re (subject)
254 "Remove \"Re:\" from subject lines. Shamelessly copied from Gnus"
255 (if (string-match wl-subject-prefix-regexp subject)
256 (substring subject (match-end 0))
259 (defun wl-draft-reply-list-symbol (with-arg)
260 "Return symbol `wl-draft-reply-*-argument-list' match condition.
261 Check WITH-ARG and From: field."
262 (if (wl-address-user-mail-address-p (or (elmo-field-body "From") ""))
264 'wl-draft-reply-myself-with-argument-list
265 'wl-draft-reply-myself-without-argument-list)
267 'wl-draft-reply-with-argument-list
268 'wl-draft-reply-without-argument-list)))
270 (defun wl-draft-reply (buf with-arg summary-buf)
271 "Reply to BUF buffer message.
272 Reply to author if WITH-ARG is non-nil."
275 to mail-followup-to cc subject in-reply-to references newsgroups
276 from to-alist cc-alist decoder)
278 (setq r-list (symbol-value (wl-draft-reply-list-symbol with-arg)))
281 (when (let ((condition (car (car r-list))))
282 (cond ((stringp condition)
283 (std11-field-body condition))
287 (if (not (std11-field-body (car condition)))
289 (setq condition (cdr condition)))
292 (funcall condition))))
293 (let ((r-to-list (nth 0 (cdr (car r-list))))
294 (r-cc-list (nth 1 (cdr (car r-list))))
295 (r-ng-list (nth 2 (cdr (car r-list)))))
296 (when (and (member "Followup-To" r-ng-list)
297 (string= (std11-field-body "Followup-To") "poster"))
298 (setq r-to-list (cons "From" r-to-list))
299 (setq r-ng-list (delete "Followup-To" (copy-sequence r-ng-list))))
300 (setq to (wl-concat-list (cons to
301 (elmo-multiple-fields-body-list
304 (setq cc (wl-concat-list (cons cc
305 (elmo-multiple-fields-body-list
308 (setq newsgroups (wl-concat-list (cons newsgroups
313 (setq r-list (cdr r-list)))
314 (error "No match field: check your `%s'"
315 (symbol-name (wl-draft-reply-list-symbol with-arg))))
316 (setq subject (std11-field-body "Subject"))
317 (setq to (wl-parse-addresses to)
318 cc (wl-parse-addresses cc))
319 (with-temp-buffer ; to keep raw buffer unibyte.
320 (elmo-set-buffer-multibyte default-enable-multibyte-characters)
321 (setq decoder (mime-find-field-decoder 'Subject 'plain))
322 (setq subject (if (and subject decoder)
323 (funcall decoder subject) subject))
327 (setq decoder (mime-find-field-decoder 'To 'plain))
328 (cons (nth 1 (std11-extract-address-components addr))
329 (if decoder (funcall decoder addr) addr)))
334 (setq decoder (mime-find-field-decoder 'Cc 'plain))
335 (cons (nth 1 (std11-extract-address-components addr))
336 (if decoder (funcall decoder addr) addr)))
338 (and wl-reply-subject-prefix
339 (setq subject (concat wl-reply-subject-prefix
340 (wl-draft-strip-subject-re
342 (setq in-reply-to (std11-field-body "Message-Id"))
343 (setq references (nconc
344 (std11-field-bodies '("References" "In-Reply-To"))
346 (setq to (delq nil (mapcar 'car to-alist)))
347 (setq cc (delq nil (mapcar 'car cc-alist)))
348 ;; if subscribed mailing list is contained in cc or to
349 ;; and myself is contained in cc,
350 ;; delete myself from cc.
351 (setq cc (wl-draft-delete-myself-from-cc to cc))
352 (when wl-insert-mail-followup-to
353 (setq mail-followup-to
354 (wl-draft-make-mail-followup-to (append to cc)))
355 (setq mail-followup-to (wl-delete-duplicates mail-followup-to nil t)))
356 (setq newsgroups (wl-parse newsgroups
357 "[ \t\f\r\n,]*\\([^ \t\f\r\n,]+\\)")
358 newsgroups (wl-delete-duplicates newsgroups)
359 newsgroups (if newsgroups (mapconcat 'identity newsgroups ",")))
360 (setq to (wl-delete-duplicates to nil t))
361 (setq cc (wl-delete-duplicates
362 (append (wl-delete-duplicates cc nil t)
363 to (copy-sequence to))
365 (and to (setq to (mapconcat
367 (if wl-draft-reply-use-address-with-full-name
368 (or (cdr (assoc addr to-alist)) addr)
371 (and cc (setq cc (mapconcat
373 (if wl-draft-reply-use-address-with-full-name
374 (or (cdr (assoc addr cc-alist)) addr)
377 (and mail-followup-to
378 (setq mail-followup-to
381 (if wl-draft-reply-use-address-with-full-name
382 (or (cdr (assoc addr (append to-alist cc-alist))) addr)
384 mail-followup-to ",\n\t")))
385 (and (null to) (setq to cc cc nil))
386 (setq references (delq nil references)
387 references (mapconcat 'identity references " ")
388 references (wl-draft-parse-msg-id-list-string references)
389 references (wl-delete-duplicates references)
390 references (if references
391 (mapconcat 'identity references "\n\t")))
393 to subject in-reply-to cc references newsgroups mail-followup-to
394 nil nil nil nil summary-buf)
395 (setq wl-draft-reply-buffer buf))
396 (run-hooks 'wl-reply-hook))
398 (defun wl-draft-add-references ()
399 (let* ((mes-id (save-excursion
400 (set-buffer mail-reply-buffer)
401 (std11-field-body "message-id")))
402 (ref (std11-field-body "References"))
403 (ref-list nil) (st nil))
404 (when (and mes-id ref)
405 (while (string-match "<[^>]+>" ref st)
407 (cons (substring ref (match-beginning 0) (setq st (match-end 0)))
410 (member mes-id ref-list))
414 (when (mail-position-on-field "References")
416 (while (looking-at "^[ \t]")
418 (setq mes-id (concat "\t" mes-id "\n")))
422 (defun wl-draft-yank-from-mail-reply-buffer (decode-it
423 &optional ignored-fields)
426 (narrow-to-region (point)(point))
429 (set-buffer mail-reply-buffer)
431 (decode-mime-charset-region (point-min) (point-max)
433 (buffer-substring-no-properties
434 (point-min) (point-max))))
436 (goto-char (point-min))
437 (wl-draft-delete-fields ignored-fields))
438 (goto-char (point-max))
440 (goto-char (point-min)))
442 (cond (mail-citation-hook (run-hooks 'mail-citation-hook))
443 (mail-yank-hooks (run-hooks 'mail-yank-hooks))
444 (t (and wl-draft-cite-func
445 (funcall wl-draft-cite-func)))) ; default cite
446 (run-hooks 'wl-draft-cited-hook)
447 (when (and wl-draft-add-references
448 (wl-draft-add-references))
449 (wl-highlight-headers 'for-draft)) ; highlight when added References:
450 (when wl-highlight-body-too
451 (wl-highlight-body-region beg (point-max)))))
453 (defun wl-draft-confirm ()
454 "Confirm send message."
457 (cond ((and (wl-message-mail-p) (wl-message-news-p))
458 "Send current draft as Mail and News? ")
459 ((wl-message-mail-p) "Send current draft as Mail? ")
460 ((wl-message-news-p) "Send current draft as News? "))))
462 (defun wl-message-field-exists-p (field)
463 "If FIELD exist and FIELD value is not empty, return non-nil."
464 (let ((value (std11-field-body field)))
466 (not (string= value "")))))
468 (defun wl-message-news-p ()
469 "If exist valid Newsgroups field, return non-nil."
470 (std11-field-body "Newsgroups"))
472 (defun wl-message-mail-p ()
473 "If exist To, Cc or Bcc field, return non-nil."
474 (or (wl-message-field-exists-p "To")
475 (wl-message-field-exists-p "Cc")
476 (wl-message-field-exists-p "Bcc")
477 ;;; This may be needed..
478 ;;; (wl-message-field-exists-p "Fcc")
481 (defun wl-draft-open-file (&optional file)
482 "Open FILE for edit."
484 ;;;(interactive "*fFile to edit: ")
485 (wl-draft-edit-string (elmo-get-file-string
487 (read-file-name "File to edit: "
488 (or wl-tmp-dir "~/"))))))
490 (defun wl-draft-edit-string (string)
491 (let ((cur-buf (current-buffer))
492 (tmp-buf (get-buffer-create " *wl-draft-edit-string*"))
493 to subject in-reply-to cc references newsgroups mail-followup-to
494 content-type content-transfer-encoding from
495 body-beg buffer-read-only)
499 (setq to (std11-field-body "To"))
502 (decode-mime-charset-string
505 (setq subject (std11-field-body "Subject"))
506 (setq subject (and subject
508 (decode-mime-charset-string
511 (setq from (std11-field-body "From")
514 (decode-mime-charset-string
517 (setq in-reply-to (std11-field-body "In-Reply-To"))
518 (setq cc (std11-field-body "Cc"))
521 (decode-mime-charset-string
524 (setq references (std11-field-body "References"))
525 (setq newsgroups (std11-field-body "Newsgroups"))
526 (setq mail-followup-to (std11-field-body "Mail-Followup-To"))
527 (setq content-type (std11-field-body "Content-Type"))
528 (setq content-transfer-encoding (std11-field-body "Content-Transfer-Encoding"))
529 (goto-char (point-min))
530 (or (re-search-forward "\n\n" nil t)
531 (search-forward (concat mail-header-separator "\n") nil t))
534 (wl-draft to subject in-reply-to cc references newsgroups
536 content-type content-transfer-encoding
537 (buffer-substring (point) (point-max))
539 (if (member (nth 1 (std11-extract-address-components from))
540 wl-user-mail-address-list)
542 (and to (mail-position-on-field "To"))
543 (delete-other-windows)
544 (kill-buffer tmp-buf)))
545 (setq buffer-read-only nil) ;;??
546 (run-hooks 'wl-draft-reedit-hook))
548 (defun wl-draft-insert-current-message (dummy)
550 (let ((mail-reply-buffer (wl-message-get-original-buffer))
551 mail-citation-hook mail-yank-hooks
552 wl-draft-add-references wl-draft-cite-func)
554 (with-current-buffer mail-reply-buffer
556 (error "No current message")
557 (wl-draft-yank-from-mail-reply-buffer nil
558 wl-ignored-forwarded-headers))))
560 (defun wl-draft-insert-get-message (dummy)
561 (let ((fld (completing-read
563 (if (memq 'read-folder wl-use-folder-petname)
564 (wl-folder-get-entity-with-petname)
565 wl-folder-entity-hashtb)
566 nil nil wl-default-spec
567 'wl-read-folder-hist))
568 (number (call-interactively
569 (function (lambda (num)
570 (interactive "nNumber: ")
572 (mail-reply-buffer (get-buffer-create "*wl-draft-insert-get-message*"))
573 mail-citation-hook mail-yank-hooks
578 (elmo-read-msg-with-cache fld number mail-reply-buffer nil))
579 (wl-draft-yank-from-mail-reply-buffer nil))
580 (kill-buffer mail-reply-buffer))))
583 ;; default body citation func
585 (defun wl-default-draft-cite ()
586 (let ((mail-yank-ignored-headers "[^:]+:")
587 (mail-yank-prefix "> ")
588 (summary-buf wl-current-summary-buffer)
589 (message-buf (get-buffer (wl-current-message-buffer)))
590 from date cite-title num entity)
592 (buffer-live-p summary-buf)
594 (buffer-live-p message-buf))
597 (set-buffer summary-buf)
600 (set-buffer message-buf)
601 wl-message-buffer-cur-number))
602 (setq entity (assoc (cdr (assq num
603 (elmo-msgdb-get-number-alist
604 wl-summary-buffer-msgdb)))
605 (elmo-msgdb-get-overview
606 wl-summary-buffer-msgdb)))
607 (setq from (elmo-msgdb-overview-entity-get-from entity))
608 (setq date (elmo-msgdb-overview-entity-get-date entity)))
609 (setq cite-title (format "At %s,\n%s wrote:"
610 (or date "some time ago")
611 (wl-summary-from-func-internal
614 (insert cite-title "\n"))
615 (mail-indent-citation)))
617 (defvar wl-draft-buffer nil "Draft buffer to yank content")
618 (defun wl-draft-yank-to-draft-buffer (buffer)
619 "Yank BUFFER content to `wl-draft-buffer'."
620 (set-buffer wl-draft-buffer)
621 (let ((mail-reply-buffer buffer))
622 (wl-draft-yank-from-mail-reply-buffer nil)
623 (kill-buffer buffer)))
625 (defun wl-draft-yank-original (&optional arg)
626 "Yank original message."
629 (let (buf mail-reply-buffer)
632 (setq buf (current-buffer)))
633 (setq mail-reply-buffer buf)
634 (wl-draft-yank-from-mail-reply-buffer nil))
635 (wl-draft-yank-current-message-entity)))
637 (defun wl-draft-hide (editing-buffer)
638 "Hide the editing draft buffer if possible."
640 (when (and editing-buffer
641 (buffer-live-p editing-buffer))
642 (set-buffer editing-buffer)
643 (let ((sum-buf wl-draft-buffer-cur-summary-buffer)
644 fld-buf sum-win fld-win)
645 (if (and wl-draft-use-frame
646 (> (length (visible-frame-list)) 1))
652 ;; stay folder window if required
653 (when wl-stay-folder-window
654 (if (setq fld-buf (get-buffer wl-folder-buffer-name))
655 (if (setq fld-win (get-buffer-window fld-buf))
656 (select-window fld-win)
657 (if wl-draft-resume-folder-window ;; resume folder window
658 (switch-to-buffer fld-buf)))))
659 (if (buffer-live-p sum-buf)
660 (if (setq sum-win (get-buffer-window sum-buf t))
661 ;; if Summary is on the frame, select it.
662 (select-window sum-win)
663 ;; if summary is not on the frame, switch to it.
664 (if (and wl-stay-folder-window
665 (or wl-draft-resume-folder-window fld-win))
666 (wl-folder-select-buffer sum-buf)
667 (switch-to-buffer sum-buf)))))))
669 (defun wl-draft-delete (editing-buffer)
670 "kill the editing draft buffer and delete the file corresponds to it."
673 (set-buffer editing-buffer)
674 (if wl-draft-buffer-file-name
676 (if (file-exists-p wl-draft-buffer-file-name)
677 (delete-file wl-draft-buffer-file-name))
678 (let ((msg (and wl-draft-buffer-file-name
679 (string-match "[0-9]+$" wl-draft-buffer-file-name)
681 (match-string 0 wl-draft-buffer-file-name)))))
682 (wl-draft-config-info-operation msg 'delete))))
683 (set-buffer-modified-p nil) ; force kill
684 (kill-buffer editing-buffer))))
686 (defun wl-draft-kill (&optional force-kill)
687 "Kill current draft buffer and quit editing."
690 (when (and (or (eq major-mode 'wl-draft-mode)
691 (eq major-mode 'mail-mode))
693 (y-or-n-p "Kill Current Draft?")))
694 (let ((cur-buf (current-buffer)))
695 (wl-draft-hide cur-buf)
696 (wl-draft-delete cur-buf)))
699 (defun wl-draft-fcc ()
700 "Add a new Fcc field, with file name completion."
702 (or (mail-position-on-field "fcc" t) ;Put new field after exiting Fcc.
703 (mail-position-on-field "to"))
706 ;; function for wl-sent-message-via
708 (defmacro wl-draft-sent-message-p (type)
709 (` (eq (nth 1 (assq (, type) wl-sent-message-via)) 'sent)))
711 (defmacro wl-draft-set-sent-message (type result &optional server-port)
712 (` (let ((element (assq (, type) wl-sent-message-via)))
714 (unless (eq (nth 1 element) (, result))
715 (setcdr element (list (, result) (, server-port)))
716 (setq wl-sent-message-modified t))
717 (push (list (, type) (, result) (, server-port)) wl-sent-message-via)
718 (setq wl-sent-message-modified t)))))
720 (defun wl-draft-sent-message-results ()
721 (let ((results wl-sent-message-via)
722 unplugged-via sent-via)
724 (cond ((eq (nth 1 (car results)) 'unplugged)
725 (push (caar results) unplugged-via))
726 ((eq (nth 1 (car results)) 'sent)
727 (push (caar results) sent-via)))
728 (setq results (cdr results)))
729 (list unplugged-via sent-via)))
731 (defun wl-draft-write-sendlog (status proto server to id)
732 "Write send log file, if `wl-draft-sendlog' is non-nil."
733 (when wl-draft-sendlog
735 (let* ((tmp-buf (get-buffer-create " *wl-draft-sendlog*"))
736 (filename (expand-file-name wl-draft-sendlog-filename
738 (filesize (nth 7 (file-attributes filename)))
739 (server (if server (concat " server=" server) ""))
741 ((memq proto '(fcc queue))
742 (format " folder=\"%s\"" to))
744 (format " ng=<%s>" to))
749 (mapcar '(lambda(x) (format "<%s>" x)) to)
752 (id (if id (concat " id=" id) ""))
753 (time (wl-sendlog-time)))
756 (insert (format "%s proto=%s stat=%s%s%s%s\n"
757 time proto status server to id))
758 (if (and wl-draft-sendlog-max-size filesize
759 (> filesize wl-draft-sendlog-max-size))
760 (rename-file filename (concat filename ".old") t))
761 (if (file-writable-p filename)
762 (write-region (point-min) (point-max)
764 (message (format "%s is not writable." filename)))
765 (kill-buffer tmp-buf)))))
767 (defun wl-draft-get-header-delimiter (&optional delete)
768 ;; If DELETE is non-nil, replace the header delimiter with a blank line
770 (goto-char (point-min))
771 (when (re-search-forward
772 (concat "^" (regexp-quote mail-header-separator) "$\\|^$") nil t)
776 (setq delimline (point-marker)))
779 (defun wl-draft-send-mail-with-qmail ()
780 "Pass the prepared message buffer to qmail-inject.
781 Refer to the documentation for the variable `send-mail-function'
782 to find out how to use this."
783 (if (and wl-draft-qmail-send-plugged
784 (not (elmo-plugged-p)))
785 (wl-draft-set-sent-message 'mail 'unplugged)
787 (let ((id (std11-field-body "Message-ID"))
788 (to (std11-field-body "To")))
792 'call-process-region 1 (point-max) wl-qmail-inject-program
794 wl-qmail-inject-args))
795 ;; qmail-inject doesn't say anything on it's stdout/stderr,
796 ;; we have to look at the retval instead
798 (wl-draft-set-sent-message 'mail 'sent)
799 (wl-draft-write-sendlog 'ok 'qmail nil (list to) id)))
800 (1 (error "qmail-inject reported permanent failure"))
801 (111 (error "qmail-inject reported transient failure"))
802 ;; should never happen
803 (t (error "qmail-inject reported unknown failure"))))))
805 (defun wl-draft-parse-msg-id-list-string (string)
806 "Get msg-id list from STRING."
808 (dolist (parsed-id (std11-parse-msg-ids-string string))
809 (when (eq (car parsed-id) 'msg-id)
810 (setq msg-id-list (cons (std11-msg-id-string parsed-id)
812 (nreverse msg-id-list)))
814 (defun wl-draft-parse-mailbox-list (field &optional remove-group-list)
815 "Get mailbox list of FIELD from current buffer.
816 The buffer is expected to be narrowed to just the headers of the message.
817 If optional argument REMOVE-GROUP-LIST is non-nil, remove group list content
818 from current buffer."
820 (let ((case-fold-search t)
821 (inhibit-read-only t)
823 mailbox-list beg seq has-group-list)
824 (goto-char (point-min))
825 (while (re-search-forward (concat "^" (regexp-quote field) "[\t ]*:")
828 (re-search-forward "^[^ \t]" nil 'move)
830 (skip-chars-backward "\n")
831 (setq seq (std11-lexical-analyze
832 (buffer-substring-no-properties beg (point))))
833 (setq addresses (std11-parse-addresses seq))
835 (cond ((eq (car (car addresses)) 'group)
836 (setq has-group-list t)
840 'std11-address-string
841 (nth 2 (car addresses))))))
842 ((eq (car (car addresses)) 'mailbox)
843 (setq address (nth 1 (car addresses)))
847 (std11-addr-to-string
848 (if (eq (car address) 'phrase-route-addr)
851 (setq addresses (cdr addresses)))
852 (when (and remove-group-list has-group-list)
853 (delete-region beg (point))
854 (insert (wl-address-string-without-group-list-contents seq))))
857 (defun wl-draft-deduce-address-list (buffer header-start header-end)
858 "Get address list suitable for smtp RCPT TO:<address>.
859 Group list content is removed if `wl-draft-remove-group-list-contents' is
861 (let ((fields '("to" "cc" "bcc"))
862 (resent-fields '("resent-to" "resent-cc" "resent-bcc"))
867 (narrow-to-region header-start header-end)
868 (goto-char (point-min))
870 (if (re-search-forward "^resent-to[\t ]*:" nil t)
871 (setq fields resent-fields)))
875 (wl-draft-parse-mailbox-list
877 wl-draft-remove-group-list-contents)))
878 (setq fields (cdr fields)))
884 (defun wl-draft-send-mail-with-smtp ()
885 "Send the prepared message buffer with SMTP."
887 (let* ((errbuf (if mail-interactive
888 (generate-new-buffer " smtp errors")
891 (default-case-fold-search t)
892 (sender (or wl-envelope-from
893 (wl-address-header-extract-address wl-from)))
894 (delimline (save-excursion
895 (goto-char (point-min))
897 (concat "^" (regexp-quote mail-header-separator)
901 (or wl-smtp-posting-server
902 ;; Compatibility stuff for FLIM 1.12.5 or earlier.
903 ;; They don't accept a function as the value of `smtp-server'.
904 (if (functionp smtp-server)
909 (let (wl-draft-remove-group-list-contents)
910 (wl-draft-deduce-address-list
911 (current-buffer) (point-min) delimline)))
912 (or smtp-server "localhost"))))
913 (smtp-service (or wl-smtp-posting-port smtp-service))
914 (smtp-local-domain (or smtp-local-domain wl-local-domain))
915 (id (std11-field-body "message-id"))
917 (if (not (elmo-plugged-p smtp-server smtp-service))
918 (wl-draft-set-sent-message 'mail 'unplugged
919 (cons smtp-server smtp-service))
922 ;; Instead of `smtp-deduce-address-list'.
923 (setq recipients (wl-draft-deduce-address-list
924 (current-buffer) (point-min) delimline))
925 (unless recipients (error "No recipients"))
926 ;; Insert an extra newline if we need it to work around
927 ;; Sun's bug that swallows newlines.
928 (goto-char (1+ delimline))
929 (if (eval mail-mailer-swallows-blank-line)
931 ;;; (run-hooks 'wl-mail-send-pre-hook)
936 (wl-draft-delete-field "bcc" delimline)
937 (wl-draft-delete-field "resent-bcc" delimline)
938 (let (process-connection-type)
941 (wl-smtp-extension-bind
943 (smtp-send-buffer sender recipients (current-buffer))
945 (wl-draft-write-sendlog 'failed 'smtp smtp-server
947 (signal (car err) (cdr err)))))
948 (wl-draft-set-sent-message 'mail 'sent)
949 (wl-draft-write-sendlog
950 'ok 'smtp smtp-server recipients id)))))
952 (kill-buffer errbuf))))))
954 (defun wl-draft-send-mail-with-pop-before-smtp ()
955 "Send the prepared message buffer with POP-before-SMTP."
958 (let ((session (elmo-pop3-get-session
960 (or wl-pop-before-smtp-user
961 elmo-default-pop3-user)
962 (or wl-pop-before-smtp-authenticate-type
963 elmo-default-pop3-authenticate-type)
964 (or wl-pop-before-smtp-server
965 elmo-default-pop3-server)
966 (or wl-pop-before-smtp-port
967 elmo-default-pop3-port)
968 (or wl-pop-before-smtp-stream-type
969 elmo-default-pop3-stream-type)))))
970 (when session (elmo-network-close-session session)))
972 (wl-draft-send-mail-with-smtp))
974 (defun wl-draft-insert-required-fields (&optional force-msgid)
975 "Insert Message-ID, Date, and From field.
976 If FORCE-MSGID, ignore 'wl-insert-message-id'."
977 ;; Insert Message-Id field...
978 (goto-char (point-min))
979 (when (and (or force-msgid
980 wl-insert-message-id)
981 (not (re-search-forward "^Message-ID[ \t]*:" nil t)))
982 (insert (concat "Message-ID: "
983 (wl-draft-make-message-id-string)
985 ;; Insert date field.
986 (goto-char (point-min))
987 (or (re-search-forward "^Date[ \t]*:" nil t)
988 (wl-draft-insert-date-field))
989 ;; Insert from field.
990 (goto-char (point-min))
991 (or (re-search-forward "^From[ \t]*:" nil t)
992 (wl-draft-insert-from-field)))
994 (defun wl-draft-normal-send-func (editing-buffer kill-when-done)
995 "Send the message in the current buffer."
997 (std11-narrow-to-header mail-header-separator)
998 (wl-draft-insert-required-fields)
999 ;; Delete null fields.
1000 (goto-char (point-min))
1001 (while (re-search-forward "^[^ \t\n:]+:[ \t]*\n" nil t)
1003 ;; ignore any blank lines in the header
1004 (while (re-search-forward "\n\n\n*" nil t)
1005 (replace-match "\n")))
1006 (run-hooks 'wl-mail-send-pre-hook) ;; X-PGP-Sig, Cancel-Lock
1007 (wl-draft-dispatch-message)
1008 (when kill-when-done
1009 ;; hide editing-buffer.
1010 (wl-draft-hide editing-buffer)
1011 ;; delete editing-buffer and its file.
1012 (wl-draft-delete editing-buffer)))
1014 (defun wl-draft-dispatch-message (&optional mes-string)
1015 "Send the message in the current buffer. Not modified the header fields."
1017 (if (and wl-draft-verbose-send mes-string)
1018 (message mes-string))
1020 (setq delimline (wl-draft-get-header-delimiter t))
1021 (unless wl-draft-fcc-list
1022 (setq wl-draft-fcc-list (wl-draft-get-fcc-list delimline)))
1024 (setq wl-sent-message-modified nil)
1027 (if (and (wl-message-mail-p)
1028 (not (wl-draft-sent-message-p 'mail)))
1029 (funcall wl-draft-send-mail-func))
1030 (if (and (wl-message-news-p)
1031 (not (wl-draft-sent-message-p 'news))
1032 (not (wl-message-field-exists-p "Resent-to")))
1033 (funcall wl-draft-send-news-func)))
1035 (let* ((status (wl-draft-sent-message-results))
1036 (unplugged-via (car status))
1037 (sent-via (nth 1 status)))
1038 ;; If one sent, process fcc folder.
1039 (if (and sent-via wl-draft-fcc-list)
1041 (wl-draft-do-fcc (wl-draft-get-header-delimiter) wl-draft-fcc-list)
1042 (setq wl-draft-fcc-list nil)))
1043 (if wl-draft-use-cache
1044 (let ((id (std11-field-body "Message-ID"))
1045 (elmo-enable-disconnected-operation t))
1046 (elmo-cache-save id nil nil nil)))
1047 ;; If one unplugged, append queue.
1048 (when (and unplugged-via
1049 wl-sent-message-modified)
1050 (if wl-draft-enable-queuing
1051 (wl-draft-queue-append wl-sent-message-via)
1052 (error "Unplugged")))
1053 (when wl-draft-verbose-send
1054 (if (and unplugged-via sent-via);; combined message
1056 (setq wl-draft-verbose-msg
1057 (format "Sending%s and Queuing%s..."
1058 sent-via unplugged-via))
1059 (message (concat wl-draft-verbose-msg "done")))
1061 (message (concat mes-string
1062 (if sent-via "done" "failed")))))))))
1063 (not wl-sent-message-modified)) ;; return value
1065 (defun wl-draft-raw-send (&optional kill-when-done force-pre-hook mes-string)
1066 "Force send current buffer as raw message."
1069 (let (wl-interactive-send
1070 ;;; wl-draft-verbose-send
1071 (wl-mail-send-pre-hook (and force-pre-hook wl-mail-send-pre-hook))
1072 ;;; wl-news-send-pre-hook
1075 (wl-draft-send kill-when-done mes-string))))
1077 (defun wl-draft-clone-local-variables ()
1078 (let ((locals (buffer-local-variables))
1081 (when (and (consp (car locals))
1083 (string-match wl-draft-clone-local-variable-regexp
1084 (symbol-name (car (car locals)))))
1085 (wl-append result (list (car (car locals)))))
1086 (setq locals (cdr locals)))
1089 (defun wl-draft-send (&optional kill-when-done mes-string)
1090 "Send current draft message.
1091 If optional argument is non-nil, current draft buffer is killed"
1093 ;; Don't call this explicitly.
1094 ;; Added to 'wl-draft-send-hook (by teranisi)
1095 ;; (wl-draft-config-exec)
1096 (run-hooks 'wl-draft-send-hook)
1097 (when (or (not wl-interactive-send)
1098 (y-or-n-p "Send current draft. OK?"))
1099 (let ((send-mail-function 'wl-draft-raw-send)
1100 (editing-buffer (current-buffer))
1101 (sending-buffer (wl-draft-generate-clone-buffer
1102 " *wl-draft-sending-buffer*"
1103 (append wl-draft-config-variables
1104 (wl-draft-clone-local-variables))))
1105 (wl-draft-verbose-msg nil)
1108 (save-excursion (set-buffer sending-buffer)
1109 (if (and (not (wl-message-mail-p))
1110 (not (wl-message-news-p)))
1111 (error "No recipient is specified"))
1112 (expand-abbrev) ; for mail-abbrevs
1113 (run-hooks 'mail-send-hook) ; translate buffer
1114 (if wl-draft-verbose-send
1115 (message (or mes-string "Sending...")))
1116 (funcall wl-draft-send-func editing-buffer kill-when-done)
1117 ;; Now perform actions on successful sending.
1118 (while mail-send-actions
1120 (apply (car (car mail-send-actions))
1121 (cdr (car mail-send-actions)))
1123 (setq mail-send-actions (cdr mail-send-actions)))
1124 (if (or (eq major-mode 'wl-draft-mode)
1125 (eq major-mode 'mail-mode))
1126 (local-set-key "\C-c\C-s" 'wl-draft-send)) ; override
1127 (if wl-draft-verbose-send
1128 (message (concat (or wl-draft-verbose-msg
1129 mes-string "Sending...")
1131 ;; kill sending buffer, anyway.
1132 (and (buffer-live-p sending-buffer)
1133 (kill-buffer sending-buffer))))))
1135 (defun wl-draft-save ()
1136 "Save current draft."
1139 (wl-draft-config-info-operation
1140 (and (string-match "[0-9]+$" wl-draft-buffer-file-name)
1142 (match-string 0 wl-draft-buffer-file-name)))
1145 (defun wl-draft-mimic-kill-buffer ()
1146 "Kill the current (draft) buffer with query."
1148 (let ((bufname (read-buffer (format "Kill buffer: (default %s) "
1151 (if (or (not bufname)
1152 (string-equal bufname "")
1153 (string-equal bufname (buffer-name)))
1154 (wl-draft-save-and-exit)
1155 (kill-buffer bufname))))
1157 (defun wl-draft-save-and-exit ()
1158 "Save current draft and exit current draft mode."
1161 (let ((editing-buffer (current-buffer)))
1162 (wl-draft-hide editing-buffer)
1163 (kill-buffer editing-buffer)))
1165 (defun wl-draft-send-and-exit ()
1166 "Send current draft message and kill it."
1170 (defun wl-draft-send-from-toolbar ()
1172 (let ((wl-interactive-send t))
1173 (wl-draft-send-and-exit)))
1175 (defun wl-draft-delete-field (field &optional delimline)
1176 (wl-draft-delete-fields (regexp-quote field) delimline))
1178 (defun wl-draft-delete-fields (regexp &optional delimline)
1181 (if (search-forward "\n\n" nil t)
1182 (setq delimline (point))
1183 (setq delimline (point-max))))
1184 (narrow-to-region (point-min) delimline)
1185 (goto-char (point-min))
1186 (let ((regexp (concat "^" regexp ":"))
1187 (case-fold-search t)
1190 (if (looking-at regexp)
1196 (if (re-search-forward "^[^ \t]" nil t)
1197 (goto-char (match-beginning 0))
1200 (if (re-search-forward "^[^ \t]" nil t)
1201 (goto-char (match-beginning 0))
1204 (defun wl-draft-get-fcc-list (header-end)
1206 (case-fold-search t))
1207 (or (markerp header-end) (error "header-end must be a marker"))
1209 (goto-char (point-min))
1210 (while (re-search-forward "^Fcc:[ \t]*" header-end t)
1212 (cons (buffer-substring-no-properties
1216 (skip-chars-backward " \t")
1220 (wl-folder-confirm-existence (eword-decode-string (car fcc-list))))
1221 (delete-region (match-beginning 0)
1222 (progn (forward-line 1) (point)))))
1225 (defun wl-draft-do-fcc (header-end &optional fcc-list)
1226 (let ((send-mail-buffer (current-buffer))
1227 (tembuf (generate-new-buffer " fcc output"))
1228 (case-fold-search t)
1230 (or (markerp header-end) (error "header-end must be a marker"))
1233 (setq fcc-list (wl-draft-get-fcc-list header-end)))
1236 ;; insert just the headers to avoid moving the gap more than
1237 ;; necessary (the message body could be arbitrarily huge.)
1238 (insert-buffer-substring send-mail-buffer 1 header-end)
1239 (wl-draft-insert-required-fields t)
1240 (goto-char (point-max))
1241 (insert-buffer-substring send-mail-buffer header-end)
1242 (let ((id (std11-field-body "Message-ID"))
1243 (elmo-enable-disconnected-operation t)
1246 (unless (or cache-saved
1247 (elmo-folder-plugged-p (car fcc-list)))
1248 (elmo-cache-save id nil nil nil) ;; for disconnected operation
1249 (setq cache-saved t))
1250 (if (elmo-append-msg (eword-decode-string (car fcc-list))
1252 (point-min) (point-max))
1254 (wl-draft-write-sendlog 'ok 'fcc nil (car fcc-list) id)
1255 (wl-draft-write-sendlog 'failed 'fcc nil (car fcc-list) id))
1256 (setq fcc-list (cdr fcc-list)))))
1257 (kill-buffer tembuf)))
1259 (defun wl-draft-on-field-p ()
1262 (goto-char (point-min))
1263 (search-forward (concat "\n" mail-header-separator "\n") nil 0)
1270 (if (or (looking-at ".*,[ \t]?$")
1271 (looking-at "^[^ \t]+:[ \t]+.*:$")); group list name
1273 (let ((pos (point)))
1276 (if (looking-at "^[ \t]")
1278 (if (re-search-forward ":" pos t) nil t)))))))
1281 (defun wl-draft (&optional to subject in-reply-to cc references newsgroups
1283 content-type content-transfer-encoding
1284 body edit-again summary-buf from)
1285 "Write and send mail/news message with Wanderlust."
1287 (unless (featurep 'wl)
1291 (wl-init 'wl-draft) ;; returns immediately if already initialized.
1293 (setq summary-buf (wl-summary-get-buffer wl-summary-buffer-folder-name)))
1294 (let ((draft-folder-spec (elmo-folder-get-spec wl-draft-folder))
1295 buf-name file-name num wl-demo change-major-mode-hook)
1296 (if (not (eq (car draft-folder-spec) 'localdir))
1297 (error "%s folder cannot be used for draft folder" wl-draft-folder))
1298 (setq num (elmo-max-of-list (or (elmo-list-folder wl-draft-folder) '(0))))
1299 (setq num (+ 1 num))
1300 ;; To get unused buffer name.
1301 (while (get-buffer (concat wl-draft-folder "/" (int-to-string num)))
1302 (setq num (+ 1 num)))
1303 (setq buf-name (find-file-noselect
1305 (elmo-get-msg-filename wl-draft-folder
1307 (if wl-draft-use-frame
1308 (switch-to-buffer-other-frame buf-name)
1309 (switch-to-buffer buf-name))
1310 (set-buffer buf-name)
1311 (if (not (string-match (regexp-quote wl-draft-folder)
1313 (rename-buffer (concat wl-draft-folder "/" (int-to-string num))))
1314 (if (or (eq wl-draft-reply-buffer-style 'full)
1315 (eq this-command 'wl-draft)
1316 (eq this-command 'wl-summary-write)
1317 (eq this-command 'wl-summary-write-current-folder))
1318 (delete-other-windows))
1321 (setq wl-sent-message-via nil)
1322 (if (stringp (or from wl-from))
1323 (insert "From: " (or from wl-from) "\n"))
1324 (and (or (interactive-p)
1325 (eq this-command 'wl-summary-write)
1327 (insert "To: " (or to "") "\n"))
1328 (and cc (insert "Cc: " (or cc "") "\n"))
1329 (insert "Subject: " (or subject "") "\n")
1330 (and newsgroups (insert "Newsgroups: " newsgroups "\n"))
1331 (and mail-followup-to (insert "Mail-Followup-To: " mail-followup-to "\n"))
1332 (and wl-insert-mail-reply-to
1333 (insert "Mail-Reply-To: "
1334 (wl-address-header-extract-address
1336 (and in-reply-to (insert "In-Reply-To: " in-reply-to "\n"))
1337 (and references (insert "References: " references "\n"))
1338 (insert (funcall wl-generate-mailer-string-func)
1340 (setq wl-draft-buffer-file-name file-name)
1341 (if mail-default-reply-to
1342 (insert "Reply-To: " mail-default-reply-to "\n"))
1343 (wl-draft-insert-ccs "Bcc: " (or wl-bcc
1344 (and mail-self-blind (user-login-name))))
1345 (wl-draft-insert-ccs "Fcc: " wl-fcc)
1347 (insert "Organization: " wl-organization "\n"))
1348 (and wl-auto-insert-x-face
1349 (file-exists-p wl-x-face-file)
1350 (wl-draft-insert-x-face-field-here))
1351 (if mail-default-headers
1352 (insert mail-default-headers))
1353 (if (not (= (preceding-char) ?\n))
1357 (setq start (point))
1359 (insert "Content-type: " content-type "\n"))
1360 (when content-transfer-encoding
1361 (insert "Content-Transfer-Encoding: " content-transfer-encoding "\n"))
1362 (if (or content-type content-transfer-encoding)
1364 (and body (insert body))
1366 (narrow-to-region start (point))
1368 (wl-draft-decode-message-in-buffer))
1371 (put-text-property (point)
1373 (insert mail-header-separator "\n")
1375 'category 'mail-header-separator)))
1376 (put-text-property (point)
1378 (insert mail-header-separator "\n")
1380 'category 'mail-header-separator)
1381 (and body (insert body)))
1383 (push-mark (point) t)
1384 (push-mark (point) t t))
1385 (as-binary-output-file
1386 (write-region (point-min)(point-max) wl-draft-buffer-file-name
1388 (wl-draft-editor-mode)
1389 (wl-draft-overload-functions)
1390 (wl-highlight-headers 'for-draft)
1391 (goto-char (point-min))
1392 (setq wl-draft-config-exec-flag t)
1394 (run-hooks 'wl-mail-setup-hook))
1395 (wl-user-agent-compose-internal) ;; user-agent
1396 (cond ((eq this-command 'wl-summary-write-current-newsgroup)
1397 (mail-position-on-field "Subject"))
1398 ((and (interactive-p) (null to))
1399 (mail-position-on-field "To"))
1401 (goto-char (point-max))))
1402 (setq wl-draft-buffer-cur-summary-buffer (or summary-buf
1404 wl-summary-buffer-name)))
1407 (defsubst wl-draft-insert-ccs (str cc)
1413 (null (and wl-draft-delete-myself-from-bcc-fcc
1415 (mapcar 'wl-address-header-extract-address
1417 (wl-parse-addresses (std11-field-body "To"))
1418 (wl-parse-addresses (std11-field-body "Cc"))))
1419 (mapcar 'downcase wl-subscribed-mailing-list)))))
1420 (insert str field "\n"))))
1422 (defun wl-draft-elmo-nntp-send ()
1423 (let ((elmo-nntp-post-pre-hook wl-news-send-pre-hook)
1424 (elmo-default-nntp-user
1425 (or wl-nntp-posting-user elmo-default-nntp-user))
1426 (elmo-default-nntp-server
1427 (or wl-nntp-posting-server elmo-default-nntp-server))
1428 (elmo-default-nntp-port
1429 (or wl-nntp-posting-port elmo-default-nntp-port))
1430 (elmo-default-nntp-stream-type
1431 (or wl-nntp-posting-stream-type elmo-default-nntp-stream-type)))
1432 (if (not (elmo-plugged-p elmo-default-nntp-server elmo-default-nntp-port))
1433 (wl-draft-set-sent-message 'news 'unplugged
1434 (cons elmo-default-nntp-server
1435 elmo-default-nntp-port))
1436 (elmo-nntp-post elmo-default-nntp-server (current-buffer))
1437 (wl-draft-set-sent-message 'news 'sent)
1438 (wl-draft-write-sendlog 'ok 'nntp elmo-default-nntp-server
1439 (std11-field-body "Newsgroups")
1440 (std11-field-body "Message-ID")))))
1442 (defun wl-draft-generate-clone-buffer (name &optional local-variables)
1443 "generate clone of current buffer named NAME."
1444 (let ((editing-buffer (current-buffer)))
1446 (set-buffer (generate-new-buffer name))
1449 (wl-draft-editor-mode)
1450 (insert-buffer editing-buffer)
1452 (while local-variables
1453 (make-local-variable (car local-variables))
1454 (set (car local-variables)
1456 (set-buffer editing-buffer)
1457 (symbol-value (car local-variables))))
1458 (setq local-variables (cdr local-variables)))
1461 (defun wl-draft-reedit (number)
1462 (let ((draft-folder-spec (elmo-folder-get-spec wl-draft-folder))
1464 buf-name file-name change-major-mode-hook)
1465 (setq file-name (expand-file-name
1466 (int-to-string number)
1468 (nth 1 draft-folder-spec)
1469 elmo-localdir-folder-path)))
1470 (unless (file-exists-p file-name)
1471 (error "File %s does not exist" file-name))
1472 (setq buf-name (find-file-noselect file-name))
1473 (if wl-draft-use-frame
1474 (switch-to-buffer-other-frame buf-name)
1475 (switch-to-buffer buf-name))
1476 (set-buffer buf-name)
1477 (if (not (string-match (regexp-quote wl-draft-folder)
1479 (rename-buffer (concat wl-draft-folder "/" (buffer-name))))
1482 (setq wl-sent-message-via nil)
1483 (setq wl-draft-buffer-file-name file-name)
1484 (wl-draft-config-info-operation number 'load)
1485 (goto-char (point-min))
1486 (or (re-search-forward "\n\n" nil t)
1487 (search-forward (concat mail-header-separator "\n") nil t))
1489 (push-mark (point) t)
1490 (push-mark (point) t t))
1491 (write-region (point-min)(point-max) wl-draft-buffer-file-name
1493 (wl-draft-overload-functions)
1494 (wl-draft-editor-mode)
1495 (wl-highlight-headers 'for-draft)
1496 (run-hooks 'wl-draft-reedit-hook)
1497 (goto-char (point-max))
1501 (defmacro wl-draft-body-goto-top ()
1503 (goto-char (point-min))
1504 (if (re-search-forward mail-header-separator nil t)
1506 (goto-char (point-max))))))
1508 (defmacro wl-draft-body-goto-bottom ()
1509 (` (goto-char (point-max))))
1511 (defmacro wl-draft-config-body-goto-header ()
1513 (goto-char (point-min))
1514 (if (re-search-forward mail-header-separator nil t)
1516 (goto-char (point-max))))))
1518 (defun wl-draft-config-sub-body (content)
1519 (wl-draft-body-goto-top)
1520 (delete-region (point) (point-max))
1521 (if content (insert (eval content))))
1523 (defun wl-draft-config-sub-top (content)
1524 (wl-draft-body-goto-top)
1525 (if content (insert (eval content))))
1527 (defun wl-draft-config-sub-bottom (content)
1528 (wl-draft-body-goto-bottom)
1529 (if content (insert (eval content))))
1531 (defun wl-draft-config-sub-header (content)
1532 (wl-draft-config-body-goto-header)
1533 (if content (insert (concat (eval content) "\n"))))
1535 (defsubst wl-draft-config-sub-file (content)
1536 (let ((coding-system-for-read wl-cs-autoconv)
1537 (file (expand-file-name (eval content))))
1538 (if (file-exists-p file)
1539 (insert-file-contents file)
1540 (error "%s: no exists file" file))))
1542 (defun wl-draft-config-sub-body-file (content)
1543 (wl-draft-body-goto-top)
1544 (delete-region (point) (point-max))
1545 (wl-draft-config-sub-file content))
1547 (defun wl-draft-config-sub-top-file (content)
1548 (wl-draft-body-goto-top)
1549 (wl-draft-config-sub-file content))
1551 (defun wl-draft-config-sub-bottom-file (content)
1552 (wl-draft-body-goto-bottom)
1553 (wl-draft-config-sub-file content))
1555 (defun wl-draft-config-sub-header-file (content)
1556 (wl-draft-config-body-goto-header)
1557 (wl-draft-config-sub-file content))
1559 (defun wl-draft-config-sub-template (content)
1560 (setq wl-draft-config-variables
1561 (wl-template-insert (eval content))))
1563 (defun wl-draft-config-sub-x-face (content)
1564 (if (and (string-match "\\.xbm\\(\\.gz\\)?$" content)
1565 (fboundp 'x-face-insert)) ; x-face.el is installed.
1566 (x-face-insert content)
1567 (wl-draft-replace-field "X-Face" (elmo-get-file-string content t) t)))
1569 (defsubst wl-draft-config-sub-func (field content)
1571 (if (setq func (assq field wl-draft-config-sub-func-alist))
1572 (let (wl-draft-config-variables)
1573 (funcall (cdr func) content)
1574 ;; for wl-draft-config-sub-template
1575 (cons t wl-draft-config-variables)))))
1577 (defsubst wl-draft-config-exec-sub (clist)
1578 (let (config local-variables)
1580 (setq config (car clist))
1583 (let ((field (car config))
1584 (content (cdr config))
1588 (wl-draft-replace-field field (eval content) t))
1589 ((setq ret-val (wl-draft-config-sub-func field content))
1590 (if (cdr ret-val) ;; for wl-draft-config-sub-template
1591 (wl-append local-variables (cdr ret-val))))
1592 ((boundp field) ;; variable
1593 (make-local-variable field)
1594 (set field (eval content))
1595 (wl-append local-variables (list field)))
1597 (error "%s: not variable" field)))))
1598 ((or (functionp config)
1599 (and (symbolp config)
1603 (error "%s: not supported type" config)))
1604 (setq clist (cdr clist)))
1607 (defun wl-draft-prepared-config-exec (&optional config-alist reply-buf)
1608 "Change headers in draft preparation time."
1610 (unless wl-draft-reedit
1613 (and (boundp 'wl-draft-prepared-config-alist)
1614 wl-draft-prepared-config-alist) ;; For compatible.
1615 wl-draft-config-alist)))
1617 (wl-draft-config-exec config-alist reply-buf)))))
1619 (defun wl-draft-config-exec (&optional config-alist reply-buf)
1620 "Change headers in draft sending time."
1622 (let ((case-fold-search t)
1623 (alist (or config-alist wl-draft-config-alist))
1624 (reply-buf (or reply-buf (and (buffer-live-p wl-draft-reply-buffer)
1625 wl-draft-reply-buffer)))
1626 (local-variables wl-draft-config-variables)
1628 (when (and (or (interactive-p)
1629 wl-draft-config-exec-flag)
1634 (setq key (caar alist)
1641 (set-buffer reply-buf)
1643 (std11-narrow-to-header)
1644 (goto-char (point-min))
1645 (re-search-forward (car clist) nil t))))
1646 (wl-draft-config-exec-sub (cdr clist))
1649 (when (save-restriction
1650 (std11-narrow-to-header mail-header-separator)
1651 (goto-char (point-min))
1652 (re-search-forward key nil t))
1653 (wl-append local-variables
1654 (wl-draft-config-exec-sub clist))
1657 (wl-append local-variables
1658 (wl-draft-config-exec-sub clist))
1660 (if (and found wl-draft-config-matchone)
1662 (setq alist (cdr alist))))
1664 (setq wl-draft-config-exec-flag nil))
1665 (run-hooks 'wl-draft-config-exec-hook)
1666 (put-text-property (point-min)(point-max) 'face nil)
1667 (wl-highlight-message (point-min)(point-max) t)
1668 (setq wl-draft-config-variables
1669 (elmo-uniq-list local-variables))))))
1671 (defun wl-draft-replace-field (field content &optional add)
1674 (let ((case-fold-search t)
1675 (inhibit-read-only t) ;; added by teranisi.
1677 (std11-narrow-to-header mail-header-separator)
1678 (goto-char (point-min))
1679 (if (re-search-forward (concat "^" (regexp-quote field) ":") nil t)
1684 (re-search-forward "^[^ \t]" nil 'move)
1686 (skip-chars-backward "\n")
1687 (delete-region beg (point))
1688 (insert " " content))
1693 (re-search-forward "^[^ \t]" nil 'move)
1695 (delete-region beg (point)))
1696 (when (and add content)
1698 (goto-char (point-max))
1699 (insert (concat field ": " content "\n"))))))))
1701 (defun wl-draft-config-info-operation (msg operation)
1702 (let* ((msgdb-dir (elmo-msgdb-expand-path wl-draft-folder))
1705 (format "%s-%d" wl-draft-config-save-filename msg)
1707 element alist variable)
1709 ((eq operation 'save)
1710 (let ((variables (elmo-uniq-list wl-draft-config-variables)))
1711 (while (setq variable (pop variables))
1712 (when (boundp variable)
1714 (list (cons variable (eval variable))))))
1715 (elmo-object-save filename alist)))
1716 ((eq operation 'load)
1717 (setq alist (elmo-object-load filename))
1718 (while (setq element (pop alist))
1719 (set (make-local-variable (car element)) (cdr element))
1720 (wl-append wl-draft-config-variables (list (car element)))))
1721 ((eq operation 'delete)
1722 (if (file-exists-p filename)
1723 (delete-file filename))))))
1725 (defun wl-draft-queue-info-operation (msg operation
1726 &optional add-sent-message-via)
1727 (let* ((msgdb-dir (elmo-msgdb-expand-path wl-queue-folder))
1730 (format "%s-%d" wl-draft-queue-save-filename msg)
1732 element alist variable)
1734 ((eq operation 'save)
1735 (let ((variables (elmo-uniq-list
1736 (append wl-draft-queue-save-variables
1737 wl-draft-config-variables
1738 (list 'wl-draft-fcc-list)))))
1739 (if add-sent-message-via
1740 (push 'wl-sent-message-via variables))
1741 (while (setq variable (pop variables))
1742 (when (boundp variable)
1744 (list (cons variable (eval variable))))))
1745 (elmo-object-save filename alist)))
1746 ((eq operation 'load)
1747 (setq alist (elmo-object-load filename))
1748 (while (setq element (pop alist))
1749 (set (make-local-variable (car element)) (cdr element))))
1750 ((eq operation 'get-sent-via)
1751 (setq alist (elmo-object-load filename))
1752 (cdr (assq 'wl-sent-message-via alist)))
1753 ((eq operation 'delete)
1754 (if (file-exists-p filename)
1755 (delete-file filename))))))
1757 (defun wl-draft-queue-append (wl-sent-message-via)
1758 (if wl-draft-verbose-send
1759 (message "Queuing..."))
1760 (let ((send-buffer (current-buffer))
1761 (message-id (std11-field-body "Message-ID")))
1762 (if (elmo-append-msg wl-queue-folder
1763 (buffer-substring (point-min) (point-max))
1767 (elmo-dop-lock-message message-id))
1768 (wl-draft-queue-info-operation
1769 (car (elmo-max-of-folder wl-queue-folder))
1770 'save wl-sent-message-via)
1771 (wl-draft-write-sendlog 'ok 'queue nil wl-queue-folder message-id)
1772 (when wl-draft-verbose-send
1773 (setq wl-draft-verbose-msg "Queuing...")
1774 (message "Queuing...done")))
1775 (wl-draft-write-sendlog 'failed 'queue nil wl-queue-folder message-id)
1776 (error "Queuing failed"))))
1778 (defun wl-draft-queue-flush ()
1779 "Flush draft queue."
1781 (let ((msgs2 (elmo-list-folder wl-queue-folder))
1784 (wl-draft-queue-flushing t)
1785 msgs failure len buffer msgid sent-via)
1786 ;; get plugged send message
1788 (setq sent-via (wl-draft-queue-info-operation (car msgs2) 'get-sent-via))
1791 (when (and (eq (nth 1 (car sent-via)) 'unplugged)
1793 (car (nth 2 (car sent-via)))
1794 (cdr (nth 2 (car sent-via)))))
1795 (wl-append msgs (list (car msgs2)))
1797 (setq sent-via (cdr sent-via))))
1798 (setq msgs2 (cdr msgs2)))
1799 (when (> (setq len (length msgs)) 0)
1800 (if (elmo-y-or-n-p (format
1801 "%d message(s) are in the sending queue. Send now?"
1803 (not elmo-dop-flush-confirm) t)
1806 (setq buffer (get-buffer-create " *wl-draft-queue-flush*"))
1809 ;; reset buffer local variables
1810 (kill-all-local-variables)
1814 (setq wl-sent-message-via nil)
1815 (wl-draft-queue-info-operation (car msgs) 'load)
1816 (elmo-read-msg-no-cache wl-queue-folder (car msgs)
1819 (setq failure (funcall
1820 wl-draft-queue-flush-send-func
1821 (format "Sending (%d/%d)..." i len)))
1822 ;;; (wl-draft-raw-send nil nil
1823 ;;; (format "Sending (%d/%d)..." i len))
1825 (elmo-display-error err t)
1830 (elmo-delete-msgs wl-queue-folder (cons (car msgs) nil))
1831 (wl-draft-queue-info-operation (car msgs) 'delete)
1832 (elmo-dop-unlock-message (std11-field-body "Message-ID"))
1833 (setq performed (+ 1 performed)))
1834 (setq msgs (cdr msgs)))
1835 (kill-buffer buffer)
1836 (message "%d message(s) are sent." performed)))
1837 (message "%d message(s) are remained to be sent." len))
1840 (defun wl-jump-to-draft-buffer (&optional arg)
1841 "Jump to the draft if exists."
1844 (wl-jump-to-draft-folder)
1845 (let ((bufs (buffer-list))
1846 (draft-regexp (concat
1849 (nth 1 (elmo-folder-get-spec wl-draft-folder))
1851 elmo-localdir-folder-path)))))
1855 (setq buf (buffer-file-name (car bufs)))
1856 (string-match draft-regexp buf))
1857 (setq draft-bufs (cons (buffer-name (car bufs)) draft-bufs)))
1858 (setq bufs (cdr bufs)))
1861 (message "No draft buffer exist."))
1864 (sort draft-bufs (function (lambda (a b) (not (string< a b))))))
1865 (if (setq buf (cdr (member (buffer-name) draft-bufs)))
1866 (setq buf (car buf))
1867 (setq buf (car draft-bufs)))
1868 (switch-to-buffer buf))))))
1870 (defun wl-jump-to-draft-folder ()
1871 (let ((msgs (reverse (elmo-list-folder wl-draft-folder)))
1872 (mybuf (buffer-name))
1875 (message "No draft message exist.")
1876 (if (string-match (concat "^" wl-draft-folder "/") mybuf)
1877 (setq msg (cadr (memq
1878 (string-to-int (substring mybuf (match-end 0)))
1880 (or msg (setq msg (car msgs)))
1881 (if (setq buf (get-buffer (format "%s/%d" wl-draft-folder msg)))
1882 (switch-to-buffer buf)
1883 (wl-draft-reedit msg)))))
1885 (defun wl-draft-highlight-and-recenter (&optional n)
1887 (if wl-highlight-body-too
1888 (let ((beg (point-min))
1890 (put-text-property beg end 'face nil)
1891 (wl-highlight-message beg end t)))
1894 ;;;; user-agent support by Sen Nagata
1896 ;; this appears to be necessarily global...
1897 (defvar wl-user-agent-compose-p nil)
1898 (defvar wl-user-agent-headers-and-body-alist nil)
1900 ;; this should be a generic function for mail-mode -- i wish there was
1901 ;; something like it in sendmail.el
1902 (defun wl-user-agent-insert-header (header-name header-value)
1903 "Insert HEADER-NAME w/ value HEADER-VALUE into a message."
1904 ;; it seems like overriding existing headers is acceptable -- should
1905 ;; we provide an option?
1907 ;; plan was: unfold header (might be folded), remove existing value, insert
1909 ;; wl doesn't seem to fold header lines yet anyway :-)
1911 (let ((kill-whole-line t)
1913 (mail-position-on-field (capitalize header-name))
1914 (setq end-of-line (point))
1916 (re-search-forward ":" end-of-line)
1917 (insert (concat " " header-value "\n"))
1920 ;; this should be a generic function for mail-mode -- i wish there was
1921 ;; something like it in sendmail.el
1923 ;; ** haven't dealt w/ case where the body is already set **
1924 (defun wl-user-agent-insert-body (body-text)
1925 "Insert a body of text, BODY-TEXT, into a message."
1926 ;; code defensively... :-P
1927 (goto-char (point-min))
1928 (search-forward mail-header-separator)
1933 (defun wl-user-agent-compose (&optional to subject other-headers continue
1934 switch-function yank-action
1936 "Support the `compose-mail' interface for wl.
1937 Only support for TO, SUBJECT, and OTHER-HEADERS has been implemented.
1938 Support for CONTINUE, YANK-ACTION, and SEND-ACTIONS has not
1939 been implemented yet. Partial support for SWITCH-FUNCTION now supported."
1941 (unless (featurep 'wl)
1943 ;; protect these -- to and subject get bound at some point, so it looks
1944 ;; to be necessary to protect the values used w/in
1945 (let ((wl-user-agent-headers-and-body-alist other-headers)
1946 (wl-draft-use-frame (eq switch-function 'switch-to-buffer-other-frame))
1947 (wl-draft-reply-buffer-style 'split))
1948 (when (eq switch-function 'switch-to-buffer-other-window)
1949 (when (one-window-p t)
1950 (if (window-minibuffer-p) (other-window 1))
1954 (if (wl-string-match-assoc "to" wl-user-agent-headers-and-body-alist
1957 (wl-string-match-assoc "to" wl-user-agent-headers-and-body-alist
1960 (setq wl-user-agent-headers-and-body-alist
1961 (cons (cons "to" to)
1962 wl-user-agent-headers-and-body-alist))))
1964 (if (wl-string-match-assoc "subject"
1965 wl-user-agent-headers-and-body-alist
1968 (wl-string-match-assoc "subject"
1969 wl-user-agent-headers-and-body-alist
1972 (setq wl-user-agent-headers-and-body-alist
1973 (cons (cons "subject" subject)
1974 wl-user-agent-headers-and-body-alist))))
1975 ;; i think this is what we want to use...
1978 ;; tell the hook-function to do its stuff
1979 (setq wl-user-agent-compose-p t)
1980 ;; because to get the hooks working, wl-draft has to think it has
1981 ;; been called interactively
1982 (call-interactively 'wl-draft))
1983 (setq wl-user-agent-compose-p nil))))
1985 (defun wl-user-agent-compose-internal ()
1986 "Manipulate headers and/or a body of a draft message."
1987 ;; being called from wl-user-agent-compose?
1988 (if wl-user-agent-compose-p
1991 (let ((headers wl-user-agent-headers-and-body-alist)
1992 (case-fold-search t))
1995 (if (not (string-match "^body$" (car (car headers))))
1996 (wl-user-agent-insert-header
1997 (car (car headers)) (cdr (car headers)))
1999 (setq headers (cdr headers))))
2000 ;; highlight headers (from wl-draft in wl-draft.el)
2001 (wl-highlight-headers 'for-draft)
2003 (if (wl-string-match-assoc "body" wl-user-agent-headers-and-body-alist
2005 (wl-user-agent-insert-body
2006 (cdr (wl-string-match-assoc
2008 wl-user-agent-headers-and-body-alist 'ignore-case)))))
2012 (product-provide (provide 'wl-draft) (require 'wl-version))
2014 ;;; wl-draft.el ends here