comparison server.php @ 3:0dcdb73cbcbf

Increased article body length to 16MB in database scheme, cleaned up the code, added a script to forcefully reload an individual article
author Ivo Smits <Ivo@UCIS.nl>
date Tue, 12 Apr 2011 01:47:09 +0200
parents 40e545510a57
children 5d62af5270dd
comparison
equal deleted inserted replaced
2:40e545510a57 3:0dcdb73cbcbf
1 #!/usr/bin/php 1 #!/usr/bin/php
2 <?php 2 <?php
3 chdir(__DIR__); 3 chdir(__DIR__);
4 require_once './pdo.php'; 4 require_once './common.php';
5 require_once './config.php';
6 5
7 $logfile = fopen('./server.log', 'w'); 6 $logfile = fopen('./server.log', 'w');
8 $currentgroup = NULL; 7 $currentgroup = NULL;
9 $currentarticle = NULL; 8 $currentarticle = NULL;
10 9
23 22
24 nntp_writeline(STDOUT, '200 pNewss ready'); 23 nntp_writeline(STDOUT, '200 pNewss ready');
25 while (TRUE) { 24 while (TRUE) {
26 $line = nntp_readline(STDIN); 25 $line = nntp_readline(STDIN);
27 if ($line === FALSE || $line === NULL) break; 26 if ($line === FALSE || $line === NULL) break;
28 $cmd = strtok($line, " \t"); 27 $cmd = strtoupper(strtok($line, " \t"));
29 switch (strtoupper($cmd)) { 28 switch ($cmd) {
30 case 'LIST': 29 case 'LIST':
31 nntp_writeline(STDOUT, '215 List of groups follows'); 30 nntp_writeline(STDOUT, '215 list of groups follows');
32 foreach ($db->evalAllAssoc('SELECT * FROM `groups`') as $group) { 31 foreach ($db->evalAllAssoc('SELECT * FROM `groups`') as $group) {
33 $groupmessages = $db->evalRow('SELECT MIN(`number`), MAX(`number`) FROM `groupmessages` WHERE `group` = ?', $group['id']); 32 $groupmessages = $db->evalRow('SELECT MIN(`number`), MAX(`number`) FROM `groupmessages` WHERE `group` = ?', $group['id']);
34 nntp_writeline(STDOUT, $group['name'].' '.intval($groupmessages[1]).' '.intval($groupmessages[0]).' n'); 33 nntp_writeline(STDOUT, $group['name'].' '.intval($groupmessages[1]).' '.intval($groupmessages[0]).' n');
35 } 34 }
36 nntp_writeline(STDOUT, '.'); 35 nntp_writeline(STDOUT, '.');
51 if ($currentarticle === FALSE) $currentarticle = NULL; 50 if ($currentarticle === FALSE) $currentarticle = NULL;
52 } 51 }
53 } 52 }
54 break; 53 break;
55 case 'STAT': 54 case 'STAT':
56 $article = nntp_get_article(strtok(" \t"));
57 if ($article === NULL) break;
58 nntp_writeline(STDOUT, '223 '.$article['messagenumber'].' <'.$article['messageid'].'> stat');
59 break;
60 case 'HEAD': 55 case 'HEAD':
61 $article = nntp_get_article(strtok(" \t"));
62 if ($article === NULL) break;
63 nntp_writeline(STDOUT, '221 '.$article['messagenumber'].' <'.$article['messageid'].'> head');
64 foreach (explode("\r\n", $article['header']) as $line) nntp_writeline(STDOUT, $line);
65 nntp_writeline(STDOUT, '.');
66 break;
67 case 'BODY': 56 case 'BODY':
68 $article = nntp_get_article(strtok(" \t"));
69 if ($article === NULL) break;
70 nntp_writeline(STDOUT, '222 '.$article['messagenumber'].' <'.$article['messageid'].'> body');
71 foreach (explode("\r\n", $article['body']) as $line) nntp_writeline(STDOUT, $line);
72 nntp_writeline(STDOUT, '.');
73 break;
74 case 'ARTICLE': 57 case 'ARTICLE':
75 $article = nntp_get_article(strtok(" \t")); 58 $article = nntp_get_article(strtok(" \t"));
76 if ($article === NULL) break; 59 if ($article === NULL) break;
77 nntp_writeline(STDOUT, '220 '.$article['messagenumber'].' <'.$article['messageid'].'> article'); 60 switch ($cmd) {
78 foreach (explode("\r\n", $article['header']) as $line) nntp_writeline(STDOUT, $line); 61 case 'STAT': nntp_writeline(STDOUT, '223 '.$article['messagenumber'].' <'.$article['messageid'].'> stat'); break;
79 nntp_writeline(STDOUT, ''); 62 case 'HEAD': nntp_writeline(STDOUT, '221 '.$article['messagenumber'].' <'.$article['messageid'].'> head'); break;
80 foreach (explode("\r\n", $article['body']) as $line) nntp_writeline(STDOUT, $line); 63 case 'BODY': nntp_writeline(STDOUT, '222 '.$article['messagenumber'].' <'.$article['messageid'].'> body'); break;
64 case 'ARTICLE': nntp_writeline(STDOUT, '220 '.$article['messagenumber'].' <'.$article['messageid'].'> article'); break;
65 default: throw new Exception('Internal error');
66 }
67 if ($cmd == 'HEAD' || $cmd == 'ARTICLE') foreach (explode("\r\n", $article['header']) as $line) nntp_writeline(STDOUT, $line);
68 if ($cmd == 'ARTICLE') nntp_writeline(STDOUT, '');
69 if ($cmd == 'BODY' || $cmd == 'ARTICLE') foreach (explode("\r\n", $article['body']) as $line) nntp_writeline(STDOUT, $line);
81 nntp_writeline(STDOUT, '.'); 70 nntp_writeline(STDOUT, '.');
82 break; 71 break;
83 case 'LAST': 72 case 'LAST':
84 if ($currentarticle === NULL) {
85 nntp_writeline(STDOUT, '420 no current article has been selected');
86 break;
87 }
88 $article = $db->evalRowAssoc('SELECT * FROM `groupmessages` WHERE `group` = ? AND `number` < ? ORDER BY `number` DESC LIMIT 1', array($currentarticle['group'], $currentarticle['number']));
89 if ($article === FALSE) {
90 nntp_writeline(STDOUT, '422 no previous article in this group');
91 } else {
92 $articlea = $db->evalRowAssoc('SELECT * FROM `messages` WHERE `id` = ?', $article['message']);
93 if ($articlea === FALSE) {
94 nntp_writeline(STDOUT, '430 no such article found');
95 return NULL;
96 }
97 $currentarticle = $article;
98 nntp_writeline(STDOUT, '223 '.$article['number'].' <'.$articlea['messageid'].'> ok');
99 }
100 break;
101 case 'NEXT': 73 case 'NEXT':
102 if ($currentarticle === NULL) { 74 if ($currentarticle === NULL) {
103 nntp_writeline(STDOUT, '420 no current article has been selected'); 75 nntp_writeline(STDOUT, '420 no current article has been selected');
104 break; 76 break;
105 } 77 }
106 $article = $db->evalRowAssoc('SELECT * FROM `groupmessages` WHERE `group` = ? AND `number` > ? ORDER BY `number` ASC LIMIT 1', array($currentarticle['group'], $currentarticle['number'])); 78 switch ($cmd) {
79 case 'LAST': $query = 'SELECT * FROM `groupmessages` WHERE `group` = ? AND `number` < ? ORDER BY `number` DESC LIMIT 1'; break;
80 case 'NEXT': $query = 'SELECT * FROM `groupmessages` WHERE `group` = ? AND `number` > ? ORDER BY `number` ASC LIMIT 1'; break;
81 default: throw new Exception('Internal error');
82 }
83 $article = $db->evalRowAssoc($query, array($currentarticle['group'], $currentarticle['number']));
107 if ($article === FALSE) { 84 if ($article === FALSE) {
108 nntp_writeline(STDOUT, '422 no previous article in this group'); 85 nntp_writeline(STDOUT, '422 no previous article in this group');
109 } else { 86 } else {
110 $articlea = $db->evalRowAssoc('SELECT * FROM `messages` WHERE `id` = ?', $article['message']); 87 $articlea = $db->evalRowAssoc('SELECT * FROM `messages` WHERE `id` = ?', $article['message']);
111 if ($articlea === FALSE) { 88 if ($articlea === FALSE) {
117 } 94 }
118 break; 95 break;
119 case 'POST': 96 case 'POST':
120 nntp_writeline(STDOUT, '340 Ok, recommended message-ID <'.md5(time().rand()).'@pNews.Core.UCIS.nl>'); 97 nntp_writeline(STDOUT, '340 Ok, recommended message-ID <'.md5(time().rand()).'@pNews.Core.UCIS.nl>');
121 $lines = nntp_readlines(STDIN); 98 $lines = nntp_readlines(STDIN);
122 $header = array(); 99 try {
123 $headers = array(); 100 $msgid = nntp_article_store($lines);
124 while (count($lines)) { 101 nntp_writeline(STDOUT, '240 Article received <'.$msgid.'>');
125 $line = array_shift($lines); 102 } catch (Exception $ex) {
126 if (!strlen($line)) break; 103 nntp_writeline(STDOUT, '441 '.$ex->getMessage());
127 $parts = explode(': ', $line, 2);
128 $headers[strtoupper($parts[0])] = $parts[1];
129 switch (strtoupper($parts[0])) {
130 case 'PATH':
131 $line = $parts[0].': pNews.Core.UCIS.nl!'.$parts[1];
132 break;
133 case 'FROM': case 'NEWSGROUPS': case 'SUBJECT': case 'DATE': case 'ORGANIZATION':
134 case 'LINES': case 'MESSAGE-ID': case 'MIME-VERSION': case 'CONTENT-TYPE': case 'CONTENT-TRANSFER-ENCODING':
135 case 'USER-AGENT': case 'REFERENCES': case 'REPLY-TO': case 'SENDER': case 'FOLLOWUP-TO':
136 case 'EXPIRES': case 'CONTROL': case 'DISTRIBUTION': case 'KEYWORDS': case 'SUMMARY':
137 case 'IN-REPLY-TO':
138 break;
139 case 'NNTP-POSTING-HOST': case 'X-TRACE': case 'XREF': case 'X-COMPLAINTS-TO':
140 case 'NNTP-POSTING-DATE':
141 $line = NULL;
142 break;
143 default:
144 writelog("Received unknown header $parts[0]");
145 }
146 if ($line !== NULL) $header[] = $line;
147 } 104 }
148 if (!isset($headers['NEWSGROUPS'])) {
149 nntp_writeline(STDOUT, '441 Missing required Newsgroups: header');
150 break;
151 }
152 if (!isset($headers['MESSAGE-ID'])) {
153 $headers['MESSAGE-ID'] = '<'.md5(time().rand()).'@pNews.Core.UCIS.nl>';
154 $header[] = 'Message-ID: '.$headers['MESSAGE-ID'];
155 }
156 if (!isset($headers['PATH'])) {
157 $headers['PATH'] = 'pNews.Core.UCIS.nl';
158 $header[] = 'Path: '.$headers['PATH'];
159 }
160 $msgid = $headers['MESSAGE-ID'];
161 if (strlen($msgid) <= 2 || $msgid[0] != '<' || $msgid[strlen($msgid)-1] != '>') {
162 nntp_writeline(STDOUT, '441 435 Bad Message-ID');
163 break;
164 }
165 $msgid = substr($msgid, 1, -1);
166 $article = $db->evalRowAssoc('SELECT * FROM `messages` WHERE `messageid` = ?', $msgid);
167 if ($article !== FALSE) {
168 nntp_writeline(STDOUT, '441 435 Duplicate');
169 return NULL;
170 }
171 $id = $db->insert('INSERT INTO `messages` (`messageid`, `header`, `body`) VALUES (?, ?, ?)', array($msgid, implode("\r\n", $header), implode("\r\n", $lines)));
172 foreach (explode(',', $headers['NEWSGROUPS']) as $groupname) {
173 $group = $db->evalRowAssoc('SELECT * FROM `groups` WHERE `name` = ?', $groupname);
174 if ($group === FALSE) continue;
175 $db->insert('INSERT INTO `groupmessages` (`group`, `message`) VALUES (?, ?)', array($group['id'], $id));
176 }
177 nntp_writeline(STDOUT, '240 Article received <'.$msgid.'>');
178 break; 105 break;
179 case 'QUIT': 106 case 'QUIT':
180 nntp_writeline(STDOUT, '205 .'); 107 nntp_writeline(STDOUT, '205 .');
181 return; 108 return;
182 case 'XOVER':
183 case 'MODE':
184 case 'CAPABILITIES':
185 nntp_writeline(STDOUT, '500 Command not implemented');
186 break;
187 default: 109 default:
188 nntp_writeline(STDOUT, '500 Command not understood'); 110 nntp_writeline(STDOUT, '500 Command not understood');
189 break; 111 break;
190 } 112 }
191 } 113 }
237 159
238 function writelog($line) { 160 function writelog($line) {
239 global $logfile; 161 global $logfile;
240 fwrite($logfile, $line."\n"); 162 fwrite($logfile, $line."\n");
241 } 163 }
242 function nntp_readline($socket) {
243 global $logfile;
244 $line = fgets($socket, 512);
245 if ($line === FALSE || $line === NULL) return $line;
246 $line = rtrim($line, "\r\n");
247 fwrite($logfile, 'R: '.$line."\n");
248 return $line;
249 }
250 function nntp_writeline($socket, $line) {
251 global $logfile;
252 fwrite($logfile, 'W: '.$line."\n");
253 fwrite($socket, $line."\r\n");
254 }
255 function nntp_readlines($socket) {
256 $line = nntp_readline($socket);
257 $lines = array();
258 while ($line != '.' && $line !== FALSE && $line !== FALSE) {
259 $lines[] = $line;
260 $line = nntp_readline($socket);
261 }
262 if ($line != '.') die("Unexpected end of message header\n");
263 return $lines;
264 }