* wl-draft.el (wl-draft-parse-msg-id-list-string): Fix.
[elisp/wanderlust.git] / elmo / elmo-nntp.el
index 1b6f1dc..1389529 100644 (file)
@@ -1,12 +1,12 @@
 ;;; elmo-nntp.el -- NNTP Interface for ELMO.
 
-;; Copyright 1998,1999,2000 Yuuichi Teranishi <teranisi@gohome.org>
-;;                          Masahiro MURATA  <muse@ba2.so-net.ne.jp>
-;; Copyright 1999,2000      Kenichi OKADA  <okada@opaopa.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>
+;;     Masahiro MURATA <muse@ba2.so-net.ne.jp>
+;;     Kenichi OKADA <okada@opaopa.org>
 ;; Keywords: mail, net news
 
 ;; This file is part of ELMO (Elisp Library for Message Orchestration).
 ;;; 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)
 
+;;; ELMO NNTP folder
+(eval-and-compile
+  (luna-define-class elmo-nntp-folder (elmo-net-folder)
+                    (group))
+  (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)))
+    (setq name (luna-call-next-method))
+    (when (string-match
+          "^\\([^:@!]*\\)\\(:[^/!]+\\)?\\(/[^/:@!]+\\)?"
+          name)
+      (elmo-nntp-folder-set-group-internal
+       folder
+       (if (match-beginning 1)
+          (elmo-match-string 1 name)))
+      ;; Setup slots for elmo-net-folder
+      (elmo-net-folder-set-user-internal folder
+                                        (if (match-beginning 2)
+                                            (elmo-match-substring 2 folder 1)
+                                          elmo-default-nntp-user))
+      (unless (elmo-net-folder-server-internal folder)
+       (elmo-net-folder-set-server-internal folder 
+                                            elmo-default-nntp-server))
+      (unless (elmo-net-folder-port-internal folder)
+       (elmo-net-folder-set-port-internal folder
+                                          elmo-default-nntp-port))
+      (unless (elmo-net-folder-stream-type-internal folder)
+       (elmo-net-folder-set-stream-type-internal
+        folder
+        elmo-default-nntp-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-dir)))))
+
+;;; NNTP Session
 (eval-and-compile
   (luna-define-class elmo-nntp-session (elmo-network-session)
                     (current-group))
@@ -90,7 +136,7 @@ Don't cache if nil.")
                                           (list-active . 2)))
 
 (defmacro elmo-nntp-get-server-command (session)
-  (` (assoc (cons (elmo-network-session-host-internal (, session))
+  (` (assoc (cons (elmo-network-session-server-internal (, session))
                  (elmo-network-session-port-internal (, session)))
            elmo-nntp-server-command-alist)))
 
@@ -102,7 +148,7 @@ Don't cache if nil.")
               (nconc elmo-nntp-server-command-alist
                      (list (cons
                             (cons
-                             (elmo-network-session-host-internal (, session))
+                             (elmo-network-session-server-internal (, session))
                              (elmo-network-session-port-internal (, session)))
                             (setq entry
                                   (vector
@@ -171,34 +217,36 @@ Don't cache if nil.")
               elmo-default-nntp-stream-type)
      (elmo-network-stream-type-spec-string type))))
 
-(defun elmo-nntp-get-session (spec &optional if-exists)
+(defun elmo-nntp-get-session (folder &optional if-exists)
   (elmo-network-get-session
    'elmo-nntp-session
    "NNTP"
-   (elmo-nntp-spec-hostname spec)
-   (elmo-nntp-spec-port spec)
-   (elmo-nntp-spec-username spec)
-   nil ; auth type
-   (elmo-nntp-spec-stream-type spec)
+   folder
    if-exists))
 
 (luna-define-method elmo-network-initialize-session ((session
                                                      elmo-nntp-session))
-  (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))
-    (or (elmo-nntp-read-response session t)
-       (error "Cannot open network"))
-    (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
-       (elmo-network-session-process-internal session)))))
-
+  (let ((process (elmo-network-session-process-internal session)))
+    (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 "20[01]")))
+        (accept-process-output process 1))
+      (setq elmo-nntp-read-point (point))
+      (or (elmo-nntp-read-response session t)
+         (error "Cannot open network"))
+      (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))
@@ -313,8 +361,7 @@ Don't cache if nil.")
       (with-current-buffer outbuf
        (erase-buffer)
        (insert-buffer-substring (elmo-network-session-buffer session)
-                                start (- end 3))
-       (elmo-delete-cr-get-content-type)))))
+                                start (- end 3))))))
 
 (defun elmo-nntp-select-group (session group &optional force)
   (let (response)
@@ -364,31 +411,41 @@ Don't cache if nil.")
         msgdb
         (nconc number-alist (list (cons max-number nil)))))))
 
-(defun elmo-nntp-list-folders (spec &optional hierarchy)
-  (let ((session (elmo-nntp-get-session spec))
+(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))
        response ret-val top-ng append-serv use-list-active start)
     (with-temp-buffer
-      (if (and (elmo-nntp-spec-group spec)
-              (elmo-nntp-select-group session (elmo-nntp-spec-group spec)))
+      (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-spec-group spec))))
+         (setq ret-val (list (elmo-nntp-folder-group-internal folder))))
       (unless (setq response (elmo-nntp-list-folders-get-cache
-                             (elmo-nntp-spec-group spec)(current-buffer)))
+                             (elmo-nntp-folder-group-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-spec-group spec)
-                           (null (string= (elmo-nntp-spec-group spec) "")))
+                  (if (and (elmo-nntp-folder-group-internal folder)
+                           (null (string= (elmo-nntp-folder-group-internal
+                                           folder) "")))
                       (concat " active"
-                              (format " %s.*" (elmo-nntp-spec-group spec)
+                              (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) (elmo-nntp-spec-group spec)
+                       (list (current-time)
+                             (elmo-nntp-folder-group-internal folder)
                              response)))
                (erase-buffer)
                (insert response))
@@ -406,22 +463,27 @@ Don't cache if nil.")
          (setq start nil)
          (while (string-match (concat "^"
                                       (regexp-quote
-                                       (or (elmo-nntp-spec-group spec)
-                                           "")) ".*$")
+                                       (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 (and (elmo-nntp-spec-group spec)
-                                    (null (string=
-                                           (elmo-nntp-spec-group spec) "")))
-                               (concat (elmo-nntp-spec-group spec)
+                           (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))
@@ -452,31 +514,34 @@ Don't cache if nil.")
        (when (> len elmo-display-progress-threshold)
          (elmo-display-progress
           'elmo-nntp-list-folders "Parsing active..." 100))))
-    (unless (string= (elmo-nntp-spec-hostname spec)
+    (unless (string= (elmo-net-folder-server-internal folder)
                     elmo-default-nntp-server)
-      (setq append-serv (concat "@" (elmo-nntp-spec-hostname spec))))
-    (unless (eq (elmo-nntp-spec-port spec) elmo-default-nntp-port)
+      (setq append-serv (concat "@" (elmo-net-folder-server-internal
+                                    folder))))
+    (unless (eq (elmo-net-folder-port-internal folder) elmo-default-nntp-port)
       (setq append-serv (concat append-serv
                                ":" (int-to-string
-                                    (elmo-nntp-spec-port spec)))))
+                                    (elmo-net-folder-port-internal folder)))))
     (unless (eq (elmo-network-stream-type-symbol
-                (elmo-nntp-spec-stream-type spec))
+                (elmo-net-folder-stream-type-internal folder))
                elmo-default-nntp-stream-type)
       (setq append-serv
            (concat append-serv
                    (elmo-network-stream-type-spec-string
-                    (elmo-nntp-spec-stream-type spec)))))
+                    (elmo-net-folder-stream-type-internal folder)))))
     (mapcar '(lambda (fld)
               (if (consp fld)
                   (list (concat "-" (car fld)
-                                (and (elmo-nntp-spec-username spec)
+                                (and (elmo-net-folder-user-internal folder)
                                      (concat
-                                      ":" (elmo-nntp-spec-username spec)))
+                                      ":"
+                                      (elmo-net-folder-user-internal folder)))
                                 (and append-serv
                                      (concat append-serv))))
                 (concat "-" fld
-                        (and (elmo-nntp-spec-username spec)
-                             (concat ":" (elmo-nntp-spec-username spec)))
+                        (and (elmo-net-folder-user-internal folder)
+                             (concat ":" (elmo-net-folder-user-internal
+                                          folder)))
                         (and append-serv
                              (concat append-serv)))))
            ret-val)))
@@ -495,12 +560,10 @@ Don't cache if nil.")
      (goto-char (point-min))
      (read (current-buffer)))))
 
-(defun elmo-nntp-list-folder (spec)
-  (let ((session (elmo-nntp-get-session spec))
-       (group   (elmo-nntp-spec-group spec))
-       (killed (and elmo-use-killed-list
-                    (elmo-msgdb-killed-list-load
-                     (elmo-msgdb-expand-path spec))))
+(luna-define-method elmo-folder-list-messages-internal ((folder
+                                                        elmo-nntp-folder))
+  (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 session))
@@ -527,39 +590,43 @@ Don't cache if nil.")
          (setq numbers (elmo-nntp-make-msglist
                         (elmo-match-string 2 response)
                         (elmo-match-string 3 response)))))
-      (elmo-living-messages numbers killed))))
+      numbers)))
+
+(luna-define-method elmo-folder-status ((folder elmo-nntp-folder))
+  (elmo-nntp-folder-status folder))
 
-(defun elmo-nntp-max-of-folder (spec)
-  (let ((killed-list (and elmo-use-killed-list
-                         (elmo-msgdb-killed-list-load
-                          (elmo-msgdb-expand-path spec))))
+(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
        (if (setq entry
                  (elmo-get-hash-val
-                  (concat (elmo-nntp-spec-group spec)
+                  (concat (elmo-nntp-folder-group-internal folder)
                           (elmo-nntp-folder-postfix
-                           (elmo-nntp-spec-username spec)
-                           (elmo-nntp-spec-hostname spec)
-                           (elmo-nntp-spec-port spec)
-                           (elmo-nntp-spec-stream-type spec)))
+                           (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-nntp-groups-hashtb))
            (progn
              (setq end-num (nth 2 entry))
-             (when (and killed-list elmo-use-killed-list
+             (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-spec-group spec)))
-      (let ((session (elmo-nntp-get-session spec))
+         (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 session
-                                 (format "group %s"
-                                         (elmo-nntp-spec-group spec)))
+                                 (format 
+                                  "group %s"
+                                  (elmo-nntp-folder-group-internal folder)))
          (setq response (elmo-nntp-read-response session))
          (if (and response
                   (string-match
@@ -570,14 +637,14 @@ Don't cache if nil.")
                               (elmo-match-string 3 response)))
                (setq e-num (string-to-int
                             (elmo-match-string 1 response)))
-               (when (and killed-list elmo-use-killed-list
+               (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"
-                      (elmo-nntp-spec-group spec))
+                      (elmo-nntp-folder-group-internal folder))
              nil)))))))
 
 (defconst elmo-nntp-overview-index
@@ -592,7 +659,6 @@ Don't cache if nil.")
     ("xref" . 8)))
 
 (defun elmo-nntp-create-msgdb-from-overview-string (str
-                                                   folder
                                                    new-mark
                                                    already-mark
                                                    seen-mark
@@ -653,7 +719,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)
@@ -667,73 +734,38 @@ Don't cache if nil.")
       (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
-    (let ((filter  numlist)
-         (session (elmo-nntp-get-session spec))
-         beg-num end-num cur length
-         ret-val ov-str use-xover dir)
-      (elmo-nntp-select-group session (elmo-nntp-spec-group spec))
-      (when (setq use-xover (elmo-nntp-xover-p session))
-       (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
-          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
-                       (elmo-nntp-spec-group spec)
-                       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 numlist
-                        new-mark already-mark seen-mark seen-list))
+(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
@@ -741,52 +773,88 @@ Don't cache if nil.")
                     ret-val
                     (elmo-nntp-create-msgdb-from-overview-string
                      ov-str
-                     (elmo-nntp-spec-group spec)
                      new-mark
                      already-mark
                      seen-mark
                      important-mark
                      seen-list
-                     filter))))))
-      (when elmo-use-killed-list
-       (setq dir (elmo-msgdb-expand-path spec))
-       (elmo-msgdb-killed-list-save
-        dir
-        (nconc
-         (elmo-msgdb-killed-list-load dir)
-         (car (elmo-list-diff
-               numlist
-               (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-spec-group spec)))
-       (if (null (elmo-nntp-read-response session))
+                     filter
+                     )))))
+       (if (null (elmo-nntp-read-response session t))
            (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)))
-
-(defun elmo-nntp-sync-number-alist (spec number-alist)
+             (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)
-      (let ((session (elmo-nntp-get-session spec)))
+      (let ((session (elmo-nntp-get-session folder))
+           (number-alist (elmo-msgdb-get-number-alist
+                          (elmo-folder-msgdb-internal 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?).
-             (elmo-nntp-select-group session (elmo-nntp-spec-group spec))
+             (elmo-nntp-select-group session
+                                     (elmo-nntp-folder-group-internal folder))
              (elmo-nntp-send-command session
                                      (format "list active %s"
-                                             (elmo-nntp-spec-group spec)))
+                                             (elmo-nntp-folder-group-internal
+                                              folder)))
              (if (null (elmo-nntp-read-response session))
                  (error "NNTP list command failed"))
              (setq max-number
@@ -798,18 +866,18 @@ Don't cache if nil.")
              (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))))
+                 (elmo-msgdb-set-number-alist
+                  (elmo-folder-msgdb-internal folder)
+                  (nconc number-alist
+                         (list (cons max-number nil))))))))))
 
-(defun elmo-nntp-msgdb-create-by-header (session numlist
+(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) numlist)
+    (elmo-nntp-retrieve-headers session (current-buffer) numbers)
     (elmo-nntp-msgdb-create-message
-     (length numlist) new-mark already-mark seen-mark seen-list)))
+     (length numbers) new-mark already-mark seen-mark seen-list)))
 
 (defun elmo-nntp-parse-xhdr-response (string)
   (let (response)
@@ -859,10 +927,16 @@ Don't cache if nil.")
       (with-current-buffer (elmo-network-session-buffer session)
        (std11-field-body "Newsgroups")))))
 
-(defun elmo-nntp-read-msg (spec number outbuf)
-  (let ((session (elmo-nntp-get-session spec)))
+(luna-define-method elmo-message-fetch-plugged ((folder elmo-nntp-folder)
+                                               number strategy
+                                               &optional section outbuf
+                                               unseen)
+  (elmo-nntp-message-fetch folder number strategy section outbuf unseen))
+
+(defun elmo-nntp-message-fetch (folder number strategy section outbuf unseen)
+  (let ((session (elmo-nntp-get-session folder)))
     (with-current-buffer (elmo-network-session-buffer session)
-      (elmo-nntp-select-group session (elmo-nntp-spec-group spec))
+      (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
@@ -876,19 +950,14 @@ Don't cache if nil.")
              (replace-match "")
              (forward-line))))))))
 
-;;(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)))
-
-(defun elmo-nntp-append-msg (spec string &optional msg no-see))
-
 (defun elmo-nntp-post (hostname content-buf)
   (let ((session (elmo-nntp-get-session
-                 (list 'nntp nil elmo-default-nntp-user
-                       hostname elmo-default-nntp-port
-                       elmo-default-nntp-stream-type)))
+                 (luna-make-entity
+                  'elmo-nntp-folder
+                  :user elmo-default-nntp-user
+                  :server hostname
+                  :port elmo-default-nntp-port
+                  :stream-type elmo-default-nntp-stream-type)))
        response has-message-id)
     (save-excursion
       (set-buffer content-buf)
@@ -940,46 +1009,36 @@ Don't cache if nil.")
        (unless (eq (forward-line 1) 0) (setq data-continue nil))
        (elmo-nntp-send-data-line session line)))))
 
-(defun elmo-nntp-delete-msgs (spec msgs)
-  "MSGS on FOLDER at SERVER pretended as Deleted. Returns nil if failed."
-  (if elmo-use-killed-list
-      (let* ((dir (elmo-msgdb-expand-path spec))
-            (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)
+(luna-define-method elmo-folder-delete-messages ((folder elmo-nntp-folder)
+                                                numbers)
+  (elmo-nntp-folder-delete-messages folder numbers))
 
-(defun elmo-nntp-check-validity (spec validity-file)
-  t)
-(defun elmo-nntp-sync-validity (spec validity-file)
+(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)
-  (let ((session (elmo-nntp-get-session spec)))
-    (if (elmo-nntp-plugged-p spec)
+(luna-define-method elmo-folder-exists-p ((folder elmo-nntp-folder))
+  (let ((session (elmo-nntp-get-session folder)))
+    (if (elmo-folder-plugged-p folder)
        (progn
-         (elmo-nntp-send-command session
-                                 (format "group %s"
-                                         (elmo-nntp-spec-group spec)))
+         (elmo-nntp-send-command
+          session
+          (format "group %s"
+                  (elmo-nntp-folder-group-internal folder)))
          (elmo-nntp-read-response session))
       t)))
 
-(defun elmo-nntp-folder-creatable-p (spec)
-  nil)
-
-(defun elmo-nntp-create-folder (spec)
-  nil) ; noop
-
 (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-spec-group spec))
+         (elmo-nntp-select-group session (elmo-nntp-folder-group-internal spec))
          (elmo-nntp-send-command session
                                  (format "xhdr %s %s"
                                          field
@@ -1002,13 +1061,13 @@ Returns a list of cons cells like (NUMBER . VALUE)"
   (let ((search-key (elmo-filter-key condition)))
     (cond
      ((string= "last" search-key)
-      (let ((numbers (or from-msgs (elmo-nntp-list-folder spec))))
+      (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-nntp-list-folder spec)))
+      (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)
@@ -1062,43 +1121,46 @@ Returns a list of cons cells like (NUMBER . VALUE)"
            (elmo-list-filter from-msgs result)
          result))))))
 
-(defun elmo-nntp-search (spec condition &optional from-msgs)
+(luna-define-method elmo-folder-search ((folder elmo-nntp-folder) 
+                                       condition &optional from-msgs)
   (let (result)
     (cond
      ((vectorp condition)
       (setq result (elmo-nntp-search-primitive
-                   spec condition from-msgs)))
+                   folder condition from-msgs)))
      ((eq (car condition) 'and)
-      (setq result (elmo-nntp-search spec (nth 1 condition) from-msgs)
+      (setq result (elmo-folder-search folder (nth 1 condition) from-msgs)
            result (elmo-list-filter result
-                                    (elmo-nntp-search
-                                     spec (nth 2 condition)
+                                    (elmo-folder-search
+                                     folder (nth 2 condition)
                                      from-msgs))))
      ((eq (car condition) 'or)
-      (setq result (elmo-nntp-search spec (nth 1 condition) from-msgs)
+      (setq result (elmo-folder-search folder (nth 1 condition) from-msgs)
            result (elmo-uniq-list
                    (nconc result
-                          (elmo-nntp-search spec (nth 2 condition)
-                                            from-msgs)))
+                          (elmo-folder-search folder (nth 2 condition)
+                                              from-msgs)))
            result (sort result '<))))))
 
-(defun elmo-nntp-get-folders-info-prepare (spec session-keys)
+(defun elmo-nntp-get-folders-info-prepare (folder session-keys)
   (condition-case ()
-      (let ((session (elmo-nntp-get-session spec))
+      (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-nntp-spec-hostname spec)
-                                   (elmo-nntp-spec-username spec)
-                                   (elmo-nntp-spec-port spec)
-                                   (elmo-nntp-spec-stream-type spec))))
+                                   (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-spec-group spec))
+                                         (elmo-nntp-folder-group-internal
+                                          folder))
                                  'noerase)
          (if elmo-nntp-get-folders-securely
              (accept-process-output
@@ -1135,7 +1197,10 @@ Returns a list of cons cells like (NUMBER . VALUE)"
            (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 ()
@@ -1196,15 +1261,15 @@ Returns a list of cons cells like (NUMBER . VALUE)"
        (replace-match "" t t))
       (copy-to-buffer outbuf (point-min) (point-max)))))
 
-(defun elmo-nntp-make-groups-hashtb (folders &optional size)
+(defun elmo-nntp-make-groups-hashtb (groups &optional size)
   (let ((hashtb (or elmo-nntp-groups-hashtb
                    (setq elmo-nntp-groups-hashtb
-                         (elmo-make-hash (or size (length folders)))))))
+                         (elmo-make-hash (or size (length groups)))))))
     (mapcar
-     '(lambda (fld)
-       (or (elmo-get-hash-val fld hashtb)
-           (elmo-set-hash-val fld nil hashtb)))
-     folders)
+     '(lambda (group)
+       (or (elmo-get-hash-val group hashtb)
+           (elmo-set-hash-val group nil hashtb)))
+     groups)
     hashtb))
 
 ;; from nntp.el [Gnus]
@@ -1307,7 +1372,8 @@ Returns a list of cons cells like (NUMBER . VALUE)"
                (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)
@@ -1331,39 +1397,13 @@ Returns a list of cons cells like (NUMBER . VALUE)"
         '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-stream-type spec)
-             (concat "!" (symbol-name
-                          (elmo-network-stream-type-symbol
-                           (elmo-nntp-spec-stream-type spec)))))))
-
-(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)
-(defalias 'elmo-nntp-folder-diff 'elmo-generic-folder-diff)
+(luna-define-method elmo-folder-append-msgdb :around
+  ((folder elmo-nntp-folder) append-msgdb)
+  ;; IMPLEMENT ME: Process crosspost here instead of following.
+  (luna-call-next-method))
 
 (require 'product)
 (product-provide (provide 'elmo-nntp) (require 'elmo-version))