changeset 7:01dc7eeaf5df

Added some more checks to article data, added some commands, added 'dot stuffing' for block transfers
author Ivo Smits <Ivo@UCIS.nl>
date Tue, 12 Apr 2011 14:10:41 +0200
parents bc6045ed0b2e
children 005339a1b2ce
files dbreindex.php fetchnews.php server.php
diffstat 3 files changed, 84 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/dbreindex.php	Tue Apr 12 11:41:35 2011 +0200
+++ b/dbreindex.php	Tue Apr 12 14:10:41 2011 +0200
@@ -17,8 +17,13 @@
 		$headerchanged = FALSE;
 		foreach (explode("\r\n", $article['header']) as $line) {
 			if (!strlen($line) || $line == '.') {
-				print("Article $article[id] Contains empty or terminating header line\n");
+				print("Article $article[id] Contains empty or terminating header line, fixing.\n");
+				$headerchanged = TRUE;
 				continue;
+			} else if (strpos($line, "\r") !== FALSE || strpos($line, "\n") !== FALSE || strpos($line, "\0")) {
+				print("Article $article[id] Contains invalid newline or NUL character in header, fixing.\n");
+				$line = str_replace(array("\r","\n","\0"), '', $line);
+				$headerchanged = TRUE;
 			}
 			$parts = explode(': ', $line, 2);
 			$headername = strtoupper($parts[0]);
@@ -48,7 +53,13 @@
 					break;
 			}
 		}
-		foreach (explode("\r\n", $article['body']) as $line) if ($line == '.') print("Article $article[id] Contains terminating body line\n");
+		foreach (explode("\r\n", $article['body']) as $line) {
+			if ($line == '.') {
+				print("Article $article[id] Contains terminating body line\n");
+			} else if (strpos($line, "\r") !== FALSE || strpos($line, "\n") !== FALSE || strpos($line, "\0")) {
+				print("Article $article[id] Contains invalid newline or NUL character in body\n");
+			}
+		}
 		if (!isset($headers['NEWSGROUPS'])) {
 			print("Article $article[id] Missing required Newsgroups header\n");
 			continue;
--- a/fetchnews.php	Tue Apr 12 11:41:35 2011 +0200
+++ b/fetchnews.php	Tue Apr 12 14:10:41 2011 +0200
@@ -88,10 +88,10 @@
 					default:
 						print("Sending unknown header $parts[0]\n");
 				}
-				if ($line !== NULL) nntp_writeline($socket, $line);
+				if ($line !== NULL) nntp_writeline_data($socket, $line);
 			}
 			nntp_writeline($socket, '');
-			foreach (explode("\r\n", $article['body']) as $line) nntp_writeline($socket, $line);
+			foreach (explode("\r\n", $article['body']) as $line) nntp_writeline_data($socket, $line);
 			nntp_writeline($socket, '.');
 			$line = nntp_readline($socket);
 			$code = strtok($line, " \t");
--- a/server.php	Tue Apr 12 11:41:35 2011 +0200
+++ b/server.php	Tue Apr 12 14:10:41 2011 +0200
@@ -26,17 +26,41 @@
 	if ($line === FALSE || $line === NULL) break;
 	$cmd = strtoupper(strtok($line, " \t"));
 	switch ($cmd) {
+		case 'CAPABILITIES':
+			nntp_writeline(STDOUT, '101 Capability list:');
+			nntp_writeline_data(STDOUT, 'VERSION 2');
+			nntp_writeline_data(STDOUT, 'IMPLEMENTATION pNewss [http://wiki.ucis.nl/pNewss]');
+			nntp_writeline_data(STDOUT, 'POST');
+			nntp_writeline_data(STDOUT, 'READER');
+			nntp_writeline_data(STDOUT, 'IHAVE');
+			nntp_writeline_data(STDOUT, 'LIST ACTIVE');
+			nntp_writeline(STDOUT, '.');
+			//HDR, LIST, OVER, STARTTLS, STREAMING, 
+			break;
+		case 'DATE':
+			nntp_writeline(STDOUT, '111 '.gmdate('YmdHis'));
+			break;
+		case 'HELP':
+			nntp_writeline(STDOUT, '100 Help text follows:');
+			nntp_writeline_data(STDOUT, 'Supported commands: CAPABILITIES,DATE,HELP,LIST,GROUP,LISTGROUP,STAT,HEAD,BODY,ARTICLE,LAST,NEXT,POST,IHAVE,QUIT');
+			nntp_writeline(STDOUT, '.');
+			break;
 		case 'LIST':
 			nntp_writeline(STDOUT, '215 list of groups follows');
 			foreach ($db->evalAllAssoc('SELECT * FROM `groups`') as $group) {
 				$groupmessages = $db->evalRow('SELECT MIN(`number`), MAX(`number`) FROM `groupmessages` WHERE `group` = ?', $group['id']);
-				nntp_writeline(STDOUT, $group['name'].' '.intval($groupmessages[1]).' '.intval($groupmessages[0]).' n');
+				nntp_writeline_data(STDOUT, $group['name'].' '.intval($groupmessages[1]).' '.intval($groupmessages[0]).' n');
 			}
 			nntp_writeline(STDOUT, '.');
 			break;
 		case 'GROUP':
+		case 'LISTGROUP':
 			$groupname = strtok(" \t");
-			$group = $db->evalRowAssoc('SELECT * FROM `groups` WHERE `name` = ?', $groupname);
+			if ($groupname === FALSE) {
+				$group = $currentgroup;
+			} else {
+				$group = $db->evalRowAssoc('SELECT * FROM `groups` WHERE `name` = ?', $groupname);
+			}
 			if ($group === FALSE) {
 				nntp_writeline(STDOUT, '411 No such group '.$groupname);
 			} else {
@@ -49,6 +73,24 @@
 					$currentarticle = $db->evalRowAssoc('SELECT * FROM `groupmessages` WHERE `group` = ? AND `number` = ?', array($group['id'], $groupmessages[0]));
 					if ($currentarticle === FALSE) $currentarticle = NULL;
 				}
+				if ($cmd == 'LISTGROUP') {
+					$range = strtok(" \t");
+					if ($range !== FALSE) {
+						$parts = explode('-', $range);
+						if (count($parts) == 1 || !strlen($parts[1])) {
+							$query = 'SELECT `number` FROM `groupmessages` WHERE `group` = ? AND `number` >= ?';
+							$queryargs = array($group['id'], $parts[0]);
+						} else {
+							$query = 'SELECT `number` FROM `groupmessages` WHERE `group` = ? AND `number` BETWEEN ? AND ?';
+							$queryargs = array($group['id'], $parts[0], $parts[1]);
+						}
+					} else {
+						$query = 'SELECT `number` FROM `groupmessages` WHERE `group` = ?';
+						$queryargs = array($group['id']);
+					}
+					foreach ($db->evalColumn($query, $queryargs) as $number) nntp_writeline_data(STDOUT, $number);
+					nntp_writeline(STDOUT, '.');
+				}
 			}
 			break;
 		case 'STAT':
@@ -64,9 +106,9 @@
 				case 'STAT': nntp_writeline(STDOUT, '223 '.$article['messagenumber'].' <'.$article['messageid'].'> stat'); break;
 				default: throw new Exception('Internal error');
 			}
-			if ($cmd == 'HEAD' || $cmd == 'ARTICLE') foreach (explode("\r\n", $article['header']) as $line) nntp_writeline(STDOUT, $line);
+			if ($cmd == 'HEAD' || $cmd == 'ARTICLE') foreach (explode("\r\n", $article['header']) as $line) nntp_writeline_data(STDOUT, $line);
 			if ($cmd == 'ARTICLE') nntp_writeline(STDOUT, '');
-			if ($cmd == 'BODY' || $cmd == 'ARTICLE') foreach (explode("\r\n", $article['body']) as $line) nntp_writeline(STDOUT, $line);
+			if ($cmd == 'BODY' || $cmd == 'ARTICLE') foreach (explode("\r\n", $article['body']) as $line) nntp_writeline_data(STDOUT, $line);
 			if ($cmd != 'STAT') nntp_writeline(STDOUT, '.');
 			break;
 		case 'LAST':
@@ -97,14 +139,34 @@
 			nntp_writeline(STDOUT, '340 Ok, recommended message-ID <'.md5(time().rand()).'@pNews.Core.UCIS.nl>');
 			$lines = nntp_readlines(STDIN);
 			try {
-				$msgid = nntp_article_store($lines);
-				nntp_writeline(STDOUT, '240 Article received <'.$msgid.'>');
+				$messageid = nntp_article_store($lines);
+				nntp_writeline(STDOUT, '240 Article received <'.$messageid.'>');
 			} catch (Exception $ex) {
 				nntp_writeline(STDOUT, '441 '.$ex->getMessage());
 			}
 			break;
+		case 'IHAVE':
+			$messageid = strtok(" \t");
+			if ($messageid === FALSE || strlen($messageid) <= 2 || $messageid[0] != '<' || $messageid[strlen($messageid)-1] == '>') {
+				nntp_writeline(STDOUT, '435 Argument error');
+				break;
+			}
+			$messageid = substr($messageid, 1, -1);
+			if ($db->evalRow('SELECT `id` FROM `messages` WHERE `messageid` = ?', $messageid) !== FALSE) {
+				nntp_writeline(STDOUT, '435 Duplicate');
+				break;
+			}
+			nntp_writeline(STDOUT, '335 Send article to be transferred');
+			$lines = nntp_readlines(STDIN);
+			try {
+				$messageid = nntp_article_store($lines);
+				nntp_writeline(STDOUT, '235 Article received <'.$messageid.'>');
+			} catch (Exception $ex) {
+				nntp_writeline(STDOUT, '437 '.$ex->getMessage());
+			}
+			break;
 		case 'QUIT':
-			nntp_writeline(STDOUT, '205 .');
+			nntp_writeline(STDOUT, '205 Bye.');
 			return;
 		default:
 			nntp_writeline(STDOUT, '500 Command not understood');