Mercurial > hg > pnewss
view server.php @ 2:40e545510a57
Added support for POSTing to the server, added readme and todo, added support for upstream synchronization using POST
author | Ivo Smits <Ivo@UCIS.nl> |
---|---|
date | Tue, 12 Apr 2011 00:29:41 +0200 |
parents | d7ab68b71c74 |
children | 0dcdb73cbcbf |
line wrap: on
line source
#!/usr/bin/php <?php chdir(__DIR__); require_once './pdo.php'; require_once './config.php'; $logfile = fopen('./server.log', 'w'); $currentgroup = NULL; $currentarticle = NULL; function exception_handler($exception) { nntp_writeline(STDOUT, ''); nntp_writeline(STDOUT, '590 Exception: '.$exception->getMessage()); die(); } function error_handler($errno, $errstr, $errfile, $errline) { nntp_writeline(STDOUT, ''); nntp_writeline(STDOUT, '590 Error in file '.$errfile.' line '.$errline.' error '.$errno.' '.$errstr); die(); } set_exception_handler('exception_handler'); set_error_handler("error_handler"); nntp_writeline(STDOUT, '200 pNewss ready'); while (TRUE) { $line = nntp_readline(STDIN); if ($line === FALSE || $line === NULL) break; $cmd = strtok($line, " \t"); switch (strtoupper($cmd)) { 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(STDOUT, '.'); break; case 'GROUP': $groupname = strtok(" \t"); $group = $db->evalRowAssoc('SELECT * FROM `groups` WHERE `name` = ?', $groupname); if ($group === FALSE) { nntp_writeline(STDOUT, '411 No such group '.$groupname); } else { $currentgroup = $group; $groupmessages = $db->evalRow('SELECT MIN(`number`), MAX(`number`), COUNT(`number`) FROM `groupmessages` WHERE `group` = ?', $group['id']); nntp_writeline(STDOUT, '211 '.intval($groupmessages[2]).' '.intval($groupmessages[0]).' '.intval($groupmessages[1]).' '.$group['name']); if ($groupmessages[0] === NULL) { $currentarticle = NULL; } else { $currentarticle = $db->evalRowAssoc('SELECT * FROM `groupmessages` WHERE `group` = ? AND `number` = ?', array($group['id'], $groupmessages[0])); if ($currentarticle === FALSE) $currentarticle = NULL; } } break; case 'STAT': $article = nntp_get_article(strtok(" \t")); if ($article === NULL) break; nntp_writeline(STDOUT, '223 '.$article['messagenumber'].' <'.$article['messageid'].'> stat'); break; case 'HEAD': $article = nntp_get_article(strtok(" \t")); if ($article === NULL) break; nntp_writeline(STDOUT, '221 '.$article['messagenumber'].' <'.$article['messageid'].'> head'); foreach (explode("\r\n", $article['header']) as $line) nntp_writeline(STDOUT, $line); nntp_writeline(STDOUT, '.'); break; case 'BODY': $article = nntp_get_article(strtok(" \t")); if ($article === NULL) break; nntp_writeline(STDOUT, '222 '.$article['messagenumber'].' <'.$article['messageid'].'> body'); foreach (explode("\r\n", $article['body']) as $line) nntp_writeline(STDOUT, $line); nntp_writeline(STDOUT, '.'); break; case 'ARTICLE': $article = nntp_get_article(strtok(" \t")); if ($article === NULL) break; nntp_writeline(STDOUT, '220 '.$article['messagenumber'].' <'.$article['messageid'].'> article'); foreach (explode("\r\n", $article['header']) as $line) nntp_writeline(STDOUT, $line); nntp_writeline(STDOUT, ''); foreach (explode("\r\n", $article['body']) as $line) nntp_writeline(STDOUT, $line); nntp_writeline(STDOUT, '.'); break; case 'LAST': if ($currentarticle === NULL) { nntp_writeline(STDOUT, '420 no current article has been selected'); break; } $article = $db->evalRowAssoc('SELECT * FROM `groupmessages` WHERE `group` = ? AND `number` < ? ORDER BY `number` DESC LIMIT 1', array($currentarticle['group'], $currentarticle['number'])); if ($article === FALSE) { nntp_writeline(STDOUT, '422 no previous article in this group'); } else { $articlea = $db->evalRowAssoc('SELECT * FROM `messages` WHERE `id` = ?', $article['message']); if ($articlea === FALSE) { nntp_writeline(STDOUT, '430 no such article found'); return NULL; } $currentarticle = $article; nntp_writeline(STDOUT, '223 '.$article['number'].' <'.$articlea['messageid'].'> ok'); } break; case 'NEXT': if ($currentarticle === NULL) { nntp_writeline(STDOUT, '420 no current article has been selected'); break; } $article = $db->evalRowAssoc('SELECT * FROM `groupmessages` WHERE `group` = ? AND `number` > ? ORDER BY `number` ASC LIMIT 1', array($currentarticle['group'], $currentarticle['number'])); if ($article === FALSE) { nntp_writeline(STDOUT, '422 no previous article in this group'); } else { $articlea = $db->evalRowAssoc('SELECT * FROM `messages` WHERE `id` = ?', $article['message']); if ($articlea === FALSE) { nntp_writeline(STDOUT, '430 no such article found'); return NULL; } $currentarticle = $article; nntp_writeline(STDOUT, '223 '.$article['number'].' <'.$articlea['messageid'].'> ok'); } break; case 'POST': nntp_writeline(STDOUT, '340 Ok, recommended message-ID <'.md5(time().rand()).'@pNews.Core.UCIS.nl>'); $lines = nntp_readlines(STDIN); $header = array(); $headers = array(); while (count($lines)) { $line = array_shift($lines); if (!strlen($line)) break; $parts = explode(': ', $line, 2); $headers[strtoupper($parts[0])] = $parts[1]; switch (strtoupper($parts[0])) { case 'PATH': $line = $parts[0].': pNews.Core.UCIS.nl!'.$parts[1]; break; case 'FROM': case 'NEWSGROUPS': case 'SUBJECT': case 'DATE': case 'ORGANIZATION': case 'LINES': case 'MESSAGE-ID': case 'MIME-VERSION': case 'CONTENT-TYPE': case 'CONTENT-TRANSFER-ENCODING': case 'USER-AGENT': case 'REFERENCES': case 'REPLY-TO': case 'SENDER': case 'FOLLOWUP-TO': case 'EXPIRES': case 'CONTROL': case 'DISTRIBUTION': case 'KEYWORDS': case 'SUMMARY': case 'IN-REPLY-TO': break; case 'NNTP-POSTING-HOST': case 'X-TRACE': case 'XREF': case 'X-COMPLAINTS-TO': case 'NNTP-POSTING-DATE': $line = NULL; break; default: writelog("Received unknown header $parts[0]"); } if ($line !== NULL) $header[] = $line; } if (!isset($headers['NEWSGROUPS'])) { nntp_writeline(STDOUT, '441 Missing required Newsgroups: header'); break; } if (!isset($headers['MESSAGE-ID'])) { $headers['MESSAGE-ID'] = '<'.md5(time().rand()).'@pNews.Core.UCIS.nl>'; $header[] = 'Message-ID: '.$headers['MESSAGE-ID']; } if (!isset($headers['PATH'])) { $headers['PATH'] = 'pNews.Core.UCIS.nl'; $header[] = 'Path: '.$headers['PATH']; } $msgid = $headers['MESSAGE-ID']; if (strlen($msgid) <= 2 || $msgid[0] != '<' || $msgid[strlen($msgid)-1] != '>') { nntp_writeline(STDOUT, '441 435 Bad Message-ID'); break; } $msgid = substr($msgid, 1, -1); $article = $db->evalRowAssoc('SELECT * FROM `messages` WHERE `messageid` = ?', $msgid); if ($article !== FALSE) { nntp_writeline(STDOUT, '441 435 Duplicate'); return NULL; } $id = $db->insert('INSERT INTO `messages` (`messageid`, `header`, `body`) VALUES (?, ?, ?)', array($msgid, implode("\r\n", $header), implode("\r\n", $lines))); foreach (explode(',', $headers['NEWSGROUPS']) as $groupname) { $group = $db->evalRowAssoc('SELECT * FROM `groups` WHERE `name` = ?', $groupname); if ($group === FALSE) continue; $db->insert('INSERT INTO `groupmessages` (`group`, `message`) VALUES (?, ?)', array($group['id'], $id)); } nntp_writeline(STDOUT, '240 Article received <'.$msgid.'>'); break; case 'QUIT': nntp_writeline(STDOUT, '205 .'); return; case 'XOVER': case 'MODE': case 'CAPABILITIES': nntp_writeline(STDOUT, '500 Command not implemented'); break; default: nntp_writeline(STDOUT, '500 Command not understood'); break; } } function nntp_get_article($article) { global $currentgroup, $currentarticle, $db; if ($article === FALSE) { if ($currentarticle === NULL) { nntp_writeline(STDOUT, '420 no current article has been selected'); return NULL; } $messagenumber = $currentarticle['number']; $article = $db->evalRowAssoc('SELECT * FROM `messages` WHERE `id` = ?', $currentarticle['message']); if ($article === FALSE) { nntp_writeline(STDOUT, '430 no such article found'); return NULL; } } elseif (strlen($article) > 2 && $article[0] == '<' && $article[strlen($article)-1] == '>') { $messagenumber = 0; $article = substr($article, 1, -1); $article = $db->evalRowAssoc('SELECT * FROM `messages` WHERE `messageid` = ?', $article); if ($article === FALSE) { nntp_writeline(STDOUT, '430 no such article found'); return NULL; } } elseif (is_numeric($article)) { if ($currentgroup === NULL) { nntp_writeline(STDOUT, '412 no newsgroup has been selected'); return NULL; } $article = $db->evalRowAssoc('SELECT * FROM `groupmessages` WHERE `group` = ? AND `number` = ?', array($currentgroup['id'], $article)); if ($article === FALSE) { nntp_writeline(STDOUT, '423 no such article number in this group'); return NULL; } $currentarticle = $article; $messagenumber = $article['number']; $article = $db->evalRowAssoc('SELECT * FROM `messages` WHERE `id` = ?', $article['message']); if ($article === FALSE) { nntp_writeline(STDOUT, '430 no such article found'); return NULL; } } else { nntp_writeline(STDOUT, '500 Error in arguments'); } $article['messagenumber'] = $messagenumber; return $article; } function writelog($line) { global $logfile; fwrite($logfile, $line."\n"); } function nntp_readline($socket) { global $logfile; $line = fgets($socket, 512); if ($line === FALSE || $line === NULL) return $line; $line = rtrim($line, "\r\n"); fwrite($logfile, 'R: '.$line."\n"); return $line; } function nntp_writeline($socket, $line) { global $logfile; fwrite($logfile, 'W: '.$line."\n"); fwrite($socket, $line."\r\n"); } function nntp_readlines($socket) { $line = nntp_readline($socket); $lines = array(); while ($line != '.' && $line !== FALSE && $line !== FALSE) { $lines[] = $line; $line = nntp_readline($socket); } if ($line != '.') die("Unexpected end of message header\n"); return $lines; }