HTTP Gateway * Download: https://news.novabbs.com/getrslight * * 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 */ /* * Shows the little menu on the thread.php where you can select the * different pages with the articles on it */ function thread_pageselect($group, $article_count, $first) { global $articles_per_page, $file_thread, $file_framethread, $name; global $text_thread, $thread_show; $pages = ceil($article_count / $articles_per_page); if ($article_count > $articles_per_page) echo $text_thread["pages"]; for ($i = 0; $i < $pages; $i ++) { // echo '['; if ($first != $i * $articles_per_page + 1) { echo ''; } else { // echo 'PAGE: '.$i.' OF: '.$pages; } // echo $i+1; if ($i == $pages - 1) { // echo $article_count; } else { // echo ($i+1)*$articles_per_page; } if ($first != $i * $articles_per_page + 1) { echo $i + 1; echo ''; } else { echo '' . strval($i + 1) . ''; // echo '] '; } } } /* * Load a thread from disk * * $group: name of the newsgroup, is needed to create the filename * * returns: an array of headerType containing the thread. */ function thread_cache_load($group) { global $spooldir, $compress_spoolfiles; $database = $spooldir . '/' . $group . '-data.db3'; $table = "threads"; if ($dbh = threads_db_open($database, $table)) { $stmt = $dbh->prepare("SELECT * FROM $table"); $stmt->execute(); while ($row = $stmt->fetch()) { $headers = unserialize($row['headers']); break; } $dbh = null; } return ($headers); } /* * Save the thread to disk * * $header: is an array of headerType containing the thread * $group: name of the newsgroup, is needed to create the filename */ function thread_cache_save($headers, $group) { global $spooldir, $compress_spoolfiles, $logdir, $config_name; $logfile = $logdir . '/newsportal.log'; $database = $spooldir . '/' . $group . '-data.db3'; $table = "threads"; if ($dbh = threads_db_open($database, $table)) { $drop = 'DROP TABLE IF EXISTS threads'; $drop_stmt = $dbh->prepare($drop); $insert_sql = 'INSERT INTO ' . $table . '(headers) VALUES(?)'; $insert_stmt = $dbh->prepare($insert_sql); $dbh->beginTransaction(); $drop_stmt->execute(); $dbh->exec("CREATE TABLE IF NOT EXISTS $table( id INTEGER PRIMARY KEY, headers TEXT, unique (headers))"); $insert_stmt->execute([ serialize($headers) ]); $dbh->commit(); $dbh = null; } } /* * remove an article from the overview-file * is needed, when article has been canceled, the article is still * in the thread spool on disc and someone wants to read this article. * the message_read function can now call this function to remove * the article. */ function thread_cache_removearticle($group, $id) { global $logdir, $config_name; $logfile = $logdir . '/newsportal.log'; $thread = thread_cache_load($group); if (! $thread) return false; $changed = false; foreach ($thread as $value) { if (($value->number == $id) || ($value->id == $id)) { // found to be deleted article // now lets rebuild the tree... if (isset($value->answers)) foreach ($value->answers as $key => $answer) { if (isset($thread[$answer])) { $thread[$answer]->isAnswer = false; } } if (isset($value->references)) foreach ($value->references as $reference) { if (isset($thread[$reference]->answers)) { $search = array_search($value->id, $thread[$reference]->answers); if (! ($search === false)) unset($thread[$reference]->answers[$search]); } } file_put_contents($logfile, "\n" . format_log_date() . " " . $config_name . " Trimming: " . $id . " from " . $group, FILE_APPEND); unset($thread[$value->id]); $changed = true; break; } } if ($changed) thread_cache_save($thread, $group); } /* * function readArticles(&$ns,$groupname,$articleList) { * for($i = 0; $i <= count($articleList)-1 ; $i++) { * $temp=read_header($ns,$articleList[$i]); * $articles[$temp->id] = $temp; * } * return $articles; * } */ /* * interpret and decode one line of overview-data from the newsserver and * put it into an headerType * * $line: the data to be interpreted * $overviewformat: the format of an overview-line, given by * thread_overview_read() * $groupname: the name of the newsgroup * * returns: headerType containing the data */ function thread_overview_interpret($line, $overviewformat, $groupname) { $return = ""; $overviewfmt = explode("\t", $overviewformat); echo " "; // keep the connection to the webbrowser alive flush(); // while generating the message-tree // $over=explode("\t",$line,count($overviewfmt)-1); $over = explode("\t", $line); // $article=new headerType; $article = (object) []; for ($i = 0; $i < count($overviewfmt) - 1; $i ++) { if ($overviewfmt[$i] == "Subject:") { $subject = preg_replace('/\[doctalk\]/i', '', headerDecode($over[$i + 1])); $article->isReply = splitSubject($subject); $article->subject = $subject; } if ($overviewfmt[$i] == "Date:") { $article->date = getTimestamp($over[$i + 1]); } if ($overviewfmt[$i] == "From:") { $fromline = address_decode(headerDecode($over[$i + 1]), "nowhere"); $article->from = $fromline[0]["mailbox"] . "@" . $fromline[0]["host"]; $article->username = $fromline[0]["mailbox"]; if (! isset($fromline[0]["personal"])) { $article->name = $fromline[0]["mailbox"]; if (strpos($article->name, '%')) { $article->name = substr($article->name, 0, strpos($article->name, '%')); } $article->name = strtr($article->name, '_', ' '); } else { $article->name = $fromline[0]["personal"]; } } if ($overviewfmt[$i] == "Message-ID:") $article->id = $over[$i + 1]; if (($overviewfmt[$i] == "References:") && ($over[$i + 1] != "")) { $article->references = explode(" ", $over[$i + 1]); } } foreach ($article->references as &$refs) { if (! strcmp($article->id, $refs)) { $refs = ""; } } $article->number = $over[0]; $article->isAnswer = false; return ($article); } /* * read the overview-format from the newsserver. This data is used * by thread_overview_interpret */ function thread_overview_read(&$ns) { $overviewfmt = array(); fputs($ns, "LIST overview.fmt\r\n"); // find out the format of the $tmp = line_read($ns); // xover-command if (substr($tmp, 0, 3) == "215") { $line = line_read($ns); while (strcmp($line, ".") != 0) { // workaround for braindead CLNews newsserver if ($line == "Author:") $overviewfmt[] = "From:"; else $overviewfmt[] = $line; $line = line_read($ns); } } else { // some stupid newsservers, like changi, don't send their overview // format // let's hope, that the format is like that from INN $overviewfmt = array( "Subject:", "From:", "Date:", "Message-ID:", "References:", "Bytes:" ); } $overviewformat = implode("\t", $overviewfmt); return $overviewformat; } function thread_mycompare($a, $b) { global $thread_sort_order, $thread_sort_type; if ($thread_sort_type != "thread") { $r = ($a->date < $b->date) ? - 1 : 1; if ($a->date == $b->date) $r = 0; } else { $r = ($a->date_thread < $b->date_thread) ? - 1 : 1; if ($a->date_thread == $b->date_thread) $r = 0; } return $r * $thread_sort_order; } /* * this function loads the (missing parts of the) thread from the newsserver. * it also loads the thread from the disk cache to detect which parts * are missing and merges this data with the parts from the * newsserver. * if it detects that the newsserver made major changes in the groups, * for example if it expired parts of the group or reset its counters, * this function deletes the cached data and make a complete rebuild. * * $ns: handle of the connection to the newsserver * $groupname: name of the newsgroup * $poll: if set to 1, this function works in polling-mode, which * means, that it also read every article from the newsserver. * This makes only sense if the article cache is activated */ function thread_load_newsserver(&$ns, $groupname, $poll) { global $spooldir, $logdir, $maxarticles, $maxfetch, $initialfetch, $maxarticles_extra, $config_name; global $text_error, $text_thread, $compress_spoolfiles, $server, $CONFIG; global $www_charset, $iconv_enable, $thread_show, $thread_sort_order; $logfile = $logdir . '/newsportal.log'; $maxfetch = 0; $idstring = "0.36," . $server . "," . $compress_spoolfiles . "," . $maxarticles . "," . $maxarticles_extra . "," . $maxfetch . "," . $initialfetch . "," . $www_charset . ',' . $iconv_enable . ',' . $thread_show["replies"]; if ($CONFIG['enable_nntp'] == '1') { $overviewfmt = array( "Subject:", "From:", "Date:", "Message-ID:", "References:", "Bytes:" ); } else { $overviewformat = thread_overview_read($ns); } $spoolfilename = $spooldir . '/' . $groupname . '-data.db3'; fputs($ns, "GROUP $groupname\r\n"); // select a group $groupinfo = explode(" ", line_read($ns)); if (substr($groupinfo[0], 0, 1) != 2) { echo "

" . $text_error["error:"] . "

"; echo "

" . $text_thread["no_such_group"] . "

"; flush(); } else { $infofilename = $spooldir . "/" . $groupname . "-info.txt"; // lets find out, in which mode wie want to read articles: // w: complete rebuild of the group-info file // a: add new articles to the group-info file // n: there are no new articles, no rebuild or actualisation // t: low watermark increased, remove expired articles $spoolopenmodus = "n"; // if the group-info file doesn't exist: create it if (! ((file_exists($infofilename)) && (file_exists($spoolfilename)))) { file_put_contents($logfile, "\n" . format_log_date() . " " . $config_name . " Threads database does not exist. Rebuilding " . $groupname, FILE_APPEND); $spoolopenmodus = "w"; } else { $infofile = fopen($infofilename, "r"); $oldid = fgets($infofile, 100); if (trim($oldid) != $idstring) { echo "\n"; file_put_contents($logfile, "\n" . format_log_date() . " " . $config_name . " config changed, Rebuilding " . $groupname, FILE_APPEND); $spoolopenmodus = "w"; } $oldgroupinfo = explode(" ", trim(fgets($infofile, 200))); fclose($infofile); if ($groupinfo[3] < $oldgroupinfo[1]) { file_put_contents($logfile, "\n" . format_log_date() . " " . $config_name . " high watermark reduced. Rebuilding " . $groupname, FILE_APPEND); $spoolopenmodus = "w"; } if ($maxarticles == 0) { if ($groupinfo[2] != $oldgroupinfo[0]) $spoolopenmodus = "w"; } else { if ($groupinfo[2] > $oldgroupinfo[0]) { file_put_contents($logfile, "\n" . format_log_date() . " " . $config_name . " low watermark increased. Trimming " . $groupname, FILE_APPEND); $spoolopenmodus = "t"; } } // if the high watermark increased, add articles to the existing spool if (($spoolopenmodus == "n") && ($groupinfo[3] > $oldgroupinfo[1])) { file_put_contents($logfile, "\n" . format_log_date() . " " . $config_name . " Adding articles to " . $groupname, FILE_APPEND); $spoolopenmodus = "a"; } } if ($spoolopenmodus == "a") { $firstarticle = $oldgroupinfo[1] + 1; $lastarticle = $groupinfo[3]; } if ($spoolopenmodus == "w") { $firstarticle = $groupinfo[2]; $lastarticle = $groupinfo[3]; } if ($spoolopenmodus != "n") { if ($maxarticles != 0) { if ($spoolopenmodus == "w") { $firstarticle = $lastarticle - $maxarticles + 1; if ($firstarticle < $groupinfo[2]) $firstarticle = $groupinfo[2]; } else { if ($lastarticle - $oldgroupinfo[0] + 1 > $maxarticles + $maxarticles_extra) { $firstarticle = $lastarticle - $maxarticles + 1; $spoolopenmodus = "w"; } } } if (($maxfetch != 0) && (($lastarticle - $firstarticle + 1) > $maxfetch)) { if ($spoolopenmodus == "w") { $tofetch = ($initialfetch != 0) ? $initialfetch : $maxfetch; $lastarticle = $firstarticle + $tofetch - 1; } else { $lastarticle = $firstarticle + $maxfetch - 1; } } } echo "\n"; // load the old spool-file, if we do not have a complete rebuild if (($spoolopenmodus != "w") && ($spoolopenmodus != "t")) { $headers = thread_cache_load($groupname); } if ($spoolopenmodus == 't') { $count = 0; for ($i = $oldgroupinfo[0]; $i < $groupinfo[2]; $i ++) { thread_cache_removearticle($groupname, $i); $count ++; } // Save the info-file $oldinfo = file($infofilename); $oldstring = explode(" ", $oldinfo[1]); $infofile = fopen($infofilename, "w"); fputs($infofile, $idstring . "\n"); fputs($infofile, $groupinfo[2] . " " . $oldstring[1] . " " . ($oldgroupinfo[2] - $count) . "\r\n"); fclose($infofile); $spoolopenmodus = "n"; } // read articles from the newsserver if ($spoolopenmodus != "n") { // order the article overviews from the newsserver if ($firstarticle == 0 && $lastarticle == 0) { fclose($ns); return false; } fputs($ns, "XOVER " . $firstarticle . "-" . $lastarticle . "\r\n"); $tmp = line_read($ns); // have the server accepted our order? if (substr($tmp, 0, 3) == "224") { $line = line_read($ns); // read overview by overview until the data ends while ($line != ".") { // parse the output of the server... $article = thread_overview_interpret($line, $overviewformat, $groupname); // ... and save it in our data structure $article->threadsize ++; $article->date_thread = $article->date; $headers[$article->id] = $article; // if we are in poll-mode: print status information and // decode the article itself, so it can be saved in the article // cache if ($poll) { echo $article->number . ", "; flush(); message_read($article->number, 0, $groupname); } // read the next line from the newsserver $line = line_read($ns); } // write information about the last article to the spool-directory $infofile = fopen($spooldir . "/" . $groupname . "-lastarticleinfo.dat", "w"); $lastarticleinfo['from'] = $article->from; $lastarticleinfo['date'] = $article->date; $lastarticleinfo['name'] = $article->name; fputs($infofile, serialize($lastarticleinfo)); fclose($infofile); } if ((isset($headers)) && (count($headers) > 0)) { foreach ($headers as $c) { if (($c->isAnswer == false) && (isset($c->references))) { // is the article an answer to an // other article? // try to find a matching article to one of the references $refmatch = false; foreach ($c->references as $reference) { if (isset($headers[$reference])) { $refmatch = $reference; } } // have we found an article, to which this article is an answer? if ($refmatch != false) { $c->isAnswer = true; $c->bestreference = $refmatch; $headers[$c->id] = $c; // the referenced article get the ID af this article as in // its answers-array $headers[$refmatch]->answers[] = $c->id; // propagate down the number of articles in this thread $d = &$headers[$c->bestreference]; do { $d->threadsize += $c->threadsize; $d->date_thread = max($c->date, $d->date_thread); } while (($headers[$d->bestreference]) && (isset($d->bestreference)) && ($d = &$headers[$d->bestreference])); } } } reset($headers); // sort the articles if (($thread_sort_order != 0) && (count($headers) > 0)) uasort($headers, 'thread_mycompare'); // Save the thread-informations thread_cache_save($headers, $groupname); // Save the info-file $infofile = fopen($infofilename, "w"); if ($spoolopenmodus == "a") $firstarticle = $oldgroupinfo[0]; fputs($infofile, $idstring . "\n"); fputs($infofile, $firstarticle . " " . $lastarticle . " " . count($headers) . "\r\n"); fclose($infofile); } // remove cached articles that are not in this group // (expired on the server or canceled) $dirhandle = opendir($spooldir); while ($cachefile = readdir($dirhandle)) { if (substr($cachefile, 0, strlen($groupname) + 1) == $groupname . "_") { $num = preg_replace('/^(.*)_(.*)\.(.*)$/i', '\2', $cachefile); if (($num < $firstarticle) || ($num > $lastarticle)) unlink($spooldir . '/' . $cachefile); } // remove the html cache files of this group if ((substr($cachefile, strlen($cachefile) - 5) == ".html") && (substr($cachefile, 0, strlen($groupname) + 1) == $groupname . "-")) unlink($spooldir . '/' . $cachefile); } } return ((isset($headers)) ? $headers : false); } } /* * Read the Overview. * Format of the overview-file: * message-id * date * subject * author * email * references * * $groupname: name of the newsgroup * $readmode: if set to 0, this function only reads data from the * newsserver, if there exists no cached data for this group * $poll: polling mode, see description at thread_load_newsserver() */ function thread_load($groupname, $readmode = 1, $poll = false) { global $text_error, $maxarticles, $server, $port; global $spooldir, $thread_sort_order, $cache_thread; global $articles_per_page; if (! testGroup($groupname)) { echo $text_error["read_access_denied"]; return; } // first assume that we have to query the newsserver $query_ns = true; // name of the file that indicates by it's timestamp when the // last query of the newsserver was $cachefile = $spooldir . '/' . $groupname . '-cache.txt'; // should we load the data only from cache if it's recent enough, or // do we have to query the newsserver every time? if (is_file($cachefile)) { if (filemtime($cachefile) + $cache_thread < time()) { if (is_file($spooldir . '/' . $groupname . '-1-' . $articles_per_page . '.html')) { unlink($spooldir . '/' . $groupname . '-1-' . $articles_per_page . '.html'); } unlink($cachefile); } } if ($cache_thread > 0) { if (file_exists($cachefile)) { // cached file exists and is new enough. so lets read it out. $articles = thread_cache_load($groupname); return $articles; $query_ns = false; } } // do we have to query the newsserver? if ($query_ns) { // look if there is new data on the newsserver $ns = nntp_open($server, $port); if ($ns == false) return false; if (($ns != false) && ($readmode > 0)) $articles = thread_load_newsserver($ns, $groupname, $poll); if ((isset($articles)) && ($articles)) { // write the file which indicates the time of the last newsserver query touch($cachefile); return $articles; } else { // uh, we didn't get articles from the newsservers... // for now, return false. but it would also make sense to get // the articles from the cache then... return false; } nntp_close($ns); } } /* * Remove re:, aw: etc. from a subject. * * $subject: a string containing the complete Subject * * The function removes the re:, aw: etc. from $subject end returns true * if it removed anything, and false if not. */ function splitSubject(&$subject) { $s = preg_replace('/^(odp:|aw:|re:|re\[2\]:| )+/i', '', $subject); $return = ($s != $subject); $subject = $s; return $return; } function str_change($str, $pos, $char) { return (substr($str, 0, $pos) . $char . substr($str, $pos + 1, strlen($str) - $pos)); } /* * calculate the graphic representation of the thread */ function thread_show_calculate($newtree, $depth, $num, $liste, $c) { global $thread_show; // displays the replies to an article? if (! $thread_show["replies"]) { // no if ((isset($c->answers[0])) && (count($c->answers) > 0)) $newtree .= "o"; else $newtree .= "o"; } else { // yes, display the replies if ((isset($c->answers[0])) && (count($c->answers) > 0)) { $newtree .= "*"; } else { if ($depth == 1) { $newtree .= "o"; } else { $newtree .= "-"; } } if (($num == count($liste) - 1) && ($depth > 1)) { $newtree = str_change($newtree, $depth - 2, "`"); } } return ($newtree); } /* * Format the message-tree * Zeichen im Baum: * o : leerer Kasten k1.gif * * : Kasten mit Zeichen drin k2.gif * i : vertikale Linie I.gif * - : horizontale Linie s.gif * + : T-Stueck T.gif * ` : Winkel L.gif */ function thread_show_treegraphic($newtree) { global $imgdir; $return = ""; for ($o = 0; $o < strlen($newtree); $o ++) { $return .= 'isReply) { $re = "Re: "; } else { $re = ""; } // is the current article to be highlighted? if ((is_array($highlightids))) { if ((in_array($c->id, $highlightids)) || (in_array($c->number, $highlightids))) { $highlight = true; } else { $highlight = false; } } else { $highlight = false; } if ($highlight) $return = ''; else { $return = 'number) . '">'; } $return .= $re . htmlspecialchars(mb_substr(trim($c->subject), 0, $thread_maxSubject)); if ($highlight) $return .= ''; else $return .= ''; if ($thread_show["latest"] == true) { $newdate = $c->date + ($offset * 60); unset($ts); $fromoutput = explode("<", html_entity_decode($c->name)); if (strlen($fromoutput[0]) < 1) { $started = $fromoutput[1]; } else { $started = $fromoutput[0]; } $return .= '
'; $return .= '

By: ' . create_name_link($started, $c->from) . ' on ' . date("D, j M Y", $newdate) . '

'; $return .= '
'; } return ($return); } /* * colorize the date inside the thread */ function thread_format_date_color($date) { global $age_count, $age_time, $age_color; $return = ""; $currentTime = time(); if ($age_count > 0) for ($t = $age_count; $t >= 1; $t --) { if ($currentTime - $date < $age_time[$t]) $color = $age_color[$t]; } if (isset($color)) return $color; else return ""; } /* * format the date inside the thread */ function thread_format_date($c) { global $age_count, $age_time, $age_color, $thread_show, $thread_show; $return = ""; $currentTime = time(); $color = ""; // show the date of the individual article or of the latest article // in the thread? if ($thread_show["lastdate"]) $date = $c->date_thread; else $date = $c->date; if ($age_count > 0) for ($t = $age_count; $t >= 1; $t --) if ($currentTime - $date < $age_time[$t]) $color = $age_color[$t]; if ($color != "") $return .= ''; $return .= date("d M ", $date); // format the date if ($color != "") $return .= ''; return ($return); } /* * format the author inside the thread */ function thread_format_author($c, $group = '', $lastmessage = 1) { global $thread_show, $anonym_address; if ($lastmessage == 1) { return thread_format_lastmessage($c, $group, $lastmessage); } // if the address the anonymous address, only return the name if ($c->from == $anonym_address) return $c->name; $return = ""; if ($thread_show["authorlink"]) $return .= ''; if (trim($c->name) != "") { $return .= htmlspecialchars(trim($c->name)); } else { if (isset($c->username)) { $s = strpos($c->username, "%"); if ($s != false) { $return .= htmlspecialchars(substr($c->username, 0, $s)); } else { $return .= htmlspecialchars($c->username); } } } if ($thread_show["authorlink"]) $return .= ""; return ($return); } /* * format the last message info in the thread */ function thread_format_lastmessage($c, $group = '') { global $thread_show, $anonym_address, $spooldir, $CONFIG; $ovfound = 0; if ($CONFIG['article_database'] == '1') { $database = $spooldir . '/' . $group . '-articles.db3'; $table = 'articles'; if (is_file($database)) { $dbh = article_db_open($database, $table); $stmt = $dbh->prepare("SELECT * FROM $table WHERE date=:date ORDER BY date DESC"); $stmt->bindParam(':date', $c->date_thread); $stmt->execute(); if ($found = $stmt->fetch()) { $ovfound = 1; } ; $dbh = null; } } else { // Tradspool $database = $spooldir . '/articles-overview.db3'; $table = 'overview'; $dbh = overview_db_open($database, $table); $stmt = $dbh->prepare("SELECT * FROM $table WHERE newsgroup=:newsgroup AND date=:date ORDER BY date DESC"); $stmt->bindParam(':newsgroup', $group); $stmt->bindParam(':date', $c->date_thread); $stmt->execute(); if ($found = $stmt->fetch()) { $ovfound = 1; } ; $dbh = null; } $fromline = address_decode(headerDecode($found['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($c->name)); if (strlen($fromoutput[0]) < 1) { $poster_name = $fromoutput[1]; } else { $poster_name = $fromoutput[0]; } } if ($ovfound == 1) { $url = 'article-flat.php?id=' . $found['number'] . '&group=' . urlencode($group) . '#' . $found['number']; $return = '

' . get_date_interval(date("D, j M Y H:i T", $c->date_thread)) . ''; } else { $return = '

' . get_date_interval(date("D, j M Y H:i T", $c->date_thread)) . '

'; } $return .= '

By: ' . create_name_link($poster_name, $name_from) . '

'; return ($return); } /* * Displays a part of the thread. This function is recursively called * It is used by thread_show */ function thread_show_recursive(&$headers, &$liste, $depth, $tree, $group, $article_first = 0, $article_last = 0, &$article_count, $highlight = false, $lastmessage = 1) { global $thread_treestyle; global $thread_show, $imgdir; global $file_article, $thread_maxSubject; global $age_count, $age_time, $age_color, $spooldir; global $frame_article; $output = ""; if ($thread_treestyle == 3) $output .= "\n"; return $output; } /* * Displays the Head (table tags, headlines etc.) of a thread */ function thread_show_head($lastmessage = 1) { global $thread_show, $thread_showTable; global $text_thread, $thread_treestyle; if (($thread_treestyle == 2) || ($thread_treestyle == 6) || ($thread_treestyle == 7)) { echo ''; echo '' . "\n"; if ($thread_show["date"]) echo '"; if ($thread_show["subject"]) echo '"; if ($thread_show["threadsize"]) echo '"; if ($thread_show["author"]) { if ($lastmessage == 1) { echo '\n"; } else { echo '\n"; } } echo "\n"; } else { if ($thread_treestyle == 1) echo "
' . $text_thread["date"] . " ' . $text_thread["subject"] . "' . $text_thread["threadsize"] . "' . $text_thread["lastmessage"] . "' . $text_thread["author"] . "
\n"; } else { if ($thread_treestyle == 1) echo "\n"; } } /* * Shows a complete thread * * $headers: The thread to be displayed * $group: name of the newsgroup * $article_first: Number of the first article to be displayed * $article_last: last article */ function thread_show(&$headers, $group, $article_first = 0, $article_last = 0, $lastmessage = 1) { global $spooldir, $text_thread; $article_count = 0; if ($headers == false) { echo $text_thread["no_articles"]; } else { // exists a cached html-output? $filename = $spooldir . "/" . $group . "-" . $article_first . "-" . $article_last . ".html"; if (! file_exists($filename)) { // no, we need to create a new html-output $output = ""; reset($headers); $c = current($headers); for ($i = 0; $i <= count($headers) - 1; $i ++) { // create the array $liste if ($c->isAnswer == false) { // where are all the articles $liste[] = $c->id; // in that don't have } // references $c = next($headers); } reset($liste); if (count($liste) > 0) { $output .= thread_show_recursive($headers, $liste, 1, "", $group, $article_first, $article_last, $article_count, $lastmessage); } // cache the html-output $file = fopen($filename, "w"); fputs($file, $output); fclose($file); } else { // yes, a cached output exists, load it! $file = fopen($filename, "r"); $output = fread($file, filesize($filename)); fclose($file); } thread_show_head($lastmessage); echo $output; thread_show_tail(); } } /* * returns the article-numbers of all articles in a given subthread * * $id: article number or message id of a article in a subthread * $thread: thread data, as returned by thread_cache_load() */ function thread_getsubthreadids($id, $thread) { // recursive helper function to walk through the subtree function thread_getsubthreadids_recursive($id) { global $thread; $answers = array( $thread[$id]->number ); // has this article answers? if (isset($thread[$id]->answers)) { // walk through the answers foreach ($thread[$id]->answers as $answer) { $answers = array_merge($answers, thread_getsubthreadids_recursive($answer)); } } $answers = array_map('json_encode', $answers); $answers = array_unique($answers); $answers = array_map('json_decode', $answers); return $answers; } // echo htmlspecialchars(print_r($thread,true)); // exists the article $id? if (! isset($thread[$id])) return false; // "rewind" the subthread to the first article in the subthread $current = $id; flush(); while (isset($thread[$id]->references)) { foreach ($thread[$id]->references as $reference) { if ((trim($reference) != '') && (isset($thread[$reference]))) { $id = $reference; continue 2; } } break; } // walk through the thread and fill up $subthread // use the recursive helper-function thread_getsubthreadids_recursive $subthread = thread_getsubthreadids_recursive($id); return $subthread; } ?>