* elmo-nntp.el (elmo-nntp-folder-list-subfolders): Don't omit
[elisp/wanderlust.git] / elmo / elmo-nntp.el
index 327bdd2..6abe77f 100644 (file)
@@ -1,4 +1,4 @@
-;;; elmo-nntp.el -- NNTP Interface for ELMO.
+;;; elmo-nntp.el --- NNTP Interface for ELMO.
 
 ;; Copyright (C) 1998,1999,2000 Yuuichi Teranishi <teranisi@gohome.org>
 ;; Copyright (C) 1998,1999,2000 Masahiro MURATA <muse@ba2.so-net.ne.jp>
 ;;
 
 ;;; Commentary:
-;; 
+;;
 
 ;;; Code:
-;; 
+;;
 
 (require 'elmo-vars)
 (require 'elmo-util)
@@ -49,7 +49,7 @@
 
 (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?).")
+\(Needed for inn 2.3 or later?\).")
 
 (defvar elmo-nntp-group-coding-system nil
   "A coding system for newsgroup string.")
       (decode-coding-string string elmo-nntp-group-coding-system)
     string))
 
+;; For debugging.
+(defvar elmo-nntp-debug nil
+  "Non-nil forces NNTP folder as debug mode.
+Debug information is inserted in the buffer \"*NNTP DEBUG*\"")
+
+;;; Debug
+(defsubst elmo-nntp-debug (message &rest args)
+  (if elmo-nntp-debug
+      (let ((biff (string-match "BIFF-" (buffer-name)))
+           pos)
+       (with-current-buffer (get-buffer-create (concat "*NNTP DEBUG*"
+                                                       (if biff "BIFF")))
+         (goto-char (point-max))
+         (setq pos (point))
+         (insert (apply 'format message args) "\n")))))
+
 ;;; ELMO NNTP folder
 (eval-and-compile
   (luna-define-class elmo-nntp-folder (elmo-net-folder)
                                           elmo-nntp-default-user
                                         (car parse)))
     (unless (elmo-net-folder-server-internal folder)
-      (elmo-net-folder-set-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-folder-group-internal folder)
     (expand-file-name (or (elmo-net-folder-server-internal folder) "nowhere")
                      (expand-file-name "nntp"
-                                       elmo-msgdb-dir)))))
+                                       elmo-msgdb-directory)))))
 
 (luna-define-method elmo-folder-newsgroups ((folder elmo-nntp-folder))
   (list (elmo-nntp-folder-group-internal folder)))
@@ -245,26 +261,31 @@ Don't cache if nil.")
 (defun elmo-nntp-get-session (folder &optional if-exists)
   (elmo-network-get-session
    'elmo-nntp-session
-   "NNTP"
+   (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)))
+  (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 "20[01]")))
-        (accept-process-output process 1))
+                 (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))
-      (or (elmo-nntp-read-response session t)
-         (error "Cannot open network"))
+      (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
@@ -282,13 +303,17 @@ Don't cache if nil.")
       (elmo-nntp-send-command session
                              (format "authinfo user %s"
                                      (elmo-network-session-user-internal
-                                      session)))
+                                      session))
+                             nil
+                             'no-log)
       (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))))
+              (elmo-get-passwd (elmo-network-session-password-key session)))
+       nil
+       'no-log)
       (or (elmo-nntp-read-response session)
          (signal 'elmo-authenticate-error '(authinfo))))))
 
@@ -300,25 +325,27 @@ Don't cache if nil.")
   (save-excursion
     (set-buffer (process-buffer process))
     (goto-char (point-max))
-    (insert output)))
+    (insert output)
+    (elmo-nntp-debug "RECEIVED: %s\n" output)))
 
 (defun elmo-nntp-send-mode-reader (session)
   (elmo-nntp-send-command session "mode reader")
   (if (null (elmo-nntp-read-response session t))
-      (error "Mode reader failed")))
-  
-(defun elmo-nntp-send-command (session command &optional noerase)
+      (message "Mode reader failed")))
+
+(defun elmo-nntp-send-command (session command &optional noerase no-log)
   (with-current-buffer (elmo-network-session-buffer session)
     (unless noerase
       (erase-buffer)
       (goto-char (point-min)))
     (setq elmo-nntp-read-point (point))
+    (elmo-nntp-debug "SEND: %s\n" (if no-log "<NO LOGGING>" command))
     (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)
+(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)
@@ -353,7 +380,9 @@ Don't cache if nil.")
                      (concat response "\n" response-string)
                    response-string)))
          (setq elmo-nntp-read-point match-end)))
-      response)))
+      (if error-msg
+         (cons response response-string)
+       response))))
 
 (defun elmo-nntp-read-raw-response (session)
   (with-current-buffer (elmo-network-session-buffer session)
@@ -386,7 +415,8 @@ Don't cache if nil.")
       (with-current-buffer outbuf
        (erase-buffer)
        (insert-buffer-substring (elmo-network-session-buffer session)
-                                start (- end 3))))))
+                                start (- end 3))))
+    t))
 
 (defun elmo-nntp-select-group (session group &optional force)
   (let (response)
@@ -401,7 +431,7 @@ Don't cache if nil.")
                                                      (and response group))
        response))))
 
-(defun elmo-nntp-list-folders-get-cache (folder buf)
+(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 "^"
@@ -409,18 +439,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))
-         (or (string= folder "")
-             (and folder
-                  (keep-lines (concat "^" (regexp-quote folder) "\\."))))
+         (or (string= group "")
+             (and group
+                  (keep-lines (concat "^" (regexp-quote group) "\\."))))
          t
          )))))
 
@@ -442,17 +478,19 @@ Don't cache if nil.")
 
 (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)
+       (case-fold-search nil)
+       response ret-val top-ng username 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 
+              (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
                              (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
@@ -472,6 +510,7 @@ Don't cache if nil.")
                  (setq elmo-nntp-list-folders-cache
                        (list (current-time)
                              (elmo-nntp-folder-group-internal folder)
+                             (elmo-net-folder-server-internal folder)
                              response)))
                (erase-buffer)
                (insert response))
@@ -484,12 +523,12 @@ Don't cache if nil.")
              (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 
+                                       (or
                                         (elmo-nntp-folder-group-internal
                                          folder)
                                         "")) ".*$")
@@ -503,7 +542,7 @@ Don't cache if nil.")
            (progn
              (setq regexp
                    (format "^\\(%s[^. ]+\\)\\([. ]\\).*\n"
-                           (if (and 
+                           (if (and
                                 (elmo-nntp-folder-group-internal folder)
                                 (null (string=
                                        (elmo-nntp-folder-group-internal
@@ -540,8 +579,16 @@ Don't cache if nil.")
        (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 username (elmo-net-folder-user-internal folder))
+    (when (and username
+              elmo-nntp-default-user
+              (string= username elmo-nntp-default-user))
+      (setq username nil))
+
+    (when (or username ; XXX: ad-hoc fix against username includes "@"
+             (not (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)
@@ -558,16 +605,15 @@ Don't cache if nil.")
     (mapcar '(lambda (fld)
               (if (consp fld)
                   (list (concat "-" (elmo-nntp-decode-group-string (car fld))
-                                (and (elmo-net-folder-user-internal folder)
+                                (and username
                                      (concat
                                       ":"
-                                      (elmo-net-folder-user-internal folder)))
+                                      username))
                                 (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 username
+                             (concat ":" username))
                         (and append-serv
                              (concat append-serv)))))
            ret-val)))
@@ -586,9 +632,9 @@ Don't cache if nil.")
      (goto-char (point-min))
      (read (current-buffer)))))
 
-(luna-define-method elmo-folder-list-messages-internal ((folder
-                                                        elmo-nntp-folder)
-                                                       &optional nohide)
+(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)
@@ -638,12 +684,12 @@ Don't cache if nil.")
                   elmo-newsgroups-hashtb))
            (progn
              (setq end-num (nth 2 entry))
-             (when(and  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\"" 
+         (error "No such newsgroup \"%s\""
                 (elmo-nntp-folder-group-internal folder)))
       (let ((session (elmo-nntp-get-session folder))
            response e-num)
@@ -651,7 +697,7 @@ Don't cache if nil.")
            (error "Connection failed"))
        (save-excursion
          (elmo-nntp-send-command session
-                                 (format 
+                                 (format
                                   "group %s"
                                   (elmo-nntp-folder-group-internal folder)))
          (setq response (elmo-nntp-read-response session))
@@ -712,10 +758,11 @@ 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
@@ -959,12 +1006,14 @@ Don't cache if nil.")
       (with-current-buffer (elmo-network-session-buffer session)
        (std11-field-body "Newsgroups")))))
 
-(luna-define-method elmo-message-fetch-with-cache-process :after
+(luna-define-method elmo-message-fetch-with-cache-process :around
   ((folder elmo-nntp-folder) number strategy &optional section unread)
-  (elmo-nntp-setup-crosspost-buffer folder number)
-  (unless unread
-    (elmo-nntp-folder-update-crosspost-message-alist
-     folder (list number))))
+  (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))
 
 (luna-define-method elmo-message-fetch-plugged ((folder elmo-nntp-folder)
                                                number strategy
@@ -1028,7 +1077,7 @@ Don't cache if nil.")
       (if (not (string-match
                "^2" (setq response (elmo-nntp-read-raw-response
                                     session))))
-         (error (concat "NNTP error: " response))))))
+         (error "NNTP error: %s" response)))))
 
 (defsubst elmo-nntp-send-data-line (session line)
   "Send LINE to SESSION."
@@ -1067,16 +1116,13 @@ Don't cache if nil.")
     (elmo-folder-set-killed-list-internal folder killed-list))
   t)
 
-(luna-define-method elmo-folder-exists-p ((folder elmo-nntp-folder))
+(luna-define-method elmo-folder-exists-p-plugged ((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-folder-group-internal folder)))
-         (elmo-nntp-read-response session))
-      t)))
+    (elmo-nntp-read-response session)))
 
 (defun elmo-nntp-retrieve-field (spec field from-msgs)
   "Retrieve FIELD values from FROM-MSGS.
@@ -1120,32 +1166,34 @@ Returns a list of cons cells like (NUMBER . VALUE)"
        numbers))
      ((or (string= "since" search-key)
          (string= "before" search-key))
-      (let* ((key-date (elmo-date-get-datevec (elmo-filter-value condition)))
-            (key-datestr (elmo-date-make-sortable-string key-date))
+      (let* ((specified-date (elmo-date-make-sortable-string
+                             (elmo-date-get-datevec (elmo-filter-value
+                                                     condition))))
             (since (string= "since" search-key))
-            result)
+            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
-                              (string< key-datestr
-                                       (elmo-date-make-sortable-string
-                                        (timezone-fix-time
-                                         (cdr pair)
-                                         (current-time-zone) nil)))
-                            (not (string< key-datestr
-                                          (elmo-date-make-sortable-string
-                                           (timezone-fix-time
-                                            (cdr pair)
-                                            (current-time-zone) nil)))))
+                              (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)
+      nil)
      (t
       (let ((val (elmo-filter-value condition))
            (negative (eq (elmo-filter-type condition) 'unmatch))
@@ -1167,27 +1215,38 @@ Returns a list of cons cells like (NUMBER . VALUE)"
            (elmo-list-filter from-msgs result)
          result))))))
 
-(luna-define-method elmo-folder-search ((folder elmo-nntp-folder) 
-                                       condition &optional from-msgs)
+(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-folder-search folder (nth 1 condition) from-msgs)
+      (setq result (elmo-nntp-search-internal folder
+                                             (nth 1 condition)
+                                             from-msgs)
            result (elmo-list-filter result
-                                    (elmo-folder-search
+                                    (elmo-nntp-search-internal
                                      folder (nth 2 condition)
                                      from-msgs))))
      ((eq (car condition) 'or)
-      (setq result (elmo-folder-search folder (nth 1 condition) from-msgs)
+      (setq result (elmo-nntp-search-internal folder
+                                             (nth 1 condition)
+                                             from-msgs)
            result (elmo-uniq-list
                    (nconc result
-                          (elmo-folder-search folder (nth 2 condition)
-                                              from-msgs)))
+                          (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 (and (elmo-folder-plugged-p folder)
+          (not (string= "body" (elmo-filter-key condition))))
+      (elmo-nntp-search-internal folder condition from-msgs)
+    (luna-call-next-method)))
+
 (defun elmo-nntp-get-folders-info-prepare (folder session-keys)
   (condition-case ()
       (let ((session (elmo-nntp-get-session folder))
@@ -1242,11 +1301,11 @@ Returns a list of cons cells like (NUMBER . VALUE)"
                (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"
-                                         (elmo-replace-in-string
-                                          postfix
-                                          "\\\\" "\\\\\\\\\\\\\\\\"))))))
+                 (while (re-search-forward "^\\(211 [0-9]+ [0-9]+ [0-9]+ [^ \n]+\\)\\(.*\\)$" nil t)
+                   (replace-match (concat (match-string 1)
+                                          (elmo-replace-in-string
+                                           postfix
+                                           "\\\\" "\\\\\\\\\\\\\\\\")))))))
          (let (len min max group)
            (while (not (eobp))
              (condition-case ()
@@ -1435,9 +1494,6 @@ Returns a list of cons cells like (NUMBER . VALUE)"
 (luna-define-method elmo-message-use-cache-p ((folder elmo-nntp-folder) number)
   elmo-nntp-use-cache)
 
-(luna-define-method elmo-folder-creatable-p ((folder elmo-nntp-folder))
-  nil)
-
 (defun elmo-nntp-parse-newsgroups (string &optional subscribe-only)
   (let ((nglist (elmo-parse string "[ \t\f\r\n,]*\\([^ \t\f\r\n,]+\\)"))
        ngs)
@@ -1482,7 +1538,7 @@ Returns a list of cons cells like (NUMBER . VALUE)"
            message-id (std11-msg-id-string
                        (car (std11-parse-msg-id-string
                              (std11-fetch-field "message-id"))))))
-    (when newsgroups 
+    (when newsgroups
       (when (setq crosspost-newsgroups
                  (delete
                   (elmo-nntp-folder-group-internal folder)
@@ -1547,18 +1603,18 @@ Returns a list of cons cells like (NUMBER . VALUE)"
        (setq elmo-crosspost-message-alist-modified t)))
     (dolist (dele cross-deletes)
       (setq elmo-crosspost-message-alist (delq
-                                         dele 
+                                         dele
                                          elmo-crosspost-message-alist)))
     (elmo-nntp-folder-set-reads-internal folder reads)))
 
-(luna-define-method elmo-folder-list-unreads-internal 
+(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 
+                               (mapcar
                                 (lambda (x)
                                   (if (member (nth 1 x) unread-marks)
                                       (car x)))