Fixed docstring.
[elisp/wanderlust.git] / elmo / elmo-nntp.el
index ee7dda8..d18689d 100644 (file)
@@ -1,10 +1,13 @@
-;;; elmo-nntp.el -- NNTP Interface for ELMO.
+;;; elmo-nntp.el --- NNTP Interface for ELMO.
 
-;; Copyright 1998,1999,2000 Yuuichi Teranishi <teranisi@gohome.org>
+;; Copyright (C) 1998,1999,2000 Yuuichi Teranishi <teranisi@gohome.org>
+;; Copyright (C) 1998,1999,2000 Masahiro MURATA <muse@ba2.so-net.ne.jp>
+;; Copyright (C) 1999,2000      Kenichi OKADA <okada@opaopa.org>
 
 ;; Author: Yuuichi Teranishi <teranisi@gohome.org>
+;;     Masahiro MURATA <muse@ba2.so-net.ne.jp>
+;;     Kenichi OKADA <okada@opaopa.org>
 ;; Keywords: mail, net news
-;; Time-stamp: <00/03/14 19:41:50 teranisi>
 
 ;; This file is part of ELMO (Elisp Library for Message Orchestration).
 
 ;;
 
 ;;; Commentary:
-;; 
+;;
 
 ;;; Code:
-;; 
+;;
 
+(require 'elmo-vars)
+(require 'elmo-util)
+(require 'elmo-date)
 (require 'elmo-msgdb)
-(eval-when-compile
-  (condition-case nil
-      (progn
-       (require 'starttls))
-    (error))
-  (require 'elmo-cache)
-  (require 'elmo-util)
-  (defun-maybe starttls-negotiate (a)))
+(require 'elmo-cache)
+(require 'elmo)
+(require 'elmo-net)
+
+(defvar elmo-nntp-overview-fetch-chop-length 200
+ "*Number of overviews to fetch in one request in nntp.")
+
+(defvar elmo-nntp-use-cache t
+  "Use cache in nntp folder.")
+
+(defvar elmo-nntp-max-number-precedes-list-active nil
+  "Non-nil means max number of msgdb is set as the max number of `list active'.
+\(Needed for inn 2.3 or later?\).")
+
+(defvar elmo-nntp-group-coding-system nil
+  "A coding system for newsgroup string.")
+
+(defsubst elmo-nntp-encode-group-string (string)
+  (if elmo-nntp-group-coding-system
+      (encode-coding-string string elmo-nntp-group-coding-system)
+    string))
+
+(defsubst elmo-nntp-decode-group-string (string)
+  (if elmo-nntp-group-coding-system
+      (decode-coding-string string elmo-nntp-group-coding-system)
+    string))
+
+;;; ELMO NNTP folder
+(eval-and-compile
+  (luna-define-class elmo-nntp-folder (elmo-net-folder)
+                    (group temp-crosses reads))
+  (luna-define-internal-accessors 'elmo-nntp-folder))
+
+(luna-define-method elmo-folder-initialize :around ((folder
+                                                    elmo-nntp-folder)
+                                                   name)
+  (let ((elmo-network-stream-type-alist
+        (if elmo-nntp-stream-type-alist
+            (setq elmo-network-stream-type-alist
+                  (append elmo-nntp-stream-type-alist
+                          elmo-network-stream-type-alist))
+          elmo-network-stream-type-alist))
+       parse)
+    (setq name (luna-call-next-method))
+    (setq parse (elmo-parse-token name ":"))
+    (elmo-nntp-folder-set-group-internal folder
+                                        (elmo-nntp-encode-group-string
+                                         (car parse)))
+    (setq parse (elmo-parse-prefixed-element ?: (cdr parse)))
+    (elmo-net-folder-set-user-internal folder
+                                      (if (eq (length (car parse)) 0)
+                                          elmo-nntp-default-user
+                                        (car parse)))
+    (unless (elmo-net-folder-server-internal folder)
+      (elmo-net-folder-set-server-internal folder
+                                          elmo-nntp-default-server))
+    (unless (elmo-net-folder-port-internal folder)
+      (elmo-net-folder-set-port-internal folder
+                                        elmo-nntp-default-port))
+    (unless (elmo-net-folder-stream-type-internal folder)
+      (elmo-net-folder-set-stream-type-internal
+       folder
+       (elmo-get-network-stream-type
+       elmo-nntp-default-stream-type)))
+    folder))
+
+(luna-define-method elmo-folder-expand-msgdb-path ((folder elmo-nntp-folder))
+  (convert-standard-filename
+   (expand-file-name
+    (elmo-nntp-folder-group-internal folder)
+    (expand-file-name (or (elmo-net-folder-server-internal folder) "nowhere")
+                     (expand-file-name "nntp"
+                                       elmo-msgdb-directory)))))
+
+(luna-define-method elmo-folder-newsgroups ((folder elmo-nntp-folder))
+  (list (elmo-nntp-folder-group-internal folder)))
+
+;;; NNTP Session
+(eval-and-compile
+  (luna-define-class elmo-nntp-session (elmo-network-session)
+                    (current-group))
+  (luna-define-internal-accessors 'elmo-nntp-session))
 
 ;;
 ;; internal variables
 Don't cache if nil.")
 
 (defvar elmo-nntp-list-folders-cache nil)
-(defvar elmo-nntp-groups-hashtb nil)
+
 (defvar elmo-nntp-groups-async nil)
 (defvar elmo-nntp-header-fetch-chop-length 200)
 
@@ -71,6 +151,8 @@ Don't cache if nil.")
 
 (defvar elmo-nntp-default-use-list-active t)
 
+(defvar elmo-nntp-default-use-xhdr t)
+
 (defvar elmo-nntp-server-command-alist nil)
 
 
@@ -78,135 +160,146 @@ Don't cache if nil.")
                                           (listgroup . 1)
                                           (list-active . 2)))
 
-(put 'elmo-nntp-setting 'lisp-indent-function 1)
-
-(defmacro elmo-nntp-setting (spec &rest body)
-  (` (let* ((ssl (elmo-nntp-spec-ssl (, spec)))
-           (port (elmo-nntp-spec-port (, spec)))
-           (user (elmo-nntp-spec-username (, spec)))
-           (server (elmo-nntp-spec-hostname (, spec)))
-           (folder (elmo-nntp-spec-group (, spec)))
-           (connection (elmo-nntp-get-connection server user port ssl))
-           (buffer  (car connection))
-           (process (cadr connection)))
-       (,@ body))))
-
-(defmacro elmo-nntp-get-server-command (server port)
-  (` (assoc (cons (, server) (, port)) elmo-nntp-server-command-alist)))
+(defmacro elmo-nntp-get-server-command (session)
+  (` (assoc (cons (elmo-network-session-server-internal (, session))
+                 (elmo-network-session-port-internal (, session)))
+           elmo-nntp-server-command-alist)))
 
-(defmacro elmo-nntp-set-server-command (server port com value)
+(defmacro elmo-nntp-set-server-command (session com value)
   (` (let (entry)
        (unless (setq entry (cdr (elmo-nntp-get-server-command
-                                (, server) (, port))))
+                                (, session))))
         (setq elmo-nntp-server-command-alist
               (nconc elmo-nntp-server-command-alist
-                     (list (cons (cons (, server) (, port))
-                                 (setq entry
-                                       (vector
-                                        elmo-nntp-default-use-xover
-                                        elmo-nntp-default-use-listgroup
-                                        elmo-nntp-default-use-list-active))
-                                 )))))
+                     (list (cons
+                            (cons
+                             (elmo-network-session-server-internal (, session))
+                             (elmo-network-session-port-internal (, session)))
+                            (setq entry
+                                  (vector
+                                   elmo-nntp-default-use-xover
+                                   elmo-nntp-default-use-listgroup
+                                   elmo-nntp-default-use-list-active
+                                   elmo-nntp-default-use-xhdr)))))))
        (aset entry
             (cdr (assq (, com) elmo-nntp-server-command-index))
             (, value)))))
 
-(defmacro elmo-nntp-xover-p (server port)
-  (` (let ((entry (elmo-nntp-get-server-command (, server) (, port))))
+(defmacro elmo-nntp-xover-p (session)
+  (` (let ((entry (elmo-nntp-get-server-command (, session))))
        (if entry
           (aref (cdr entry)
                 (cdr (assq 'xover elmo-nntp-server-command-index)))
         elmo-nntp-default-use-xover))))
 
-(defmacro elmo-nntp-set-xover (server port value)
-  (` (elmo-nntp-set-server-command (, server) (, port) 'xover (, value))))
+(defmacro elmo-nntp-set-xover (session value)
+  (` (elmo-nntp-set-server-command (, session) 'xover (, value))))
 
-(defmacro elmo-nntp-listgroup-p (server port)
-  (` (let ((entry (elmo-nntp-get-server-command (, server) (, port))))
+(defmacro elmo-nntp-listgroup-p (session)
+  (` (let ((entry (elmo-nntp-get-server-command (, session))))
        (if entry
           (aref (cdr entry)
                 (cdr (assq 'listgroup elmo-nntp-server-command-index)))
         elmo-nntp-default-use-listgroup))))
 
-(defmacro elmo-nntp-set-listgroup (server port value)
-  (` (elmo-nntp-set-server-command (, server) (, port) 'listgroup (, value))))
+(defmacro elmo-nntp-set-listgroup (session value)
+  (` (elmo-nntp-set-server-command (, session) 'listgroup (, value))))
 
-(defmacro elmo-nntp-list-active-p (server port)
-  (` (let ((entry (elmo-nntp-get-server-command (, server) (, port))))
+(defmacro elmo-nntp-list-active-p (session)
+  (` (let ((entry (elmo-nntp-get-server-command (, session))))
        (if entry
           (aref (cdr entry)
                 (cdr (assq 'list-active elmo-nntp-server-command-index)))
         elmo-nntp-default-use-list-active))))
 
-(defmacro elmo-nntp-set-list-active (server port value)
-  (` (elmo-nntp-set-server-command (, server) (, port) 'list-active (, value))))
+(defmacro elmo-nntp-set-list-active (session value)
+  (` (elmo-nntp-set-server-command (, session) 'list-active (, value))))
+
+(defmacro elmo-nntp-xhdr-p (session)
+  (` (let ((entry (elmo-nntp-get-server-command (, session))))
+       (if entry
+          (aref (cdr entry)
+                (cdr (assq 'xhdr elmo-nntp-server-command-index)))
+        elmo-nntp-default-use-xhdr))))
+
+(defmacro elmo-nntp-set-xhdr (session value)
+  (` (elmo-nntp-set-server-command (, session) 'xhdr (, value))))
 
 (defsubst elmo-nntp-max-number-precedes-list-active-p ()
   elmo-nntp-max-number-precedes-list-active)
 
-(defsubst elmo-nntp-folder-postfix (user server port ssl)
+(defsubst elmo-nntp-folder-postfix (user server port type)
   (concat
    (and user (concat ":" user))
    (if (and server
-           (null (string= server elmo-default-nntp-server)))
+           (null (string= server elmo-nntp-default-server)))
        (concat "@" server))
    (if (and port
-           (null (eq port elmo-default-nntp-port)))
+           (null (eq port elmo-nntp-default-port)))
        (concat ":" (if (numberp port)
                       (int-to-string port) port)))
-   (unless (eq ssl elmo-default-nntp-ssl)
-     (if (eq ssl 'starttls)
-        "!!"
-       (if ssl "!")))))
-
-(defun elmo-nntp-flush-connection ()
-  (interactive)
-  (let ((cache elmo-nntp-connection-cache)
-       buffer process)
-    (while cache
-      (setq buffer (car (cdr (car cache))))
-      (if buffer (kill-buffer buffer))
-      (setq process (car (cdr (cdr (car cache)))))
-      (if process (delete-process process))
-      (setq cache (cdr cache)))
-    (setq elmo-nntp-connection-cache nil)))
-
-(defun elmo-nntp-get-connection (server user port ssl)
-  (let* ((user-at-host (format "%s@%s" user server))
-        (user-at-host-on-port (concat 
-                               user-at-host ":" (int-to-string port)
-                               (if (eq ssl 'starttls) "!!" (if ssl "!"))))
-        ret-val result buffer process errmsg proc-stat)
-    (if (not (elmo-plugged-p server port))
-       (error "Unplugged"))
-    (setq ret-val (assoc user-at-host-on-port elmo-nntp-connection-cache))
-    (if (and ret-val 
-            (or (eq  (setq proc-stat 
-                           (process-status (cadr (cdr ret-val))))
-                     'closed)
-                (eq proc-stat 'exit)))
-       ;; connection is closed...
-       (progn
-         (kill-buffer (car (cdr ret-val)))
-         (setq elmo-nntp-connection-cache 
-               (delete ret-val elmo-nntp-connection-cache))
-         (setq ret-val nil)))
-    (if ret-val
-       (cdr ret-val)
-      (setq result (elmo-nntp-open-connection server user port ssl))
-      (if (null result)
-         (progn
-           (if process (delete-process process))
-           (if buffer (kill-buffer buffer))
-           (error "Connection failed"))
-       (setq buffer (car result))
-       (setq process (cdr result))
-       (setq elmo-nntp-connection-cache
-             (nconc elmo-nntp-connection-cache
-                    (list
-                     (cons user-at-host-on-port
-                           (setq ret-val (list buffer process nil))))))
-       ret-val))))
+   (unless (eq (elmo-network-stream-type-symbol type)
+              elmo-nntp-default-stream-type)
+     (elmo-network-stream-type-spec-string type))))
+
+(defun elmo-nntp-get-session (folder &optional if-exists)
+  (elmo-network-get-session
+   'elmo-nntp-session
+   (concat
+    (if (elmo-folder-biff-internal folder)
+       "BIFF-")
+    "NNTP")
+   folder
+   if-exists))
+
+(luna-define-method elmo-network-initialize-session ((session
+                                                     elmo-nntp-session))
+  (let ((process (elmo-network-session-process-internal session))
+       response)
+    (set-process-filter (elmo-network-session-process-internal session)
+                       'elmo-nntp-process-filter)
+    (with-current-buffer (elmo-network-session-buffer session)
+      (setq elmo-nntp-read-point (point-min))
+      ;; Skip garbage output from process before greeting.
+      (while (and (memq (process-status process) '(open run))
+                 (goto-char (point-max))
+                 (forward-line -1)
+                 (not (looking-at "^[2-5][0-9][0-9]")))
+       (accept-process-output process 1))
+      (setq elmo-nntp-read-point (point))
+      (setq response (elmo-nntp-read-response session t t))
+      (unless (car response)
+         (signal 'elmo-open-error (list (cdr response))))
+      (if elmo-nntp-send-mode-reader
+         (elmo-nntp-send-mode-reader session))
+      (when (eq (elmo-network-stream-type-symbol
+                (elmo-network-session-stream-type-internal session))
+               'starttls)
+       (elmo-nntp-send-command session "starttls")
+       (or (elmo-nntp-read-response session)
+           (error "Cannot open starttls session"))
+       (starttls-negotiate process)))))
+
+(luna-define-method elmo-network-authenticate-session ((session
+                                                       elmo-nntp-session))
+  (with-current-buffer (elmo-network-session-buffer session)
+    (when (elmo-network-session-user-internal session)
+      (elmo-nntp-send-command session
+                             (format "authinfo user %s"
+                                     (elmo-network-session-user-internal
+                                      session)))
+      (or (elmo-nntp-read-response session)
+         (signal 'elmo-authenticate-error '(authinfo)))
+      (elmo-nntp-send-command
+       session
+       (format "authinfo pass %s"
+              (elmo-get-passwd (elmo-network-session-password-key session))))
+      (or (elmo-nntp-read-response session)
+         (signal 'elmo-authenticate-error '(authinfo))))))
+
+(luna-define-method elmo-network-setup-session ((session
+                                                elmo-nntp-session))
+  (run-hooks 'elmo-nntp-opened-hook))
 
 (defun elmo-nntp-process-filter (process output)
   (save-excursion
@@ -214,20 +307,34 @@ Don't cache if nil.")
     (goto-char (point-max))
     (insert output)))
 
-(defun elmo-nntp-read-response (buffer process &optional not-command)
-  (save-excursion
-    (set-buffer buffer)
-    (let ((case-fold-search nil)
+(defun elmo-nntp-send-mode-reader (session)
+  (elmo-nntp-send-command session "mode reader")
+  (if (null (elmo-nntp-read-response session t))
+      (message "Mode reader failed")))
+
+(defun elmo-nntp-send-command (session command &optional noerase)
+  (with-current-buffer (elmo-network-session-buffer session)
+    (unless noerase
+      (erase-buffer)
+      (goto-char (point-min)))
+    (setq elmo-nntp-read-point (point))
+    (process-send-string (elmo-network-session-process-internal
+                         session) command)
+    (process-send-string (elmo-network-session-process-internal
+                         session) "\r\n")))
+
+(defun elmo-nntp-read-response (session &optional not-command error-msg)
+  (with-current-buffer (elmo-network-session-buffer session)
+    (let ((process (elmo-network-session-process-internal session))
+         (case-fold-search nil)
          (response-string nil)
          (response-continue t)
-         (return-value nil)
-         match-end)
+         response match-end)
       (while response-continue
        (goto-char elmo-nntp-read-point)
        (while (not (search-forward "\r\n" nil t))
          (accept-process-output process)
          (goto-char elmo-nntp-read-point))
-
        (setq match-end (point))
        (setq response-string
              (buffer-substring elmo-nntp-read-point (- match-end 2)))
@@ -235,81 +342,74 @@ Don't cache if nil.")
        (if (looking-at "[23][0-9]+ .*$")
            (progn (setq response-continue nil)
                   (setq elmo-nntp-read-point match-end)
-                  (setq return-value 
-                        (if return-value 
-                            (concat return-value "\n" response-string)
+                  (setq response
+                        (if response
+                            (concat response "\n" response-string)
                           response-string)))
          (if (looking-at "[^23][0-9]+ .*$")
              (progn (setq response-continue nil)
                     (setq elmo-nntp-read-point match-end)
-                    (setq return-value nil))
+                    (setq response nil))
            (setq elmo-nntp-read-point match-end)
            (if not-command
                (setq response-continue nil))
-           (setq return-value 
-                 (if return-value 
-                     (concat return-value "\n" response-string)
+           (setq response
+                 (if response
+                     (concat response "\n" response-string)
                    response-string)))
          (setq elmo-nntp-read-point match-end)))
-      return-value)))
-
-(defun elmo-nntp-read-raw-response (buffer process)
-  (save-excursion
-    (set-buffer buffer)
-    (let ((case-fold-search nil))
-      (goto-char elmo-nntp-read-point)
-      (while (not (search-forward "\r\n" nil t))
-       (accept-process-output process)
-       (goto-char elmo-nntp-read-point))
-      (buffer-substring elmo-nntp-read-point (- (point) 2)))))
-
-(defun elmo-nntp-read-contents (buffer process)
-  (save-excursion
-    (set-buffer buffer)
-    (let ((case-fold-search nil)
-         match-end)
-      (goto-char elmo-nntp-read-point)
-      (while (not (re-search-forward "^\\.\r\n" nil t))
-       (accept-process-output process)
-       (goto-char elmo-nntp-read-point))
-      (setq match-end (point))
-      (elmo-delete-cr
-       (buffer-substring elmo-nntp-read-point 
-                        (- match-end 3))))))
-
-(defun elmo-nntp-read-body (buffer process outbuf)
-  (with-current-buffer buffer
+      (if error-msg
+         (cons response response-string)
+       response))))
+
+(defun elmo-nntp-read-raw-response (session)
+  (with-current-buffer (elmo-network-session-buffer session)
+    (goto-char elmo-nntp-read-point)
+    (while (not (search-forward "\r\n" nil t))
+      (accept-process-output (elmo-network-session-process-internal
+                             session))
+      (goto-char elmo-nntp-read-point))
+    (buffer-substring elmo-nntp-read-point (- (point) 2))))
+
+(defun elmo-nntp-read-contents (session)
+  (with-current-buffer (elmo-network-session-buffer session)
+    (goto-char elmo-nntp-read-point)
+    (while (not (re-search-forward "^\\.\r\n" nil t))
+      (accept-process-output (elmo-network-session-process-internal
+                             session))
+      (goto-char elmo-nntp-read-point))
+    (elmo-delete-cr
+     (buffer-substring elmo-nntp-read-point
+                      (- (point) 3)))))
+
+(defun elmo-nntp-read-body (session outbuf)
+  (with-current-buffer (elmo-network-session-buffer session)
+    (goto-char elmo-nntp-read-point)
+    (while (not (re-search-forward "^\\.\r\n" nil t))
+      (accept-process-output (elmo-network-session-process-internal session))
+      (goto-char elmo-nntp-read-point))
     (let ((start elmo-nntp-read-point)
-         end)
-      (goto-char start)
-      (while (not (re-search-forward "^\\.\r\n" nil t))
-       (accept-process-output process)
-       (goto-char start))
-      (setq end (point))
+         (end  (point)))
       (with-current-buffer outbuf
        (erase-buffer)
-       (insert-buffer-substring buffer start (- end 3))
-       (elmo-delete-cr-get-content-type)))))
-
-(defun elmo-nntp-goto-folder (server folder user port ssl)
-  (let* ((connection (elmo-nntp-get-connection server user port ssl))
-        (buffer  (car connection))
-        (process (cadr connection))
-        (cwf     (caddr connection)))
-    (save-excursion
-      (condition-case ()
-         (if (not (string= cwf folder))
-             (progn
-               (elmo-nntp-send-command buffer 
-                                       process 
-                                       (format "group %s" folder))
-               (if (elmo-nntp-read-response buffer process)
-                   (setcar (cddr connection) folder)))
-           t)
-       (error
-        nil)))))
-
-(defun elmo-nntp-list-folders-get-cache (folder buf)
+       (insert-buffer-substring (elmo-network-session-buffer session)
+                                start (- end 3))))
+    t))
+
+(defun elmo-nntp-select-group (session group &optional force)
+  (let (response)
+    (when (or force
+             (not (string= (elmo-nntp-session-current-group-internal session)
+                           group)))
+      (unwind-protect
+         (progn
+           (elmo-nntp-send-command session (format "group %s" group))
+           (setq response (elmo-nntp-read-response session)))
+       (elmo-nntp-session-set-current-group-internal session
+                                                     (and response group))
+       response))))
+
+(defun elmo-nntp-list-folders-get-cache (group server buf)
   (when (and elmo-nntp-list-folders-use-cache
             elmo-nntp-list-folders-cache
             (string-match (concat "^"
@@ -317,17 +417,24 @@ Don't cache if nil.")
                                    (or
                                     (nth 1 elmo-nntp-list-folders-cache)
                                     "")))
-                          (or folder "")))
+                          (or group ""))
+            (string-match (concat "^"
+                                  (regexp-quote
+                                   (or
+                                    (nth 2 elmo-nntp-list-folders-cache)
+                                    "")))
+                          (or server "")))
     (let* ((cache-time (car elmo-nntp-list-folders-cache)))
       (unless (elmo-time-expire cache-time
                                elmo-nntp-list-folders-use-cache)
        (save-excursion
          (set-buffer buf)
          (erase-buffer)
-         (insert (nth 2 elmo-nntp-list-folders-cache))
+         (insert (nth 3 elmo-nntp-list-folders-cache))
          (goto-char (point-min))
-         (and folder
-              (keep-lines (concat "^" (regexp-quote folder) "\\.")))
+         (or (string= group "")
+             (and group
+                  (keep-lines (concat "^" (regexp-quote group) "\\."))))
          t
          )))))
 
@@ -343,62 +450,84 @@ Don't cache if nil.")
         msgdb
         (nconc number-alist (list (cons max-number nil)))))))
 
-(defun elmo-nntp-list-folders (spec &optional hierarchy)
-  (elmo-nntp-setting spec
-   (let* ((cwf     (caddr connection))  
-         (tmp-buffer (get-buffer-create " *ELMO NNTP list folders TMP*"))
-         response ret-val top-ng append-serv use-list-active start)
-    (save-excursion
-      (set-buffer tmp-buffer)
-      (if (and folder
-              (elmo-nntp-goto-folder server folder user port ssl))
-         (setq ret-val (list folder))) ;; add top newsgroups
+(luna-define-method elmo-folder-list-subfolders ((folder elmo-nntp-folder)
+                                                &optional one-level)
+  (elmo-nntp-folder-list-subfolders folder one-level))
+
+(defun elmo-nntp-folder-list-subfolders (folder one-level)
+  (let ((session (elmo-nntp-get-session folder))
+       (case-fold-search nil)
+       response ret-val top-ng append-serv use-list-active start)
+    (with-temp-buffer
+      (set-buffer-multibyte nil)
+      (if (and (elmo-nntp-folder-group-internal folder)
+              (elmo-nntp-select-group
+               session
+               (elmo-nntp-folder-group-internal folder)))
+         ;; add top newsgroups
+         (setq ret-val (list (elmo-nntp-folder-group-internal folder))))
       (unless (setq response (elmo-nntp-list-folders-get-cache
-                             folder tmp-buffer))
-       (when (setq use-list-active (elmo-nntp-list-active-p server port))
-         (elmo-nntp-send-command buffer
-                                 process
-                                 (concat "list"
-                                         (if (and folder
-                                                  (null (string= folder "")))
-                                             (concat " active"
-                                                     (format " %s.*" folder) ""))))
-         (if (elmo-nntp-read-response buffer process t)
-             (if (null (setq response (elmo-nntp-read-contents
-                                       buffer process)))
+                             (elmo-nntp-folder-group-internal folder)
+                             (elmo-net-folder-server-internal folder)
+                             (current-buffer)))
+       (when (setq use-list-active (elmo-nntp-list-active-p session))
+         (elmo-nntp-send-command
+          session
+          (concat "list"
+                  (if (and (elmo-nntp-folder-group-internal folder)
+                           (not (string= (elmo-nntp-folder-group-internal
+                                          folder) "")))
+                      (concat " active"
+                              (format " %s.*"
+                                      (elmo-nntp-folder-group-internal folder)
+                                      "")))))
+         (if (elmo-nntp-read-response session t)
+             (if (null (setq response (elmo-nntp-read-contents session)))
                  (error "NNTP List folders failed")
                (when elmo-nntp-list-folders-use-cache
                  (setq elmo-nntp-list-folders-cache
-                       (list (current-time) folder response)))
+                       (list (current-time)
+                             (elmo-nntp-folder-group-internal folder)
+                             (elmo-net-folder-server-internal folder)
+                             response)))
                (erase-buffer)
                (insert response))
-           (elmo-nntp-set-list-active server port nil)
+           (elmo-nntp-set-list-active session nil)
            (setq use-list-active nil)))
        (when (null use-list-active)
-         (elmo-nntp-send-command buffer process "list")
-         (if (null (and (elmo-nntp-read-response buffer process t)
-                        (setq response (elmo-nntp-read-contents
-                                        buffer process))))
+         (elmo-nntp-send-command session "list")
+         (if (null (and (elmo-nntp-read-response session t)
+                        (setq response (elmo-nntp-read-contents session))))
              (error "NNTP List folders failed"))
          (when elmo-nntp-list-folders-use-cache
            (setq elmo-nntp-list-folders-cache
-                 (list (current-time) nil response)))
+                 (list (current-time) nil nil response)))
          (erase-buffer)
          (setq start nil)
          (while (string-match (concat "^"
                                       (regexp-quote
-                                       (or folder "")) ".*$")
+                                       (or
+                                        (elmo-nntp-folder-group-internal
+                                         folder)
+                                        "")) ".*$")
                               response start)
            (insert (match-string 0 response) "\n")
            (setq start (match-end 0)))))
       (goto-char (point-min))
       (let ((len (count-lines (point-min) (point-max)))
            (i 0) regexp)
-       (if hierarchy
+       (if one-level
            (progn
              (setq regexp
                    (format "^\\(%s[^. ]+\\)\\([. ]\\).*\n"
-                           (if folder (concat folder "\\.") "")))
+                           (if (and
+                                (elmo-nntp-folder-group-internal folder)
+                                (null (string=
+                                       (elmo-nntp-folder-group-internal
+                                        folder) "")))
+                               (concat (elmo-nntp-folder-group-internal
+                                        folder)
+                                       "\\.") "")))
              (while (looking-at regexp)
                (setq top-ng (elmo-match-buffer 1))
                (if (string= (elmo-match-buffer 2) " ")
@@ -409,44 +538,56 @@ Don't cache if nil.")
                      (setq ret-val (delete top-ng ret-val)))
                  (if (not (assoc top-ng ret-val))
                      (setq ret-val (nconc ret-val (list (list top-ng))))))
-               (setq i (1+ i))
-               (and (zerop (% i 10))
-                    (elmo-display-progress
-                     'elmo-nntp-list-folders "Parsing active..."
-                     (/ (* i 100) len)))
-               (forward-line 1)
-               ))
+               (when (> len elmo-display-progress-threshold)
+                 (setq i (1+ i))
+                 (if (or (zerop (% i 10)) (= i len))
+                     (elmo-display-progress
+                      'elmo-nntp-list-folders "Parsing active..."
+                      (/ (* i 100) len))))
+               (forward-line 1)))
          (while (re-search-forward "\\([^ ]+\\) .*\n" nil t)
            (setq ret-val (nconc ret-val
                                 (list (elmo-match-buffer 1))))
-           (setq i (1+ i))
-           (and (zerop (% i 10))
-                (elmo-display-progress
-                 'elmo-nntp-list-folders "Parsing active..."
-                 (/ (* i 100) len))))))
-      (kill-buffer tmp-buffer)
-      (unless (string= server elmo-default-nntp-server)
-       (setq append-serv (concat "@" server)))
-      (unless (eq port elmo-default-nntp-port)
-       (setq append-serv (concat append-serv ":" (int-to-string port))))
-      (unless (eq ssl elmo-default-nntp-ssl)
-       (if ssl
-           (setq append-serv (concat append-serv "!")))
-       (if (eq ssl 'starttls)
-           (setq append-serv (concat append-serv "!"))))
-      (mapcar '(lambda (fld)
-                (if (consp fld)
-                    (list (concat "-" (car fld)
-                                  (and user
-                                       (concat ":" user))
-                                  (and append-serv
-                                       (concat append-serv))))
-                  (concat "-" fld
-                          (and user
-                               (concat ":" user))
-                          (and append-serv 
-                               (concat append-serv)))))
-             ret-val)))))
+           (when (> len elmo-display-progress-threshold)
+             (setq i (1+ i))
+             (if (or (zerop (% i 10)) (= i len))
+                 (elmo-display-progress
+                  'elmo-nntp-list-folders "Parsing active..."
+                  (/ (* i 100) len))))))
+       (when (> len elmo-display-progress-threshold)
+         (elmo-display-progress
+          'elmo-nntp-list-folders "Parsing active..." 100))))
+    (unless (string= (elmo-net-folder-server-internal folder)
+                    elmo-nntp-default-server)
+      (setq append-serv (concat "@" (elmo-net-folder-server-internal
+                                    folder))))
+    (unless (eq (elmo-net-folder-port-internal folder) elmo-nntp-default-port)
+      (setq append-serv (concat append-serv
+                               ":" (int-to-string
+                                    (elmo-net-folder-port-internal folder)))))
+    (unless (eq (elmo-network-stream-type-symbol
+                (elmo-net-folder-stream-type-internal folder))
+               elmo-nntp-default-stream-type)
+      (setq append-serv
+           (concat append-serv
+                   (elmo-network-stream-type-spec-string
+                    (elmo-net-folder-stream-type-internal folder)))))
+    (mapcar '(lambda (fld)
+              (if (consp fld)
+                  (list (concat "-" (elmo-nntp-decode-group-string (car fld))
+                                (and (elmo-net-folder-user-internal folder)
+                                     (concat
+                                      ":"
+                                      (elmo-net-folder-user-internal folder)))
+                                (and append-serv
+                                     (concat append-serv))))
+                (concat "-" (elmo-nntp-decode-group-string fld)
+                        (and (elmo-net-folder-user-internal folder)
+                             (concat ":" (elmo-net-folder-user-internal
+                                          folder)))
+                        (and append-serv
+                             (concat append-serv)))))
+           ret-val)))
 
 (defun elmo-nntp-make-msglist (beg-str end-str)
   (elmo-set-work-buf
@@ -462,76 +603,92 @@ Don't cache if nil.")
      (goto-char (point-min))
      (read (current-buffer)))))
 
-(defun elmo-nntp-list-folder (spec)
-  (elmo-nntp-setting spec
-   (let* ((server (format "%s" server)) ;; delete text property
-         response retval use-listgroup)
+(luna-define-method elmo-folder-list-messages-plugged ((folder
+                                                       elmo-nntp-folder)
+                                                      &optional nohide)
+  (let ((session (elmo-nntp-get-session folder))
+       (group   (elmo-nntp-folder-group-internal folder))
+       response numbers use-listgroup)
     (save-excursion
-      (when (setq use-listgroup (elmo-nntp-listgroup-p server port))
-       (elmo-nntp-send-command buffer
-                               process
-                               (format "listgroup %s" folder))
-       (if (not (elmo-nntp-read-response buffer process t))
+      (when (setq use-listgroup (elmo-nntp-listgroup-p session))
+       (elmo-nntp-send-command session
+                               (format "listgroup %s" group))
+       (if (not (elmo-nntp-read-response session t))
            (progn
-             (elmo-nntp-set-listgroup server port nil)
+             (elmo-nntp-set-listgroup session nil)
              (setq use-listgroup nil))
-         (if (null (setq response (elmo-nntp-read-contents buffer process)))
+         (if (null (setq response (elmo-nntp-read-contents session)))
              (error "Fetching listgroup failed"))
-         (setq retval (elmo-string-to-list response))))
-      (if use-listgroup
-         retval
-       (elmo-nntp-send-command buffer 
-                               process 
-                               (format "group %s" folder))
-       (if (null (setq response (elmo-nntp-read-response buffer process)))
-           (error "Select folder failed"))
-       (setcar (cddr connection) folder)
-       (if (and
-            (string-match "211 \\([0-9]+\\) \\([0-9]+\\) \\([0-9]+\\) [^.].+$" 
-                          response)
-            (> (string-to-int (elmo-match-string 1 response)) 0))
-           (elmo-nntp-make-msglist
-            (elmo-match-string 2 response)
-            (elmo-match-string 3 response))
-         nil))))))
-
-(defun elmo-nntp-max-of-folder (spec)
-  (let* ((port (elmo-nntp-spec-port spec))
-        (user (elmo-nntp-spec-username spec))
-        (server (elmo-nntp-spec-hostname spec))
-        (ssl (elmo-nntp-spec-ssl spec))
-        (folder (elmo-nntp-spec-group spec)))
+         (setq numbers (elmo-string-to-list response))
+         (elmo-nntp-session-set-current-group-internal session
+                                                       group)))
+      (unless use-listgroup
+       (elmo-nntp-send-command session (format "group %s" group))
+       (if (null (setq response (elmo-nntp-read-response session)))
+           (error "Select group failed"))
+       (when (and
+              (string-match
+               "211 \\([0-9]+\\) \\([0-9]+\\) \\([0-9]+\\) [^.].+$"
+               response)
+              (> (string-to-int (elmo-match-string 1 response)) 0))
+         (setq numbers (elmo-nntp-make-msglist
+                        (elmo-match-string 2 response)
+                        (elmo-match-string 3 response)))))
+      numbers)))
+
+(luna-define-method elmo-folder-status ((folder elmo-nntp-folder))
+  (elmo-nntp-folder-status folder))
+
+(defun elmo-nntp-folder-status (folder)
+  (let ((killed-list (elmo-msgdb-killed-list-load
+                     (elmo-folder-msgdb-path folder)))
+       end-num entry)
     (if elmo-nntp-groups-async
-       (let* ((fld (concat folder
-                           (elmo-nntp-folder-postfix user server port ssl)))
-              (entry (elmo-get-hash-val fld elmo-nntp-groups-hashtb)))
-         (if entry
-             (cons (nth 2 entry)
-                   (car entry))
-           (error "No such newsgroup \"%s\"" fld)))
-      (let* ((connection (elmo-nntp-get-connection server user port ssl))
-            (buffer  (car connection))
-            (process (cadr connection))
-            response e-num end-num)
-       (if (not connection)
+       (if (setq entry
+                 (elmo-get-hash-val
+                  (concat (elmo-nntp-folder-group-internal folder)
+                          (elmo-nntp-folder-postfix
+                           (elmo-net-folder-user-internal folder)
+                           (elmo-net-folder-server-internal folder)
+                           (elmo-net-folder-port-internal folder)
+                           (elmo-net-folder-stream-type-internal folder)))
+                  elmo-newsgroups-hashtb))
+           (progn
+             (setq end-num (nth 2 entry))
+             (when (and killed-list
+                        (elmo-number-set-member end-num killed-list))
+               ;; Max is killed.
+               (setq end-num nil))
+             (cons end-num (car entry)))
+         (error "No such newsgroup \"%s\""
+                (elmo-nntp-folder-group-internal folder)))
+      (let ((session (elmo-nntp-get-session folder))
+           response e-num)
+       (if (null session)
            (error "Connection failed"))
        (save-excursion
-         (elmo-nntp-send-command buffer 
-                                 process 
-                                 (format "group %s" folder))
-         (setq response (elmo-nntp-read-response buffer process))
-         (if (and response 
-                  (string-match 
-                   "211 \\([0-9]+\\) \\([0-9]+\\) \\([0-9]+\\) [^.].+$" 
+         (elmo-nntp-send-command session
+                                 (format
+                                  "group %s"
+                                  (elmo-nntp-folder-group-internal folder)))
+         (setq response (elmo-nntp-read-response session))
+         (if (and response
+                  (string-match
+                   "211 \\([0-9]+\\) \\([0-9]+\\) \\([0-9]+\\) [^.].+$"
                    response))
              (progn
                (setq end-num (string-to-int
                               (elmo-match-string 3 response)))
                (setq e-num (string-to-int
                             (elmo-match-string 1 response)))
+               (when (and killed-list
+                          (elmo-number-set-member end-num killed-list))
+                 ;; Max is killed.
+                 (setq end-num nil))
                (cons end-num e-num))
            (if (null response)
-               (error "Selecting newsgroup \"%s\" failed" folder)
+               (error "Selecting newsgroup \"%s\" failed"
+                      (elmo-nntp-folder-group-internal folder))
              nil)))))))
 
 (defconst elmo-nntp-overview-index
@@ -545,8 +702,7 @@ Don't cache if nil.")
     ("lines" . 7)
     ("xref" . 8)))
 
-(defun elmo-nntp-create-msgdb-from-overview-string (str 
-                                                   folder
+(defun elmo-nntp-create-msgdb-from-overview-string (str
                                                    new-mark
                                                    already-mark
                                                    seen-mark
@@ -559,12 +715,12 @@ Don't cache if nil.")
     (setq ov-list (elmo-nntp-parse-overview-string str))
     (while ov-list
       (setq ov-entity (car ov-list))
-      ;; INN bug??
-;      (if (or (> (setq num (string-to-int (aref ov-entity 0)))
-;               99999)
-;            (<= num 0))
-;        (setq num 0))
-;     (setq num (int-to-string num))
+;;; INN bug??
+;;;   (if (or (> (setq num (string-to-int (aref ov-entity 0)))
+;;;             99999)
+;;;          (<= num 0))
+;;;      (setq num 0))
+;;;  (setq num (int-to-string num))
       (setq num (string-to-int (aref ov-entity 0)))
       (when (or (null numlist)
                (memq num numlist))
@@ -573,23 +729,24 @@ Don't cache if nil.")
        (while extras
          (setq ext (downcase (car extras)))
          (when (setq field-index (cdr (assoc ext elmo-nntp-overview-index)))
-           (setq field (aref ov-entity field-index))
-           (when (eq field-index 8) ;; xref
-             (setq field (elmo-msgdb-remove-field-string field)))
-           (setq extra (cons (cons ext field) extra)))
+            (when (> (length ov-entity) field-index)
+             (setq field (aref ov-entity field-index))
+             (when (eq field-index 8) ;; xref
+               (setq field (elmo-msgdb-remove-field-string field)))
+              (setq extra (cons (cons ext field) extra))))
          (setq extras (cdr extras)))
        (setq overview
-             (elmo-msgdb-append-element 
+             (elmo-msgdb-append-element
               overview
               (cons (aref ov-entity 4)
                     (vector num
-                            (elmo-msgdb-get-last-message-id 
+                            (elmo-msgdb-get-last-message-id
                              (aref ov-entity 5))
                             ;; from
-                            (elmo-mime-string (elmo-delete-char 
+                            (elmo-mime-string (elmo-delete-char
                                                ?\"
-                                               (or 
-                                                (aref ov-entity 2) 
+                                               (or
+                                                (aref ov-entity 2)
                                                 elmo-no-from) 'uni))
                             ;; subject
                             (elmo-mime-string (or (aref ov-entity 1)
@@ -607,7 +764,8 @@ Don't cache if nil.")
        (setq message-id (aref ov-entity 4))
        (setq seen (member message-id seen-list))
        (if (setq gmark (or (elmo-msgdb-global-mark-get message-id)
-                           (if (elmo-cache-exists-p message-id);; XXX
+                           (if (elmo-file-cache-status
+                                (elmo-file-cache-get message-id))
                                (if seen
                                    nil
                                  already-mark)
@@ -616,154 +774,168 @@ Don't cache if nil.")
                                      seen-mark)
                                new-mark))))
            (setq mark-alist
-                 (elmo-msgdb-mark-append mark-alist 
+                 (elmo-msgdb-mark-append mark-alist
                                          num gmark))))
       (setq ov-list (cdr ov-list)))
     (list overview number-alist mark-alist)))
 
-(defun elmo-nntp-msgdb-create-as-numlist (spec numlist new-mark already-mark
-                                              seen-mark important-mark
-                                              seen-list)
-  "Create msgdb for SPEC for NUMLIST."
-  (elmo-nntp-msgdb-create spec numlist new-mark already-mark
-                         seen-mark important-mark seen-list
-                         t))
-
-(defun elmo-nntp-msgdb-create (spec numlist new-mark already-mark
-                                   seen-mark important-mark 
-                                   seen-list &optional as-num)
-  (when numlist
-    (save-excursion
-     (elmo-nntp-setting spec
-      (let* ((cwf     (caddr connection))
-            (filter  (and as-num numlist))
-            beg-num end-num cur length
-            ret-val ov-str use-xover)
-       (if (and folder
-                (not (string= cwf folder))
-                (null (elmo-nntp-goto-folder server folder user port ssl)))
-           (error "group %s not found" folder))
-       (when (setq use-xover (elmo-nntp-xover-p server port))
-         (setq beg-num (car numlist)
-               cur beg-num
-               end-num (nth (1- (length numlist)) numlist)
-               length  (+ (- end-num beg-num) 1))
-         (message "Getting overview...")
-         (while (<= cur end-num)
-           (elmo-nntp-send-command buffer process 
-                                   (format 
-                                    "xover %s-%s" 
-                                    (int-to-string cur)
-                                    (int-to-string 
-                                     (+ cur 
-                                        elmo-nntp-overview-fetch-chop-length))))
-           (with-current-buffer buffer
-             (if ov-str
-                 (setq ret-val 
-                       (elmo-msgdb-append
-                        ret-val
-                        (elmo-nntp-create-msgdb-from-overview-string 
-                         ov-str
-                         folder
-                         new-mark
-                         already-mark
-                         seen-mark
-                         important-mark
-                         seen-list
-                         filter
-                         )))))
-           (if (null (elmo-nntp-read-response buffer process t))
-               (progn
-                 (setq cur end-num);; exit while loop
-                 (elmo-nntp-set-xover server port nil)
-                 (setq use-xover nil))
-             (if (null (setq ov-str (elmo-nntp-read-contents buffer process)))
-                 (error "Fetching overview failed")))
-           (setq cur (+ elmo-nntp-overview-fetch-chop-length cur 1))
-           (elmo-display-progress
-            'elmo-nntp-msgdb-create "Getting overview..." 
-            (/ (* (+ (- (min cur
-                             end-num)
-                        beg-num) 1) 100) length))))
-       (if (not use-xover)
-           (setq ret-val (elmo-nntp-msgdb-create-by-header
-                          folder buffer process numlist
-                          new-mark already-mark seen-mark seen-list))
-         (with-current-buffer buffer
-           (if ov-str
-               (setq ret-val 
-                     (elmo-msgdb-append
-                      ret-val
-                      (elmo-nntp-create-msgdb-from-overview-string 
-                       ov-str
-                       folder
-                       new-mark
-                       already-mark
-                       seen-mark
-                       important-mark
-                       seen-list
-                       filter)))))
-         (message "Getting overview...done."))
-       ;; If there are canceled messages, overviews are not obtained
-       ;; to max-number(inn 2.3?).
-       (when (and (elmo-nntp-max-number-precedes-list-active-p)
-                  (elmo-nntp-list-active-p server port))
-         (elmo-nntp-send-command buffer process 
-                                 (format "list active %s" folder))
-         (if (null (elmo-nntp-read-response buffer process))
-             (progn
-               (elmo-nntp-set-list-active server port nil)
-               (error "NNTP list command failed")))
-         (elmo-nntp-catchup-msgdb 
-          ret-val 
-          (nth 1 (read (concat "(" (elmo-nntp-read-contents 
-                                    buffer process) ")")))))
-       ret-val)))))
-
-(defun elmo-nntp-sync-number-alist (spec number-alist)
+(luna-define-method elmo-folder-msgdb-create ((folder elmo-nntp-folder)
+                                             numbers new-mark already-mark
+                                             seen-mark important-mark
+                                             seen-list)
+  (elmo-nntp-folder-msgdb-create folder numbers new-mark already-mark
+                                seen-mark important-mark
+                                seen-list))
+
+(defun elmo-nntp-folder-msgdb-create (folder numbers new-mark already-mark
+                                            seen-mark important-mark
+                                            seen-list)
+  (let ((filter numbers)
+       (session (elmo-nntp-get-session folder))
+       beg-num end-num cur length
+       ret-val ov-str use-xover dir)
+    (elmo-nntp-select-group session (elmo-nntp-folder-group-internal
+                                    folder))
+    (when (setq use-xover (elmo-nntp-xover-p session))
+      (setq beg-num (car numbers)
+           cur beg-num
+           end-num (nth (1- (length numbers)) numbers)
+           length  (+ (- end-num beg-num) 1))
+      (message "Getting overview...")
+      (while (<= cur end-num)
+       (elmo-nntp-send-command
+        session
+        (format
+         "xover %s-%s"
+         (int-to-string cur)
+         (int-to-string
+          (+ cur
+             elmo-nntp-overview-fetch-chop-length))))
+       (with-current-buffer (elmo-network-session-buffer session)
+         (if ov-str
+             (setq ret-val
+                   (elmo-msgdb-append
+                    ret-val
+                    (elmo-nntp-create-msgdb-from-overview-string
+                     ov-str
+                     new-mark
+                     already-mark
+                     seen-mark
+                     important-mark
+                     seen-list
+                     filter
+                     )))))
+       (if (null (elmo-nntp-read-response session t))
+           (progn
+             (setq cur end-num);; exit while loop
+             (elmo-nntp-set-xover session nil)
+             (setq use-xover nil))
+         (if (null (setq ov-str (elmo-nntp-read-contents session)))
+             (error "Fetching overview failed")))
+       (setq cur (+ elmo-nntp-overview-fetch-chop-length cur 1))
+       (when (> length elmo-display-progress-threshold)
+         (elmo-display-progress
+          'elmo-nntp-msgdb-create "Getting overview..."
+          (/ (* (+ (- (min cur end-num)
+                      beg-num) 1) 100) length))))
+      (when (> length elmo-display-progress-threshold)
+       (elmo-display-progress
+        'elmo-nntp-msgdb-create "Getting overview..." 100)))
+    (if (not use-xover)
+       (setq ret-val (elmo-nntp-msgdb-create-by-header
+                      session numbers
+                      new-mark already-mark seen-mark seen-list))
+      (with-current-buffer (elmo-network-session-buffer session)
+       (if ov-str
+           (setq ret-val
+                 (elmo-msgdb-append
+                  ret-val
+                  (elmo-nntp-create-msgdb-from-overview-string
+                   ov-str
+                   new-mark
+                   already-mark
+                   seen-mark
+                   important-mark
+                   seen-list
+                   filter))))))
+    (elmo-folder-set-killed-list-internal
+     folder
+     (nconc
+      (elmo-folder-killed-list-internal folder)
+      (car (elmo-list-diff
+           numbers
+           (mapcar 'car
+                   (elmo-msgdb-get-number-alist
+                    ret-val))))))
+    ;; If there are canceled messages, overviews are not obtained
+    ;; to max-number(inn 2.3?).
+    (when (and (elmo-nntp-max-number-precedes-list-active-p)
+              (elmo-nntp-list-active-p session))
+      (elmo-nntp-send-command session
+                             (format "list active %s"
+                                     (elmo-nntp-folder-group-internal
+                                      folder)))
+      (if (null (elmo-nntp-read-response session))
+         (progn
+           (elmo-nntp-set-list-active session nil)
+           (error "NNTP list command failed")))
+      (elmo-nntp-catchup-msgdb
+       ret-val
+       (nth 1 (read (concat "(" (elmo-nntp-read-contents
+                                session) ")")))))
+    ret-val))
+
+(luna-define-method elmo-folder-update-number ((folder elmo-nntp-folder))
   (if (elmo-nntp-max-number-precedes-list-active-p)
-      (elmo-nntp-setting spec
-       (if (elmo-nntp-list-active-p server port)
-           (let* ((cwf (caddr connection))
-                  msgdb-max max-number)
+      (let ((session (elmo-nntp-get-session folder))
+           (number-alist (elmo-msgdb-get-number-alist
+                          (elmo-folder-msgdb folder))))
+       (if (elmo-nntp-list-active-p session)
+           (let (msgdb-max max-number)
              ;; If there are canceled messages, overviews are not obtained
              ;; to max-number(inn 2.3?).
-             (if (and folder
-                      (not (string= cwf folder))
-                      (null (elmo-nntp-goto-folder
-                             server folder user port ssl)))
-                 (error "group %s not found" folder))
-             (elmo-nntp-send-command buffer process
-                                     (format "list active %s" folder))
-             (if (null (elmo-nntp-read-response buffer process))
+             (elmo-nntp-select-group session
+                                     (elmo-nntp-folder-group-internal folder))
+             (elmo-nntp-send-command session
+                                     (format "list active %s"
+                                             (elmo-nntp-folder-group-internal
+                                              folder)))
+             (if (null (elmo-nntp-read-response session))
                  (error "NNTP list command failed"))
              (setq max-number
                    (nth 1 (read (concat "(" (elmo-nntp-read-contents
-                                             buffer process) ")"))))
+                                             session) ")"))))
              (setq msgdb-max
                    (car (nth (max (- (length number-alist) 1) 0)
                              number-alist)))
              (if (or (and number-alist (not msgdb-max))
                      (and msgdb-max max-number
                           (< msgdb-max max-number)))
-                 (nconc number-alist
-                        (list (cons max-number nil)))
-               number-alist))
-         number-alist))))
-
-(defun elmo-nntp-msgdb-create-by-header (folder buffer process numlist
-                                               new-mark already-mark 
-                                               seen-mark seen-list)
-  (let ((tmp-buffer (get-buffer-create " *ELMO Overview TMP*"))
-       ret-val)
-    (elmo-nntp-retrieve-headers
-     buffer tmp-buffer process numlist)
-    (setq ret-val
-         (elmo-nntp-msgdb-create-message
-          tmp-buffer (length numlist) folder new-mark already-mark 
-          seen-mark seen-list))
-    (kill-buffer tmp-buffer)
-    ret-val))
+                 (elmo-msgdb-set-number-alist
+                  (elmo-folder-msgdb folder)
+                  (nconc number-alist
+                         (list (cons max-number nil))))))))))
+
+(defun elmo-nntp-msgdb-create-by-header (session numbers
+                                                new-mark already-mark
+                                                seen-mark seen-list)
+  (with-temp-buffer
+    (elmo-nntp-retrieve-headers session (current-buffer) numbers)
+    (elmo-nntp-msgdb-create-message
+     (length numbers) new-mark already-mark seen-mark seen-list)))
+
+(defun elmo-nntp-parse-xhdr-response (string)
+  (let (response)
+    (with-temp-buffer
+      (insert string)
+      (goto-char (point-min))
+      (while (not (eobp))
+       (if (looking-at "^\\([0-9]+\\) \\(.*\\)$")
+           (setq response (cons (cons (string-to-int (elmo-match-buffer 1))
+                                      (elmo-match-buffer 2))
+                                response)))
+       (forward-line 1)))
+    (nreverse response)))
 
 (defun elmo-nntp-parse-overview-string (string)
   (save-excursion
@@ -778,214 +950,90 @@ Don't cache if nil.")
       (while (not (eobp))
        (end-of-line)
        (setq ret-list (save-match-data
-                        (apply 'vector (split-string 
-                                        (buffer-substring beg (point)) 
+                        (apply 'vector (split-string
+                                        (buffer-substring beg (point))
                                         "\t"))))
        (beginning-of-line)
        (forward-line 1)
        (setq beg (point))
        (setq ret-val (nconc ret-val (list ret-list))))
-;      (kill-buffer tmp-buffer)
+;;;   (kill-buffer tmp-buffer)
       ret-val)))
 
-(defun elmo-nntp-get-overview (server beg end folder user port ssl)
-  (save-excursion
-    (let* ((connection (elmo-nntp-get-connection server user port ssl))
-          (buffer  (car connection))
-          (process (cadr connection))
-;         (cwf     (caddr connection))  
-          response errmsg ov-str)  
-      (catch 'done
-       (if folder
-           (if (null (elmo-nntp-goto-folder server folder user port ssl))
-               (progn
-                 (setq errmsg (format "group %s not found." folder))
-                 (throw 'done nil))))
-       (elmo-nntp-send-command buffer process 
-                               (format "xover %s-%s" beg end))
-       (if (null (setq response (elmo-nntp-read-response
-                                 buffer process t)))
-           (progn
-             (setq errmsg "Getting overview failed.")
-             (throw 'done nil)))
-       (if (null (setq response (elmo-nntp-read-contents
-                                 buffer process)))
-           (progn
-             ;(setq errmsg "Fetching header failed")
-             (throw 'done nil)))
-       (setq ov-str response)
-       )
-      (if errmsg
-         (progn 
-           (message errmsg)
-           nil)
-       ov-str))))
-
-
-(defun elmo-nntp-get-message (server user number folder outbuf port ssl)
-  "Get nntp message on FOLDER at SERVER. 
-Returns message string."
-  (save-excursion
-    (let* ((connection (elmo-nntp-get-connection server user port ssl))
-          (buffer  (car connection))
-          (process (cadr connection))
-          (cwf     (caddr connection))  
-          response errmsg)
-      (catch 'done
-       (if (and folder
-                (not (string= cwf folder)))
-           (if (null (elmo-nntp-goto-folder server folder user port ssl))
-               (progn
-                 (setq errmsg (format "group %s not found." folder))
-                 (throw 'done nil))))
-       (elmo-nntp-send-command buffer process 
-                               (format "article %s" number))
-       (if (null (setq response (elmo-nntp-read-response
-                                 buffer process t)))
-           (progn
-             (setq errmsg "Fetching message failed")
-             (set-buffer outbuf)
-             (erase-buffer)
-             (insert "\n\n")
-             (throw 'done nil)))
-       (setq response (elmo-nntp-read-body buffer process outbuf))
-       (set-buffer outbuf)
-       (goto-char (point-min))
-       (while (re-search-forward "^\\." nil t)
-         (replace-match "")
-         (forward-line))
-       )
-      (if errmsg
-         (progn 
-           (message errmsg)
-           nil))
-      response)))
-
-(defun elmo-nntp-get-newsgroup-by-msgid (msgid server user port ssl)
+(defun elmo-nntp-get-newsgroup-by-msgid (msgid server user port type)
   "Get nntp header string."
   (save-excursion
-    (let* ((connection (elmo-nntp-get-connection server user port ssl))
-          (buffer  (car connection))
-          (process (cadr connection)))
-      (elmo-nntp-send-command buffer process 
+    (let ((session (elmo-nntp-get-session
+                   (luna-make-entity
+                    'elmo-nntp-folder
+                    :user user
+                    :server server
+                    :port port
+                    :stream-type type))))
+      (elmo-nntp-send-command session
                              (format "head %s" msgid))
-      (if (elmo-nntp-read-response buffer process)
-         (elmo-nntp-read-contents buffer process))
-      (set-buffer buffer)
-      (std11-field-body "Newsgroups"))))
-
-(defun elmo-nntp-open-connection (server user portnum ssl)
-  "Open NNTP connection and returns 
-the list of (process session-buffer current-working-folder).
-Return nil if connection failed."
-  (let ((process nil)
-       (host server)
-       (port (or portnum
-                 elmo-default-nntp-port))
-       (user-at-host (format "%s@%s" user server))
-       process-buffer)
-    (as-binary-process
-     (catch 'done
-       (setq process-buffer
-            (get-buffer-create (format " *NNTP session to %s:%d" host port)))
-       (save-excursion
-        (set-buffer process-buffer)
-        (elmo-set-buffer-multibyte nil)
-        (erase-buffer))
-       (setq process
-            (elmo-open-network-stream "NNTP" process-buffer host port ssl))
-       (and (null process) (throw 'done nil))
-       (set-process-filter process 'elmo-nntp-process-filter)
-       ;; flush connections when exiting...?
-       ;; (add-hook 'kill-emacs-hook 'elmo-nntp-flush-connection)
-       (save-excursion
-        (set-buffer process-buffer)
-        (elmo-set-buffer-multibyte nil)
-        (make-local-variable 'elmo-nntp-read-point)
-        (setq elmo-nntp-read-point (point-min))
-        (if (null (elmo-nntp-read-response process-buffer process t))
-            (throw 'done nil))
-        (if elmo-nntp-send-mode-reader
-            (elmo-nntp-send-mode-reader process-buffer process))
-        ;; starttls
-        (if (eq ssl 'starttls)
-            (if (progn
-                  (elmo-nntp-send-command process-buffer process "starttls")
-                  (elmo-nntp-read-response process-buffer process))
-                (starttls-negotiate process)
-              (error "STARTTLS aborted")))
-        (if user
-            (progn
-              (elmo-nntp-send-command process-buffer process
-                                      (format "authinfo user %s" user))
-              (if (null (elmo-nntp-read-response process-buffer process))
-                  (error "Authinfo failed"))
-              (elmo-nntp-send-command process-buffer process
-                                      (format "authinfo pass %s"
-                                              (elmo-get-passwd user-at-host)))
-              (if (null (elmo-nntp-read-response process-buffer process))
-                  (progn
-                    (elmo-remove-passwd user-at-host)
-                    (error "Authinfo failed")))))
-        (run-hooks 'elmo-nntp-opened-hook)) ; XXX
-       (cons process-buffer process)))))
-
-(defun elmo-nntp-send-mode-reader (buffer process)
-  (elmo-nntp-send-command buffer
-                         process
-                         "mode reader")
-  (if (null (elmo-nntp-read-response buffer process t))
-      (error "mode reader failed")))
-  
-(defun elmo-nntp-send-command (buffer process command &optional noerase)
-  "Send COMMAND string to server with sequence number."
-  (save-excursion
-    (set-buffer buffer)
-    (when (not noerase)
-      (erase-buffer)
-      (goto-char (point-min)))
-    (setq elmo-nntp-read-point (point))
-    (process-send-string process command)
-    (process-send-string process "\r\n")))
-
-(defun elmo-nntp-read-msg (spec msg outbuf)
-  (elmo-nntp-get-message (elmo-nntp-spec-hostname spec)
-                        (elmo-nntp-spec-username spec)
-                        msg 
-                        (elmo-nntp-spec-group spec)
-                        outbuf 
-                        (elmo-nntp-spec-port spec)
-                        (elmo-nntp-spec-ssl spec)))
-
-;(defun elmo-msgdb-nntp-overview-create-range (spec beg end mark)
-;    (elmo-nntp-overview-create-range hostname beg end mark folder)))
-
-;(defun elmo-msgdb-nntp-max-of-folder (spec)
-;    (elmo-nntp-max-of-folder hostname folder)))
+      (if (elmo-nntp-read-response session)
+         (elmo-nntp-read-contents session))
+      (with-current-buffer (elmo-network-session-buffer session)
+       (std11-field-body "Newsgroups")))))
+
+(luna-define-method elmo-message-fetch-with-cache-process :around
+  ((folder elmo-nntp-folder) number strategy &optional section unread)
+  (when (luna-call-next-method)
+    (elmo-nntp-setup-crosspost-buffer folder number)
+    (unless unread
+      (elmo-nntp-folder-update-crosspost-message-alist
+       folder (list number)))
+    t))
 
-(defun elmo-nntp-append-msg (spec string &optional msg no-see))
+(luna-define-method elmo-message-fetch-plugged ((folder elmo-nntp-folder)
+                                               number strategy
+                                               &optional section outbuf
+                                               unread)
+  (elmo-nntp-message-fetch folder number strategy section outbuf unread))
+
+(defun elmo-nntp-message-fetch (folder number strategy section outbuf unread)
+  (let ((session (elmo-nntp-get-session folder))
+       newsgroups)
+    (with-current-buffer (elmo-network-session-buffer session)
+      (elmo-nntp-select-group session (elmo-nntp-folder-group-internal folder))
+      (elmo-nntp-send-command session (format "article %s" number))
+      (if (null (elmo-nntp-read-response session t))
+         (progn
+           (with-current-buffer outbuf (erase-buffer))
+           (message "Fetching message failed")
+           nil)
+       (prog1 (elmo-nntp-read-body session outbuf)
+         (with-current-buffer outbuf
+           (goto-char (point-min))
+           (while (re-search-forward "^\\." nil t)
+             (replace-match "")
+             (forward-line))
+           (elmo-nntp-setup-crosspost-buffer folder number)
+           (unless unread
+             (elmo-nntp-folder-update-crosspost-message-alist
+              folder (list number)))))))))
 
 (defun elmo-nntp-post (hostname content-buf)
-  (let* (;(folder (nth 1 spec))
-        (connection 
-         (elmo-nntp-get-connection 
-          hostname 
-          elmo-default-nntp-user
-          elmo-default-nntp-port elmo-default-nntp-ssl))
-        (buffer (car connection))
-        (process (cadr connection))
-        response has-message-id
-        )
+  (let ((session (elmo-nntp-get-session
+                 (luna-make-entity
+                  'elmo-nntp-folder
+                  :user elmo-nntp-default-user
+                  :server hostname
+                  :port elmo-nntp-default-port
+                  :stream-type
+                  (elmo-get-network-stream-type
+                   elmo-nntp-default-stream-type))))
+       response has-message-id)
     (save-excursion
       (set-buffer content-buf)
       (goto-char (point-min))
       (if (search-forward mail-header-separator nil t)
          (delete-region (match-beginning 0)(match-end 0)))
       (setq has-message-id (std11-field-body "message-id"))
-      (elmo-nntp-send-command buffer process "post")
-      (if (string-match "^340" (setq response 
-                                    (elmo-nntp-read-raw-response 
-                                     buffer process)))
+      (elmo-nntp-send-command session "post")
+      (if (string-match "^340" (setq response
+                                    (elmo-nntp-read-raw-response session)))
          (if (string-match "recommended ID \\(<[^@]+@[^>]+>\\)" response)
              (unless has-message-id
                (goto-char (point-min))
@@ -993,139 +1041,242 @@ Return nil if connection failed."
                                (elmo-match-string 1 response)
                                "\n"))))
        (error "POST failed"))
-      (current-buffer)
       (run-hooks 'elmo-nntp-post-pre-hook)
-      (set-buffer buffer)
-      (elmo-nntp-send-data process content-buf)
-      (elmo-nntp-send-command buffer process ".")
-      ;(elmo-nntp-read-response buffer process t)
-      (if (not (string-match 
+      (elmo-nntp-send-buffer session content-buf)
+      (elmo-nntp-send-command session ".")
+;;;   (elmo-nntp-read-response buffer process t)
+      (if (not (string-match
                "^2" (setq response (elmo-nntp-read-raw-response
-                                    buffer process))))
+                                    session))))
          (error (concat "NNTP error: " response))))))
 
-(defun elmo-nntp-send-data-line (process data)
-  (goto-char (point-max))
-
+(defsubst elmo-nntp-send-data-line (session line)
+  "Send LINE to SESSION."
   ;; Escape "." at start of a line
-  (if (eq (string-to-char data) ?.)
-      (process-send-string process "."))
-  (process-send-string process data)
-  (process-send-string process "\r\n"))
-
-(defun elmo-nntp-send-data (process buffer)
-  (let
-      ((data-continue t)
-       (sending-data nil)
-       this-line
-       this-line-end)
-    (save-excursion
-      (set-buffer buffer)
-      (goto-char (point-min)))
-
-    (while data-continue
-      (save-excursion
-       (set-buffer buffer)
+  (if (eq (string-to-char line) ?.)
+      (process-send-string (elmo-network-session-process-internal
+                           session) "."))
+  (process-send-string (elmo-network-session-process-internal
+                       session) line)
+  (process-send-string (elmo-network-session-process-internal
+                       session) "\r\n"))
+
+(defun elmo-nntp-send-buffer (session databuf)
+  "Send data content of DATABUF to SESSION."
+  (let ((data-continue t)
+       line bol)
+    (with-current-buffer databuf
+      (goto-char (point-min))
+      (while data-continue
        (beginning-of-line)
-       (setq this-line (point))
+       (setq bol (point))
        (end-of-line)
-       (setq this-line-end (point))
-       (setq sending-data nil)
-       (setq sending-data (buffer-substring this-line this-line-end))
-       (if (/= (forward-line 1) 0)
-           (setq data-continue nil)))
-
-      (elmo-nntp-send-data-line process sending-data))))
-
-
-(defun elmo-nntp-delete-msgs (spec msgs)
-  "MSGS on FOLDER at SERVER pretended as Deleted. Returns nil if failed."
-  (let* ((dir (elmo-msgdb-expand-path nil spec))
-;       (msgs (mapcar 'string-to-int msgs))
-        (killed-list (elmo-msgdb-killed-list-load dir)))
-    (mapcar '(lambda (msg)
-              (setq killed-list
-                    (elmo-msgdb-set-as-killed killed-list msg)))
-           msgs)
-    (elmo-msgdb-killed-list-save dir killed-list)
-    t))
-
-(defun elmo-nntp-check-validity (spec validity-file)
-  t)
-(defun elmo-nntp-sync-validity (spec validity-file)
+       (setq line (buffer-substring bol (point)))
+       (unless (eq (forward-line 1) 0) (setq data-continue nil))
+       (elmo-nntp-send-data-line session line)))))
+
+(luna-define-method elmo-folder-delete-messages ((folder elmo-nntp-folder)
+                                                numbers)
+  (elmo-nntp-folder-delete-messages folder numbers))
+
+(defun elmo-nntp-folder-delete-messages (folder numbers)
+  (let ((killed-list (elmo-folder-killed-list-internal folder)))
+    (dolist (number numbers)
+      (setq killed-list
+           (elmo-msgdb-set-as-killed killed-list number)))
+    (elmo-folder-set-killed-list-internal folder killed-list))
   t)
 
-(defun elmo-nntp-folder-exists-p (spec)
-  (if (elmo-nntp-plugged-p spec)
-      (elmo-nntp-setting spec
-       (elmo-nntp-send-command buffer
-                               process
-                               (format "group %s" folder))
-       (elmo-nntp-read-response buffer process))
-    t))
-
-(defun elmo-nntp-folder-creatable-p (spec)
-  nil)
-
-(defun elmo-nntp-create-folder (spec)
-  nil) ; noop
-
-(defun elmo-nntp-search (spec condition &optional from-msgs)
-  (error "Search by %s for %s is not implemented yet." condition (car spec))
-  nil)
-
-(defun elmo-nntp-get-folders-info-prepare (spec connection-keys)
+(luna-define-method elmo-folder-exists-p-plugged ((folder elmo-nntp-folder))
+  (let ((session (elmo-nntp-get-session folder)))
+         (elmo-nntp-send-command
+          session
+          (format "group %s"
+                  (elmo-nntp-folder-group-internal folder)))
+    (elmo-nntp-read-response session)))
+
+(defun elmo-nntp-retrieve-field (spec field from-msgs)
+  "Retrieve FIELD values from FROM-MSGS.
+Returns a list of cons cells like (NUMBER . VALUE)"
+  (let ((session (elmo-nntp-get-session spec)))
+    (if (elmo-nntp-xhdr-p session)
+       (progn
+         (elmo-nntp-select-group session (elmo-nntp-folder-group-internal spec))
+         (elmo-nntp-send-command session
+                                 (format "xhdr %s %s"
+                                         field
+                                         (if from-msgs
+                                             (format
+                                              "%d-%d"
+                                              (car from-msgs)
+                                              (nth
+                                               (max
+                                                (- (length from-msgs) 1) 0)
+                                               from-msgs))
+                                           "0-")))
+         (if (elmo-nntp-read-response session t)
+             (elmo-nntp-parse-xhdr-response
+              (elmo-nntp-read-contents session))
+           (elmo-nntp-set-xhdr session nil)
+           (error "NNTP XHDR command failed"))))))
+
+(defun elmo-nntp-search-primitive (spec condition &optional from-msgs)
+  (let ((search-key (elmo-filter-key condition)))
+    (cond
+     ((string= "last" search-key)
+      (let ((numbers (or from-msgs (elmo-folder-list-messages spec))))
+       (nthcdr (max (- (length numbers)
+                       (string-to-int (elmo-filter-value condition)))
+                    0)
+               numbers)))
+     ((string= "first" search-key)
+      (let* ((numbers (or from-msgs (elmo-folder-list-messages spec)))
+            (rest (nthcdr (string-to-int (elmo-filter-value condition) )
+                          numbers)))
+       (mapcar '(lambda (x) (delete x numbers)) rest)
+       numbers))
+     ((or (string= "since" search-key)
+         (string= "before" search-key))
+      (let* ((specified-date (elmo-date-make-sortable-string
+                             (elmo-date-get-datevec (elmo-filter-value
+                                                     condition))))
+            (since (string= "since" search-key))
+            field-date  result)
+       (if (eq (elmo-filter-type condition) 'unmatch)
+           (setq since (not since)))
+       (setq result
+             (delq nil
+                   (mapcar
+                    (lambda (pair)
+                      (setq field-date
+                            (elmo-date-make-sortable-string
+                             (timezone-fix-time
+                              (cdr pair)
+                              (current-time-zone) nil)))
+                      (if (if since
+                              (or (string= specified-date field-date)
+                                  (string< specified-date field-date))
+                            (string< field-date
+                                     specified-date))
+                          (car pair)))
+                    (elmo-nntp-retrieve-field spec "date" from-msgs))))
+       (if from-msgs
+           (elmo-list-filter from-msgs result)
+         result)))
+     ((string= "body" search-key)
+      (error
+"Search by BODY is not supported (Toggle the plug off to search from caches)"))
+     (t
+      (let ((val (elmo-filter-value condition))
+           (negative (eq (elmo-filter-type condition) 'unmatch))
+           (case-fold-search t)
+           result)
+       (setq result
+             (delq nil
+                   (mapcar
+                    (lambda (pair)
+                      (if (string-match val
+                                        (eword-decode-string
+                                         (decode-mime-charset-string
+                                          (cdr pair) elmo-mime-charset)))
+                          (unless negative (car pair))
+                        (if negative (car pair))))
+                    (elmo-nntp-retrieve-field spec search-key
+                                              from-msgs))))
+       (if from-msgs
+           (elmo-list-filter from-msgs result)
+         result))))))
+
+(defun elmo-nntp-search-internal (folder condition from-msgs)
+  (let (result)
+    (cond
+     ((vectorp condition)
+      (setq result (elmo-nntp-search-primitive
+                   folder condition from-msgs)))
+     ((eq (car condition) 'and)
+      (setq result (elmo-nntp-search-internal folder
+                                             (nth 1 condition)
+                                             from-msgs)
+           result (elmo-list-filter result
+                                    (elmo-nntp-search-internal
+                                     folder (nth 2 condition)
+                                     from-msgs))))
+     ((eq (car condition) 'or)
+      (setq result (elmo-nntp-search-internal folder
+                                             (nth 1 condition)
+                                             from-msgs)
+           result (elmo-uniq-list
+                   (nconc result
+                          (elmo-nntp-search-internal folder
+                                                     (nth 2 condition)
+                                                     from-msgs)))
+           result (sort result '<))))))
+
+(luna-define-method elmo-folder-search :around ((folder elmo-nntp-folder)
+                                               condition &optional from-msgs)
+  (if (elmo-folder-plugged-p folder)
+      (elmo-nntp-search-internal folder condition from-msgs)
+    (luna-call-next-method)))
+
+(defun elmo-nntp-get-folders-info-prepare (folder session-keys)
   (condition-case ()
-      (elmo-nntp-setting spec
-       (let (key count)
-         (save-excursion
-           (set-buffer buffer)
-           (unless (setq key (assoc (cons buffer process) connection-keys))
-             (erase-buffer)
-             (setq key (cons (cons buffer process)
-                             (vector 0 server user port ssl)))
-             (setq connection-keys (nconc connection-keys (list key))))
-           (elmo-nntp-send-command buffer 
-                                   process 
-                                   (format "group %s" folder)
-                                   t ;; don't erase-buffer
-                                   )
-           (if elmo-nntp-get-folders-securely
-               (accept-process-output process 1))
-           (setq count (aref (cdr key) 0))
-           (aset (cdr key) 0 (1+ count)))))
+      (let ((session (elmo-nntp-get-session folder))
+           key count)
+       (with-current-buffer (elmo-network-session-buffer session)
+         (unless (setq key (assoc session session-keys))
+           (erase-buffer)
+           (setq key (cons session
+                           (vector 0
+                                   (elmo-net-folder-server-internal folder)
+                                   (elmo-net-folder-user-internal folder)
+                                   (elmo-net-folder-port-internal folder)
+                                   (elmo-net-folder-stream-type-internal
+                                    folder))))
+           (setq session-keys (nconc session-keys (list key))))
+         (elmo-nntp-send-command session
+                                 (format "group %s"
+                                         (elmo-nntp-folder-group-internal
+                                          folder))
+                                 'noerase)
+         (if elmo-nntp-get-folders-securely
+             (accept-process-output
+              (elmo-network-session-process-internal session)
+              1))
+         (setq count (aref (cdr key) 0))
+         (aset (cdr key) 0 (1+ count))))
     (error
      (when elmo-auto-change-plugged
        (sit-for 1))
      nil))
-  connection-keys)
+  session-keys)
 
-(defun elmo-nntp-get-folders-info (connection-keys)
-  (let ((connections connection-keys)
+(defun elmo-nntp-get-folders-info (session-keys)
+  (let ((sessions session-keys)
        (cur (get-buffer-create " *ELMO NNTP Temp*")))
-    (while connections
-      (let* ((connect (caar connections))
-            (key     (cdar connections))
-            (buffer  (car connect))
-            (process (cdr connect))
+    (while sessions
+      (let* ((session (caar sessions))
+            (key     (cdar sessions))
             (count   (aref key 0))
             (server  (aref key 1))
             (user    (aref key 2))
             (port    (aref key 3))
-            (ssl     (aref key 4))
-            (hashtb (or elmo-nntp-groups-hashtb
-                        (setq elmo-nntp-groups-hashtb
+            (type    (aref key 4))
+            (hashtb (or elmo-newsgroups-hashtb
+                        (setq elmo-newsgroups-hashtb
                               (elmo-make-hash count)))))
        (save-excursion
-         (elmo-nntp-groups-read-response buffer cur process count)
+         (elmo-nntp-groups-read-response session cur count)
          (set-buffer cur)
          (goto-char (point-min))
          (let ((case-replace nil)
-               (postfix (elmo-nntp-folder-postfix user server port ssl)))
+               (postfix (elmo-nntp-folder-postfix user server port type)))
            (if (not (string= postfix ""))
                (save-excursion
                  (replace-regexp "^\\(211 [0-9]+ [0-9]+ [0-9]+ [^ \n]+\\).*$"
-                                 (concat "\\1" postfix)))))
+                                 (concat "\\1"
+                                         (elmo-replace-in-string
+                                          postfix
+                                          "\\\\" "\\\\\\\\\\\\\\\\"))))))
          (let (len min max group)
            (while (not (eobp))
              (condition-case ()
@@ -1138,16 +1289,16 @@ Return nil if connection failed."
                         (list len min max)))
                (error (and group (symbolp group) (set group nil))))
              (forward-line 1))))
-       (setq connections (cdr connections))))
+       (setq sessions (cdr sessions))))
     (kill-buffer cur)))
 
 ;; original is 'nntp-retrieve-groups [Gnus]
-(defun elmo-nntp-groups-read-response (buffer tobuffer process count)
+(defun elmo-nntp-groups-read-response (session outbuf count)
   (let* ((received 0)
         (last-point (point-min)))
-    (save-excursion
-      (set-buffer buffer)
-      (accept-process-output process 1)
+    (with-current-buffer (elmo-network-session-buffer session)
+      (accept-process-output
+       (elmo-network-session-process-internal session) 1)
       (discard-input)
       ;; Wait for all replies.
       (message "Getting folders info...")
@@ -1159,13 +1310,17 @@ Return nil if connection failed."
                       (1+ received)))
               (setq last-point (point))
               (< received count))
-       (accept-process-output process 1)
+       (accept-process-output (elmo-network-session-process-internal session)
+                              1)
        (discard-input)
-       (and (zerop (% received 10))
-            (elmo-display-progress
-             'elmo-nntp-groups-read-response "Getting folders info..."
-             (/ (* received 100) count)))
-       )
+       (when (> count elmo-display-progress-threshold)
+         (if (or (zerop (% received 10)) (= received count))
+             (elmo-display-progress
+              'elmo-nntp-groups-read-response "Getting folders info..."
+              (/ (* received 100) count)))))
+      (when (> count elmo-display-progress-threshold)
+       (elmo-display-progress
+        'elmo-nntp-groups-read-response "Getting folders info..." 100))
       ;; Wait for the reply from the final command.
       (goto-char (point-max))
       (re-search-backward "^[0-9]" nil t)
@@ -1173,24 +1328,14 @@ Return nil if connection failed."
        (while (progn
                 (goto-char (point-max))
                 (not (re-search-backward "\r?\n" (- (point) 3) t)))
-         (accept-process-output process 1)
+         (accept-process-output
+          (elmo-network-session-process-internal session) 1)
          (discard-input)))
       ;; Now all replies are received.  We remove CRs.
       (goto-char (point-min))
       (while (search-forward "\r" nil t)
        (replace-match "" t t))
-      (copy-to-buffer tobuffer (point-min) (point-max)))))
-
-(defun elmo-nntp-make-groups-hashtb (folders &optional size)
-  (let ((hashtb (or elmo-nntp-groups-hashtb
-                   (setq elmo-nntp-groups-hashtb
-                         (elmo-make-hash (or size (length folders)))))))
-    (mapcar
-     '(lambda (fld)
-       (or (elmo-get-hash-val fld hashtb)
-           (elmo-set-hash-val fld nil hashtb)))
-     folders)
-    hashtb))
+      (copy-to-buffer outbuf (point-min) (point-max)))))
 
 ;; from nntp.el [Gnus]
 
@@ -1207,10 +1352,9 @@ Return nil if connection failed."
    (t
     nil)))
 
-(defun elmo-nntp-retrieve-headers (buffer tobuffer process articles)
+(defun elmo-nntp-retrieve-headers (session outbuf articles)
   "Retrieve the headers of ARTICLES."
-  (save-excursion
-    (set-buffer buffer)
+  (with-current-buffer (elmo-network-session-buffer session)
     (erase-buffer)
     (let ((number (length articles))
          (count 0)
@@ -1219,50 +1363,49 @@ Return nil if connection failed."
          article)
       ;; Send HEAD commands.
       (while (setq article (pop articles))
-       (elmo-nntp-send-command
-        buffer
-        process
-        (format "head %s" article)
-        t ;; not erase-buffer
-        )
+       (elmo-nntp-send-command session
+                               (format "head %s" article)
+                               'noerase)
        (setq count (1+ count))
        ;; Every 200 requests we have to read the stream in
        ;; order to avoid deadlocks.
        (when (or (null articles)       ;All requests have been sent.
                  (zerop (% count elmo-nntp-header-fetch-chop-length)))
-         (accept-process-output process 1)
+         (accept-process-output
+          (elmo-network-session-process-internal session) 1)
          (discard-input)
          (while (progn
-                  (set-buffer buffer)
                   (goto-char last-point)
                   ;; Count replies.
                   (while (elmo-nntp-next-result-arrived-p)
                     (setq last-point (point))
                     (setq received (1+ received)))
                   (< received count))
-           (and (zerop (% received 20))
-                (elmo-display-progress
-                 'elmo-nntp-retrieve-headers "Getting headers..."
-                 (/ (* received 100) number)))
-           (accept-process-output process 1)
-           (discard-input)
-           )))
+           (when (> number elmo-display-progress-threshold)
+             (if (or (zerop (% received 20)) (= received number))
+                 (elmo-display-progress
+                  'elmo-nntp-retrieve-headers "Getting headers..."
+                  (/ (* received 100) number))))
+           (accept-process-output
+            (elmo-network-session-process-internal session) 1)
+           (discard-input))))
+      (when (> number elmo-display-progress-threshold)
+       (elmo-display-progress
+        'elmo-nntp-retrieve-headers "Getting headers..." 100))
       (message "Getting headers...done")
       ;; Remove all "\r"'s.
       (goto-char (point-min))
       (while (search-forward "\r\n" nil t)
        (replace-match "\n"))
-      (copy-to-buffer tobuffer (point-min) (point-max)))))
+      (copy-to-buffer outbuf (point-min) (point-max)))))
 
 ;; end of from Gnus
 
-(defun elmo-nntp-msgdb-create-message (buffer len folder new-mark 
-                                             already-mark seen-mark seen-list)
+(defun elmo-nntp-msgdb-create-message (len new-mark
+                                          already-mark seen-mark seen-list)
   (save-excursion
-    (let (beg
-         overview number-alist mark-alist
-         entity i num gmark seen message-id)
-      (set-buffer buffer)
+    (let (beg overview number-alist mark-alist
+             entity i num gmark seen message-id)
       (elmo-set-buffer-multibyte nil)
       (goto-char (point-min))
       (setq i 0)
@@ -1271,7 +1414,7 @@ Return nil if connection failed."
        (setq beg (save-excursion (forward-line 1) (point)))
        (setq num
              (and (looking-at "^2[0-9]*[ ]+\\([0-9]+\\)")
-                  (string-to-int 
+                  (string-to-int
                    (elmo-match-buffer 1))))
        (elmo-nntp-next-result-arrived-p)
        (when num
@@ -1282,68 +1425,174 @@ Return nil if connection failed."
              (setq entity
                    (elmo-msgdb-create-overview-from-buffer num))
              (when entity
-               (setq overview 
+               (setq overview
                      (elmo-msgdb-append-element
                       overview entity))
                (setq number-alist
-                     (elmo-msgdb-number-add number-alist
-                                            (elmo-msgdb-overview-entity-get-number entity)
-                                            (car entity)))
+                     (elmo-msgdb-number-add
+                      number-alist
+                      (elmo-msgdb-overview-entity-get-number entity)
+                      (car entity)))
                (setq message-id (car entity))
                (setq seen (member message-id seen-list))
-               (if (setq gmark 
+               (if (setq gmark
                          (or (elmo-msgdb-global-mark-get message-id)
-                             (if (elmo-cache-exists-p message-id);; XXX
+                             (if (elmo-file-cache-status
+                                  (elmo-file-cache-get message-id))
                                  (if seen
                                      nil
                                    already-mark)
                                (if seen
-                                   seen-mark
+                                   (if elmo-nntp-use-cache
+                                       seen-mark)
                                  new-mark))))
                    (setq mark-alist
-                         (elmo-msgdb-mark-append 
-                          mark-alist 
+                         (elmo-msgdb-mark-append
+                          mark-alist
                           num gmark)))
                ))))
-       (setq i (1+ i))
-       (and (zerop (% i 20))
-            (elmo-display-progress
-             'elmo-nntp-msgdb-create-message "Creating msgdb..."
-             (/ (* i 100) len)))
-       )
-      (message "Creating msgdb...done.")
+       (when (> len elmo-display-progress-threshold)
+         (setq i (1+ i))
+         (if (or (zerop (% i 20)) (= i len))
+             (elmo-display-progress
+              'elmo-nntp-msgdb-create-message "Creating msgdb..."
+              (/ (* i 100) len)))))
+      (when (> len elmo-display-progress-threshold)
+       (elmo-display-progress
+        'elmo-nntp-msgdb-create-message "Creating msgdb..." 100))
       (list overview number-alist mark-alist))))
 
-(defun elmo-nntp-use-cache-p (spec number)
+(luna-define-method elmo-message-use-cache-p ((folder elmo-nntp-folder) number)
   elmo-nntp-use-cache)
 
-(defun elmo-nntp-local-file-p (spec number)
-  nil)
-
-(defun elmo-nntp-port-label (spec)
-  (concat "nntp"
-         (if (elmo-nntp-spec-ssl spec) "!ssl" "")))
-
-(defsubst elmo-nntp-portinfo (spec)
-  (list (elmo-nntp-spec-hostname spec) 
-       (elmo-nntp-spec-port spec)))
-
-(defun elmo-nntp-plugged-p (spec)
-  (apply 'elmo-plugged-p
-        (append (elmo-nntp-portinfo spec)
-                (list nil (quote (elmo-nntp-port-label spec))))))
-
-(defun elmo-nntp-set-plugged (spec plugged add)
-  (apply 'elmo-set-plugged plugged
-        (append (elmo-nntp-portinfo spec)
-                (list nil nil (quote (elmo-nntp-port-label spec)) add))))
-
-(defalias 'elmo-nntp-list-folder-unread 
-  'elmo-generic-list-folder-unread)
-(defalias 'elmo-nntp-list-folder-important
-  'elmo-generic-list-folder-important)
-(defalias 'elmo-nntp-commit 'elmo-generic-commit)
+(defun elmo-nntp-parse-newsgroups (string &optional subscribe-only)
+  (let ((nglist (elmo-parse string "[ \t\f\r\n,]*\\([^ \t\f\r\n,]+\\)"))
+       ngs)
+    (if (not subscribe-only)
+       nglist
+      (dolist (ng nglist)
+       (if (intern-soft ng elmo-newsgroups-hashtb)
+           (setq ngs (cons ng ngs))))
+      ngs)))
+
+;;; Crosspost processing.
+
+;; 1. setup crosspost alist.
+;;    1.1. When message is fetched and is crossposted message,
+;;         it is remembered in `temp-crosses' slot.
+;;         temp-crosses slot is a list of cons cell:
+;;         (NUMBER . (MESSAGE-ID (LIST-OF-NEWSGROUPS) 'ng))
+;;    1.2. In elmo-folder-close, `temp-crosses' slot is cleared,
+;;    1.3. In elmo-folder-mark-as-read, move crosspost entry
+;;         from `temp-crosses' slot to `elmo-crosspost-message-alist'.
+
+;; 2. process crosspost alist.
+;;    2.1. At elmo-folder-process-crosspost, setup `reads' slot from
+;;         `elmo-crosspost-message-alist'.
+;;    2.2. remove crosspost entry for current newsgroup from
+;;         `elmo-crosspost-message-alist'.
+;;    2.3. elmo-folder-list-unreads return unread message list according to
+;;         `reads' slot.
+;;         (There's a problem that if `elmo-folder-list-unreads'
+;;           never executed, crosspost information is thrown away.)
+;;    2.4. In elmo-folder-close, `read' slot is cleared,
+
+(defun elmo-nntp-setup-crosspost-buffer (folder number)
+;;    1.1. When message is fetched and is crossposted message,
+;;         it is remembered in `temp-crosses' slot.
+;;         temp-crosses slot is a list of cons cell:
+;;         (NUMBER . (MESSAGE-ID (LIST-OF-NEWSGROUPS) 'ng))
+  (let (newsgroups crosspost-newsgroups message-id)
+    (save-restriction
+      (std11-narrow-to-header)
+      (setq newsgroups (std11-fetch-field "newsgroups")
+           message-id (std11-msg-id-string
+                       (car (std11-parse-msg-id-string
+                             (std11-fetch-field "message-id"))))))
+    (when newsgroups
+      (when (setq crosspost-newsgroups
+                 (delete
+                  (elmo-nntp-folder-group-internal folder)
+                  (elmo-nntp-parse-newsgroups newsgroups t)))
+       (unless (assq number
+                     (elmo-nntp-folder-temp-crosses-internal folder))
+         (elmo-nntp-folder-set-temp-crosses-internal
+          folder
+          (cons (cons number (list message-id crosspost-newsgroups 'ng))
+                (elmo-nntp-folder-temp-crosses-internal folder))))))))
+
+(luna-define-method elmo-folder-close-internal ((folder elmo-nntp-folder))
+;;    1.2. In elmo-folder-close, `temp-crosses' slot is cleared,
+  (elmo-nntp-folder-set-temp-crosses-internal folder nil)
+  (elmo-nntp-folder-set-reads-internal folder nil)
+  )
+
+(defun elmo-nntp-folder-update-crosspost-message-alist (folder numbers)
+;;    1.3. In elmo-folder-mark-as-read, move crosspost entry
+;;         from `temp-crosses' slot to `elmo-crosspost-message-alist'.
+  (let (elem)
+    (dolist (number numbers)
+      (when (setq elem (assq number
+                            (elmo-nntp-folder-temp-crosses-internal folder)))
+       (unless (assoc (cdr (cdr elem)) elmo-crosspost-message-alist)
+         (setq elmo-crosspost-message-alist
+               (cons (cdr elem) elmo-crosspost-message-alist)))
+       (elmo-nntp-folder-set-temp-crosses-internal
+        folder
+        (delq elem (elmo-nntp-folder-temp-crosses-internal folder)))))))
+
+(luna-define-method elmo-folder-mark-as-read ((folder elmo-nntp-folder)
+                                             numbers)
+  (elmo-nntp-folder-update-crosspost-message-alist folder numbers)
+  t)
 
-(provide 'elmo-nntp)
+(luna-define-method elmo-folder-process-crosspost ((folder elmo-nntp-folder)
+                                                  &optional
+                                                  number-alist)
+  (elmo-nntp-folder-process-crosspost folder number-alist))
+
+(defun elmo-nntp-folder-process-crosspost (folder number-alist)
+;;    2.1. At elmo-folder-process-crosspost, setup `reads' slot from
+;;         `elmo-crosspost-message-alist'.
+;;    2.2. remove crosspost entry for current newsgroup from
+;;         `elmo-crosspost-message-alist'.
+  (let (cross-deletes reads entity ngs)
+    (dolist (cross elmo-crosspost-message-alist)
+      (if number-alist
+         (when (setq entity (rassoc (nth 0 cross) number-alist))
+           (setq reads (cons (car entity) reads)))
+       (when (setq entity (elmo-msgdb-overview-get-entity
+                           (nth 0 cross)
+                           (elmo-folder-msgdb folder)))
+         (setq reads (cons (elmo-msgdb-overview-entity-get-number entity)
+                           reads))))
+      (when entity
+       (if (setq ngs (delete (elmo-nntp-folder-group-internal folder)
+                             (nth 1 cross)))
+           (setcar (cdr cross) ngs)
+         (setq cross-deletes (cons cross cross-deletes)))
+       (setq elmo-crosspost-message-alist-modified t)))
+    (dolist (dele cross-deletes)
+      (setq elmo-crosspost-message-alist (delq
+                                         dele
+                                         elmo-crosspost-message-alist)))
+    (elmo-nntp-folder-set-reads-internal folder reads)))
+
+(luna-define-method elmo-folder-list-unreads-internal
+  ((folder elmo-nntp-folder) unread-marks mark-alist)
+  ;;    2.3. elmo-folder-list-unreads return unread message list according to
+  ;;         `reads' slot.
+  (let ((mark-alist (or mark-alist (elmo-msgdb-get-mark-alist
+                                   (elmo-folder-msgdb folder)))))
+    (elmo-living-messages (delq nil
+                               (mapcar
+                                (lambda (x)
+                                  (if (member (nth 1 x) unread-marks)
+                                      (car x)))
+                                mark-alist))
+                         (elmo-nntp-folder-reads-internal folder))))
+
+(require 'product)
+(product-provide (provide 'elmo-nntp) (require 'elmo-version))
 
 ;;; elmo-nntp.el ends here