comparison common.php @ 12:7917bd536187 draft

Added hook for new articles, detect send/write failures, fixed handling of multiline headers, add Date header if it doesn't exist, add option to disable peers, fixes for synchronization with INN, added streaming mode support, small fixes
author Ivo Smits <Ivo@UCIS.nl>
date Wed, 12 Jun 2013 22:22:07 +0200
parents e0807e0b1a67
children cccd73f72bf6
comparison
equal deleted inserted replaced
11:e0807e0b1a67 12:7917bd536187
22 22
23 The views and conclusions contained in the software and documentation are those of the 23 The views and conclusions contained in the software and documentation are those of the
24 authors and should not be interpreted as representing official policies, either expressed 24 authors and should not be interpreted as representing official policies, either expressed
25 or implied, of Ivo Smits.*/ 25 or implied, of Ivo Smits.*/
26 26
27 $pnewss_hooks = array('article_stored' => array());
28
27 require_once './pdo.php'; 29 require_once './pdo.php';
28 require_once './config.php'; 30 require_once './config.php';
29 31
30 function nntp_readline($socket) { 32 function nntp_readline($socket) {
31 $line = fgets($socket, 512); 33 $line = fgets($socket, 512);
38 if (strlen($line) && $line[0] == '.') $line = '.'.$line; 40 if (strlen($line) && $line[0] == '.') $line = '.'.$line;
39 nntp_writeline($socket, $line); 41 nntp_writeline($socket, $line);
40 } 42 }
41 function nntp_writeline($socket, $line) { 43 function nntp_writeline($socket, $line) {
42 writelog('W: '.$line); 44 writelog('W: '.$line);
43 fwrite($socket, $line."\r\n"); 45 $line .= "\r\n";
46 while (strlen($line)) {
47 $written = fwrite($socket, $line);
48 if ($written === FALSE || $written <= 0) throw new Exception('Write operation failed');
49 $line = substr($line, $written);
50 }
44 } 51 }
45 function nntp_readlines($socket) { 52 function nntp_readlines($socket) {
46 $line = nntp_readline($socket); 53 $line = nntp_readline($socket);
47 $lines = array(); 54 $lines = array();
48 while ($line != '.' && $line !== FALSE && $line !== FALSE) { 55 while ($line != '.' && $line !== FALSE && $line !== FALSE) {
52 } 59 }
53 if ($line != '.') throw new Exception('Unexpected end of message'); 60 if ($line != '.') throw new Exception('Unexpected end of message');
54 return $lines; 61 return $lines;
55 } 62 }
56 63
64 function pnewss_call_hooks($hooks, $args) {
65 foreach ($hooks as $hook) call_user_func_array($hook, $args);
66 }
67
57 function nntp_article_store($lines, $header = array()) { 68 function nntp_article_store($lines, $header = array()) {
58 global $db; 69 global $db, $pnewss_hooks;
59 $headers = array(); 70 $headers = array();
60 if (!count($header)) { 71 if (!count($header)) {
61 while (count($lines)) { 72 while (count($lines)) {
62 $line = array_shift($lines); 73 $line = array_shift($lines);
63 if (!strlen($line)) break; 74 if (!strlen($line)) break;
64 $header[] = $line; 75 $header[] = $line;
65 } 76 }
66 } 77 }
78 $headername = NULL;
67 foreach ($header as $headerid => $line) { 79 foreach ($header as $headerid => $line) {
68 if (!strlen($line) || $line == '.') throw new Exception('Empty or terminating header line'); 80 if (!strlen($line) || $line == '.') throw new Exception('Empty or terminating header line');
69 if (strpos($line, "\r") !== FALSE || strpos($line, "\n") !== FALSE || strpos($line, "\0")) throw new Exception('Invalid newline or NUL character in header line'); 81 if (strpos($line, "\r") !== FALSE || strpos($line, "\n") !== FALSE || strpos($line, "\0")) throw new Exception('Invalid newline or NUL character in header line');
82 if (strlen($line) && strlen($headername) && ($line[0] == ' ' || $line[0] == "\t") && isset($headers[$headername])) {
83 $headers[$headername] .= ' '.trim($line, " \t");
84 unset($header[$headerid]);
85 continue;
86 }
70 $parts = explode(': ', $line, 2); 87 $parts = explode(': ', $line, 2);
71 $headername = strtoupper($parts[0]); 88 $headername = strtoupper($parts[0]);
72 switch ($headername) { 89 switch ($headername) {
73 case 'PATH': case 'FROM': case 'NEWSGROUPS': case 'SUBJECT': case 'DATE': case 'MESSAGE-ID': case 'SENDER': 90 case 'PATH': case 'FROM': case 'NEWSGROUPS': case 'SUBJECT': case 'DATE': case 'MESSAGE-ID': case 'SENDER':
74 if (isset($headers[$headername])) throw new Exception('Duplicate header: '.$headername); 91 if (isset($headers[$headername])) throw new Exception('Duplicate header: '.$headername);
84 case 'NNTP-POSTING-DATE': 101 case 'NNTP-POSTING-DATE':
85 unset($header[$headerid]); 102 unset($header[$headerid]);
86 $line = NULL; 103 $line = NULL;
87 break; 104 break;
88 default: 105 default:
106 if ($headername[0] == 'X' && $headername[1] == '-') break;
89 writelog("Received unknown header $headername"); 107 writelog("Received unknown header $headername");
90 } 108 }
91 } 109 }
92 foreach ($lines as $line) { 110 foreach ($lines as $line) {
93 if ($line == '.') throw new Exception('Terminating body line'); 111 if ($line == '.') throw new Exception('Terminating body line');
100 if ($group === FALSE) continue; 118 if ($group === FALSE) continue;
101 $newsgroups[] = $group['id']; 119 $newsgroups[] = $group['id'];
102 } 120 }
103 if (!count($newsgroups)) throw new Exception('No known newsgroups listed'); 121 if (!count($newsgroups)) throw new Exception('No known newsgroups listed');
104 if (!isset($headers['MESSAGE-ID'])) $headers['MESSAGE-ID'] = '<'.md5(time().rand()).'@pNewss.Core.UCIS.nl>'; 122 if (!isset($headers['MESSAGE-ID'])) $headers['MESSAGE-ID'] = '<'.md5(time().rand()).'@pNewss.Core.UCIS.nl>';
123 if (!isset($headers['DATE'])) $headers['DATE'] = gmdate('r');
105 $messageid = $headers['MESSAGE-ID']; 124 $messageid = $headers['MESSAGE-ID'];
106 if (strlen($messageid) < 3 || strlen($messageid) > 250 || $messageid[0] != '<' || strpos($messageid, '>') !== strlen($messageid) - 1) throw new Exception('Bad Message-ID'); 125 if (strlen($messageid) < 3 || strlen($messageid) > 250 || $messageid[0] != '<' || strpos($messageid, '>') !== strlen($messageid) - 1) throw new Exception('Bad Message-ID');
107 $messageid = substr($messageid, 1, -1); 126 $messageid = substr($messageid, 1, -1);
108 $article = $db->evalRowAssoc('SELECT * FROM `messages` WHERE `messageid` = ?', $messageid); 127 $article = $db->evalRowAssoc('SELECT * FROM `messages` WHERE `messageid` = ?', $messageid);
109 if ($article !== FALSE) throw new Exception('Duplicate'); 128 if ($article !== FALSE) throw new Exception('Duplicate');
111 foreach (array('Path', 'From', 'Newsgroups', 'Subject', 'Date', 'Message-ID', 'Sender') as $headername) { 130 foreach (array('Path', 'From', 'Newsgroups', 'Subject', 'Date', 'Message-ID', 'Sender') as $headername) {
112 if (isset($headers[strtoupper($headername)])) $header[] = $headername.': '.$headers[strtoupper($headername)]; 131 if (isset($headers[strtoupper($headername)])) $header[] = $headername.': '.$headers[strtoupper($headername)];
113 } 132 }
114 $id = $db->insert('INSERT INTO `messages` (`messageid`, `header`, `body`) VALUES (?, ?, ?)', array($messageid, implode("\r\n", $header), implode("\r\n", $lines))); 133 $id = $db->insert('INSERT INTO `messages` (`messageid`, `header`, `body`) VALUES (?, ?, ?)', array($messageid, implode("\r\n", $header), implode("\r\n", $lines)));
115 foreach ($newsgroups as $groupid) $db->insert('INSERT INTO `groupmessages` (`group`, `message`) VALUES (?, ?)', array($groupid, $id)); 134 foreach ($newsgroups as $groupid) $db->insert('INSERT INTO `groupmessages` (`group`, `message`) VALUES (?, ?)', array($groupid, $id));
135 pnewss_call_hooks($pnewss_hooks['article_stored'], array(array('messageid' => $messageid, 'headers' => $headers, 'body' => $lines, 'dbid' => $id)));
116 return $messageid; 136 return $messageid;
117 } 137 }