;;; mew-nntp.el for reading

;; Author:  Mew developing team
;; Created: Feb  1, 1999

;;; Code:

(require 'mew)

(defvar mew-nntp-msgid-file ".mew-msgid")
(defvar mew-nntp-folder-alist-file ".mew-folder-alist")
(defvar mew-nntp-folder-alist nil)
;; without mew-folder-nntp
(defvar mew-nntp-folder-alist2-file ".mew-folder-alist2")
(defvar mew-nntp-folder-alist2 nil)

(defvar mew-nntp-skip-uidl t)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; NNTP info
;;;

(defvar mew-nntp-info-list
  '("server" "port" "process" "ssh-process" "ssl-process" "ssl-p" "status"
    "directive" "bnm" "mdb"
    "rtrs" "refs" "range"
    "rttl" "rcnt" "hlds"
    "user" "account"
    "size" "get-body" "no-msg" "case" "msgdb" "done" "dispatched" "error"
    "max"
    "newsgroup" "msgid" "truncated"
    "virtual-info" "disp-info" "status-buf"))

(mew-info-defun "mew-nntp-" mew-nntp-info-list)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; FSM
;;;

(defvar mew-nntp-fsm
  '(("greeting"    nil ("20[01]" . "mode-reader"))
    ("mode-reader" nil (t        . "authinfo"))
    ("authinfo"    nil ("381"    . "authpass"))
    ("authpass"    nil ("281"    . "group") (t . "wpwd"))
    ("group"       nil ("211"    . "xover"))
    ("xover"	   t   ("224"    . "pre-article"))
    ("article"     t   ("22[01]" . "post-article") (t . "next-article"))
    ("list"        t   ("215"    . "post-list"))
    ("pre-quit"    nil (t        . "quit2"))
    ("quit"        nil ("205"    . "noop"))))

(defun mew-nntp-fsm-by-status (status)
  (assoc status mew-nntp-fsm))

(defun mew-nntp-fsm-next (status code)
  (cdr (mew-assoc-match2 code (nthcdr 2 (mew-nntp-fsm-by-status status)) 0)))

(defun mew-nntp-fsm-reply (status)
  (nth 1 (mew-nntp-fsm-by-status status)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Filters
;;;

(defun mew-nntp-secure-p (pnm)
  (or (mew-nntp-get-ssh-process pnm) (mew-nntp-get-ssl-p pnm)))

(defun mew-nntp-command-mode-reader (pro pnm)
  (mew-net-status (mew-nntp-get-status-buf pnm)
		  "Connecting"
		  nil
		  (mew-nntp-secure-p pnm))
  (mew-nntp-process-send-string pro "MODE READER"))

(defun mew-nntp-command-authinfo (pro pnm)
  (let ((user (mew-nntp-get-user pnm)))
    (if user
	(mew-nntp-process-send-string pro "AUTHINFO USER %s" user)
      (mew-nntp-set-status pnm "group")
      (mew-nntp-command-group pro pnm))))

(defun mew-nntp-command-authpass (pro pnm)
  (let* ((prompt (format "NNTP password (%s): " (mew-nntp-get-account pnm)))
         (pass (mew-nntp-input-passwd prompt pnm)))
    (mew-nntp-process-send-string pro "AUTHINFO PASS %s" pass)))

(defun mew-nntp-command-wpwd (_pro pnm)
  (mew-nntp-message pnm "NNTP password is wrong!")
  (mew-passwd-set-passwd (mew-nntp-passtag pnm) nil)
  (mew-nntp-set-status pnm "pre-quit"))

(defun mew-nntp-command-group (pro pnm)
  (let ((directive (mew-nntp-get-directive pnm))
	(newsgroup (mew-nntp-get-newsgroup pnm)))
    (cond
     ((eq directive 'list)
      (mew-nntp-set-status pnm "list")
      (mew-nntp-command-list pro pnm))
     (t
      (mew-nntp-process-send-string pro "GROUP %s" newsgroup)))))

(defun mew-nntp-command-xover (pro pnm)
  (let ((directive (mew-nntp-get-directive pnm))
	(refs (mew-nntp-get-refs pnm)) ;; (uid siz del (+fld msg))
	(bnm (mew-nntp-get-bnm pnm))
	(range (mew-nntp-get-range pnm))
	max first last)
    (if (and mew-nntp-skip-uidl (eq directive 'get))
	(mew-nntp-command-dispatch pro pnm directive refs nil)
      (mew-net-status (mew-nntp-get-status-buf pnm)
		      "Checking" nil (mew-nntp-secure-p pnm))
      (cond
       ((eq directive 'scan)
	(if (eq range nil) ;; update
	    (setq max (mew-lisp-load
		       (mew-expand-file bnm mew-nntp-msgid-file))))))
      ;; 221 total first last newsgroup (xxx)
      (if (re-search-forward "^[0-9]+ +[0-9]+ +\\([0-9]+\\) +\\([0-9]+\\) +[-.a-zA-Z0-9]+" nil t)
	  (progn
	    (setq first (string-to-number (mew-match-string 1)))
	    (setq last (string-to-number (mew-match-string 2)))
	    (cond
	     ((stringp max)
	      (setq max (string-to-number max)))
	     (max ;; backward compatibility
	      ;; reversed
	      (setq max (car max))
	      (setq max (string-to-number max)))
	     ((and (eq directive 'scan) (integerp range))
	      (setq max (- last range))
	      (if (< max first) (setq max (1- first))))
	     (t
	      (setq max (1- first))))
	    (mew-nntp-set-max pnm max)
	    (mew-nntp-process-send-string pro "XOVER %d-" (1+ max)))
	(mew-nntp-set-status pnm "quit")
	(mew-nntp-command-quit pro pnm)))))

(defun mew-nntp-command-pre-article (pro pnm)
  (let* ((directive (mew-nntp-get-directive pnm))
	 (max (mew-nntp-get-max pnm))
	 (refs (mew-nntp-get-refs pnm))
	 ;; (uid siz del (+fld msg))
	 (range (mew-nntp-get-range pnm))
	 uid siz rtr rtrs hlds)
    (goto-char (point-min))
    ;; num subj from date msg-id ref siz lines
    (while (re-search-forward "^\\([0-9]+\\)\t[^\t\n]*\t[^\t\n]*\t[^\t\n]*\t<[^>\t\n]+>\t[^\t\n]*\t\\([0-9]*\\)" nil t)
      (setq uid (mew-match-string 1))
      (setq siz (mew-match-string 2))
      (if (string= uid "") (setq uid nil))
      (cond
       ((eq directive 'get)
	(setq rtr (assoc uid refs))
	(if rtr (setq rtrs (cons rtr rtrs))))
       ((eq directive 'scan)
	(if (and uid (or range ;; all, last:n
			 (> (string-to-number uid) max))) ;; update
	    (setq rtrs (cons (mew-make-refileinfo :uid uid :size siz) rtrs))))
       ((eq directive 'sync)
	(if uid (setq hlds (cons uid hlds))))))
    (mew-nntp-set-msgid pnm (mew-refileinfo-get-uid (car rtrs))) ;; 'scan
    ;; last:n xxx
    ;;    (when (and (eq directive 'scan) (integerp range))
    ;;      (mew-ntake range rtrs))
    (setq rtrs (nreverse rtrs))
    (setq hlds (nreverse hlds))
    (mew-nntp-command-dispatch pro pnm directive rtrs hlds)))

(defun mew-nntp-command-dispatch (pro pnm directive rtrs hlds)
  (let ((rttl (length rtrs)))
    (mew-nntp-set-rtrs pnm rtrs)
    (mew-nntp-set-rttl pnm rttl)
    (mew-nntp-set-hlds pnm hlds)
    (mew-nntp-set-dispatched pnm t)
    (cond
     ((eq directive 'sync)
      (mew-nntp-set-status pnm "quit")
      (mew-nntp-command-quit pro pnm))
     ((= rttl 0)
      (mew-nntp-message pnm "No new messages")
      (mew-nntp-set-status pnm "quit")
      (mew-nntp-command-quit pro pnm))
     ((= rttl 1)
      (mew-nntp-message pnm "Retrieving 1 message in background...")
      (mew-nntp-set-status pnm "article")
      (mew-nntp-command-article pro pnm))
     (t
      (mew-nntp-message pnm "Retrieving %d messages in background..." rttl)
      (mew-nntp-set-status pnm "article")
      (mew-nntp-command-article pro pnm)))))

(defun mew-nntp-command-article (pro pnm)
  (mew-net-status2 (mew-nntp-get-status-buf pnm)
		   (mew-nntp-get-rttl pnm)
		   (mew-nntp-get-rcnt pnm)
		   (mew-refileinfo-get-size (car (mew-nntp-get-rtrs pnm)))
		   'zero
		   (mew-nntp-secure-p pnm))
  (let* ((directive (mew-nntp-get-directive pnm))
	 (rtrs (mew-nntp-get-rtrs pnm))
	 (rtr (car rtrs))
	 (uid (mew-refileinfo-get-uid rtr))
	 (siz (mew-refileinfo-get-size rtr))
	 (lim (mew-nntp-get-size pnm))
	 (get-body (mew-nntp-get-get-body pnm)))
    (cond
     ((or (null rtr) (eq directive 'biff))
      (mew-nntp-set-truncated pnm nil)
      (mew-nntp-set-status pnm "quit")
      (mew-nntp-command-quit pro pnm))
     ((eq directive 'get)
      (mew-nntp-set-truncated pnm nil)
      (mew-nntp-process-send-string pro "ARTICLE %s" uid))
     ((and (eq directive 'scan) (not get-body))
      (mew-nntp-set-truncated pnm t)
      (mew-nntp-process-send-string pro "HEAD %s" uid))
     ((or (= lim 0) (<= (string-to-number siz) lim))
      (mew-nntp-set-truncated pnm nil)
      (mew-nntp-process-send-string pro "ARTICLE %s" uid))
     (t
      (mew-nntp-set-truncated pnm t)
      (mew-nntp-process-send-string pro "HEAD %s" uid)))))

(defun mew-nntp-command-post-article (pro pnm)
  (let* ((directive (mew-nntp-get-directive pnm))
	 (width (1- (mew-scan-width)))
	 (rtrs (mew-nntp-get-rtrs pnm))
	 (rtr (car rtrs))
	 (uid (mew-refileinfo-get-uid rtr))
	 (siz (mew-refileinfo-get-size rtr))
	 (fld-msg (mew-refileinfo-get-folders rtr))
	 (truncated (mew-nntp-get-truncated pnm))
	 fld msg vec file msg-file lmsg)
    (cond
     ((null fld-msg)
      (setq fld (mew-nntp-get-bnm pnm)))
     ((stringp fld-msg)
      (setq fld fld-msg))
     ((listp fld-msg)
      (mew-set '(fld msg) fld-msg)
      (setq lmsg msg)))
    (goto-char (point-min))
    (forward-line)
    (delete-region (point-min) (point))
    ;; line delimiters
    (mew-eol-fix-for-read)
    (mew-dot-delete)
    (cond
     ((eq directive 'scan)
      (setq msg uid)
      (setq file (mew-expand-new-msg fld msg)))
     (t
      (setq msg-file (mew-net-get-new-message
		      pnm fld msg 'mew-nntp-get-msgdb 'mew-nntp-set-msgdb))
      (setq msg (car msg-file) file (cdr msg-file))))
    (goto-char (point-min))
    (if truncated
	(mew-header-insert-xmu uid siz t)
      (mew-header-insert-xmu uid siz nil))
    (catch 'write-error
      (condition-case nil
	  (let ((write-region-inhibit-fsync mew-use-async-write))
	    (mew-frwlet mew-cs-dummy mew-cs-text-for-write
	      (write-region (point-min) (point-max) file nil 'no-msg)))
	(error
	 (mew-nntp-set-status pnm "quit")
	 (mew-nntp-command-quit pro pnm)
	 (throw 'write-error nil)))
      (when (file-exists-p file)
	(mew-set-file-modes file)
	(mew-set-file-type file)
	(mew-set-buffer-multibyte t)
	(setq vec (mew-scan-header))
	(mew-scan-set-folder vec fld)
	(mew-scan-set-message vec msg)
	(mew-scan-body vec)
	(mew-set-buffer-multibyte nil)
	(mew-scan-insert-line fld vec width lmsg))
      (mew-nntp-command-next-article pro pnm))))

(defun mew-nntp-command-next-article (pro pnm)
  (let* ((rtrs (mew-nntp-get-rtrs pnm)))
    (mew-nntp-set-rcnt pnm (1+ (mew-nntp-get-rcnt pnm)))
    (mew-nntp-set-rtrs pnm (cdr rtrs))
    (mew-nntp-set-status pnm "article")
    (mew-nntp-command-article pro pnm)))

(defun mew-nntp-command-list (pro pnm)
  (mew-net-status (mew-nntp-get-status-buf pnm)
		  "Listing"
		  nil
		  (mew-nntp-secure-p pnm))
  (mew-nntp-message pnm "Collecting newsgroup list...")
  (mew-nntp-process-send-string pro "LIST"))

(defun mew-nntp-command-post-list (pro pnm)
  (let ((case (mew-nntp-get-case pnm))
	group group2 groups groups2)
    (goto-char (point-min))
    (forward-line)
    (delete-region (point-min) (point))
    ;; line delimiters
    (mew-eol-fix-for-read)
    (mew-dot-delete)
    (while (not (eobp))
      (when (looking-at "\\([a-z][^ \t\n]+\\)")
	(setq group2 (mew-match-string 1))
	(setq group (concat mew-folder-nntp group2))
	(setq groups (cons (mew-folder-func group) groups))
	(setq groups2 (cons (mew-folder-func group2) groups2)))
      (forward-line))
    (if (null case) (setq case mew-case-default))
    (setq groups (nreverse groups))
    (mew-nntp-folder-save case groups groups2)
    (mew-nntp-folder-alist-set case groups)
    (mew-nntp-folder-alist2-set case groups2)
    (mew-nntp-set-status pnm "quit")
    (mew-nntp-command-quit pro pnm)))

(defun mew-nntp-command-quit (pro pnm)
  (mew-nntp-set-done pnm t)
  (mew-nntp-process-send-string pro "QUIT"))

(defun mew-nntp-command-quit2 (pro pnm)
  (mew-nntp-set-done pnm t)
  (mew-nntp-set-error pnm t)
  (mew-nntp-set-status pnm "quit")
  (mew-nntp-process-send-string pro "QUIT"))

(defun mew-nntp-command-noop (_pro _pnm)
  ())

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Sub functions
;;;

(defconst mew-nntp-info-prefix "mew-nntp-info-")

(defun mew-nntp-info-name (case newsgroup)
  (let ((server (mew-nntp-server case))
	(port (mew-*-to-string (mew-nntp-port case)))
	(sshsrv (mew-nntp-ssh-server case))
	(name mew-nntp-info-prefix))
    (setq name (concat name server "/" newsgroup))
    (unless (mew-port-equal port mew-nntp-port)
      (setq name (concat name ":" port)))
    (if sshsrv
	(concat name "%" sshsrv)
      name)))

(defun mew-nntp-buffer-name (pnm)
  (concat mew-buffer-prefix pnm))

(defun mew-nntp-process-send-string (pro &rest args)
  (let ((str (apply 'format args)))
    (mew-nntp-debug "=SEND=" str)
    (if (and (processp pro) (eq (process-status pro) 'open))
	(process-send-string pro (concat str mew-cs-eol))
      (message "NNTP time out"))))

(defun mew-nntp-passtag (pnm)
  (let ((server (mew-nntp-get-server pnm))
	(port (mew-nntp-get-port pnm))
	(user (mew-nntp-get-user pnm)))
    (concat user "@" server ":" port)))

(defun mew-nntp-message (pnm &rest args)
  (or (mew-nntp-get-no-msg pnm) (apply 'message args)))

(defun mew-bnm-to-newsgroup (bnm)
  (mew-folder-string (mew-case:folder-folder bnm)))

(defun mew-nntp-input-passwd (prompt pnm)
  (let* ((tag (mew-nntp-passtag pnm))
         (pro (mew-nntp-get-process pnm))
         (pass (mew-input-passwd prompt tag)))
    (unless (and (processp pro) (eq (process-status pro) 'open))
      (mew-passwd-set-passwd tag nil))
    pass))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Opening NNTP
;;;

(defun mew-nntp-open (pnm case server port no-msg starttlsp)
  (let ((sprt (mew-*-to-port port))
	(sslnp (mew-ssl-native-p (mew-nntp-ssl case)))
	pro tm)
    (condition-case emsg
	(progn
	  (setq tm (run-at-time mew-nntp-timeout-time nil 'mew-nntp-timeout))
	  (or no-msg (message "Connecting to the NNTP server..."))
	  (setq pro (mew-open-network-stream pnm nil server sprt
					     'nntp sslnp starttlsp case))
	  (setq pro (car pro))
	  (when (not (processp pro)) (signal 'quit nil))
	  (mew-process-silent-exit pro)
	  (mew-set-process-cs pro mew-cs-text-for-net mew-cs-text-for-net)
	  (or no-msg (message "Connecting to the NNTP server...done")))
      (quit
       (or no-msg (message "Cannot connect to the NNTP server"))
       (setq pro nil))
      (error
       (or no-msg (message "%s, %s" (nth 1 emsg) (nth 2 emsg)))
       (setq pro nil)))
    (if tm (cancel-timer tm))
    pro))

(defun mew-nntp-timeout ()
  ;; Do not timeout if the NSM query pane is active.
  (unless (get-buffer "*Network Security Manager*")
    (message "NNTP connection timed out (%d seconds)"
	     mew-nntp-timeout-time)
    (signal 'quit nil)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Launcher
;;;

(defun mew-nntp-retrieve (case directive bnm &rest args)
  (let* ((server (mew-nntp-server case))
         (user (mew-nntp-user case))
	 (port (mew-*-to-string (mew-nntp-port case)))
	 (sshsrv (mew-nntp-ssh-server case))
	 (sslp (mew-nntp-ssl case))
	 (sslport (mew-nntp-ssl-port case))
	 (sslnp (mew-ssl-native-p (mew-nntp-ssl case)))
	 (starttlsp
	  (mew-ssl-starttls-p (mew-nntp-ssl case)
			      (mew-*-to-string (mew-nntp-port case))
			      (mew-nntp-ssl-port case)))
	 (newsgroup (mew-bnm-to-newsgroup bnm))
	 (pnm (mew-nntp-info-name case newsgroup))
	 (buf (get-buffer-create (mew-nntp-buffer-name pnm)))
	 (no-msg (eq directive 'biff))
	 process sshname sshpro sslname sslpro lport tls
	 virtual-info disp-info virtual)
    (if (mew-nntp-get-process pnm)
	(message "Another NNTP process is running. Try later")
      (cond
       (sslnp
	(let ((serv (if starttlsp port sslport)))
	  (setq process (mew-nntp-open pnm case server serv no-msg starttlsp))))
       (sshsrv
	(setq sshpro (mew-open-ssh-stream case server port sshsrv))
	(when sshpro
	  (setq sshname (process-name sshpro))
	  (setq lport (mew-ssh-pnm-to-lport sshname))
	  (when lport
	    (setq process (mew-nntp-open pnm case "localhost" lport no-msg nil)))))
       (sslp
	(when starttlsp (setq tls mew-tls-nntp))
	(setq sslpro (mew-open-ssl-stream case server sslport tls))
	(when sslpro
	  (setq sslname (process-name sslpro))
	  (setq lport (mew-ssl-pnm-to-lport sslname))
	  (when lport
	    (setq process (mew-nntp-open pnm case mew-ssl-localhost lport no-msg nil)))))
       (t
	(setq process (mew-nntp-open pnm case server port no-msg nil))))
      (when process
	(mew-summary-lock process "NNTPing" (or sshpro sslp))
	(mew-sinfo-set-summary-form (mew-get-summary-form bnm))
	(mew-sinfo-set-summary-column (mew-get-summary-column bnm))
	(mew-sinfo-set-unread-mark nil)
	(mew-sinfo-set-scan-id nil)
	(mew-sinfo-set-scan-md5 nil)
	(mew-info-clean-up pnm)
	(mew-nntp-set-no-msg pnm no-msg) ;; must come here
	(mew-nntp-message pnm "Communicating with the NNTP server...")
	(mew-nntp-set-process pnm process)
	(mew-nntp-set-ssh-process pnm sshpro)
	(mew-nntp-set-ssl-process pnm sslpro)
	(mew-nntp-set-ssl-p pnm sslp)
	(mew-nntp-set-server pnm server)
	(mew-nntp-set-port pnm port)
	(mew-nntp-set-user pnm user)
        (mew-nntp-set-account pnm (format "%s@%s" user server))
	(mew-nntp-set-status pnm "greeting")
	(mew-nntp-set-directive pnm directive)
	(mew-nntp-set-bnm pnm bnm)
	(mew-nntp-set-status-buf pnm bnm)
	(mew-nntp-set-rcnt pnm 1)
	(mew-nntp-set-rttl pnm 0)
	(mew-nntp-set-size pnm (mew-nntp-size case))
	(mew-nntp-set-newsgroup pnm newsgroup)
	(mew-nntp-set-case pnm case)
	;;
	(cond
	 ((eq directive 'get)
	  (mew-sinfo-set-unread-mark (mew-get-unread-mark bnm))
	  (mew-nntp-set-refs pnm (nth 0 args))
	  (setq virtual-info (nth 1 args))
	  (mew-nntp-set-virtual-info pnm virtual-info)
	  (setq disp-info (nth 1 args))
	  (mew-nntp-set-disp-info pnm disp-info)
	  (setq virtual (mew-net-virtual-info-get-virtual virtual-info))
	  (when virtual
	    (mew-nntp-set-status-buf pnm virtual)
	    (with-current-buffer virtual
	      (mew-summary-lock process "NNTPing" (or sshpro sslp)))))
	 ((eq directive 'scan)
	  (mew-nntp-set-range pnm (nth 0 args))
	  (mew-nntp-set-get-body pnm (nth 1 args))
	  (if (mew-nntp-get-range pnm)
	      (progn
		(mew-nntp-set-mdb pnm (mew-summary-mark-collect4))
		(mew-net-folder-clean))
	    (mew-sinfo-set-unread-mark (mew-get-unread-mark bnm))))
	 ((eq directive 'sync)
	  ))
	(mew-sinfo-set-start-point (point)) ;; after erase-buffer
	(set-process-sentinel process 'mew-nntp-sentinel)
	(set-process-filter process 'mew-nntp-filter)
	(set-process-buffer process buf)
	(when sslnp
	  ;; GnuTLS requires a client-initiated command after the
	  ;; session is established or upgraded to use TLS because
	  ;; no additional greeting from the server.
	  (mew-nntp-command-mode-reader process pnm))
	))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Filter and sentinel
;;;

(defun mew-nntp-debug (label string)
  (when (mew-debug 'net)
    (with-current-buffer (get-buffer-create mew-buffer-debug)
      (goto-char (point-max))
      (insert (format "\n<%s>\n%s\n" label string)))))

(defun mew-nntp-filter (process string)
  (let* ((pnm (process-name process))
	 (status (mew-nntp-get-status pnm))
	 (mulrep (mew-nntp-fsm-reply status))
	 stay next func code)
    (mew-nntp-debug (upcase status) string)
    (mew-filter
     ;; Process's buffer
     (goto-char (point-max))
     (mew-set-buffer-multibyte nil)
     (insert string)
     (when (string= status "article")
       (mew-net-status2 (mew-nntp-get-status-buf pnm)
			(mew-nntp-get-rttl pnm)
			(mew-nntp-get-rcnt pnm)
			(mew-refileinfo-get-size (car (mew-nntp-get-rtrs pnm)))
			nil
			(mew-nntp-secure-p pnm)))
     (cond
      ((and (and (goto-char (1- (point-max))) (looking-at "\n$"))
	    (and (goto-char (point-min)) (looking-at "^\\([45][0-9][0-9]\\)")))
       ;; this is an error code. this cannot be a multiple-line reply.
       (setq code (mew-match-string 1))
       (setq next (mew-nntp-fsm-next status code)))
      ((and (or (and mulrep
		     (goto-char (point-max))
		     (= (forward-line -1) 0)
		     (looking-at "^\\.\r?$"))
		(and (not mulrep)
		     (goto-char (1- (point-max)))
		     (looking-at "\n$")))
	    (and (goto-char (point-min)) (looking-at "^\\([0-9][0-9][0-9]\\)")))
       (setq code (mew-match-string 1))
       (setq next (mew-nntp-fsm-next status code)))
      (t
       (setq stay t)))
     (unless stay
       (unless next (setq next "quit"))
       (mew-nntp-set-status pnm next)
       (setq func (intern-soft (concat "mew-nntp-command-" next)))
       (goto-char (point-min))
       (if (fboundp func)
	   (funcall func process pnm)
	 (error "No function called %s" (symbol-name func)))
       (if (and process (equal (process-buffer process) (current-buffer)))
	   (mew-erase-buffer))))))

(defun mew-nntp-sentinel (process event)
  (let* ((pnm (process-name process))
	 (directive (mew-nntp-get-directive pnm))
	 (mdb (mew-nntp-get-mdb pnm))
	 (sshpro (mew-nntp-get-ssh-process pnm))
	 (sslpro (mew-nntp-get-ssl-process pnm))
	 (rttl (mew-nntp-get-rttl pnm))
	 (bnm (or (mew-nntp-get-bnm pnm) (current-buffer)))
	 (hlds (mew-nntp-get-hlds pnm))
	 (msgid (mew-nntp-get-msgid pnm))
	 (done (mew-nntp-get-done pnm))
	 (error (mew-nntp-get-error pnm))
	 (file (mew-expand-file bnm mew-nntp-msgid-file))
	 (buf (process-buffer process))
	 (virtual-info (mew-nntp-get-virtual-info pnm))
	 (disp-info (mew-nntp-get-disp-info pnm)))
    (save-excursion
      (mew-nntp-debug "NNTP SENTINEL" event)
      (set-process-buffer process nil)
      (set-buffer bnm)
      (mew-summary-mark-recover mdb)
      (mew-remove-buffer buf)
      (if (not done)
	  (let* ((rtrs (mew-nntp-get-rtrs pnm))
		 (lefts (length rtrs))
		 (msgid (mew-refileinfo-get-uid (car rtrs)))
		 recovered)
	    (mew-nntp-message pnm "NNTP connection is lost")
	    (when (mew-nntp-get-dispatched pnm)
	      (cond
	       ((eq directive 'scan)
		(setq msgid (number-to-string (1- (string-to-number msgid))))
		(mew-lisp-save file msgid nil 'unlimit)
		(setq recovered t)))
	      (when recovered
		(mew-nntp-message
		 pnm
		 "%d message retrieved. %d messages are left due to an error"
		 (- rttl lefts) lefts)
		(mew-summary-folder-cache-save))))
	(if virtual-info (mew-summary-retrieve-message-for-virtual virtual-info))
	(cond
	 (error
	  ;; retain the error message
	  )
	 ((eq directive 'list)
	  (mew-nntp-message pnm "Collecting newsgroup list...done"))
	 ((eq directive 'sync)
	  (mew-nntp-message pnm "Synchronizing messages...")
	  (mew-net-folder-sync bnm hlds)
	  (mew-nntp-message pnm "Synchronizing messages...done")
	  (mew-summary-folder-cache-save))
	 ((eq directive 'get)
	  (cond
	   ((= rttl 0)
	    (mew-nntp-message pnm "No new messages"))
	   ((= rttl 1)
	    (mew-nntp-message pnm "1 message retrieved")
	    (mew-summary-folder-cache-save))
	   (t
	    (mew-nntp-message pnm "%d messages retrieved" rttl)
	    (mew-summary-folder-cache-save))))
	 ((eq directive 'scan)
	  (cond
	   ((or (= rttl 0) (null msgid))
	    (mew-nntp-message pnm "No messages scanned"))
	   ((= rttl 1)
	    (mew-nntp-message pnm "1 message scanned")
	    (mew-lisp-save file msgid nil 'unlimit)
	    (mew-summary-folder-cache-save))
	   (t
	    (mew-nntp-message pnm "%d messages scanned" rttl)
	    (mew-lisp-save file msgid nil 'unlimit)
	    (mew-summary-folder-cache-save))))))
      ;;
      (and mew-use-async-write (mew-unix-sync))
      (mew-net-status-clear (mew-nntp-get-status-buf pnm))
      (mew-info-clean-up pnm)
      (set-buffer-modified-p nil)
      (mew-summary-unlock)
      (if (and (processp sshpro) (not mew-ssh-keep-connection))
	  (process-send-string sshpro "exit\n"))
      (if (and (processp sslpro) (not mew-ssl-keep-connection))
	  (delete-process sslpro))
      (mew-net-disp-info-display disp-info)
      (run-hooks 'mew-nntp-sentinel-hook))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Newsgroup alist
;;;

(defun mew-nntp-folder-clean-up ()
  (setq mew-nntp-folder-alist nil)
  (setq mew-nntp-folder-alist2 nil))

(defun mew-nntp-folder-alist (&optional case)
  (let ((ent (assoc (or case mew-case-default) mew-nntp-folder-alist))
	alist)
    (if (and ent (cdr ent))
	(cdr ent)
      (setq alist (mew-nntp-folder-load case))
      (if alist
	  alist
	(list (mew-folder-func (mew-nntp-newsgroup case)))))))

(defun mew-nntp-folder-alist2 (&optional case)
  (let ((ent (assoc (or case mew-case-default) mew-nntp-folder-alist2)))
    (if ent
	(cdr ent)
      (mew-nntp-folder-load case 'two))))

(defun mew-nntp-folder-load (case &optional two)
  (let* ((fld (mew-nntp-folder case))
	 (file (mew-expand-file fld mew-nntp-folder-alist-file))
	 (groups (mew-lisp-load file))
	 (file2 (mew-expand-file fld mew-nntp-folder-alist2-file))
	 (groups2 (mew-lisp-load file2)))
    (mew-nntp-folder-alist-set case groups)
    (mew-nntp-folder-alist2-set case groups2)
    (if two groups2 groups)))

(defun mew-nntp-folder-save (case groups groups2)
  (let* ((fld (mew-nntp-folder case))
	 (dir (mew-expand-folder fld))
	 (file (expand-file-name mew-nntp-folder-alist-file dir))
	 (file2 (expand-file-name mew-nntp-folder-alist2-file dir)))
    (mew-check-directory dir)
    (mew-lisp-save file groups 'nobackup 'unlimit)
    (mew-lisp-save file2 groups2 'nobackup 'unlimit)))

(defun mew-nntp-folder-alist-set (case groups)
  (setq mew-nntp-folder-alist
	(cons (cons (or case mew-case-default) groups)
	      (delete (assoc (or case mew-case-default) mew-nntp-folder-alist)
		      mew-nntp-folder-alist))))

(defun mew-nntp-folder-alist2-set (case groups)
  (setq mew-nntp-folder-alist2
	(cons (cons (or case mew-case-default) groups)
	      (delete (assoc (or case mew-case-default) mew-nntp-folder-alist2)
		      mew-nntp-folder-alist2))))

(defun mew-nntp-update (case)
  (let ((bnm (mew-summary-folder-name 'ext)))
    (mew-nntp-retrieve case 'list bnm)))

(provide 'mew-nntp)

;;; Copyright Notice:

;; Copyright (C) 1999-2023 Mew developing team.
;; All rights reserved.

;; Redistribution and use in source and binary forms, with or without
;; modification, are permitted provided that the following conditions
;; are met:
;;
;; 1. Redistributions of source code must retain the above copyright
;;    notice, this list of conditions and the following disclaimer.
;; 2. Redistributions in binary form must reproduce the above copyright
;;    notice, this list of conditions and the following disclaimer in the
;;    documentation and/or other materials provided with the distribution.
;; 3. Neither the name of the team nor the names of its contributors
;;    may be used to endorse or promote products derived from this software
;;    without specific prior written permission.
;;
;; THIS SOFTWARE IS PROVIDED BY THE TEAM AND CONTRIBUTORS ``AS IS'' AND
;; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
;; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
;; PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE TEAM OR CONTRIBUTORS BE
;; LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
;; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
;; SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
;; BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
;; OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
;; IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

;;; mew-nntp.el ends here
