X-Git-Url: http://git.chise.org/gitweb/?a=blobdiff_plain;f=elmo%2Felmo-nntp.el;h=9d0eb95f6a3107f21ff4b73d02d661f2c98ad738;hb=ba3cbf9aa81cbbeaf6d7016121f364ae3784bf79;hp=813d31acad38983c332e2607a8b17d9a783f7960;hpb=1390ba9645d9da9599dad97990cbed616e9c0c1a;p=elisp%2Fwanderlust.git diff --git a/elmo/elmo-nntp.el b/elmo/elmo-nntp.el index 813d31a..9d0eb95 100644 --- a/elmo/elmo-nntp.el +++ b/elmo/elmo-nntp.el @@ -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 ;; Copyright (C) 1998,1999,2000 Masahiro MURATA @@ -28,17 +28,111 @@ ;; ;;; Commentary: -;; +;; ;;; Code: -;; +;; +(require 'elmo-vars) +(require 'elmo-util) +(require 'elmo-date) (require 'elmo-msgdb) -(eval-when-compile - (require 'elmo-cache) - (require 'elmo-util)) +(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)) + +;; 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) + (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)) + explicit-user 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 explicit-user (eq ?: (string-to-char (cdr parse)))) + (setq parse (elmo-parse-prefixed-element ?: (cdr parse))) + (elmo-net-folder-set-user-internal folder + (if (eq (length (car parse)) 0) + (unless explicit-user + 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)) @@ -57,7 +151,7 @@ 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) @@ -85,7 +179,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))) @@ -97,7 +191,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 @@ -156,43 +250,46 @@ Don't cache if nil.") (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 (elmo-network-stream-type-symbol type) - elmo-default-nntp-stream-type) + elmo-nntp-default-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) + (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 (elmo-network-session-stream-type-internal session)) 'starttls) @@ -208,45 +305,49 @@ 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)))))) (luna-define-method elmo-network-setup-session ((session elmo-nntp-session)) - (if elmo-nntp-send-mode-reader - (elmo-nntp-send-mode-reader session)) (run-hooks 'elmo-nntp-opened-hook)) (defun elmo-nntp-process-filter (process output) - (save-excursion - (set-buffer (process-buffer process)) - (goto-char (point-max)) - (insert output))) + (when (buffer-live-p (process-buffer process)) + (with-current-buffer (process-buffer process) + (goto-char (point-max)) + (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 "" 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) @@ -281,7 +382,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) @@ -315,7 +418,8 @@ Don't cache if nil.") (erase-buffer) (insert-buffer-substring (elmo-network-session-buffer session) start (- end 3)) - (elmo-delete-cr-get-content-type))))) + (elmo-delete-cr-buffer))) + t)) (defun elmo-nntp-select-group (session group &optional force) (let (response) @@ -330,7 +434,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 "^" @@ -338,58 +442,80 @@ 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 ))))) (defsubst elmo-nntp-catchup-msgdb (msgdb max-number) - (let (msgdb-max number-alist) - (setq number-alist (elmo-msgdb-get-number-alist msgdb)) - (setq msgdb-max (car (nth (max (- (length number-alist) 1) 0) - number-alist))) - (if (or (not msgdb-max) - (and msgdb-max max-number - (< msgdb-max max-number))) - (elmo-msgdb-set-number-alist - msgdb - (nconc number-alist (list (cons max-number nil))))))) - -(defun elmo-nntp-list-folders (spec &optional hierarchy) - (let ((session (elmo-nntp-get-session spec)) - response ret-val top-ng append-serv use-list-active start) + (let ((numbers (elmo-msgdb-list-messages msgdb)) + msgdb-max) + (setq msgdb-max (if numbers (apply #'max numbers) 0)) + (when (and msgdb-max + max-number + (< msgdb-max max-number)) + (let ((i (1+ msgdb-max)) + killed) + (while (<= i max-number) + (setq killed (cons i killed)) + (incf i)) + (nreverse killed))))) + +(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 username 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))) + (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-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) + (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-spec-group spec) - (null (string= (elmo-nntp-spec-group spec) ""))) + (if (and (elmo-nntp-folder-group-internal folder) + (not (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) + (elmo-net-folder-server-internal folder) response))) (erase-buffer) (insert response)) @@ -402,28 +528,33 @@ 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 (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) + (if (and (elmo-nntp-folder-group-internal folder) (null (string= - (elmo-nntp-spec-group spec) ""))) - (concat (elmo-nntp-spec-group spec) - "\\.") ""))) + (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) " ") @@ -453,31 +584,41 @@ 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) - 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 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) (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-default-nntp-stream-type) + (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-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) + (list (concat "-" (elmo-nntp-decode-group-string (car fld)) + (and username (concat - ":" (elmo-nntp-spec-username spec))) + ":" + username)) (and append-serv (concat append-serv)))) - (concat "-" fld - (and (elmo-nntp-spec-username spec) - (concat ":" (elmo-nntp-spec-username spec))) + (concat "-" (elmo-nntp-decode-group-string fld) + (and username + (concat ":" username)) (and append-serv (concat append-serv))))) ret-val))) @@ -496,12 +637,11 @@ 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-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 session)) @@ -528,39 +668,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-nntp-groups-hashtb)) + (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-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 @@ -571,14 +715,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,17 +736,14 @@ Don't cache if nil.") ("lines" . 7) ("xref" . 8))) -(defun elmo-nntp-create-msgdb-from-overview-string (str - folder - new-mark - already-mark - seen-mark - important-mark - seen-list +(defun elmo-nntp-create-msgdb-from-overview-string (folder + str + flag-table &optional numlist) - (let (ov-list gmark message-id seen - ov-entity overview number-alist mark-alist num - extras extra ext field field-index) + (let ((new-msgdb (elmo-make-msgdb)) + ov-list message-id entity + ov-entity num + extras extra ext field field-index flags) (setq ov-list (elmo-nntp-parse-overview-string str)) (while ov-list (setq ov-entity (car ov-list)) @@ -620,197 +761,161 @@ 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 - overview - (cons (aref ov-entity 4) - (vector num - (elmo-msgdb-get-last-message-id - (aref ov-entity 5)) - ;; from - (elmo-mime-string (elmo-delete-char - ?\" - (or - (aref ov-entity 2) - elmo-no-from) 'uni)) - ;; subject - (elmo-mime-string (or (aref ov-entity 1) - elmo-no-subject)) - (aref ov-entity 3) ;date - nil ; to - nil ; cc - (string-to-int - (aref ov-entity 6)) ; size - extra ; extra-field-list - )))) - (setq number-alist - (elmo-msgdb-number-add number-alist num - (aref ov-entity 4))) - (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 seen - nil - already-mark) - (if seen - (if elmo-nntp-use-cache - seen-mark) - new-mark)))) - (setq mark-alist - (elmo-msgdb-mark-append mark-alist - num gmark)))) + (setq entity (elmo-msgdb-make-message-entity + (elmo-msgdb-message-entity-handler new-msgdb) + :message-id (aref ov-entity 4) + :number num + :references (elmo-msgdb-get-last-message-id + (aref ov-entity 5)) + :from (elmo-mime-string (elmo-delete-char + ?\" + (or + (aref ov-entity 2) + elmo-no-from) 'uni)) + :subject (elmo-mime-string (or (aref ov-entity 1) + elmo-no-subject)) + :date (aref ov-entity 3) + :size (string-to-int (aref ov-entity 6)) + :extra extra)) + (setq message-id (elmo-message-entity-field entity 'message-id) + flags (elmo-flag-table-get flag-table message-id)) + (elmo-global-flags-set flags folder num message-id) + (elmo-msgdb-append-entity new-msgdb entity flags)) (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)) + new-msgdb)) + +(luna-define-method elmo-folder-msgdb-create ((folder elmo-nntp-folder) + numbers flag-table) + (elmo-nntp-folder-msgdb-create folder numbers flag-table)) + +(defun elmo-nntp-folder-msgdb-create (folder numbers flag-table) + (let ((filter numbers) + (session (elmo-nntp-get-session folder)) + (new-msgdb (elmo-make-msgdb)) + beg-num end-num cur length + new-msgdb 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 - (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)) + (elmo-msgdb-append + new-msgdb + (elmo-nntp-create-msgdb-from-overview-string + folder + ov-str + flag-table + 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) - (if (elmo-nntp-max-number-precedes-list-active-p) - (let ((session (elmo-nntp-get-session spec))) - (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-send-command session - (format "list active %s" - (elmo-nntp-spec-group spec))) - (if (null (elmo-nntp-read-response session)) - (error "NNTP list command failed")) - (setq max-number - (nth 1 (read (concat "(" (elmo-nntp-read-contents - 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 (session numlist - new-mark already-mark - seen-mark seen-list) + (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 new-msgdb (elmo-nntp-msgdb-create-by-header + session numbers flag-table)) + (with-current-buffer (elmo-network-session-buffer session) + (if ov-str + (elmo-msgdb-append + new-msgdb + (elmo-nntp-create-msgdb-from-overview-string + folder + ov-str + flag-table + filter))))) + (elmo-folder-set-killed-list-internal + folder + (nconc + (elmo-folder-killed-list-internal folder) + (car (elmo-list-diff + numbers + (elmo-msgdb-list-messages new-msgdb))))) + ;; 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"))) + (let ((killed (elmo-nntp-catchup-msgdb + new-msgdb + (nth 1 (read (concat "(" (elmo-nntp-read-contents + session) ")")))))) + (when killed + (elmo-folder-kill-messages folder killed)))) + new-msgdb)) + +(luna-define-method elmo-folder-update-number ((folder elmo-nntp-folder)) + (when (elmo-nntp-max-number-precedes-list-active-p) + (let ((session (elmo-nntp-get-session folder))) + (when (elmo-nntp-list-active-p session) + (let ((numbers (elmo-folder-list-messages folder nil 'in-msgdb)) + 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-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 + session) ")")))) + (setq msgdb-max (if numbers (apply #'max numbers) 0)) + (when (and msgdb-max + max-number + (< msgdb-max max-number)) + (let ((i (1+ msgdb-max)) + killed) + (while (<= i max-number) + (setq killed (cons i killed)) + (incf i)) + (elmo-folder-kill-messages folder (nreverse killed))))))))) + +(defun elmo-nntp-msgdb-create-by-header (session numbers flag-table) (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) flag-table))) (defun elmo-nntp-parse-xhdr-response (string) (let (response) @@ -852,7 +957,12 @@ Don't cache if nil.") "Get nntp header string." (save-excursion (let ((session (elmo-nntp-get-session - (list 'nntp nil user server port type)))) + (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 session) @@ -860,10 +970,26 @@ 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-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)) + +(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-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 @@ -875,21 +1001,22 @@ Don't cache if nil.") (goto-char (point-min)) (while (re-search-forward "^\\." nil t) (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)) + (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 ((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-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) @@ -914,7 +1041,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." @@ -941,38 +1068,18 @@ 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))) +(luna-define-method elmo-folder-delete-messages ((folder elmo-nntp-folder) + numbers) + (elmo-folder-kill-messages folder numbers) t) -(defun elmo-nntp-check-validity (spec validity-file) - t) -(defun elmo-nntp-sync-validity (spec validity-file) - t) - -(defun elmo-nntp-folder-exists-p (spec) - (let ((session (elmo-nntp-get-session spec))) - (if (elmo-nntp-plugged-p spec) - (progn - (elmo-nntp-send-command session - (format "group %s" - (elmo-nntp-spec-group spec))) - (elmo-nntp-read-response session)) - t))) - -(defun elmo-nntp-folder-creatable-p (spec) - nil) - -(defun elmo-nntp-create-folder (spec) - nil) ; noop +(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. @@ -980,7 +1087,7 @@ 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 @@ -1003,45 +1110,47 @@ 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) 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)) @@ -1063,43 +1172,57 @@ 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) +(defun elmo-nntp-search-internal (folder condition 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-nntp-search-internal folder + (nth 1 condition) + from-msgs) result (elmo-list-filter result - (elmo-nntp-search - spec (nth 2 condition) + (elmo-nntp-search-internal + folder (nth 2 condition) from-msgs)))) ((eq (car condition) 'or) - (setq result (elmo-nntp-search spec (nth 1 condition) from-msgs) + (setq result (elmo-nntp-search-internal folder + (nth 1 condition) + from-msgs) result (elmo-uniq-list (nconc result - (elmo-nntp-search spec (nth 2 condition) - from-msgs))) + (elmo-nntp-search-internal folder + (nth 2 condition) + from-msgs))) result (sort result '<)))))) -(defun elmo-nntp-get-folders-info-prepare (spec session-keys) +(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 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 @@ -1124,8 +1247,8 @@ Returns a list of cons cells like (NUMBER . VALUE)" (user (aref key 2)) (port (aref key 3)) (type (aref key 4)) - (hashtb (or elmo-nntp-groups-hashtb - (setq elmo-nntp-groups-hashtb + (hashtb (or elmo-newsgroups-hashtb + (setq elmo-newsgroups-hashtb (elmo-make-hash count))))) (save-excursion (elmo-nntp-groups-read-response session cur count) @@ -1135,8 +1258,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" 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 () @@ -1197,17 +1323,6 @@ 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) - (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)) - ;; from nntp.el [Gnus] (defsubst elmo-nntp-next-result-arrived-p () @@ -1264,19 +1379,16 @@ Returns a list of cons cells like (NUMBER . VALUE)" (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")) + ;; Replace all CRLF with LF. + (elmo-delete-cr-buffer) (copy-to-buffer outbuf (point-min) (point-max))))) ;; end of from Gnus -(defun elmo-nntp-msgdb-create-message (len new-mark - already-mark seen-mark seen-list) +(defun elmo-nntp-msgdb-create-message (len flag-table) (save-excursion - (let (beg overview number-alist mark-alist - entity i num gmark seen message-id) + (let ((new-msgdb (elmo-make-msgdb)) + beg entity i num message-id) (elmo-set-buffer-multibyte nil) (goto-char (point-min)) (setq i 0) @@ -1294,33 +1406,15 @@ Returns a list of cons cells like (NUMBER . VALUE)" (save-restriction (narrow-to-region beg (point)) (setq entity - (elmo-msgdb-create-overview-from-buffer num)) + (elmo-msgdb-create-message-entity-from-buffer + (elmo-msgdb-message-entity-handler new-msgdb) num)) (when entity - (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))) - (setq message-id (car entity)) - (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 seen - nil - already-mark) - (if seen - (if elmo-nntp-use-cache - seen-mark) - new-mark)))) - (setq mark-alist - (elmo-msgdb-mark-append - mark-alist - num gmark))) - )))) + (setq message-id + (elmo-message-entity-field entity 'message-id)) + (elmo-msgdb-append-entity + new-msgdb + entity + (elmo-flag-table-get flag-table message-id)))))) (when (> len elmo-display-progress-threshold) (setq i (1+ i)) (if (or (zerop (% i 20)) (= i len)) @@ -1330,41 +1424,138 @@ Returns a list of cons cells like (NUMBER . VALUE)" (when (> len elmo-display-progress-threshold) (elmo-display-progress 'elmo-nntp-msgdb-create-message "Creating msgdb..." 100)) - (list overview number-alist mark-alist)))) + new-msgdb))) -(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) +(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-flag-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-flag-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-set-flag :before ((folder elmo-nntp-folder) + numbers + flag + &optional is-local) + (when (eq flag 'read) + (elmo-nntp-folder-update-crosspost-message-alist folder numbers))) + +(luna-define-method elmo-folder-unset-flag :before ((folder elmo-nntp-folder) + numbers + flag + &optional is-local) + (when (eq flag 'unread) + (elmo-nntp-folder-update-crosspost-message-alist folder numbers))) + +(defsubst elmo-nntp-folder-process-crosspost (folder) +;; 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) + (when (setq entity (elmo-message-entity folder (nth 0 cross))) + (setq reads (cons (elmo-message-entity-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-process-crosspost ((folder elmo-nntp-folder)) + (elmo-nntp-folder-process-crosspost folder)) + +(luna-define-method elmo-folder-list-flagged :around ((folder elmo-nntp-folder) + flag &optional in-msgdb) + ;; 2.3. elmo-folder-list-unreads return unread message list according to + ;; `reads' slot. + (let ((msgs (luna-call-next-method))) + (if in-msgdb + msgs + (case flag + (unread + (elmo-living-messages msgs (elmo-nntp-folder-reads-internal folder))) + ;; Should consider read, digest and any flag? + (otherwise + msgs))))) (require 'product) (product-provide (provide 'elmo-nntp) (require 'elmo-version))