# HG changeset patch # User Ivo Smits # Date 1302554687 -7200 # Node ID d7ab68b71c747fcab4e0fcc00cd4120244b7227f Initial commit diff -r 000000000000 -r d7ab68b71c74 config.php.example --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/config.php.example Mon Apr 11 22:44:47 2011 +0200 @@ -0,0 +1,2 @@ +evalAllAssoc('SELECT * FROM `peers`') as $peer) { + $socket = stream_socket_client($peer['address']); + if ($socket === FALSE) die("Could not connect to peer $peer[address]\n"); + $line = nntp_readline($socket); + $code = strtok($line, " \t"); + if ($code != 200) die("Error code $code from $peer[address]\n"); + foreach ($db->evalAllAssoc('SELECT * FROM `peergroups` WHERE `peer` = ?', $peer['id']) as $peergroup) { + $group = $db->evalRowAssoc('SELECT * FROM `groups` WHERE `id` = ?', $peergroup['group']); + nntp_writeline($socket, 'GROUP '.$group['name']); + $line = nntp_readline($socket); + $code = strtok($line, " \t"); + if ($code != 211) die("Error code $code from $peer[address]\n"); + strtok(" \t"); //number of articles + $low = strtok(" \t"); + $high = strtok(" \t"); + strtok(" \t"); //group name + if ($low != $peergroup['low'] || $high != $peergroup['high'] || $peergroup['low'] === NULL || $peergroup['high'] === NULL) { + for ($i = $low; $i <= $high; $i++) { + if ($i >= $peergroup['low'] && $i <= $peergroup['high'] && $peergroup['low'] !== NULL && $peergroup['high'] !== NULL) continue; + nntp_writeline($socket, 'STAT '.$i); + $line = nntp_readline($socket); + $code = strtok($line, " \t"); + if ($code == 423) { + print("Gap in article numbering at $i\n"); + continue; + } + if ($code != 223) die("Error code $code from $peer[address]\n"); + strtok(" \t"); //article number + $messageid = strtok(" \t"); + if ($messageid[0] != '<' || $messageid[strlen($messageid)-1] != '>') die("Malformed message ID $messageid\n"); + $messageid = substr($messageid, 1, -1); + $message = $db->evalRowAssoc('SELECT * FROM `messages` WHERE `messageid` = ?', $messageid); + if ($message) { + $groupmessage = $db->evalRowAssoc('SELECT * FROM `groupmessages` WHERE `group` = ? AND `message` = ?', array($group['id'], $message['id'])); + if (!$groupmessage) { + $db->insert('INSERT INTO `groupmessages` (`group`, `message`) VALUES (?, ?)', array($group['id'], $message['id'])); + } + } else { + nntp_writeline($socket, 'HEAD '.$i); + $line = nntp_readline($socket); + $code = strtok($line, " \t"); + if ($code != 221) die("Error code $code from $peer[address]\n"); + strtok(" \t"); //article number + $lines = nntp_readlines($socket); + $header = implode("\r\n", $lines); + + nntp_writeline($socket, 'BODY '.$i); + $line = nntp_readline($socket); + $code = strtok($line, " \t"); + if ($code != 222) die("Error code $code from $peer[address]\n"); + strtok(" \t"); //article number + $lines = nntp_readlines($socket); + $body = implode("\r\n", $lines); + + $id = $db->insert('INSERT INTO `messages` (`messageid`, `header`, `body`) VALUES (?, ?, ?)', array($messageid, $header, $body)); + $db->insert('INSERT INTO `groupmessages` (`group`, `message`) VALUES (?, ?)', array($group['id'], $id)); + } + } + $db->update('UPDATE `peergroups` SET `low` = ?, `high` = ? WHERE `peer` = ? AND `group` = ?', array($low, $high, $peergroup['peer'], $peergroup['group'])); + } + } + nntp_writeline($socket, 'QUIT'); + fclose($socket); +} + +function nntp_readline($socket) { + $line = rtrim(fgets($socket, 512), "\r\n"); + print('R: '.$line."\n"); + return $line; +} +function nntp_writeline($socket, $line) { + print('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; +} diff -r 000000000000 -r d7ab68b71c74 pdo.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pdo.php Mon Apr 11 22:44:47 2011 +0200 @@ -0,0 +1,88 @@ +pdo = new PDO($dsn, $user, $pass, $options); + } catch (PDOException $e) { + throw new Exception('Could not connect to database: '.$e->getMessage()); + } + $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + } + public function __call($name, $arguments) { + if ($name == 'eval') { + $q = array_shift($arguments); + return $this->evalField($q, $arguments); + } + } + + private function queryi($query, $args = NULL) { + $this->numQueries++; + if ($args === NULL) { + //$stmt = $db_pdo->query($query); + $stmt = $this->pdo->prepare($query); + $stmt->execute(); + } else { + if (!is_array($args)) $args = array($args); + $stmt = $this->pdo->prepare($query); + $stmt->execute($args); + } + return $stmt; + } + function insert($query, $args = NULL) { + $this->queryi($query, $args)->closeCursor(); + return $this->pdo->lastInsertId(); + } + function update($query, $args = NULL) { + global $db_pdo; + $stmt = $this->queryi($query, $args); + $cnt = $stmt->rowCount(); + $stmt->closeCursor(); + return $cnt; + } + function evalField($query, $args = NULL) { + $stmt = $this->queryi($query, $args); + $ret = $stmt->fetchColumn(); + $stmt->closeCursor(); + return $ret; + } + function evalRow($query, $args = NULL) { + $stmt = $this->queryi($query, $args); + $ret = $stmt->fetch(PDO::FETCH_NUM); + $stmt->closeCursor(); + return $ret; + } + function evalRowAssoc($query, $args = NULL) { + $stmt = $this->queryi($query, $args); + $ret = $stmt->fetch(PDO::FETCH_ASSOC); + $stmt->closeCursor(); + return $ret; + } + function evalAllAssoc($query, $args = NULL) { + $stmt = $this->queryi($query, $args); + $ret = $stmt->fetchAll(PDO::FETCH_ASSOC); + $stmt->closeCursor(); + return $ret; + } + function evalAllKVP($query, $args = NULL) { + $stmt = $this->queryi($query, $args); + $ret = $stmt->fetchAll(PDO::FETCH_KEY_PAIR); + $stmt->closeCursor(); + return $ret; + } + function evalColumn($query, $args = NULL) { + $stmt = $this->queryi($query, $args); + $ret = $stmt->fetchAll(PDO::FETCH_COLUMN); + $stmt->closeCursor(); + return $ret; + } +} diff -r 000000000000 -r d7ab68b71c74 server.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/server.php Mon Apr 11 22:44:47 2011 +0200 @@ -0,0 +1,204 @@ +#!/usr/bin/php +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 '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; +}