* wl-draft.el (wl-draft-save):
[elisp/wanderlust.git] / wl / wl-draft.el
index 659ead0..9168336 100644 (file)
@@ -1,4 +1,4 @@
-;;; wl-draft.el -- Message draft mode for Wanderlust.
+;;; wl-draft.el --- Message draft mode for Wanderlust.
 
 ;; Copyright (C) 1998,1999,2000 Yuuichi Teranishi <teranisi@gohome.org>
 ;; Copyright (C) 1998,1999,2000 Masahiro MURATA <muse@ba2.so-net.ne.jp>
@@ -53,7 +53,6 @@
   (autoload 'wl-addrmgr "wl-addrmgr"))
 
 (defvar wl-draft-buf-name "Draft")
-(defvar wl-draft-cite-function 'wl-default-draft-cite)
 (defvar wl-draft-buffer-file-name nil)
 (defvar wl-draft-field-completion-list nil)
 (defvar wl-draft-verbose-send t)
 (defvar wl-draft-queue-flush-send-function 'wl-draft-dispatch-message)
 (defvar wl-sent-message-via nil)
 (defvar wl-sent-message-modified nil)
+(defvar wl-sent-message-queued nil)
 (defvar wl-draft-fcc-list nil)
 (defvar wl-draft-reedit nil)
 (defvar wl-draft-reply-buffer nil)
 (defvar wl-draft-forward nil)
+(defvar wl-draft-parent-folder nil)
 
 (defvar wl-draft-config-sub-func-alist
-  '((body        . wl-draft-config-sub-body)
-    (top         . wl-draft-config-sub-top)
-    (bottom      . wl-draft-config-sub-bottom)
-    (header      . wl-draft-config-sub-header)
-    (body-file   . wl-draft-config-sub-body-file)
-    (top-file    . wl-draft-config-sub-top-file)
-    (bottom-file . wl-draft-config-sub-bottom-file)
-    (header-file . wl-draft-config-sub-header-file)
-    (template    . wl-draft-config-sub-template)
-    (x-face      . wl-draft-config-sub-x-face)))
+  '((body              . wl-draft-config-sub-body)
+    (top               . wl-draft-config-sub-top)
+    (bottom            . wl-draft-config-sub-bottom)
+    (header            . wl-draft-config-sub-header)
+    (header-top                . wl-draft-config-sub-header-top)
+    (header-bottom     . wl-draft-config-sub-header)
+    (part-top          . wl-draft-config-sub-part-top)
+    (part-bottom       . wl-draft-config-sub-part-bottom)
+    (body-file         . wl-draft-config-sub-body-file)
+    (top-file          . wl-draft-config-sub-top-file)
+    (bottom-file       . wl-draft-config-sub-bottom-file)
+    (header-file       . wl-draft-config-sub-header-file)
+    (template          . wl-draft-config-sub-template)
+    (x-face            . wl-draft-config-sub-x-face)))
 
 (make-variable-buffer-local 'wl-draft-buffer-file-name)
 (make-variable-buffer-local 'wl-draft-buffer-cur-summary-buffer)
 (make-variable-buffer-local 'wl-draft-config-variables)
 (make-variable-buffer-local 'wl-draft-config-exec-flag)
 (make-variable-buffer-local 'wl-sent-message-via)
+(make-variable-buffer-local 'wl-sent-message-queued)
 (make-variable-buffer-local 'wl-draft-fcc-list)
 (make-variable-buffer-local 'wl-draft-reply-buffer)
+(make-variable-buffer-local 'wl-draft-parent-folder)
+
+(defsubst wl-smtp-password-key (user mechanism server)
+  (format "SMTP:%s/%s@%s"
+         user mechanism server))
 
 (defmacro wl-smtp-extension-bind (&rest body)
   (` (let* ((smtp-sasl-mechanisms
             (function
              (lambda (prompt)
                (elmo-get-passwd
-                (format "%s@%s"
-                        smtp-sasl-user-name
-                        smtp-server)))))
+                (wl-smtp-password-key
+                 smtp-sasl-user-name
+                 (car smtp-sasl-mechanisms)
+                 smtp-server)))))
        (,@ body))))
 
 (defun wl-draft-insert-date-field ()
@@ -275,7 +287,9 @@ Reply to author if WITH-ARG is non-nil."
 ;;;(save-excursion
   (let (r-list
        to mail-followup-to cc subject in-reply-to references newsgroups
-       from to-alist cc-alist decoder)
+       from to-alist cc-alist decoder parent-folder)
+    (set-buffer summary-buf)
+    (setq parent-folder (wl-summary-buffer-folder-name))
     (set-buffer buf)
     (setq r-list (symbol-value (wl-draft-reply-list-symbol with-arg)))
     (catch 'done
@@ -355,10 +369,18 @@ Reply to author if WITH-ARG is non-nil."
       (setq mail-followup-to
            (wl-draft-make-mail-followup-to (append to cc)))
       (setq mail-followup-to (wl-delete-duplicates mail-followup-to nil t)))
-    (setq newsgroups (wl-parse newsgroups
-                              "[ \t\f\r\n,]*\\([^ \t\f\r\n,]+\\)")
-         newsgroups (wl-delete-duplicates newsgroups)
-         newsgroups (if newsgroups (mapconcat 'identity newsgroups ",")))
+    (with-temp-buffer                  ; to keep raw buffer unibyte.
+      (elmo-set-buffer-multibyte default-enable-multibyte-characters)
+      (setq newsgroups (wl-parse newsgroups
+                                "[ \t\f\r\n,]*\\([^ \t\f\r\n,]+\\)")
+           newsgroups (wl-delete-duplicates newsgroups)
+           newsgroups
+           (if newsgroups
+               (mapconcat
+                (lambda (grp)
+                  (setq decoder (mime-find-field-decoder 'Newsgroups 'plain))
+                  (if decoder (funcall decoder grp) grp))
+                newsgroups ","))))
     (setq to (wl-delete-duplicates to nil t))
     (setq cc (wl-delete-duplicates
              (append (wl-delete-duplicates cc nil t)
@@ -393,15 +415,19 @@ Reply to author if WITH-ARG is non-nil."
                         (mapconcat 'identity references "\n\t")))
     (wl-draft
      to subject in-reply-to cc references newsgroups mail-followup-to
-     nil nil nil nil summary-buf)
+     nil nil nil nil summary-buf nil parent-folder)
     (setq wl-draft-reply-buffer buf))
   (run-hooks 'wl-reply-hook))
 
 (defun wl-draft-add-references ()
+  (wl-draft-add-in-reply-to "References"))
+
+(defun wl-draft-add-in-reply-to (&optional alt-field)
   (let* ((mes-id (save-excursion
                   (set-buffer mail-reply-buffer)
                   (std11-field-body "message-id")))
-        (ref (std11-field-body "References"))
+        (field (or alt-field "In-Reply-To"))
+        (ref (std11-field-body field))
         (ref-list nil) (st nil))
     (when (and mes-id ref)
       (while (string-match "<[^>]+>" ref st)
@@ -413,7 +439,7 @@ Reply to author if WITH-ARG is non-nil."
        (setq mes-id nil)))
     (when mes-id
       (save-excursion
-       (when (mail-position-on-field "References")
+       (when (mail-position-on-field field)
          (forward-line)
          (while (looking-at "^[ \t]")
            (forward-line))
@@ -444,8 +470,10 @@ Reply to author if WITH-ARG is non-nil."
          (mail-yank-hooks (run-hooks 'mail-yank-hooks))
          (wl-draft-cite-function (funcall wl-draft-cite-function))) ; default cite
     (run-hooks 'wl-draft-cited-hook)
-    (when (and wl-draft-add-references
-              (wl-draft-add-references))
+    (when (if wl-draft-add-references
+             (wl-draft-add-references)
+           (if wl-draft-add-in-reply-to
+               (wl-draft-add-in-reply-to)))
       (wl-highlight-headers 'for-draft)) ; highlight when added References:
     (when wl-highlight-body-too
       (wl-highlight-body-region beg (point-max)))))
@@ -486,7 +514,8 @@ Reply to author if WITH-ARG is non-nil."
   (wl-draft-edit-string (elmo-get-file-string
                         (or file
                             (read-file-name "File to edit: "
-                                            (or wl-tmp-dir "~/"))))))
+                                            (or wl-temporary-file-directory
+                                                "~/"))))))
 
 (defun wl-draft-edit-string (string)
   (let ((cur-buf (current-buffer))
@@ -551,7 +580,8 @@ Reply to author if WITH-ARG is non-nil."
   (let (original-buffer
        mail-reply-buffer
        mail-citation-hook mail-yank-hooks
-       wl-draft-add-references wl-draft-cite-function)
+       wl-draft-add-references wl-draft-add-in-reply-to
+       wl-draft-cite-function)
     (with-current-buffer wl-draft-buffer-cur-summary-buffer
       (with-current-buffer wl-message-buffer
        (setq original-buffer (wl-message-get-original-buffer))
@@ -613,8 +643,10 @@ Reply to author if WITH-ARG is non-nil."
            (setq from (elmo-msgdb-overview-entity-get-from entity)))
          (setq cite-title (format "At %s,\n%s wrote:"
                                   (or date "some time ago")
-                                  (wl-summary-from-func-internal
-                                   (or from "you"))))))
+                                  (if wl-default-draft-cite-decorate-author
+                                    (wl-summary-from-func-internal
+                                     (or from "you"))
+                                    (or from "you"))))))
     (and cite-title
         (insert cite-title "\n"))
     (mail-indent-citation)))
@@ -641,7 +673,6 @@ Reply to author if WITH-ARG is non-nil."
 
 (defun wl-draft-hide (editing-buffer)
   "Hide the editing draft buffer if possible."
-  (interactive)
   (when (and editing-buffer
             (buffer-live-p editing-buffer))
     (set-buffer editing-buffer)
@@ -708,6 +739,15 @@ Reply to author if WITH-ARG is non-nil."
       (mail-position-on-field "to"))
   (insert "\nFcc: "))
 
+;; Imported from message.el.
+(defun wl-draft-elide-region (b e)
+  "Elide the text in the region.
+An ellipsis (from `wl-draft-elide-ellipsis') will be inserted where the
+text was killed."
+  (interactive "r")
+  (kill-region b e)
+  (insert wl-draft-elide-ellipsis))
+
 ;; function for wl-sent-message-via
 
 (defmacro wl-draft-sent-message-p (type)
@@ -736,10 +776,9 @@ Reply to author if WITH-ARG is non-nil."
 (defun wl-draft-write-sendlog (status proto server to id)
   "Write send log file, if `wl-draft-sendlog' is non-nil."
   (when wl-draft-sendlog
-    (save-excursion
-      (let* ((tmp-buf (get-buffer-create " *wl-draft-sendlog*"))
-            (filename (expand-file-name wl-draft-sendlog-filename
-                                        elmo-msgdb-dir))
+    (with-temp-buffer
+      (let* ((filename (expand-file-name wl-draft-sendlog-filename
+                                        elmo-msgdb-directory))
             (filesize (nth 7 (file-attributes filename)))
             (server (if server (concat " server=" server) ""))
             (to (if to (cond
@@ -756,18 +795,15 @@ Reply to author if WITH-ARG is non-nil."
                   ""))
             (id (if id (concat " id=" id) ""))
             (time (wl-sendlog-time)))
-       (set-buffer tmp-buf)
-       (erase-buffer)
        (insert (format "%s proto=%s stat=%s%s%s%s\n"
                        time proto status server to id))
        (if (and wl-draft-sendlog-max-size filesize
                 (> filesize wl-draft-sendlog-max-size))
            (rename-file filename (concat filename ".old") t))
        (if (file-writable-p filename)
-           (write-region (point-min) (point-max)
-                         filename t 'no-msg)
-         (message (format "%s is not writable." filename)))
-       (kill-buffer tmp-buf)))))
+           (write-region-as-binary (point-min) (point-max)
+                                   filename t 'no-msg)
+         (message "%s is not writable." filename))))))
 
 (defun wl-draft-get-header-delimiter (&optional delete)
   ;; If DELETE is non-nil, replace the header delimiter with a blank line
@@ -789,6 +825,7 @@ to find out how to use this."
           (not (elmo-plugged-p)))
       (wl-draft-set-sent-message 'mail 'unplugged)
     ;; send the message
+    (run-hooks 'wl-mail-send-pre-hook) ;; X-PGP-Sig, Cancel-Lock
     (let ((id (std11-field-body "Message-ID"))
          (to (std11-field-body "To")))
       (case
@@ -879,7 +916,7 @@ from current buffer."
   "Get address list suitable for smtp RCPT TO:<address>.
 Group list content is removed if `wl-draft-remove-group-list-contents' is
 non-nil."
-  (let ((fields        '("to" "cc" "bcc"))
+  (let ((fields '("to" "cc" "bcc"))
        (resent-fields '("resent-to" "resent-cc" "resent-bcc"))
        (case-fold-search t)
        addrs recipients)
@@ -949,7 +986,7 @@ non-nil."
            (goto-char (1+ delimline))
            (if (eval mail-mailer-swallows-blank-line)
                (newline))
-;;;        (run-hooks 'wl-mail-send-pre-hook)
+           (run-hooks 'wl-mail-send-pre-hook) ;; X-PGP-Sig, Cancel-Lock
            (if mail-interactive
                (save-excursion
                  (set-buffer errbuf)
@@ -965,6 +1002,12 @@ non-nil."
                    (error
                     (wl-draft-write-sendlog 'failed 'smtp smtp-server
                                             recipients id)
+                    (if (/= (nth 1 err) 334)
+                        (elmo-remove-passwd
+                         (wl-smtp-password-key
+                          smtp-sasl-user-name
+                          (car smtp-sasl-mechanisms)
+                          smtp-server)))
                     (signal (car err) (cdr err)))))
                 (wl-draft-set-sent-message 'mail 'sent)
                 (wl-draft-write-sendlog
@@ -975,22 +1018,27 @@ non-nil."
 (defun wl-draft-send-mail-with-pop-before-smtp ()
   "Send the prepared message buffer with POP-before-SMTP."
   (require 'elmo-pop3)
-  (condition-case ()
-      (let ((session (elmo-pop3-get-session
-                     (luna-make-entity
-                      'elmo-pop3-folder
-                      :user (or wl-pop-before-smtp-user
-                                elmo-pop3-default-user)
-                      :server (or wl-pop-before-smtp-server
-                                  elmo-pop3-default-server)
-                      :port (or wl-pop-before-smtp-port
-                                elmo-pop3-default-port)
-                      :auth (or wl-pop-before-smtp-authenticate-type
-                                elmo-pop3-default-authenticate-type)
-                      :stream-type (or wl-pop-before-smtp-stream-type
-                                       elmo-pop3-default-stream-type)))))
-       (when session (elmo-network-close-session session)))
-    (error))
+  (let ((session
+        (luna-make-entity
+         'elmo-pop3-folder
+         :user   (or wl-pop-before-smtp-user
+                     elmo-pop3-default-user)
+         :server (or wl-pop-before-smtp-server
+                     elmo-pop3-default-server)
+         :port   (or wl-pop-before-smtp-port
+                     elmo-pop3-default-port)
+         :auth   (or wl-pop-before-smtp-authenticate-type
+                     elmo-pop3-default-authenticate-type)
+         :stream-type (or wl-pop-before-smtp-stream-type
+                          elmo-pop3-default-stream-type))))
+    (condition-case error
+       (progn
+         (elmo-pop3-get-session session)
+         (when session (elmo-network-close-session session)))
+      (error
+       (elmo-network-close-session session)
+       (unless (string= (nth 1 error) "Unplugged")
+        (signal (car error)(cdr error))))))
   (wl-draft-send-mail-with-smtp))
 
 (defun wl-draft-insert-required-fields (&optional force-msgid)
@@ -1002,7 +1050,7 @@ If FORCE-MSGID, ignore 'wl-insert-message-id'."
                 wl-insert-message-id)
             (not (re-search-forward "^Message-ID[ \t]*:" nil t)))
     (insert (concat "Message-ID: "
-                   (wl-draft-make-message-id-string)
+                   (funcall wl-message-id-function)
                    "\n")))
   ;; Insert date field.
   (goto-char (point-min))
@@ -1025,7 +1073,7 @@ If FORCE-MSGID, ignore 'wl-insert-message-id'."
     ;; ignore any blank lines in the header
     (while (re-search-forward "\n\n\n*" nil t)
       (replace-match "\n")))
-  (run-hooks 'wl-mail-send-pre-hook) ;; X-PGP-Sig, Cancel-Lock
+;;;  (run-hooks 'wl-mail-send-pre-hook) ;; X-PGP-Sig, Cancel-Lock
   (wl-draft-dispatch-message)
   (when kill-when-done
     ;; hide editing-buffer.
@@ -1048,11 +1096,21 @@ If FORCE-MSGID, ignore 'wl-insert-message-id'."
        (progn
          (if (and (wl-message-mail-p)
                   (not (wl-draft-sent-message-p 'mail)))
-             (funcall wl-draft-send-mail-function))
+             (if (or (not (or wl-draft-force-queuing
+                              wl-draft-force-queuing-mail))
+                     (memq 'mail wl-sent-message-queued))
+                 (funcall wl-draft-send-mail-function)
+               (push 'mail wl-sent-message-queued)
+               (wl-draft-set-sent-message 'mail 'unplugged)))
          (if (and (wl-message-news-p)
                   (not (wl-draft-sent-message-p 'news))
                   (not (wl-message-field-exists-p "Resent-to")))
-             (funcall wl-draft-send-news-function)))
+             (if (or (not (or wl-draft-force-queuing
+                              wl-draft-force-queuing-news))
+                     (memq 'news wl-sent-message-queued))
+                 (funcall wl-draft-send-news-function)
+               (push 'news wl-sent-message-queued)
+               (wl-draft-set-sent-message 'news 'unplugged))))
       ;;
       (let* ((status (wl-draft-sent-message-results))
             (unplugged-via (car status))
@@ -1071,7 +1129,9 @@ If FORCE-MSGID, ignore 'wl-insert-message-id'."
        (when (and unplugged-via
                   wl-sent-message-modified)
          (if wl-draft-enable-queuing
-             (wl-draft-queue-append wl-sent-message-via)
+             (progn
+               (wl-draft-queue-append wl-sent-message-via)
+               (setq wl-sent-message-modified 'requeue))
            (error "Unplugged")))
        (when wl-draft-verbose-send
          (if (and unplugged-via sent-via);; combined message
@@ -1092,7 +1152,7 @@ If FORCE-MSGID, ignore 'wl-insert-message-id'."
     (let (wl-interactive-send
 ;;;      wl-draft-verbose-send
          (wl-mail-send-pre-hook (and force-pre-hook wl-mail-send-pre-hook))
-;;;      wl-news-send-pre-hook
+         (wl-news-send-pre-hook (and force-pre-hook wl-news-send-pre-hook))
          mail-send-hook
          mail-send-actions)
       (wl-draft-send kill-when-done mes-string))))
@@ -1156,14 +1216,39 @@ If KILL-WHEN-DONE is non-nil, current draft buffer is killed"
             (kill-buffer sending-buffer))))))
 
 (defun wl-draft-save ()
-  "Save current draft."
+  "Save current draft.
+Derived from `message-save-drafts' in T-gnus."
   (interactive)
-  (save-buffer)
-  (wl-draft-config-info-operation
-   (and (string-match "[0-9]+$" wl-draft-buffer-file-name)
-       (string-to-int
-        (match-string 0 wl-draft-buffer-file-name)))
-   'save))
+  (if (buffer-modified-p)
+      (progn
+       (message "Saving %s..." wl-draft-buffer-file-name)
+       (let ((msg (buffer-substring-no-properties (point-min) (point-max))))
+         (with-temp-file wl-draft-buffer-file-name
+           (insert msg)
+           ;; If no header separator, insert it.
+           (save-excursion
+             (goto-char (point-min))
+             (unless (re-search-forward
+                      (concat "^" (regexp-quote mail-header-separator) "$")
+                      nil t)
+               (goto-char (point-min))
+               (if (re-search-forward "\n\n" nil t)
+                   (replace-match (concat "\n" mail-header-separator "\n"))
+                 (goto-char (point-max))
+                 (insert (if (eq (char-before) ?\n) "" "\n")
+                         mail-header-separator "\n"))))
+           (let ((mime-header-encode-method-alist
+                  '((eword-encode-unstructured-field-body))))
+             (mime-edit-translate-buffer))
+           (wl-draft-get-header-delimiter t)))
+       (set-buffer-modified-p nil)
+       (wl-draft-config-info-operation
+        (and (string-match "[0-9]+$" wl-draft-buffer-file-name)
+             (string-to-int
+              (match-string 0 wl-draft-buffer-file-name)))
+        'save)
+       (message "Saving %s...done" wl-draft-buffer-file-name))
+    (message "(No changes need to be saved)")))
 
 (defun wl-draft-mimic-kill-buffer ()
   "Kill the current (draft) buffer with query."
@@ -1195,20 +1280,20 @@ If KILL-WHEN-DONE is non-nil, current draft buffer is killed"
   (let ((wl-interactive-send t))
     (wl-draft-send-and-exit)))
 
-(defun wl-draft-delete-field (field &optional delimline)
-  (wl-draft-delete-fields (regexp-quote field) delimline))
+(defun wl-draft-delete-field (field &optional delimline replace)
+  (wl-draft-delete-fields (regexp-quote field) delimline replace))
 
-(defun wl-draft-delete-fields (regexp &optional delimline)
+(defun wl-draft-delete-fields (field &optional delimline replace)
   (save-restriction
     (unless delimline
+      (goto-char (point-min))
       (if (search-forward "\n\n" nil t)
          (setq delimline (point))
        (setq delimline (point-max))))
     (narrow-to-region (point-min) delimline)
     (goto-char (point-min))
-    (let ((regexp (concat "^" regexp ":"))
-         (case-fold-search t)
-         last)
+    (let ((regexp (concat "^" field ":"))
+         (case-fold-search t))
       (while (not (eobp))
        (if (looking-at regexp)
            (progn
@@ -1218,7 +1303,9 @@ If KILL-WHEN-DONE is non-nil, current draft buffer is killed"
                 (forward-line 1)
                 (if (re-search-forward "^[^ \t]" nil t)
                     (goto-char (match-beginning 0))
-                  (point-max)))))
+                  (point-max))))
+             (if replace
+                 (insert (concat field ": " replace "\n"))))
          (forward-line 1)
          (if (re-search-forward "^[^ \t]" nil t)
              (goto-char (match-beginning 0))
@@ -1306,25 +1393,72 @@ If KILL-WHEN-DONE is non-nil, current draft buffer is killed"
   (let ((alphabet '(?A ?B ?C ?D ?E ?F ?G ?H ?I ?J ?K ?L ?M ?N ?O ?P ?Q ?R ?S ?T ?U ?V ?W ?X ?Y ?Z)))
     (nth (abs (% (random) 26)) alphabet)))
 
+;;;;;;;;;;;;;;;;
 ;;;###autoload
 (defun wl-draft (&optional to subject in-reply-to cc references newsgroups
                           mail-followup-to
                           content-type content-transfer-encoding
-                          body edit-again summary-buf from)
+                          body edit-again summary-buf from parent-folder)
   "Write and send mail/news message with Wanderlust."
   (interactive)
-  (unless (featurep 'wl)
-    (require 'wl))
+  (require 'wl)
   (unless wl-init
     (wl-load-profile)
     (wl-folder-init)
     (elmo-init)
     (wl-plugged-init t))
-  (wl-init) ; returns immediately if already initialized.
-  (if (interactive-p)
-      (setq summary-buf (wl-summary-get-buffer (wl-summary-buffer-folder-name))))
-  (let ((draft-folder (wl-folder-get-elmo-folder wl-draft-folder))
-       buf-name file-name num wl-demo change-major-mode-hook)
+  (let (wl-demo)
+    (wl-init)) ; returns immediately if already initialized.
+
+  (let (buf-name header-alist)
+    (setq buf-name
+         (wl-draft-create-buffer
+          (or
+           (eq this-command 'wl-draft)
+           (eq this-command 'wl-summary-write)
+           (eq this-command 'wl-summary-write-current-folder))
+          parent-folder summary-buf))
+    (setq header-alist
+         (list
+          (cons "From: " (or from wl-from))
+          (cons "To: " (or to
+                           (and
+                            (or (interactive-p)
+                                (eq this-command 'wl-summary-write))
+                            "")))
+          (cons "Cc: " cc)
+          (cons "Subject: " (or subject ""))
+          (cons "Newsgroups: " newsgroups)
+          (cons "Mail-Followup-To: " mail-followup-to)
+          (cons "In-Reply-To: " in-reply-to)
+          (cons "References: " references)))
+    (setq header-alist (append header-alist
+                              (wl-draft-default-headers)
+                              (if body (list "" body))))
+    (wl-draft-create-contents header-alist)
+    (if edit-again
+       (wl-draft-decode-body
+        content-type content-transfer-encoding))
+    (wl-draft-insert-mail-header-separator)
+    (wl-draft-prepare-edit)
+    (if (interactive-p)
+       (run-hooks 'wl-mail-setup-hook))
+
+    (goto-char (point-min))
+    (wl-user-agent-compose-internal) ;; user-agent
+    (cond ((eq this-command 'wl-summary-write-current-newsgroup)
+          (mail-position-on-field "Subject"))
+         ((and (interactive-p) (null to))
+          (mail-position-on-field "To"))
+         (t
+          (goto-char (point-max))))
+    buf-name))
+
+(defun wl-draft-create-buffer (&optional full parent-folder summary-buf)
+  (let* ((draft-folder (wl-folder-get-elmo-folder wl-draft-folder))
+        (parent-folder (or parent-folder (wl-summary-buffer-folder-name)))
+        (summary-buf (or summary-buf (wl-summary-get-buffer parent-folder)))
+       buf-name file-name num change-major-mode-hook)
     (if (not (elmo-folder-message-file-p draft-folder))
        (error "%s folder cannot be used for draft folder" wl-draft-folder))
     (setq num (elmo-max-of-list
@@ -1346,94 +1480,111 @@ If KILL-WHEN-DONE is non-nil, current draft buffer is killed"
                           (buffer-name)))
        (rename-buffer (concat wl-draft-folder "/" (int-to-string num))))
     (if (or (eq wl-draft-reply-buffer-style 'full)
-           (eq this-command 'wl-draft)
-           (eq this-command 'wl-summary-write)
-           (eq this-command 'wl-summary-write-current-folder))
+           full)
        (delete-other-windows))
     (auto-save-mode -1)
     (wl-draft-mode)
+    (make-local-variable 'truncate-partial-width-windows)
+    (setq truncate-partial-width-windows nil)
     (setq truncate-lines wl-draft-truncate-lines)
     (setq wl-sent-message-via nil)
-    (if (stringp (or from wl-from))
-       (insert "From: " (or from wl-from) "\n"))
-    (and (or (interactive-p)
-            (eq this-command 'wl-summary-write)
-            to)
-        (insert "To: " (or to "") "\n"))
-    (and cc (insert "Cc: " (or cc "") "\n"))
-    (insert "Subject: " (or subject "") "\n")
-    (and newsgroups (insert "Newsgroups: " newsgroups "\n"))
-    (and mail-followup-to (insert "Mail-Followup-To: " mail-followup-to "\n"))
-    (and wl-insert-mail-reply-to
-        (insert "Mail-Reply-To: "
-                (wl-address-header-extract-address
-                 wl-from) "\n"))
-    (and in-reply-to (insert "In-Reply-To: " in-reply-to "\n"))
-    (and references (insert "References: " references "\n"))
-    (insert (funcall wl-generate-mailer-string-function) "\n")
+    (setq wl-sent-message-queued nil)
     (setq wl-draft-buffer-file-name file-name)
-    (if mail-default-reply-to
-       (insert "Reply-To: " mail-default-reply-to "\n"))
-    (wl-draft-insert-ccs "Bcc: " (or wl-bcc
-                              (and mail-self-blind (user-login-name))))
-    (wl-draft-insert-ccs "Fcc: " wl-fcc)
-    (if wl-organization
-       (insert "Organization: " wl-organization "\n"))
-    (and wl-auto-insert-x-face
-        (file-exists-p wl-x-face-file)
-        (wl-draft-insert-x-face-field-here))
-    (if mail-default-headers
-       (insert mail-default-headers))
-    (if (not (= (preceding-char) ?\n))
-       (insert ?\n))
-    (if edit-again
-       (let (start)
-         (setq start (point))
-         (when content-type
-           (insert "Content-type: " content-type "\n"))
-         (when content-transfer-encoding
-           (insert "Content-Transfer-Encoding: " content-transfer-encoding "\n"))
-         (if (or content-type content-transfer-encoding)
-             (insert "\n"))
-         (and body (insert body))
-         (save-restriction
-           (narrow-to-region start (point))
-           (and edit-again
-                (wl-draft-decode-message-in-buffer))
-           (widen)
-           (goto-char start)
-           (put-text-property (point)
-                              (progn
-                                (insert mail-header-separator "\n")
-                                (1- (point)))
-                              'category 'mail-header-separator)))
-      (put-text-property (point)
-                        (progn
-                          (insert mail-header-separator "\n")
-                          (1- (point)))
-                        'category 'mail-header-separator)
-      (and body (insert body)))
-    (as-binary-output-file
-     (write-region (point-min)(point-max) wl-draft-buffer-file-name
-                  nil t))
+    (setq wl-draft-config-exec-flag t)
+    (setq wl-draft-parent-folder parent-folder)
+    (setq wl-draft-buffer-cur-summary-buffer summary-buf)
+    buf-name))
+
+(defun wl-draft-create-contents (header-alist)
+  "header-alist' sample
+'(function  ;; funcall
+  string    ;; insert string
+  (string . string)    ;;  insert string string
+  (string . function)  ;;  insert string (funcall)
+  (string . nil)       ;;  insert nothing
+  (function . (arg1 arg2 ..))  ;; call function with argument
+  nil                  ;;  insert nothing
+"
+  (unless (eq major-mode 'wl-draft-mode)
+    (error "wl-draft-create-header must be use in wl-draft-mode."))
+  (let ((halist header-alist)
+       field value)
+    (while halist
+      (cond
+       ;; function
+       ((functionp (car halist)) (funcall (car halist)))
+       ;; string
+       ((stringp (car halist)) (insert (car halist) "\n"))
+       ;; cons
+       ((consp (car halist))
+       (setq field (car (car halist)))
+       (setq value (cdr (car halist)))
+       (cond
+        ((functionp field) (apply field value))
+        ((stringp field)
+         (cond
+          ((stringp value) (insert field value "\n"))
+          ((functionp value) (insert field (funcall value) "\n"))
+          ((not value))
+          (t
+           (debug))))
+        ;;
+        ((not field))
+        (t
+         (debug))
+        )))
+      (setq halist (cdr halist)))))
+
+(defun wl-draft-prepare-edit ()
+  (unless (eq major-mode 'wl-draft-mode)
+    (error "wl-draft-create-header must be use in wl-draft-mode."))
+  (let (change-major-mode-hook)
     (wl-draft-editor-mode)
+    (add-hook 'local-write-file-hooks 'wl-draft-save)
     (wl-draft-overload-functions)
     (wl-highlight-headers 'for-draft)
-    (goto-char (point-min))
-    (setq wl-draft-config-exec-flag t)
-    (if (interactive-p)
-       (run-hooks 'wl-mail-setup-hook))
-    (wl-user-agent-compose-internal) ;; user-agent
-    (cond ((eq this-command 'wl-summary-write-current-newsgroup)
-          (mail-position-on-field "Subject"))
-         ((and (interactive-p) (null to))
-          (mail-position-on-field "To"))
-         (t
-          (goto-char (point-max))))
-    (setq wl-draft-buffer-cur-summary-buffer (or summary-buf
-                                                (get-buffer
-                                                 wl-summary-buffer-name)))
-    buf-name))
+    (wl-draft-save)
+    (clear-visited-file-modtime)))
+
+(defun wl-draft-decode-header ()
+  (save-excursion
+    (std11-narrow-to-header)
+    (wl-draft-decode-message-in-buffer)
+    (widen)))
+
+(defun wl-draft-decode-body (&optional content-type content-transfer-encoding)
+  (let ((content-type
+        (or content-type
+               (std11-field-body "content-type")))
+       (content-transfer-encoding
+        (or content-transfer-encoding
+            (std11-field-body "content-transfer-encoding")))
+       delimline)
+    (save-excursion
+      (std11-narrow-to-header)
+      (wl-draft-delete-field "content-type")
+      (wl-draft-delete-field "content-transfer-encoding")
+      (goto-char (point-max))
+      (setq delimline (point-marker))
+      (widen)
+      (narrow-to-region delimline (point-max))
+      (goto-char (point-min))
+      (when content-type
+       (insert "Content-type: " content-type "\n"))
+      (when content-transfer-encoding
+       (insert "Content-Transfer-Encoding: " content-transfer-encoding "\n"))
+      (wl-draft-decode-message-in-buffer)
+      (goto-char (point-min))
+      (unless (re-search-forward "^$" (point-at-eol) t)
+       (insert "\n"))
+      (widen)
+      delimline)))
+
+;;; subroutine for wl-draft-create-contents
+;;; must be used in wl-draft-mode
+(defun wl-draft-check-new-line ()
+  (if (not (= (preceding-char) ?\n))
+      (insert ?\n)))
 
 (defsubst wl-draft-insert-ccs (str cc)
   (let ((field
@@ -1450,6 +1601,46 @@ If KILL-WHEN-DONE is non-nil, current draft buffer is killed"
                         (mapcar 'downcase wl-subscribed-mailing-list)))))
        (insert str field "\n"))))
 
+(defsubst wl-draft-default-headers ()
+  (list
+   (cons "Mail-Reply-To: " (and wl-insert-mail-reply-to
+                               (wl-address-header-extract-address
+                                wl-from)))
+   (cons "" wl-generate-mailer-string-function)
+   (cons "Reply-To: " mail-default-reply-to)
+   (cons 'wl-draft-insert-ccs
+        (list "Bcc: " (or wl-bcc
+                          (and mail-self-blind (user-login-name)))))
+   (cons 'wl-draft-insert-ccs
+        (list "Fcc: " wl-fcc))
+   (cons "Organization: " wl-organization)
+   (and wl-auto-insert-x-face
+       (file-exists-p wl-x-face-file)
+       'wl-draft-insert-x-face-field-here) ;; allow nil
+   mail-default-headers
+   ;; check \n at th end of line for `mail-default-headers'
+   'wl-draft-check-new-line
+;   wl-draft-default-headers
+;   'wl-draft-check-new-line
+   ))
+
+(defun wl-draft-insert-mail-header-separator (&optional delimline)
+  (save-excursion
+    (if delimline
+       (goto-char delimline)
+      (goto-char (point-min))
+      (if (search-forward "\n\n" nil t)
+         (delete-backward-char 1)
+       (goto-char (point-max))))
+    (wl-draft-check-new-line)
+    (put-text-property (point)
+                      (progn
+                        (insert mail-header-separator "\n")
+                        (1- (point)))
+                      'category 'mail-header-separator)))
+
+;;;;;;;;;;;;;;;;
+
 (defun wl-draft-elmo-nntp-send ()
   (let ((elmo-nntp-post-pre-hook wl-news-send-pre-hook)
        (elmo-nntp-default-user
@@ -1492,35 +1683,49 @@ If KILL-WHEN-DONE is non-nil, current draft buffer is killed"
 (defun wl-draft-reedit (number)
   (let ((draft-folder (wl-folder-get-elmo-folder wl-draft-folder))
        (wl-draft-reedit t)
-       buf-name file-name change-major-mode-hook)
+       buffer file-name change-major-mode-hook)
     (setq file-name (elmo-message-file-name draft-folder number))
     (unless (file-exists-p file-name)
       (error "File %s does not exist" file-name))
-    (setq buf-name (find-file-noselect file-name))
-    (if wl-draft-use-frame
-       (switch-to-buffer-other-frame buf-name)
-      (switch-to-buffer buf-name))
-    (set-buffer buf-name)
-    (if (not (string-match (regexp-quote wl-draft-folder)
-                          (buffer-name)))
-       (rename-buffer (concat wl-draft-folder "/" (buffer-name))))
-    (auto-save-mode -1)
-    (wl-draft-mode)
-    (setq wl-sent-message-via nil)
-    (setq wl-draft-buffer-file-name file-name)
-    (wl-draft-config-info-operation number 'load)
-    (goto-char (point-min))
-    (or (re-search-forward "\n\n" nil t)
-       (search-forward (concat mail-header-separator "\n") nil t))
-    (write-region (point-min)(point-max) wl-draft-buffer-file-name
-                 nil t)
-    (wl-draft-overload-functions)
-    (wl-draft-editor-mode)
-    (wl-highlight-headers 'for-draft)
-    (run-hooks 'wl-draft-reedit-hook)
-    (goto-char (point-max))
-    buf-name
-    ))
+    (if (setq buffer (get-buffer
+                     (concat wl-draft-folder "/"
+                             (number-to-string number))))
+       (progn
+         (if wl-draft-use-frame
+             (switch-to-buffer-other-frame buffer)
+           (switch-to-buffer buffer))
+         (set-buffer buffer))
+      (setq buffer (get-buffer-create (number-to-string number)))
+      (if wl-draft-use-frame
+         (switch-to-buffer-other-frame buffer)
+       (switch-to-buffer buffer))
+      (set-buffer buffer)
+      (insert-file-contents-as-binary file-name)
+      (let((mime-edit-again-ignored-field-regexp
+           "^\\(Content-.*\\|Mime-Version\\):"))
+       (wl-draft-decode-message-in-buffer))
+      (wl-draft-insert-mail-header-separator)
+      (if (not (string-match (regexp-quote wl-draft-folder)
+                            (buffer-name)))
+         (rename-buffer (concat wl-draft-folder "/" (buffer-name))))
+      (auto-save-mode -1)
+      (wl-draft-mode)
+      (setq buffer-file-name file-name)
+      (make-local-variable 'truncate-partial-width-windows)
+      (setq truncate-partial-width-windows nil)
+      (setq truncate-lines wl-draft-truncate-lines)
+      (setq wl-sent-message-via nil)
+      (setq wl-sent-message-queued nil)
+      (setq wl-draft-buffer-file-name file-name)
+      (wl-draft-config-info-operation number 'load)
+      (goto-char (point-min))
+      (wl-draft-overload-functions)
+      (wl-draft-editor-mode)
+      (add-hook 'local-write-file-hooks 'wl-draft-save)
+      (wl-highlight-headers 'for-draft)
+      (run-hooks 'wl-draft-reedit-hook)
+      (goto-char (point-max))
+      buffer)))
 
 (defmacro wl-draft-body-goto-top ()
   (` (progn
@@ -1556,6 +1761,18 @@ If KILL-WHEN-DONE is non-nil, current draft buffer is killed"
   (wl-draft-config-body-goto-header)
   (if content (insert (concat (eval content) "\n"))))
 
+(defun wl-draft-config-sub-header-top (content)
+  (goto-char (point-min))
+  (if content (insert (concat (eval content) "\n"))))
+
+(defun wl-draft-config-sub-part-top (content)
+  (goto-char (mime-edit-content-beginning))
+  (if content (insert (concat (eval content) "\n"))))
+
+(defun wl-draft-config-sub-part-bottom (content)
+  (goto-char (mime-edit-content-end))
+  (if content (insert (concat (eval content) "\n"))))
+
 (defsubst wl-draft-config-sub-file (content)
   (let ((coding-system-for-read wl-cs-autoconv)
        (file (expand-file-name (eval content))))
@@ -1761,7 +1978,9 @@ If KILL-WHEN-DONE is non-nil, current draft buffer is killed"
                                wl-draft-config-variables
                                (list 'wl-draft-fcc-list)))))
        (if add-sent-message-via
-           (push 'wl-sent-message-via variables))
+           (progn
+             (push 'wl-sent-message-queued variables)
+             (push 'wl-sent-message-via variables)))
        (while (setq variable (pop variables))
          (when (boundp variable)
            (wl-append alist
@@ -1800,7 +2019,9 @@ If KILL-WHEN-DONE is non-nil, current draft buffer is killed"
   "Flush draft queue."
   (interactive)
   (let* ((queue-folder (wl-folder-get-elmo-folder wl-queue-folder))
-        (msgs2 (elmo-folder-list-messages queue-folder))
+        (msgs2 (progn
+                 (elmo-folder-open-internal queue-folder)
+                 (elmo-folder-list-messages queue-folder)))
         (i 0)
         (performed 0)
         (wl-draft-queue-flushing t)
@@ -1811,9 +2032,10 @@ If KILL-WHEN-DONE is non-nil, current draft buffer is killed"
       (catch 'found
        (while sent-via
          (when (and (eq (nth 1 (car sent-via)) 'unplugged)
-                    (elmo-plugged-p
-                     (car (nth 2 (car sent-via)))
-                     (cdr (nth 2 (car sent-via)))))
+                    (or (not (nth 2 (car sent-via)))
+                        (elmo-plugged-p
+                         (car (nth 2 (car sent-via)))
+                         (cdr (nth 2 (car sent-via))))))
            (wl-append msgs (list (car msgs2)))
            (throw 'found t))
          (setq sent-via (cdr sent-via))))
@@ -1850,15 +2072,21 @@ If KILL-WHEN-DONE is non-nil, current draft buffer is killed"
                   (setq failure t))
                  (quit
                   (setq failure t)))
-               (unless failure
-                 (elmo-folder-delete-messages
-                  queue-folder (cons (car msgs) nil))
-                 (wl-draft-queue-info-operation (car msgs) 'delete)
-                 (setq performed (+ 1 performed)))
+               (if (eq wl-sent-message-modified 'requeue)
+                   (progn
+                     (elmo-folder-delete-messages
+                      queue-folder (cons (car msgs) nil))
+                     (wl-draft-queue-info-operation (car msgs) 'delete))
+                 (unless failure
+                   (elmo-folder-delete-messages
+                    queue-folder (cons (car msgs) nil))
+                   (wl-draft-queue-info-operation (car msgs) 'delete)
+                   (setq performed (+ 1 performed))))
                (setq msgs (cdr msgs)))
              (kill-buffer buffer)
              (message "%d message(s) are sent." performed)))
        (message "%d message(s) are remained to be sent." len))
+      (elmo-folder-close queue-folder)
       len)))
 
 (defun wl-jump-to-draft-buffer (&optional arg)
@@ -1874,7 +2102,8 @@ If KILL-WHEN-DONE is non-nil, current draft buffer is killed"
          buf draft-bufs)
       (while bufs
        (if (and
-            (setq buf (buffer-file-name (car bufs)))
+            (setq buf (with-current-buffer (car bufs)
+                        wl-draft-buffer-file-name))
             (string-match draft-regexp buf))
            (setq draft-bufs (cons (buffer-name (car bufs)) draft-bufs)))
        (setq bufs (cdr bufs)))