rocksolid-light/Rocksolid_Light/rocksolid/newsportal.php

2226 lines
72 KiB
PHP

<?php
/*
* NNTP<->HTTP Gateway
* Download: https://news.novabbs.com/get
*
* Based on Newsportal by Florian Amrhein
*
* E-Mail: retroguy@novabbs.com
* Web: https://news.novabbs.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
if (file_exists("lib/types.inc.php"))
include "lib/types.inc.php";
if (file_exists("lib/thread.inc.php"))
include "lib/thread.inc.php";
if (file_exists("lib/message.inc.php"))
include "lib/message.inc.php";
if (file_exists("lib/post.inc.php"))
include "lib/post.inc.php";
$CONFIG = include ($config_file);
/*
* opens the connection to the NNTP-Server
*
* $server: adress of the NNTP-Server
* $port: port of the server
*/
function nntp_open($nserver = 0, $nport = 0)
{
global $text_error, $CONFIG;
global $server, $port;
// echo "<br>NNTP OPEN<br>";
if (! isset($CONFIG['enable_nntp']) || $CONFIG['enable_nntp'] != true) {
$CONFIG['server_auth_user'] = $CONFIG['remote_auth_user'];
$CONFIG['server_auth_pass'] = $CONFIG['remote_auth_pass'];
}
$authorize = ((isset($CONFIG['server_auth_user'])) && (isset($CONFIG['server_auth_pass'])) && ($CONFIG['server_auth_user'] != ""));
if ($nserver == 0)
$nserver = $server;
if ($nport == 0)
$nport = $port;
$ns = @fsockopen($nserver, $nport);
// if the connection to the news server fails, inform the user and stop processing.
if ($ns == false) {
echo '<center><p>' . $text_error["error:"] . " " . $text_error["connection_failed"] . '.</p>';
echo '<br>';
echo '<p>Please wait a few moments and try again. If you see the same error, notify the owner that their Message Server is offline.</p>';
echo '</center>';
exit(0);
}
$weg = line_read($ns); // kill the first line
if (substr($weg, 0, 2) != "20") {
echo "<p>" . $text_error["error:"] . $weg . "</p>";
fclose($ns);
$ns = false;
} else {
if ($ns != false) {
fputs($ns, "MODE reader\r\n");
$weg = line_read($ns); // and once more
if ((substr($weg, 0, 2) != "20") && ((! $authorize) || ((substr($weg, 0, 3) != "480") && ($authorize)))) {
echo "<p>" . $text_error["error:"] . $weg . "</p>";
fclose($ns);
$ns = false;
}
}
if ((isset($CONFIG['server_auth_user'])) && (isset($CONFIG['server_auth_pass'])) && ($CONFIG['server_auth_user'] != "")) {
fputs($ns, "AUTHINFO USER " . $CONFIG['server_auth_user'] . "\r\n");
$weg = line_read($ns);
fputs($ns, "AUTHINFO PASS " . $CONFIG['server_auth_pass'] . "\r\n");
$weg = line_read($ns);
/* Only check auth if reading and posting same server */
// NNTP Response NOT 281 (Authorization failed)
if (substr($weg, 0, 3) != "281" && ! (isset($post_server)) && ($post_server != "")) {
echo "<p>" . $text_error["error:"] . "</p>";
echo "<p>" . $text_error["auth_error"] . "</p>";
}
}
}
if ($ns == false)
echo "<p>" . $text_error["connection_failed"] . "</p>";
return $ns;
}
function nntp2_open($nserver = 0, $nport = 0)
{
global $text_error, $CONFIG;
// echo "<br>NNTP OPEN<br>";
$authorize = ((isset($CONFIG['remote_auth_user'])) && (isset($CONFIG['remote_auth_pass'])) && ($CONFIG['remote_auth_user'] != ""));
if ($nserver == 0)
$nserver = $CONFIG['remote_server'];
if ($nport == 0)
$nport = $CONFIG['remote_port'];
if ($CONFIG['remote_ssl']) {
if ($nport == $CONFIG['remote_port']) {
$nport = $CONFIG['remote_ssl'];
}
var_dump($ns = fsockopen("ssl://" . $nserver, $nport, $error, $errorString, 30));
var_dump($errorString);
var_dump($error);
// $ns=@fsockopen('ssl://'.$nserver.":".$nport);
} else {
if (isset($CONFIG['socks_host']) && $CONFIG['socks_host'] !== '') {
$ns = fsocks4asockopen($CONFIG['socks_host'], $CONFIG['socks_port'], $nserver, $nport);
} else {
$ns = @fsockopen('tcp://' . $nserver . ":" . $nport);
}
}
// $ns=@fsockopen($nserver,$nport);
// echo "PORT: ".$nport." ns: ".$ns;
$weg = line_read($ns); // kill the first line
if (substr($weg, 0, 2) != "20") {
echo "<p>" . $text_error["error:"] . $weg . "</p>";
if ($ns) {
fclose($ns);
}
$ns = false;
} else {
if ($ns != false) {
fputs($ns, "MODE reader\r\n");
$weg = line_read($ns); // and once more
if ((substr($weg, 0, 2) != "20") && ((! $authorize) || ((substr($weg, 0, 3) != "480") && ($authorize)))) {
echo "<p>" . $text_error["error:"] . $weg . "</p>";
fclose($ns);
$ns = false;
}
}
if ((isset($CONFIG['remote_auth_user'])) && (isset($CONFIG['remote_auth_pass'])) && ($CONFIG['remote_auth_user'] != "")) {
fputs($ns, "AUTHINFO USER " . $CONFIG['remote_auth_user'] . "\r\n");
$weg = line_read($ns);
fputs($ns, "AUTHINFO PASS " . $CONFIG['remote_auth_pass'] . "\r\n");
$weg = line_read($ns);
/* Only check auth if reading and posting same server */
if (substr($weg, 0, 3) != "281" && ! (isset($post_server)) && ($post_server != "")) {
echo "<p>" . $text_error["error:"] . "</p>";
echo "<p>" . $text_error["auth_error"] . "</p>";
}
}
}
if ($ns == false)
echo "<p>" . $text_error["connection_failed"] . "</p>";
return $ns;
}
function fsocks4asockopen($proxyHostname, $proxyPort, $targetHostname, $targetPort)
{
$sock = fsockopen($proxyHostname, $proxyPort);
if ($sock === false)
return false;
fwrite($sock, pack("CCnCCCCC", 0x04, 0x01, $targetPort, 0x00, 0x00, 0x00, 0x01, 0x00) . $targetHostname . pack("C", 0x00));
$response = fread($sock, 16);
$values = unpack("xnull/Cret/nport/Nip", $response);
if ($values["ret"] == 0x5a)
return $sock;
else {
fclose($sock);
return false;
}
}
/*
* Close a NNTP connection
*
* $ns: the handle of the connection
*/
function nntp_close(&$ns)
{
if ($ns != false) {
fputs($ns, "QUIT\r\n");
fclose($ns);
}
}
/*
* Validates an email adress
*
* $address: a string containing the email-address to be validated
*
* returns true if the address passes the tests, false otherwise.
*/
function validate_email($address)
{
global $validate_email;
$return = true;
if (($validate_email >= 1) && ($return == true))
/* Need to clean up this regex to work properly with preg_match
$return = (preg_match('^[-!#$%&\'*+\\./0-9=?A-Z^_A-z{|}~]+'.'@'.
'[-!#$%&\'*+\\/0-9=?A-Z^_A-z{|}~]+\.'.
'[-!#$%&\'*+\\./0-9=?A-Z^_A-z{|}~]+$',$address));
*/
$return = 1;
if (($validate_email >= 2) && ($return == true)) {
$addressarray = address_decode($address, "garantiertungueltig");
$return = checkdnsrr($addressarray[0]["host"], "MX");
if (! $return)
$return = checkdnsrr($addressarray[0]["host"], "A");
}
return ($return);
}
/*
* decodes a block of 7bit-data in uuencoded format to it's original
* 8bit format.
* The headerline containing filename and permissions doesn't have to
* be included.
*
* $data: The uuencoded data as a string
*
* returns the 8bit data as a string
*
* Note: this function is very slow and doesn't recognize incorrect code.
*/
function uudecode_line($line)
{
$data = substr($line, 1);
$length = ord($line[0]) - 32;
$decoded = "";
for ($i = 0; $i < (strlen($data) >> 2); $i ++) {
$pack = substr($data, $i << 2, 4);
$upack = "";
$bitmaske = 0;
for ($o = 0; $o < 4; $o ++) {
$g = ((ord($pack[3 - $o]) - 32));
if ($g == 64)
$g = 0;
$bitmaske = $bitmaske | ($g << (6 * $o));
}
$schablone = 255;
for ($o = 0; $o < 3; $o ++) {
$c = ($bitmaske & $schablone) >> ($o << 3);
$schablone = ($schablone << 8);
$upack = chr($c) . $upack;
}
$decoded .= $upack;
}
$decoded = substr($decoded, 0, $length);
return $decoded;
}
/*
* decodes uuencoded Attachments.
*
* $data: the encoded data
*
* returns the decoded data
*/
function uudecode($data)
{
$d = explode("\n", $data);
$u = "";
for ($i = 0; $i < count($d) - 1; $i ++)
$u .= uudecode_line($d[$i]);
return $u;
}
/*
* returns the mimetype of an filename
*
* $name: the complete filename of a file
*
* returns a string containing the mimetype
*/
function get_mimetype_by_filename($name)
{
$ending = strtolower(strrchr($name, "."));
switch ($ending) {
case ".jpg":
case ".jpeg":
$type = "image/jpeg";
break;
case ".gif":
$type = "image/gif";
break;
case ".png":
$type = "image/png";
break;
case ".bmp":
$type = "image/bmp";
break;
default:
$type = "text/plain";
}
return $type;
}
function get_mimetype_by_string($filedata)
{
if (function_exists('finfo_open')) {
$f = finfo_open();
return finfo_buffer($f, $filedata, FILEINFO_MIME_TYPE);
} else {
return false;
}
}
/*
* Test, if the access to a group is allowed. This is true, if $testgroup is
* false or the groupname is in groups.txt
*
* $groupname: name of the group to be checked
*
* returns true, if access is allowed
*/
function testGroup($groupname)
{
global $CONFIG, $testgroup, $file_groups, $config_dir;
$groupname = strtolower($groupname);
if ($testgroup) {
$gf = fopen($file_groups, "r");
while (! feof($gf)) {
$read = trim(line_read($gf));
$read = preg_replace('/\t/', ' ', $read);
$read = strtolower($read);
$pos = strpos($read, " ");
if ($pos != false) {
if (substr($read, 0, $pos) == trim($groupname))
return true;
} else {
if ($read == trim($groupname))
return true;
}
}
fclose($gf);
if ($groupname == $CONFIG['spamgroup']) {
return true;
} else {
/* Find section */
$menulist = file($config_dir . "menu.conf", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($menulist as $menu) {
if ($menu[0] == '#') {
continue;
}
$menuitem = explode(':', $menu);
if ($menuitem[1] == '0') {
continue;
}
$glfp = fopen($config_dir . $menuitem[0] . "/groups.txt", 'r');
$section = "";
while ($gl = fgets($glfp)) {
$group_name = preg_split("/( |\t)/", $gl, 2);
if (stripos(trim($groupname), trim($group_name[0])) !== false) {
fclose($glfp);
return true;
}
}
}
fclose($glfp);
return false;
}
} else {
return true;
}
}
function get_section_by_group($groupname)
{
global $CONFIG, $config_dir;
$menulist = file($config_dir . "menu.conf", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
// Get first group in Newsgroups
$groupname = preg_split("/( |\,)/", $groupname, 2);
$groupname = $groupname[0];
foreach ($menulist as $menu) {
if ($menu[0] == '#') {
continue;
}
$menuitem = explode(':', $menu);
if ($menuitem[1] == '0') {
continue;
}
$section = "";
$gldata = file($config_dir . $menuitem[0] . "/groups.txt");
foreach ($gldata as $gl) {
$group_name = preg_split("/( |\t)/", $gl, 2);
if (strtolower(trim($groupname)) == strtolower(trim($group_name[0]))) {
$section = $menuitem[0];
return $section;
}
}
}
return false;
}
function testGroups($newsgroups)
{
$groups = explode(",", $newsgroups);
$count = count($groups);
$return = "";
$o = 0;
for ($i = 0; $i < $count; $i ++) {
if (testgroup($groups[$i]) && (! function_exists("npreg_group_has_write_access") || npreg_group_has_write_access($groups[$i]))) {
if ($o > 0)
$return .= ",";
$o ++;
$return .= $groups[$i];
}
}
return ($return);
}
/*
* read one line from the NNTP-server
*/
function line_read(&$ns)
{
if ($ns != false) {
$t = str_replace("\n", "", str_replace("\r", "", fgets($ns, 1200)));
return $t;
}
}
/*
* Split an internet-address string into its parts. An address string could
* be for example:
* - user@host.domain (Realname)
* - "Realname" <user@host.domain>
* - user@host.domain
*
* The address will be split into user, host (incl. domain) and realname
*
* $adrstring: The string containing the address in internet format
* $defaulthost: The name of the host which should be returned if the
* address-string doesn't contain a hostname.
*
* returns an hash containing the fields "mailbox", "host" and "personal"
*/
function address_decode($adrstring, $defaulthost)
{
$parsestring = trim($adrstring);
$len = strlen($parsestring);
$at_pos = strpos($parsestring, '@'); // find @
$ka_pos = strpos($parsestring, "("); // find (
$kz_pos = strpos($parsestring, ')'); // find )
$ha_pos = strpos($parsestring, '<'); // find <
$hz_pos = strpos($parsestring, '>'); // find >
$space_pos = strpos($parsestring, ')'); // find ' '
$email = "";
$mailbox = "";
$host = "";
$personal = "";
if ($space_pos != false) {
if (($ka_pos != false) && ($kz_pos != false)) {
$personal = substr($parsestring, $ka_pos + 1, $kz_pos - $ka_pos - 1);
$email = trim(substr($parsestring, 0, $ka_pos - 1));
}
} else {
$email = $adrstring;
}
if (($ha_pos != false) && ($hz_pos != false)) {
$email = trim(substr($parsestring, $ha_pos + 1, $hz_pos - $ha_pos - 1));
$personal = substr($parsestring, 0, $ha_pos - 1);
}
if ($at_pos != false) {
$mailbox = substr($email, 0, strpos($email, '@'));
$host = substr($email, strpos($email, '@') + 1);
} else {
$mailbox = $email;
$host = $defaulthost;
}
$personal = trim($personal);
if (substr($personal, 0, 1) == '"')
$personal = substr($personal, 1);
if (substr($personal, strlen($personal) - 1, 1) == '"')
$personal = substr($personal, 0, strlen($personal) - 1);
$result["mailbox"] = trim($mailbox);
$result["host"] = trim($host);
if ($personal != "")
$result["personal"] = $personal;
$complete[] = $result;
return ($complete);
}
/*
* Read the groupnames from groups.txt, and get additional informations
* of the groups from the newsserver
*
* when load=0, returns cached group list
* when load=1, checks if the cache should be used, and returns nothing
* when force_reload=true, rebuilds group list cache
*/
function groups_read($server, $port, $load = 0, $force_reload = false)
{
global $gl_age, $file_groups, $spooldir, $config_name, $cache_index;
// is there a cached version, and is it actual enough?
$cachefile = $spooldir . '/' . $config_name . '-groups.dat';
// if cache is new enough, don't recreate it
clearstatcache(TRUE, $cachefile);
if (! $force_reload && $load == 1 && file_exists($cachefile) && (filemtime($cachefile) + $cache_index > time())) {
return;
}
if (! $force_reload && file_exists($cachefile) && $load == 0) {
// cached file exists and is new enough, so lets read it out.
$file = fopen($cachefile, "r");
$data = "";
while (! feof($file)) {
$data .= fgets($file, 1000);
}
fclose($file);
$newsgroups = unserialize($data);
} else {
// force a refresh of the group list
$ns = nntp_open($server, $port);
if ($ns == false)
return false;
// $gf=fopen($file_groups,"r");
$gfdata = file($file_groups);
// if we want to mark groups with new articles with colors, we will later
// need the format of the overview
$overviewformat = thread_overview_read($ns);
foreach ($gfdata as $gf) {
$gruppe = new newsgroupType();
$tmp = preg_replace('/\t/', ' ', trim($gf));
if (substr($tmp, 0, 1) == ":") {
$gruppe->text = substr($tmp, 1);
$newsgroups[] = $gruppe;
} elseif (strlen($tmp) > 0) {
// is there a description in groups.txt?
$gr = explode(" ", $tmp, 2);
if (isset($gr[1])) { // Yes
$gruppe->name = $gr[0];
$desc = $gr[1];
} else { // No
// no, get it from the newsserver.
$gruppe->name = $tmp;
if (is_file($spooldir . '/' . $tmp . '-title')) {
$response = file_get_contents($spooldir . '/' . $tmp . '-title');
$desc = strrchr($response, "\t");
} else {
$desc = "-";
}
}
if (strcmp($desc, "") == 0)
$desc = "-";
$gruppe->description = $desc;
fputs($ns, "GROUP " . $gruppe->name . "\r\n");
$t = explode(" ", line_read($ns));
if ($t[0] == "211")
$gruppe->count = $t[1];
else {
nntp_close($ns);
$ns = nntp_open($server, $port);
if ($ns == false)
return false;
fputs($ns, "GROUP " . $gruppe->name . "\r\n");
$t = explode(" ", line_read($ns));
if ($t[0] == "211")
$gruppe->count = $t[1];
else
continue;
}
// mark group with new articles with colors
if ($gl_age) {
fputs($ns, 'XOVER ' . $t[3] . "\r\n");
$tmp = explode(" ", line_read($ns));
if ($tmp[0] == "224") {
$tmp = line_read($ns);
if ($tmp != ".") {
$head = thread_overview_interpret($tmp, $overviewformat, $gruppe->name);
$tmp = line_read($ns);
$gruppe->age = $head->date;
}
}
}
if ((strcmp(trim($gruppe->name), "") != 0) && (substr($gruppe->name, 0, 1) != "#"))
$newsgroups[] = $gruppe;
}
}
nntp_close($ns);
// write the data to the cachefile
file_put_contents($cachefile, serialize($newsgroups));
}
if ($load == 0) {
return $newsgroups;
} else {
return;
}
}
function groups_show($gruppen)
{
global $gl_age, $frame, $spooldir, $logdir, $CONFIG, $spoolnews;
if ($gruppen == false)
return;
global $file_thread, $text_groups;
$logfile = $logdir . '/debug.log';
$c = count($gruppen);
$acttype = "keins";
echo '<table class="np_groups_table" cellspacing="0"><tr class="np_thread_head"><td width="45px" class="np_thread_head">';
echo 'Latest</td><td style="text-align: center;">Newsgroup</td><td width="8%" class="np_thread_head">Messages</td><td width="20%" class="np_thread_head" >Last Message</td></tr>';
$subs = array();
$nonsubs = array();
$user = null;
if (isset($_COOKIE['mail_name'])) {
if ($userdata = get_user_mail_auth_data($_COOKIE['mail_name'])) {
$userfile = $spooldir . '/' . strtolower($_COOKIE['mail_name']) . '-articleviews.dat';
}
}
for ($i = 0; $i < $c; $i ++) {
unset($groupdisplay);
$g = $gruppen[$i];
if (isset($g->text)) {
if ($acttype != "text") {
$acttype = "text";
}
} else {
if ($acttype != "group") {
$acttype = "group";
}
/* Display group name and description */
if (isset($userdata[$g->name])) {
$lineclass = "np_thread_line2";
} else {
$lineclass = "np_thread_line1";
}
$groupdisplay = '<tr class="' . $lineclass . '"><td style="text-align: center;" class="' . $lineclass . '">';
$groupdisplay .= '<a href="overboard.php?thisgroup=' . _rawurlencode($g->name) . '">';
if ((isset($_SESSION['theme'])) && file_exists('../common/themes/' . $_SESSION['theme'] . '/images/latest.png')) {
$latest_image = '../common/themes/' . $_SESSION['theme'] . '/images/latest.png';
} else {
$latest_image = '../common/images/latest.png';
}
$groupdisplay .= '<img src="' . $latest_image . '">';
$groupdisplay .= '</a>';
$groupdisplay .= '</td>';
$groupdisplay .= '<td class="' . $lineclass . '">';
$groupdisplay .= '<span class="np_group_line_text">';
$groupdisplay .= '<a ';
$groupdisplay .= 'target="' . $frame['content'] . '" ';
// Get last article info from overview
$database = $spooldir . '/articles-overview.db3';
$table = 'overview';
$overview_dbh = overview_db_open($database);
$overview_query = $overview_dbh->prepare('SELECT * FROM overview WHERE newsgroup=:newsgroup ORDER BY date DESC LIMIT 2');
$overview_query->execute([
'newsgroup' => $g->name
]);
$found = 0;
while ($row = $overview_query->fetch()) {
$found = 1;
break;
}
if ($found == 1) {
$lastarticleinfo['date'] = $row['date'];
}
$new = false;
$new_style_on = '';
$new_style_off = '';
if (isset($userdata[$g->name]) && ($userdata[$g->name] < $lastarticleinfo['date'])) {
$new_style_on = '<b><i>*';
$new_style_off = '</i></b>';
$new = true;
}
$groupdisplay .= 'href="' . $file_thread . '?group=' . _rawurlencode($g->name) . '"><span class="np_group_line_text">' . $new_style_on . group_display_name($g->name) . $new_style_off . "</span></a>\n";
if ($new) {
echo '</i></b>';
}
if ($g->description != "-") {
$groupdisplay .= '</span><br><p class="np_group_desc">' . $g->description . '</p>';
}
$overview_dbh = null;
if (isset($userdata[$g->name])) {
$groupdisplay .= '</span><p class="np_group_user_tools">';
$groupdisplay .= '<a class="np_group_user_tools" href="index.php?unsub=' . _rawurlencode($g->name) . '">(unsubscribe)</a>';
if ($new) {
$groupdisplay .= '&nbsp;<a href="overboard.php?thisgroup=' . _rawurlencode($g->name) . '&time=' . $userdata[$g->name] . '"><b>(new)</b></a> ';
}
$groupdisplay .= '</p';
}
/* Display article count */
$groupdisplay .= '</td><td class="' . $lineclass . '">';
if ($gl_age)
$datecolor = thread_format_date_color($g->age);
$groupdisplay .= '<small>';
if ($datecolor != "")
$groupdisplay .= '<font color="' . $datecolor . '">' . $g->count . '</font>';
else
$groupdisplay .= $g->count;
$groupdisplay .= '</small>';
/* Display latest article info */
$groupdisplay .= '</td><td class="' . $lineclass . '"><div class="np_last_posted_date">';
if ($found == 1) {
$poster = address_decode($row['name'], "nowhere");
$lastarticleinfo['from'] = $poster[0]['mailbox'] . "@" . $poster[0]['host'];
if (isset($poster[0]['personal'])) {
$lastarticleinfo['name'] = $poster[0]['personal'];
} else {
$lastarticleinfo['name'] = $poster[0]['mailbox'];
}
$fromoutput[0] = $poster[0]['mailbox'] . "@" . $poster[0]['host'];
}
$groupdisplay .= get_date_interval(date("D, j M Y H:i T", $lastarticleinfo['date']));
$groupdisplay .= '<table><tr><td>';
$groupdisplay .= '<font class="np_last_posted_date">by: ';
$groupdisplay .= create_name_link(mb_decode_mimeheader(html_entity_decode($lastarticleinfo['name'])), $lastarticleinfo['from']);
$groupdisplay .= '</td></tr></table>';
}
if (isset($groupdisplay)) {
$groupdisplay .= "\n";
flush();
if (isset($userdata[$g->name])) {
$subs[] = $groupdisplay;
} else {
$nonsubs[] = $groupdisplay;
}
}
}
foreach ($subs as $sub) {
echo $sub;
}
foreach ($nonsubs as $nonsub) {
echo $nonsub;
}
echo "</td></div></table>\n";
}
/*
* print the group names from an array to the webpage
*/
function groups_show_frames($gruppen)
{
global $gl_age, $frame, $spooldir;
if ($gruppen == false)
return;
global $file_thread, $text_groups;
$c = count($gruppen);
echo '<div class="np_index_groupblock">';
$acttype = "keins";
for ($i = 0; $i < $c; $i ++) {
$g = $gruppen[$i];
if (isset($g->text)) {
if ($acttype != "text") {
$acttype = "text";
if ($i > 0)
echo '</div>';
echo '<div class="np_index_grouphead">';
}
echo $g->text;
} else {
if ($acttype != "group") {
$acttype = "group";
if ($i > 0)
echo '</div>';
echo '<div class="np_index_groupblock">';
}
echo '<div class="np_index_group">';
echo '<b><a ';
echo 'target="' . $frame['content'] . '" ';
echo 'href="' . $file_thread . '?group=' . _rawurlencode($g->name) . '">' . group_display_name($g->name) . "</a></b>\n";
if ($gl_age)
$datecolor = thread_format_date_color($g->age);
echo '<small>(';
if ($datecolor != "")
echo '<font color="' . $datecolor . '">' . $g->count . '</font>';
else
echo $g->count;
echo ')</small>';
if ($g->description != "-")
echo '<br><small>' . $g->description . '</small>';
echo '</div>';
}
echo "\n";
flush();
}
echo "</div></div>\n";
}
/*
* gets a list of available articles in the group $groupname
*/
/*
* function getArticleList(&$ns,$groupname) {
* fputs($ns,"LISTGROUP $groupname \r\n");
* $line=line_read($ns);
* $line=line_read($ns);
* while(strcmp($line,".") != 0) {
* $articleList[] = trim($line);
* $line=line_read($ns);
* }
* if (!isset($articleList)) $articleList="-";
* return $articleList;
* }
*/
/*
* Decode quoted-printable or base64 encoded headerlines
*
* $value: The to be decoded line
*
* returns the decoded line
*/
function headerDecode($value)
{
return mb_decode_mimeheader($value);
}
/*
* calculates an Unix timestamp out of a Date-Header in an article
*
* $value: Value of the Date: header
*
* returns an Unix timestamp
*/
function getTimestamp($value)
{
global $CONFIG;
return strtotime($value);
}
function parse_header($hdr, $number = "")
{
for ($i = count($hdr) - 1; $i > 0; $i --)
if (preg_match("/^(\x09|\x20)/", $hdr[$i]))
$hdr[$i - 1] = $hdr[$i - 1] . " " . ltrim($hdr[$i]);
$header = new headerType();
$header->isAnswer = false;
for ($count = 0; $count < count($hdr); $count ++) {
$variable = substr($hdr[$count], 0, strpos($hdr[$count], " "));
$value = trim(substr($hdr[$count], strpos($hdr[$count], " ") + 1));
switch (strtolower($variable)) {
case "from:":
$fromline = address_decode(headerDecode($value), "nowhere");
if (! isset($fromline[0]["host"]))
$fromline[0]["host"] = "";
$header->from = $fromline[0]["mailbox"] . "@" . $fromline[0]["host"];
$header->username = $fromline[0]["mailbox"];
if (! isset($fromline[0]["personal"])) {
$header->name = "";
} else {
$header->name = $fromline[0]["personal"];
}
break;
case "message-id:":
$header->id = $value;
break;
case "subject:":
$header->subject = headerDecode($value);
break;
case "newsgroups:":
$header->newsgroups = $value;
break;
case "organization:":
$header->organization = headerDecode($value);
break;
case "content-transfer-encoding:":
$header->content_transfer_encoding = trim(strtolower($value));
break;
case "content-disposition:":
$getname = preg_split("/name\=/", $value, 2);
if (isset($getname[1])) {
$header->content_type_name = array(
$getname[1]
);
}
break;
case "content-type:":
$header->content_type = array();
$subheader = explode(";", $value);
$header->content_type[0] = strtolower(trim($subheader[0]));
for ($i = 1; $i < count($subheader); $i ++) {
$gleichpos = strpos($subheader[$i], "=");
if ($gleichpos) {
$subvariable = trim(substr($subheader[$i], 0, $gleichpos));
$subvalue = trim(substr($subheader[$i], $gleichpos + 1));
if (($subvalue[0] == '"') && ($subvalue[strlen($subvalue) - 1] == '"'))
$subvalue = substr($subvalue, 1, strlen($subvalue) - 2);
switch ($subvariable) {
case "charset":
$header->content_type_charset = array(
strtolower($subvalue)
);
break;
case "name":
$header->content_type_name = array(
$subvalue
);
break;
case "boundary":
$header->content_type_boundary = $subvalue;
break;
case "format":
$header->content_type_format = array(
$subvalue
);
}
}
}
break;
case "references:":
$ref = trim($value);
while (strpos($ref, "> <") != false) {
$header->references[] = substr($ref, 0, strpos($ref, " "));
$ref = substr($ref, strpos($ref, "> <") + 2);
}
$header->references[] = trim($ref);
break;
case "date:":
$header->date = getTimestamp(trim($value));
break;
case "followup-to:":
$header->followup = trim($value);
break;
case "x-newsreader:":
case "x-mailer:":
case "x-rslight-to:":
$header->rslight_to = trim($value);
break;
case "x-rslight-site:":
$header->rslight_site = trim($value);
break;
case "user-agent:":
$header->user_agent = trim($value);
break;
case "x-face:": // not ready
// echo "<p>-".base64_decode($value)."-</p>";
break;
case "x-no-archive:":
$header->xnoarchive = strtolower(trim($value));
}
}
if (! isset($header->content_type[0]))
$header->content_type[0] = "text/plain";
if (! isset($header->content_transfer_encoding))
$header->content_transfer_encoding = "8bit";
if ($number != "")
$header->number = $number;
return $header;
}
/*
* convert the charset of a text
*/
function recode_charset($text, $source = false, $dest = false)
{
global $iconv_enable, $www_charset;
if ($dest == false)
$dest = $www_charset;
if (($iconv_enable) && ($source != false)) {
$return = iconv($source, $dest . "//TRANSLIT", $text);
if ($return != "")
return $return;
else
return $text;
} else {
return $text;
}
}
function decode_body($body, $encoding)
{
$bodyzeile = "";
switch ($encoding) {
case "base64":
$body = base64_decode($body);
break;
case "quoted-printable":
$body = Quoted_printable_decode($body);
$body = str_replace("=\n", "", $body);
// default:
// $body=str_replace("\n..\n","\n.\n",$body);
}
return $body;
}
/*
* makes URLs clickable
*
* $text: A text-line probably containing links.
*
* the function returns the text-line with HTML-Links to the links or
* email-adresses.
*/
function html_parse($text)
{
global $frame_externallink;
if ((isset($frame_externallink)) && ($frame_externallink != "")) {
$target = ' TARGET="' . $frame_externallink . '" ';
} else {
$target = ' ';
}
$ntext = "";
// split every line into it's words
$words = explode(" ", $text);
$n = count($words);
$is_link = 0;
for ($i = 0; $i < $n; $i ++) {
$word = $words[$i];
// add the spaces between the words
if ($i > 0)
$ntext .= " ";
$ntext .= $word;
}
return ($ntext);
}
function display_links_in_body($text)
{
preg_match_all('/(https?|ftp|scp|news|gopher|gemini|telnet):\/\/[a-zA-Z0-9.?%=\-\+\;\:\~\@\!\(\)\#&_\/]+/', $text, $matches);
$found = array();
$isquote = false;
if (strpos($text, ">") == 0) {
$isquote = true;
echo '<blockquote class="np_article_quote">';
}
foreach ($matches[0] as $match) {
if (! $match) {
continue;
}
if (in_array($match, $found)) {
continue;
}
$found[] = $match;
$linkurl = preg_replace("/(<|>)/", ' ', htmlspecialchars_decode($match));
$url = preg_replace("/(<|>)/", ' ', $match);
$pattern = preg_quote($url);
$pattern = "!$pattern!";
$text = preg_replace($pattern, '<a href="' . $linkurl . '" rel="nofollow" target="_blank">' . $url . '</a>', $text, 1);
}
echo $text;
if ($isquote) {
echo '</blockquote>';
}
}
/*
* read the header of an article in plaintext into an array
* $articleNumber can be the number of an article or its message-id.
*/
function readPlainHeader(&$ns, $group, $articleNumber)
{
fputs($ns, "GROUP $group\r\n");
$line = line_read($ns);
fputs($ns, "HEAD $articleNumber\r\n");
$line = line_read($ns);
if (substr($line, 0, 3) != "221") {
echo $text_error["article_not_found"];
$header = false;
} else {
$line = line_read($ns);
$body = "";
while (strcmp(trim($line), ".") != 0) {
$body .= $line . "\n";
$line = line_read($ns);
}
return explode("\n", str_replace("\r\n", "\n", $body));
}
}
/*
* cancel an article on the newsserver
*
* DO NOT USE THIS FUNCTION, IF YOU DON'T KNOW WHAT YOU ARE DOING!
*
* $ns: The handler of the NNTP-Connection
* $group: The group of the article
* $id: the Number of the article inside the group or the message-id
*/
function message_cancel($subject, $from, $newsgroups, $ref, $body, $id)
{
global $server, $port, $send_poster_host, $CONFIG, $text_error;
global $www_charset;
flush();
$ns = nntp_open($server, $port);
if ($ns != false) {
fputs($ns, "POST\r\n");
$weg = line_read($ns);
fputs($ns, 'Subject: ' . quoted_printable_encode($subject) . "\r\n");
fputs($ns, 'From: ' . $from . "\r\n");
fputs($ns, 'Newsgroups: ' . $newsgroups . "\r\n");
fputs($ns, "Mime-Version: 1.0\r\n");
fputs($ns, "Content-Type: text/plain; charset=" . $www_charset . "\r\n");
fputs($ns, "Content-Transfer-Encoding: 8bit\r\n");
if ($send_poster_host)
fputs($ns, 'X-HTTP-Posting-Host: ' . gethostbyaddr(getenv("REMOTE_ADDR")) . "\r\n");
if ($ref != false)
fputs($ns, 'References: ' . $ref . "\r\n");
if (isset($CONFIG['organization']))
fputs($ns, 'Organization: ' . quoted_printable_encode($CONFIG['organization']) . "\r\n");
fputs($ns, "Control: cancel " . $id . "\r\n");
$body = str_replace("\n.\r", "\n..\r", $body);
$body = str_replace("\r", '', $body);
$b = explode("\n", $body);
$body = "";
for ($i = 0; $i < count($b); $i ++) {
if ((strpos(substr($b[$i], 0, strpos($b[$i], " ")), ">") != false) | (strcmp(substr($b[$i], 0, 1), ">") == 0)) {
$body .= textwrap(stripSlashes($b[$i]), 78, "\r\n") . "\r\n";
} else {
$body .= textwrap(stripSlashes($b[$i]), 74, "\r\n") . "\r\n";
}
}
fputs($ns, "\r\n" . $body . "\r\n.\r\n");
$message = line_read($ns);
nntp_close($ns);
} else {
$message = $text_error["post_failed"];
}
return $message;
}
function rslight_encrypt($data, $key)
{
$encryption_key = base64_decode($key);
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
$encrypted = openssl_encrypt($data, 'aes-256-cbc', $encryption_key, 0, $iv);
return base64_encode($encrypted . '::' . $iv);
}
function _rawurlencode($string)
{
$string = rawurlencode(str_replace('+', '%2B', $string));
return $string;
}
function _rawurldecode($string)
{
$string = rawurldecode(str_replace('%2B', '+', $string));
return $string;
}
function rslight_decrypt($data, $key)
{
$encryption_key = base64_decode($key);
list ($encrypted_data, $iv) = explode('::', base64_decode($data), 2);
return openssl_decrypt($encrypted_data, 'aes-256-cbc', $encryption_key, 0, $iv);
}
function group_display_name($gname)
{
global $config_dir;
$namelist = file($config_dir . "rename.conf", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($namelist as $name) {
if ($name[0] == '#') {
continue;
}
$nameitem = explode(':', $name);
if (! strcmp(trim($nameitem[0]), trim($gname))) {
return $nameitem[1];
}
}
return $gname;
}
function check_bbs_auth($username, $password)
{
global $config_dir, $CONFIG;
if ($username == '' && $password == '') {
return false;
}
$workpath = $config_dir . "users/";
$username = trim(strtolower($username));
$userFilename = $workpath . $username;
$keyFilename = $config_dir . "/userconfig/" . $username;
// Create accounts for $anonymous and $CONFIG['server_auth_user'] if not exist
if ($username == strtolower($CONFIG['anonusername'])) {
if (filemtime($config_dir . "rslight.inc.php") > filemtime($userFilename)) {
if ($userFileHandle = fopen($userFilename, 'w+')) {
fwrite($userFileHandle, password_hash($CONFIG['anonuserpass'], PASSWORD_DEFAULT));
fclose($userFileHandle);
}
}
}
if ($username == strtolower($CONFIG['server_auth_user'])) {
if (filemtime($config_dir . "rslight.inc.php") > filemtime($userFilename)) {
if ($userFileHandle = fopen($userFilename, 'w+')) {
fwrite($userFileHandle, password_hash($CONFIG['server_auth_pass'], PASSWORD_DEFAULT));
fclose($userFileHandle);
}
}
}
if (trim($username) == strtolower($CONFIG['anonusername']) && $CONFIG['anonuser'] != true) {
return FALSE;
}
if ($userFileHandle = fopen($userFilename, 'r')) {
$userFileInfo = fread($userFileHandle, filesize($userFilename));
fclose($userFileHandle);
if (password_verify($password, $userFileInfo)) {
touch($userFilename);
$ok = TRUE;
} else {
return FALSE;
}
} else {
$ok = FALSE;
}
if ($ok) {
return TRUE;
} else {
if (isset($CONFIG['auto_create']) && $CONFIG['auto_create'] == true) {
if ($userFileHandle = @fopen($userFilename, 'w+')) {
fwrite($userFileHandle, password_hash($password, PASSWORD_DEFAULT));
fclose($userFileHandle);
chmod($userFilename, 0666);
}
$newkey = base64_encode(openssl_random_pseudo_bytes(44));
if ($userFileHandle = @fopen($keyFilename, 'w+')) {
fwrite($userFileHandle, 'encryptionkey:' . $newkey);
fclose($userFileHandle);
chmod($userFilename, 0666);
}
return TRUE;
} else {
return FALSE;
}
}
}
function check_encryption_groups($request)
{
global $config_path;
$groupsFilename = $config_path . "encryption_ok.txt";
if ($groupsFileHandle = @fopen($groupsFilename, 'r')) {
while (! feof($groupsFileHandle)) {
$buffer = fgets($groupsFileHandle);
$buffer = str_replace(array(
"\r",
"\n"
), '', $buffer);
if (! strcmp($buffer, $request)) {
fclose($groupsFileHandle);
return TRUE;
}
}
fclose($groupsFileHandle);
} else {
return FALSE;
}
}
function set_user_config($username, $request, $newval)
{
global $config_dir;
$userconfigpath = $config_dir . "userconfig/";
$username = strtolower($username);
$userFilename = $userconfigpath . $username;
$userData = file($userFilename, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$userFileHandle = fopen($userFilename, 'w');
$found = 0;
foreach ($userData as $data) {
if (strpos($data, $request . ':') !== FALSE) {
fputs($userFileHandle, $request . ':' . $newval . "\r\n");
$found = 1;
} else {
fputs($userFileHandle, $data . "\r\n");
}
}
if ($found == 0) {
fputs($userFileHandle, $request . ':' . $newval . "\r\n");
}
fclose($userFileHandle);
return;
}
function get_user_config($username, $request)
{
global $config_dir;
$userconfigpath = $config_dir . "userconfig/";
$username = strtolower($username);
$userFilename = $userconfigpath . $username;
if ($userFileHandle = @fopen($userFilename, 'r')) {
while (! feof($userFileHandle)) {
$buffer = fgets($userFileHandle);
if (strpos($buffer, $request . ':') !== FALSE) {
$userdataline = $buffer;
fclose($userFileHandle);
$userdatafound = explode(':', $userdataline);
return trim($userdatafound[1]);
}
}
fclose($userFileHandle);
return FALSE;
} else {
return FALSE;
}
}
function is_multibyte($s)
{
return mb_strlen($s, 'utf-8') < strlen($s);
}
function check_spam($subject, $from, $newsgroups, $ref, $body, $msgid, $useheaders = false)
{
global $msgid_generate, $msgid_fqdn, $spooldir, $logdir;
global $CONFIG;
$spamdir = $spooldir . '/spam';
if (! is_dir($spamdir)) {
mkdir($spamdir);
}
$logfile = $logdir . '/spam.log';
$spamfile = tempnam($spooldir, 'spam-');
if ($useheaders) {
// Add headers
$head = '';
if (trim($subject) != '') {
$head .= 'Subject: ' . $subject . "\r\n";
}
if (trim($from) != '') {
$head .= 'From: ' . $from . "\r\n";
}
if (trim($newsgroups) != '') {
$head .= 'Newsgroups: ' . $newsgroups . "\r\n";
}
if (trim($ref) != '') {
$head .= 'References: ' . $ref . "\r\n";
}
if (trim($msgid) != '') {
$head .= 'Message-ID: ' . $msgid . "\r\n";
}
$message = $head . "\r\n" . $body;
} else {
$message = $body;
}
file_put_contents($spamfile, $message);
$spamcommand = $CONFIG['spamc'] . ' -E < ' . $spamfile;
ob_start();
passthru($spamcommand, $res);
$spamresult = ob_get_contents();
ob_end_clean();
$spam_fail = 1;
foreach (explode(PHP_EOL, $spamresult) as $line) {
$line = str_replace(array(
"\n\r",
"\n",
"\r"
), '', $line);
if (strpos($line, 'X-Spam-Checker-Version:') !== FALSE) {
$spamcheckerversion = $line;
$spam_fail = 0;
}
if (strpos($line, 'X-Spam-Level:') !== FALSE) {
$spamlevel = $line;
}
if ((strpos($line, "X-Spam-Flag: YES") === 0) && ($res !== 1)) {
$res = 1;
}
}
unlink($spamfile);
if ($res === 1) {
file_put_contents($logfile, "\n" . format_log_date() . " identified spam: " . $from . " " . $newsgroups . " " . $msgid, FILE_APPEND);
file_put_contents($spamdir . '/' . $msgid, $spamresult);
} else {
file_put_contents($logfile, "\n" . format_log_date() . " clean message: " . $from . " " . $newsgroups . " " . $msgid, FILE_APPEND);
}
return array(
'res' => $res,
'spamresult' => $spamresult,
'spamcheckerversion' => $spamcheckerversion,
'spamlevel' => $spamlevel,
'spam_fail' => $spam_fail
);
}
function format_log_date()
{
return date('M d H:i:s');
}
function create_name_link($name, $data = null)
{
global $CONFIG;
$name = preg_replace('/\"/', '', $name);
if ($data) {
$data = urlencode(base64_encode($data));
}
if ((strpos($name, '...@') !== false && (isset($CONFIG['hide_email']) && $CONFIG['hide_email'] == true)) && ! $data) {
$return = '<span class="visited">' . substr(htmlspecialchars($name), 0, 20) . '</span>';
} else {
$return = '<a href="search.php?command=search&searchpoint=Poster&terms=' . $name . '&data=' . $data . '"><span class="visited">' . substr(htmlspecialchars($name), 0, 20) . '</span></a>';
}
return ($return);
}
function truncate_email($address)
{
$before_at = explode('@', $address);
$namelen = strlen($before_at[0]);
if ($namelen > 3) {
$endname = $namelen - 3;
if ($endname > 8)
$endname = 8;
if ($endname < 3)
$endname ++;
if ($endname < 3)
$endname ++;
} else {
$endname = $namelen;
}
return substr($before_at[0], 0, $endname) . '...' . substr($address, $namelen, strlen($address));
}
function get_date_interval($value)
{
$current = time();
$datetime1 = date_create($value);
$datetime2 = date_create("@$current");
$interval = date_diff($datetime1, $datetime2);
if (! $interval) {
return '(date error)';
}
$years = $interval->format('%y') . " Years ";
$months = $interval->format('%m') . " Months ";
$days = $interval->format('%d') . " Days ";
$hours = $interval->format('%h') . " Hours ";
$minutes = $interval->format('%i') . " Minutes ";
if ($interval->format('%y') == 1) {
$years = $interval->format('%y') . " Year ";
}
if ($interval->format('%m') == 1) {
$months = $interval->format('%m') . " Month ";
}
if ($interval->format('%d') == 1) {
$days = $interval->format('%d') . " Day ";
}
if ($interval->format('%h') == 1) {
$hours = $interval->format('%h') . " Hour ";
}
if ($interval->format('%i') == 1) {
$minutes = $interval->format('%i') . " Minute ";
}
if ($interval->format('%y') == 0) {
$years = '';
}
if ($interval->format('%m') == 0) {
$months = '';
}
if ($interval->format('%d') == 0) {
$days = '';
}
if ($interval->format('%h') == 0) {
$hours = '';
}
if ($interval->format('%i') == 0) {
$minutes = '';
}
if ($years > 0) {
$days = '';
$hours = '';
$minutes = '';
}
if ($months > 0) {
$hours = '';
$minutes = '';
}
if ($days > 0) {
$minutes = '';
}
$variance = $interval->format($years . $months . $days . $hours . $minutes . ' ago');
if (strlen($variance) < 5) {
$variance = " now";
}
return $variance;
}
function create_xref_from_msgid($msgid, $thisgroup = null, $thisnumber = null)
{
global $spooldir, $CONFIG;
$database = $spooldir . '/articles-overview.db3';
$table = 'overview';
$overview_dbh = overview_db_open($database, $table);
$overview_stmt = $overview_dbh->prepare("SELECT * FROM overview WHERE msgid=:msgid");
$overview_stmt->bindParam(':msgid', $msgid);
$overview_stmt->execute();
$found = false;
$xref = "Xref: " . $CONFIG['pathhost'];
while ($row = $overview_stmt->fetch()) {
if ($row['newsgroup'] == $thisgroup && $thisgroup != null) {
$found = true;
}
$xref .= ' ' . $row['newsgroup'] . ':' . $row['number'];
}
if (! $found) {
$xref .= ' ' . $thisgroup . ':' . $thisnumber;
}
$overview_dbh = null;
return ($xref);
}
function get_search_snippet($body, $content_type = '')
{
$body = quoted_printable_decode($body);
if ($content_type !== '') {
$mysnippet = recode_charset($body, $content_type, "utf8");
} else {
$mysnippet = $body;
}
if ($bodyend = strrpos($mysnippet, "\n---\n")) {
$mysnippet = substr($mysnippet, 0, $bodyend);
} else {
if ($bodyend = strrpos($mysnippet, "\n-- ")) {
$mysnippet = substr($mysnippet, 0, $bodyend);
} else {
if ($bodyend = strrpos($mysnippet, "\n.")) {
$mysnippet = substr($mysnippet, 0, $bodyend);
}
}
}
$mysnippet = preg_replace('/\n.{0,5}>(.*)/', '', $mysnippet);
$snipstart = strpos($mysnippet, ":\n");
if (substr_count(trim(substr($mysnippet, 0, $snipstart)), "\n") < 2) {
$mysnippet = substr($mysnippet, $snipstart + 1);
} else {
$mysnippet = substr($mysnippet, 0);
}
return $mysnippet;
}
function mail_db_open($database, $table = 'messages')
{
try {
$dbh = new PDO('sqlite:' . $database);
} catch (PDOException $e) {
echo 'Connection failed: ' . $e->getMessage();
exit();
}
$dbh->exec("CREATE TABLE IF NOT EXISTS messages(
id INTEGER PRIMARY KEY,
msgid TEXT UNIQUE,
mail_from TEXT,
mail_viewed TEXT,
rcpt_to TEXT,
rcpt_viewed TEXT,
rcpt_target TEXT,
date TEXT,
subject TEXT,
message TEXT,
from_hide TEXT,
to_hide TEXT)");
return ($dbh);
}
function threads_db_open($database, $table = "threads")
{
try {
$dbh = new PDO('sqlite:' . $database);
} catch (PDOException $e) {
echo 'Connection failed: ' . $e->getMessage();
exit();
}
$dbh->exec("CREATE TABLE IF NOT EXISTS $table(
id INTEGER PRIMARY KEY,
headers TEXT,
unique (headers))");
return ($dbh);
}
function history_db_open($database, $table = 'history')
{
try {
$dbh = new PDO('sqlite:' . $database);
} catch (PDOException $e) {
echo 'Connection failed: ' . $e->getMessage();
exit();
}
$dbh->exec("CREATE TABLE IF NOT EXISTS $table(
id INTEGER PRIMARY KEY,
newsgroup TEXT,
number TEXT,
msgid TEXT,
status TEXT,
statusdate TEXT,
statusreason TEXT,
statusnotes TEXT,
unique (newsgroup, msgid),
unique (newsgroup, number))");
$stmt = $dbh->query('CREATE INDEX IF NOT EXISTS id_status on ' . $table . '(status)');
$stmt->execute();
$stmt = $dbh->query('CREATE INDEX IF NOT EXISTS id_newsgroup on ' . $table . '(newsgroup)');
$stmt->execute();
$stmt = $dbh->query('CREATE INDEX IF NOT EXISTS id_msgid on ' . $table . '(msgid)');
$stmt->execute();
$stmt = $dbh->query('CREATE INDEX IF NOT EXISTS id_newsgroup_number on ' . $table . '(newsgroup,number)');
$stmt->execute();
$stmt = $dbh->query('CREATE INDEX IF NOT EXISTS id_statusdate on ' . $table . '(statusdate)');
$stmt->execute();
return ($dbh);
}
function overview_db_open($database, $table = 'overview')
{
try {
$dbh = new PDO('sqlite:' . $database);
} catch (PDOException $e) {
echo 'Connection failed: ' . $e->getMessage();
exit();
}
$dbh->exec("CREATE TABLE IF NOT EXISTS $table(
id INTEGER PRIMARY KEY,
newsgroup TEXT,
number TEXT,
msgid TEXT,
date TEXT,
datestring TEXT,
name TEXT,
subject TEXT,
refs TEXT,
bytes TEXT,
lines TEXT,
xref TEXT,
unique (newsgroup, msgid),
unique (newsgroup, number))");
$stmt = $dbh->query('CREATE INDEX IF NOT EXISTS id_date on ' . $table . '(date)');
$stmt->execute();
$stmt = $dbh->query('CREATE INDEX IF NOT EXISTS id_newsgroup on ' . $table . '(newsgroup)');
$stmt->execute();
$stmt = $dbh->query('CREATE INDEX IF NOT EXISTS id_msgid on ' . $table . '(msgid)');
$stmt->execute();
$stmt = $dbh->query('CREATE INDEX IF NOT EXISTS id_newsgroup_number on ' . $table . '(newsgroup,number)');
$stmt->execute();
$stmt = $dbh->query('CREATE INDEX IF NOT EXISTS id_name on ' . $table . '(name)');
$stmt->execute();
return ($dbh);
}
function article_db_open($database, $table = 'articles')
{
try {
$dbh = new PDO('sqlite:' . $database);
} catch (PDOException $e) {
echo 'Connection failed: ' . $e->getMessage();
exit();
}
$dbh->exec("CREATE TABLE IF NOT EXISTS $table(
id INTEGER PRIMARY KEY,
newsgroup TEXT,
number TEXT UNIQUE,
msgid TEXT UNIQUE,
date TEXT,
name TEXT,
subject TEXT,
search_snippet TEXT,
article TEXT)");
$stmt = $dbh->query('CREATE INDEX IF NOT EXISTS db_number on ' . $table . '(number)');
$stmt->execute();
$stmt = $dbh->query('CREATE INDEX IF NOT EXISTS db_date on ' . $table . '(date)');
$stmt->execute();
$stmt = $dbh->query('CREATE INDEX IF NOT EXISTS db_msgid on ' . $table . '(msgid)');
$stmt->execute();
$stmt = $dbh->query('CREATE INDEX IF NOT EXISTS db_name on ' . $table . '(name)');
$stmt->execute();
$dbh->exec("CREATE VIRTUAL TABLE IF NOT EXISTS search_fts USING fts5(
newsgroup,
number,
msgid,
date,
name,
subject,
search_snippet)");
$dbh->exec("CREATE TRIGGER IF NOT EXISTS after_articles_insert AFTER INSERT ON $table BEGIN
INSERT INTO search_fts(newsgroup, number, msgid, date, name, subject, search_snippet) VALUES(new.newsgroup, new.number, new.msgid, new.date, new.name, new.subject, new.search_snippet);
END;");
$dbh->exec("CREATE TRIGGER IF NOT EXISTS after_articles_delete AFTER DELETE ON $table BEGIN
DELETE FROM search_fts WHERE msgid = old.msgid;
END;");
return ($dbh);
}
function np_get_db_article($article, $group, $makearray = 1, $dbh = null)
{
global $config_dir, $path, $groupconfig, $config_name, $logdir, $spooldir;
$logfile = $logdir . '/newsportal.log';
$msg2 = "";
$closeme = 0;
$database = $spooldir . '/' . $group . '-articles.db3';
if (! $dbh) {
if (! is_file($database)) {
return FALSE;
}
$dbh = article_db_open($database);
$closeme = 1;
}
$ok_article = 0;
// By Message-ID
if (! is_numeric($article)) {
$stmt = $dbh->prepare("SELECT * FROM articles WHERE msgid like :terms");
$stmt->bindParam(':terms', $article);
$stmt->execute();
while ($found = $stmt->fetch()) {
$msg2 = $found['article'];
$ok_article = 1;
break;
}
} else {
$stmt = $dbh->prepare("SELECT * FROM articles WHERE number = :terms");
$stmt->bindParam(':terms', $article);
$stmt->execute();
while ($found = $stmt->fetch()) {
$msg2 = $found['article'];
$ok_article = 1;
break;
}
}
if ($closeme == 1) {
$dbh = null;
}
if ($ok_article !== 1) {
// file_put_contents($logfile, "\n".format_log_date()." ".$config_name." DEBUG: ".$article." from ".$group." not found in database", FILE_APPEND);
return FALSE;
}
// file_put_contents($logfile, "\n".format_log_date()." ".$config_name." DEBUG: fetched: ".$article." from ".$group, FILE_APPEND);
if ($makearray == 1) {
$thisarticle = preg_split("/\r\n|\n|\r/", trim($msg2));
array_pop($thisarticle);
return $thisarticle;
} else {
return trim($msg2);
}
}
function get_poster_name($name)
{
$fromline = address_decode($name, "nowhere");
if (! isset($fromline[0]["host"]))
$fromline[0]["host"] = "";
$name_from = $fromline[0]["mailbox"] . "@" . $fromline[0]["host"];
$name_username = $fromline[0]["mailbox"];
if (! isset($fromline[0]["personal"])) {
$poster_name = $fromline[0]["mailbox"];
} else {
$poster_name = $fromline[0]["personal"];
}
if (trim($poster_name) == '') {
$fromoutput = explode("<", html_entity_decode($name));
if (strlen($fromoutput[0]) < 1) {
$poster_name = $fromoutput[1];
} else {
$poster_name = $fromoutput[0];
}
}
$thisposter['name'] = $poster_name;
$thisposter['from'] = $name_from;
return ($thisposter);
}
function save_config_value($configfile, $name, $value) {
$list = file($configfile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$saveconfig = fopen($configfile, 'w+');
foreach ($list as $save) {
$name = explode(':', $save);
if (strcmp($name[0], $group) == 0) {
fputs($saveconfig, $group . ":" . $article . "\n");
} else {
fputs($saveconfig, $save . "\n");
}
}
}
function get_config_file_value($configfile, $request)
{
if ($configFileHandle = @fopen($configfile, 'r')) {
while (! feof($configFileHandle)) {
$buffer = fgets($configFileHandle);
if (strpos($buffer, $request . ':') !== FALSE) {
$dataline = $buffer;
fclose($configFileHandle);
$datafound = explode(':', $dataline);
return trim($datafound[1]);
}
}
fclose($configFileHandle);
return FALSE;
} else {
return FALSE;
}
}
// This function is specific to user configuration values
function get_config_value($configfile, $request)
{
global $config_dir;
if ($configFileHandle = @fopen($config_dir . '/' . $configfile, 'r')) {
while (! feof($configFileHandle)) {
$buffer = fgets($configFileHandle);
if (strpos($buffer, $request . ':') !== FALSE) {
$dataline = $buffer;
fclose($configFileHandle);
$datafound = explode(':', $dataline);
return trim($datafound[1]);
}
}
fclose($configFileHandle);
return FALSE;
} else {
return FALSE;
}
}
function disable_page_by_user_agent($client_device, $useragent, $script = "Page")
{
global $logdir, $config_name;
if ($client_device == $useragent) {
$logfile = $logdir . '/device.log';
file_put_contents($logfile, "\n" . date('M d H:i:s') . " " . $config_name . " " . $script . " disabled for '" . $useragent . "' Exiting...", FILE_APPEND);
if ($client_device == "bot") {
$_SESSION['bot'] = true;
}
return true;
} else {
return false;
}
}
function throttle_hits($client_device)
{
global $CONFIG, $logdir;
$client_device = get_client_user_agent_info();
$_SESSION['rsactive'] = true;
if ($client_device == "bot") {
$_SESSION['bot'] = 'true';
}
$logfile = $logdir . '/newsportal.log';
if (! isset($_SESSION['starttime'])) {
$_SESSION['starttime'] = time();
$_SESSION['views'] = 0;
}
$_SESSION['views'] ++;
// $loadrate = allowed article request per second
$loadrate = .15;
$rate = fdiv($_SESSION['views'], (time() - $_SESSION['starttime']));
if (($rate > $loadrate) && ($_SESSION['views'] > 50)) {
header("HTTP/1.0 429 Too Many Requests");
if (! isset($_SESSION['throttled'])) {
file_put_contents($logfile, "\n" . format_log_date() . " " . $config_name . " Too many requests from " . $_SERVER['REMOTE_ADDR'] . " throttling", FILE_APPEND);
$_SESSION['throttled'] = true;
}
exit(0);
}
if (isset($_SESSION['throttled'])) {
unset($_SESSION['throttled']);
}
}
function get_client_user_agent_info()
{
global $config_dir;
// Try to get browser info to use for extra formatting of page
$ua = strtolower($_SERVER["HTTP_USER_AGENT"]);
$devices = array(
"bot",
"spider",
"mobile",
"lynx",
"w3m",
"links",
"ipad",
"tablet"
);
$client_device = "desktop";
foreach ($devices as $device) {
if (strpos($ua, $device) !== false) {
$client_device = $device;
break;
}
}
if ($client_device == "spider") {
$client_device = "bot";
}
// Log client device if enabled by semaphore
if (file_exists($config_dir . '/devicelog.enable')) {
$client_ip = getenv("REMOTE_ADDR");
$logfile = $logdir . '/device.log';
file_put_contents($logfile, "\n" . date('M d H:i:s') . " " . $config_name . " Client: " . $client_ip . " browser: " . $client_device, FILE_APPEND);
file_put_contents($logfile, "\nFull UA: " . $ua, FILE_APPEND);
}
return $client_device;
}
function get_user_mail_auth_data($user)
{
global $spooldir;
$userdata = array();
$user = strtolower($user);
$pkey_config = get_user_config($user, "pkey");
if (! isset($_COOKIE['pkey'])) {
$_COOKIE['pkey'] = null;
}
$pkey_cookie = $_COOKIE['pkey'];
if ((! isset($_COOKIE['pkey'])) || $pkey_config == false || $pkey_cookie == false) {
return false;
}
if ($pkey_config == $pkey_cookie) {
$userfile = $spooldir . '/' . $user . '-articleviews.dat';
if (is_file($userfile)) {
$userdata = unserialize(file_get_contents($userfile));
}
return $userdata;
}
return false;
}
function write_access_log()
{
global $logdir;
$accessfile = $logdir . '/access.log';
$currentPageUrl = $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"];
file_put_contents($accessfile, "\n" . format_log_date() . " " . $currentPageUrl, FILE_APPEND);
}
function verify_gpg_signature($res, $signed_text)
{
$result = gnupg_verify($res, $signed_text, false);
if ($result == false) {
return false;
}
if ((($result[0]['summary'] > 3)) || $result[0]['validity'] == 2) {
return false; // Bad signature
} else {
return true; // Good signature
}
}
function get_next_article_number($group)
{
$ok_article = get_article_list($group);
sort($ok_article);
$local = $ok_article[key(array_slice($ok_article, - 1, 1, true))];
if (! is_numeric($local)) {
$local = 0;
}
$local = $local + 1;
if ($local < 1) {
$local = 1;
}
while (is_deleted_post($group, $local)) {
$local ++;
}
return $local;
}
function check_duplicate_msgid($msgid, $group)
{
global $spooldir, $logdir;
$found = false;
$database = $spooldir . '/articles-overview.db3';
$table = 'overview';
$dbh = overview_db_open($database, $table);
$stmt = $dbh->prepare("SELECT * FROM $table WHERE msgid=:msgid AND newsgroup=:newsgroup");
$stmt->bindParam(':msgid', $msgid);
$stmt->bindParam(':newsgroup', $group);
$stmt->execute();
while ($row = $stmt->fetch()) {
if($row['msgid'] == $msgid) {
$found = true;
}
}
$dbh = null;
$database = $spooldir . '/history.db3';
$table = 'history';
$dbh = history_db_open($database, $table);
$stmt = $dbh->prepare("SELECT * FROM $table WHERE msgid=:msgid AND newsgroup=:newsgroup");
$stmt->bindParam(':msgid', $msgid);
$stmt->bindParam(':newsgroup', $group);
$stmt->execute();
while ($row = $stmt->fetch()) {
if($row['msgid'] == $msgid) {
$found = true;
}
}
$dbh = null;
return $found;
}
function insert_article_from_array($this_article, $check_duplicates = true)
{
global $CONFIG, $config_name, $spooldir, $logdir;
$logfile = $logdir . '/spoolnews.log';
$group = $this_article['group'];
$grouppath = $path . preg_replace('/\./', '/', $group);
if ($check_duplicates) {
if (check_duplicate_msgid($this_article['mid'], $group)) {
echo "\n(newsportal)Duplicate Message-ID for: " . $group . ":" . $this_article['mid'];
file_put_contents($logfile, "\n" . format_log_date() . " " . $config_name . " Duplicate Message-ID for: " . $group . ":" . $this_article['mid'], FILE_APPEND);
return "441 Insert failed (duplicate)\r\n";
}
}
// Open articles Database
if ($CONFIG['article_database'] == '1') {
$article_dbh = article_db_open($spooldir . '/' . $group . '-articles.db3');
$article_sql = 'INSERT OR IGNORE INTO articles(newsgroup, number, msgid, date, name, subject, article, search_snippet) VALUES(?,?,?,?,?,?,?,?)';
$article_stmt = $article_dbh->prepare($article_sql);
}
// Open overview database
$database = $spooldir . '/articles-overview.db3';
$table = 'overview';
$overview_dbh = overview_db_open($database, $table);
$overview_sql = 'INSERT OR IGNORE INTO overview(newsgroup, number, msgid, date, datestring, name, subject, refs, bytes, lines, xref) VALUES(?,?,?,?,?,?,?,?,?,?,?)';
$overview_stmt = $overview_dbh->prepare($overview_sql);
// Overview
$overview_stmt->execute([
$group,
$this_article['local'],
$this_article['mid'],
$this_article['epochdate'],
$this_article['stringdate'],
$this_article['from'],
$this_article['subject'],
$this_article['references'],
$this_article['bytes'],
$this_article['lines'],
$this_article['xref']
]);
$overview_dbh = null;
$references = "";
// Articles
if ($CONFIG['article_database'] == '1') {
$article_stmt->execute([
$group,
$this_article['local'],
$this_article['mid'],
$this_article['epochdate'],
$this_article['from'],
$this_article['subject'],
$this_article['article'],
$this_article['snippet']
]);
unlink($grouppath . "/" . $this_article['local']);
$article_dbh = null;
} else {
if ($article_date > time())
$article_date = time();
touch($grouppath . "/" . $this_article['local'], $article_date);
}
echo "\nSpooling: " . $group . " " . $this_article['local'];
file_put_contents($logfile, "\n" . format_log_date() . " " . $config_name . " Spooling: " . $group . ":" . $this_article['local'], FILE_APPEND);
$status = "spooled";
$statusdate = time();
$statusreason = "imported";
add_to_history($group, $this_article['local'], $this_article['mid'], $status, $statusdate, $statusreason, $statusnotes);
}
function is_deleted_post($group, $number)
{
global $spooldir;
$database = $spooldir . '/history.db3';
$table = 'history';
$dbh = history_db_open($database, $table);
$stmt = $dbh->prepare("SELECT * FROM $table WHERE newsgroup=:newsgroup AND number=:newsnum");
$stmt->bindParam(':newsgroup', $group);
$stmt->bindParam(':newsnum', $number);
$stmt->execute();
$status = false;
while ($row = $stmt->fetch()) {
if ($row['status'] == "deleted") {
$status = "430 Article Deleted";
break;
}
}
$dbh = null;
return $status;
}
function add_to_history($group, $number, $msgid, $status, $statusdate, $statusreason = null, $statusnotes = null)
{
global $spooldir;
$history = $spooldir . '/history.db3';
$history_dbh = history_db_open($history);
$history_sql = 'INSERT OR REPLACE INTO history(newsgroup, number, msgid, status, statusdate, statusreason, statusnotes) VALUES(?,?,?,?,?,?,?)';
$history_stmt = $history_dbh->prepare($history_sql);
$history_stmt->execute([
$group,
$number,
$msgid,
$status,
$statusdate,
$statusreason,
$statusnotes
]);
$history_dbh = null;
}
function clear_history_by_group($group)
{
global $spooldir;
$history = $spooldir . '/history.db3';
$history_dbh = history_db_open($history);
$clear_stmt = $history_dbh->prepare("DELETE FROM history WHERE newsgroup=:group");
$clear_stmt->bindParam(':group', $group);
$clear_stmt->execute();
$history_dbh = null;
}
function get_db_data_from_msgid($msgid, $group)
{
global $spooldir;
$database = $spooldir . '/' . $group . '-articles.db3';
if (! is_file($database)) {
return false;
}
$articles_dbh = article_db_open($database);
$articles_query = $articles_dbh->prepare('SELECT * FROM articles WHERE msgid=:messageid');
$articles_query->execute([
'messageid' => $msgid
]);
$found = 0;
while ($row = $articles_query->fetch()) {
$found = 1;
break;
}
$dbh = null;
if ($found) {
return $row;
} else {
return false;
}
}
function get_data_from_msgid($msgid)
{
global $spooldir;
$database = $spooldir . '/articles-overview.db3';
$articles_dbh = overview_db_open($database);
$articles_query = $articles_dbh->prepare('SELECT * FROM overview WHERE msgid=:messageid');
$articles_query->execute([
'messageid' => $msgid
]);
$found = 0;
while ($row = $articles_query->fetch()) {
$found = 1;
break;
}
$dbh = null;
if ($found) {
return $row;
} else {
return false;
}
}
function prune_dir_by_days($path, $days)
{
if ($filenames = array_diff(scandir($path), array(
'..',
'.'
))) {
foreach ($filenames as $file) {
$filelastmodified = filemtime($path . $file);
if ((time() - $filelastmodified) > $days * 86400) {
if (is_file($path . $file)) {
unlink($path . $file);
}
}
}
} else {
return false;
}
return true;
}
function send_admin_message($admin, $from, $subject, $message)
{
global $config_dir, $spooldir;
if (($to = get_config_value('aliases.conf', strtolower($admin))) == false) {
$to = strtolower($admin);
}
$to = trim($to);
$from = $to;
$database = $spooldir . '/mail.db3';
$dbh = mail_db_open($database);
if (! $dbh) {
echo "Database error\n";
return false;
}
$date = time();
$msgid = '<' . md5(strtolower($to) . strtolower($from) . strtolower($subject) . strtolower($message)) . '>';
$sql = 'INSERT OR IGNORE INTO messages(msgid, mail_from, rcpt_to, rcpt_target, date, subject, message, from_hide, to_hide, mail_viewed, rcpt_viewed) VALUES(?,?,?,?,?,?,?,?,?,?,?)';
$stmt = $dbh->prepare($sql);
$target = "local";
$mail_viewed = "true";
$rcpt_viewed = null;
$q = $stmt->execute([
$msgid,
$from,
$to,
$target,
$date,
$subject,
$message,
null,
null,
false,
false
]);
$dbh = null;
return true;
}