[gnu.emacs.gnus] Infinite loop bug in nntp-retrieve-headers

popovich@park.cs.columbia.edu (Steve Popovich) (03/13/89)

This message applies to GNUS version 3.11.

There is a bug in nntp-retrieve-headers that can cause an infinite
loop when selecting a newsgroup.  The bug appears when the NNTP server
sends GNUS a header that doesn't start with any of the keywords it is
expecting.  In my case, the culprit was an article with both a "From: "
header and, later, a "from:" header (all lower case, with no whitespace
after the colon).  The bogus "from:" header caused the endless loop,
because nntp-retrieve-headers will advance to the next line only after
each line is recognized.  The answer is of course to make sure that
non-matching lines will eventually get skipped over.  The following
is one way of doing this; replace your version of nntp-retrieve-headers
with this or a similarly redefined version to avoid the bug.
	-Steve

(defun nntp-retrieve-headers (sequence)
  "Return list of article headers specified by SEQUENCE of article id.
The format of list is `([NUMBER SUBJECT FROM XREF LINES DATE MESSAGE-ID] ...)'.
Reader macros for the vector are defined as `nntp-header-FIELD'.
Writer macros for the vector are defined as `nntp-set-header-FIELD'.
News group must be selected before calling me."
  (save-excursion
    (set-buffer nntp-server-buffer)
    (erase-buffer)
    (let ((number (length sequence))
	  (last-point (point-min))
	  (received 0)
	  (count 0)
	  (headers nil)			;Result list.
	  (article 0)
	  (subject nil)
	  (message-id)
	  (from nil)
	  (xref nil)
	  (lines 0)
	  (date nil))
      ;; Send HEAD command.
      (while sequence
	(nntp-send-strings-to-server "HEAD" (car sequence))
	(setq sequence (cdr sequence))
	(setq count (1+ count))
	;; Every 400 header requests we have to read stream in order
	;;  to avoid deadlock.
	(if (or (null sequence)		;All requests have been sent.
		(zerop (% count nntp-maximum-request)))
	    (progn
	      (accept-process-output)
	      (while (progn
		       (goto-char last-point)
		       ;; Count replies.
		       (while (re-search-forward "^[0-9]" nil t)
			 (setq received (1+ received)))
		       (setq last-point (point))
		       (< received count))
		;; If number of headers is greater than 100, give
		;;  informative messages.
		(and (> number nntp-large-newsgroup)
		     (zerop (% received 20))
		     (message "NNTP: %d%% of headers received."
			      (/ (* received 100) number)))
		(nntp-accept-response))
	      ))
	)
      ;; Wait for text of last command.
      (goto-char (point-max))
      (re-search-backward "^[0-9]" nil t)
      (if (looking-at "^[23]")
	  (while (progn
		   (goto-char (- (point-max) 3))
		   (not (looking-at "^\\.\r$")))
	    (nntp-accept-response)
	    ))
      (if (> number nntp-large-newsgroup)
	  (message "NNTP: 100%% of headers received."))
      ;; Now all of replies are received.
      ;; First, delete unnecessary lines.
      (goto-char (point-min))
      (delete-non-matching-lines
       "^Subject:\\|^Xref:\\|^From:\\|^Lines:\\|^Date:\\|^[23]")
      (if (> number nntp-large-newsgroup)
	  (message "NNTP: Parsing headers..."))
      (setq received number)
      ;; Then examines replies.
      (while (not (eobp))
	(cond ((looking-at "^[23].*[ \t]+\\([0-9]+\\)[ \t]+\\(<.+>\\)")
	       (setq article
		     (string-to-int
		      (buffer-substring (match-beginning 1) (match-end 1))))
	       (setq message-id
		     (buffer-substring (match-beginning 2) (match-end 2)))
	       (forward-line 1)
	       ;; Set default value.
	       (setq subject nil)
	       (setq xref nil)
	       (setq from nil)
	       (setq lines 0)
	       (setq date nil)
	       ;; It is better to extract From:, Subject:, Date:,
	       ;;  Lines: and Xref: field values in *THIS* order.
	       ;; Forward-line each time after getting expected value
	       ;;  in order to reduce count of string matching.
	       (while (looking-at "^[^23]")
		 (let ((no-match t))
		   (if (looking-at "^From:[ \t]\\(.*\\)\r$")
		       (progn
			 (setq from (buffer-substring (match-beginning 1)
						      (match-end 1)))
			 (forward-line 1)
			 (setq no-match nil)))
		   (if (looking-at "^Subject:[ \t]\\(.*\\)\r$")
		       (progn
			 (setq subject (buffer-substring (match-beginning 1)
							 (match-end 1)))
			 (forward-line 1)
			 (setq no-match nil)))
		   (if (looking-at "^Date:[ \t]\\(.*\\)\r$")
		       (progn
			 (setq date (buffer-substring (match-beginning 1)
						      (match-end 1)))
			 (forward-line 1)
			 (setq no-match nil)))
		   (if (looking-at "^Lines:[ \t]\\(.*\\)\r$")
		       (progn
			 (setq lines (string-to-int
				      (buffer-substring (match-beginning 1)
							(match-end 1))))
			 (forward-line 1)
			 (setq no-match nil)))
		   (if (looking-at "^Xref:[ \t]\\(.*\\)\r$")
		       (progn
			 (setq xref (buffer-substring (match-beginning 1)
						      (match-end 1)))
			 (forward-line 1)
			 (setq no-match nil)))
		   (if no-match
		       (forward-line 1))
		   ))
	       (if (null subject)
		   (setq subject "(None)"))
	       (if (null from)
		   (setq from "Unknown User"))
	       (setq headers
		     (cons (vector article subject from
				   xref lines date message-id)
			   headers))
	       )
	      (t (forward-line 1))	;Skip invalid field (ex. Subject:abc)
	      )
	(setq received (1- received))
	(and (> number nntp-large-newsgroup)
	     (zerop (% received 20))
	     (message "NNTP: Parsing headers... %d%%"
		      (/ (* received 100) number)))
	)
      (if (> number nntp-large-newsgroup)
	  (message "NNTP: Parsing headers... done"))
      (nreverse headers)
      )))

umerin@photon.stars.flab.fujitsu.junet (Masanobu UMEDA) (03/18/89)

   Date: 12 Mar 89 21:56:50 GMT
   From: fgw!uunet!columbia.edu!cs!popovich  (Steve Popovich)
   Organization: Columbia University

   This message applies to GNUS version 3.11.

   There is a bug in nntp-retrieve-headers that can cause an infinite
   loop when selecting a newsgroup.  The bug appears when the NNTP server
   sends GNUS a header that doesn't start with any of the keywords it is
   expecting.

I hope the following patch will solve these kinds of problems. Please
try it.

Masanobu UMEDA
umerin@flab.Fujitsu.JUNET
umerin%flab.Fujitsu.JUNET@uunet.uu.NET

*** /tmp/,RCSt1a00422	Sat Mar 18 11:18:25 1989
--- nntp.el	Sat Mar 18 11:13:42 1989
***************
*** 271,276 ****
--- 271,279 ----
  		       (setq xref (buffer-substring (match-beginning 1)
  						    (match-end 1)))
  		       (forward-line 1)))
+ 		 ;; Skip invalid field (ex. Subject:abc)
+ 		 (if (looking-at "^[^:]*:[^ \t]")
+ 		     (forward-line 1))
  		 )
  	       (if (null subject)
  		   (setq subject "(None)"))
***************
*** 606,614 ****
      ;; Initialize communication buffer.
      (setq nntp-server-buffer (get-buffer-create " *nntpd*"))
      (set-buffer nntp-server-buffer)
-     (kill-all-local-variables)
      (buffer-flush-undo (current-buffer))
      (erase-buffer)
      (setq nntp-server-process
  	  (open-network-stream "nntpd" (current-buffer)
  			       host (or service "nntp")))
--- 609,618 ----
      ;; Initialize communication buffer.
      (setq nntp-server-buffer (get-buffer-create " *nntpd*"))
      (set-buffer nntp-server-buffer)
      (buffer-flush-undo (current-buffer))
      (erase-buffer)
+     (kill-all-local-variables)
+     (setq case-fold-search t)		;Should ignore case.
      (setq nntp-server-process
  	  (open-network-stream "nntpd" (current-buffer)
  			       host (or service "nntp")))