# HG changeset patch # User Ivo Smits # Date 1302610241 -7200 # Node ID 01dc7eeaf5df11107b6a8965c199f4d80a101441 # Parent bc6045ed0b2ef637ec26f2a167ae5ec8042f1655 Added some more checks to article data, added some commands, added 'dot stuffing' for block transfers diff -r bc6045ed0b2e -r 01dc7eeaf5df dbreindex.php --- 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; diff -r bc6045ed0b2e -r 01dc7eeaf5df fetchnews.php --- 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"); diff -r bc6045ed0b2e -r 01dc7eeaf5df server.php --- 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');