From 3a2037dcfed5f71d57dbe1f615952ad605f3152a Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Sun, 11 Jun 2006 07:27:10 +0000 Subject: [PATCH] oyo --- newsgroup/lib/check.php | 6 + newsgroup/lib/message.inc.php | 539 +++++++++++++++++ newsgroup/lib/post.inc.php | 195 +++++++ newsgroup/lib/thread.inc.php | 1030 +++++++++++++++++++++++++++++++++ newsgroup/lib/types.inc.php | 79 +++ newsgroup/lib/validator.inc | 243 ++++++++ 6 files changed, 2092 insertions(+) create mode 100755 newsgroup/lib/check.php create mode 100755 newsgroup/lib/message.inc.php create mode 100755 newsgroup/lib/post.inc.php create mode 100755 newsgroup/lib/thread.inc.php create mode 100755 newsgroup/lib/types.inc.php create mode 100755 newsgroup/lib/validator.inc diff --git a/newsgroup/lib/check.php b/newsgroup/lib/check.php new file mode 100755 index 0000000..05460a2 --- /dev/null +++ b/newsgroup/lib/check.php @@ -0,0 +1,6 @@ +$iconv_enable=false + in config.inc.php to disable automatic charset recoding.'); +?> \ No newline at end of file diff --git a/newsgroup/lib/message.inc.php b/newsgroup/lib/message.inc.php new file mode 100755 index 0000000..7a44c4b --- /dev/null +++ b/newsgroup/lib/message.inc.php @@ -0,0 +1,539 @@ +header=parse_header($rawheader); + // Now we know if the message is a mime-multipart message: + $content_type=split("/",$message->header->content_type[0]); + if ($content_type[0]=="multipart") { + $message->header->content_type=array(); + // We have multible bodies, so we split the message into its parts + $boundary="--".$message->header->content_type_boundary; + // lets find the first part + while($rawmessage[$i] != $boundary) + $i++; + $i++; + $part=array(); + while($i<=$count_rawmessage) { + if (($rawmessage[$i]==$boundary) || ($i==$count_rawmessage-1) || + ($rawmessage[$i]==$boundary.'--')) { + $partmessage=message_parse($part); + // merge the content-types of the message with those of the part + for ($o=0; $oheader->content_type); $o++) { + $message->header->content_type[]= + $partmessage->header->content_type[$o]; + $message->header->content_type_charset[]= + $partmessage->header->content_type_charset[$o]; + $message->header->content_type_name[]= + $partmessage->header->content_type_name[$o]; + $message->header->content_type_format[]= + $partmessage->header->content_type_format[$o]; + $message->body[]=$partmessage->body[$o]; + } + $part=array(); + } else { + if ($i<$count_rawmessage) + $part[]=$rawmessage[$i]; + } + if ($rawmessage[$i]==$boundary.'--') break; + $i++; + } + // Is this a multipart/alternative multipart-message? Do we have to + // delete all non plain/text parts? + if (($attachment_delete_alternative) && + ($content_type[1]=="alternative")) { + $plaintext=false; + for ($o=0; $oheader->content_type); $o++) { + if ($message->header->content_type[$o]=="text/plain") + $plaintext=true; // we found at least one text/plain + } + if ($plaintext) { // now we can delete the other parts + for ($o=0; $oheader->content_type); $o++) { + if ($message->header->content_type[$o]!="text/plain") { + unset($message->header->content_type[$o]); + unset($message->header->content_type_name[$o]); + unset($message->header->content_type_charset[$o]); + unset($message->header->content_type_format[$o]); + unset($message->body[$o]); + } + } + } + } + } else { + // No mime-attachments in the message: + $body=""; + $uueatt=0; // as default we have no uuencoded attachments + for($i++;$i<$count_rawmessage; $i++) { + // do we have an inlay uuencoded file? + if ((strtolower(substr($rawmessage[$i],0,5))!="begin") || + ($attachment_uudecode==false)) { + $body.=$rawmessage[$i]."\n"; + // yes, it seems, we have! + } else { + $old_i=$i; + $uue_infoline_raw=$rawmessage[$i]; + $uue_infoline=explode(" ",$uue_infoline_raw); + $uue_data=""; + $i++; + while($rawmessage[$i]!="end") { + if (strlen(trim($rawmessage[$i])) > 2) + $uue_data.=$rawmessage[$i]."\n"; + $i++; + } + // now write the data in an attachment + $uueatt++; + $message->body[$uueatt]=uudecode($uue_data); + $message->header->content_type_name[$uueatt]=""; + for ($o=2; $oheader->content_type_name[$uueatt].=$uue_infoline[$o]; + $message->header->content_type[$uueatt]= + get_mimetype_by_filename($message->header->content_type_name[$uueatt]); + } + } + //if ($message->header->content_type[0]=="text/plain") { + $body=decode_body($body,$message->header->content_transfer_encoding); + $body=recode_charset($body, + $message->header->content_type_charset[0], + $www_charset); + if ($body=="") $body=" "; + //} + $message->body[0]=$body; + } + if (!isset($message->header->content_type_charset)) + $message->header->content_type_charset=array($www_charset); + if (!isset($message->header->content_type_name)) + $message->header->content_type_name=array("unnamed"); + if (!isset($message->header->content_type_format)) + $message->header->content_type_format=array("fixed"); + for ($o=0; $obody); $o++) { + if (!isset($message->header->content_type_charset[$o])) + $message->header->content_type_charset[$o]=$www_charset; + if (!isset($message->header->content_type_name[$o])) + $message->header->content_type_name[$o]="unnamed"; + if (!isset($message->header->content_type_format[$o])) + $message->header->content_type_format[$o]="fixed"; + } + return $message; +} + + +/* + * read an article from the newsserver or the spool-directory + * + * $id: the Message-ID of an article + * $bodynum: the number of the attachment: + * -1: return only the header without any bodies or attachments. + * 0: the body + * 1: the first attachment... + * + * The function returns an article as an messageType or false if the article + * doesn't exists on the newsserver or doesn't contain the given + * attachment. + */ +function message_read($id,$bodynum=0,$group="") { + global $cache_articles,$spooldir,$text_error,$ns; + if (!testGroup($group)) { + echo $text_error["read_access_denied"]; + return; + } + $message = new messageType; + if ((isset($cache_articles)) && ($cache_articles == true)) { + // Try to load a cached article + if ((ereg('^[0-9]+$',$id)) && ($group != '')) + $filename=$group.'_'.$id; + else + $filename=base64_encode($id); + $cachefilename_header=$spooldir."/".$filename.'.header'; + $cachefilename_body=$spooldir."/".$filename.'.body'; + if (file_exists($cachefilename_header)) { + $cachefile=fopen($cachefilename_header,"r"); + $message->header=unserialize(fread($cachefile,filesize($cachefilename_header))); + fclose($cachefile); + } else { + unset($message->header); + } + // Is a non-existing attachment of an article requested? + if ((isset($message->header)) && + ($bodynum!= -1) && + (!isset($message->header->content_type[$bodynum]))) + return false; + if ((file_exists($cachefilename_body.$bodynum)) && + ($bodynum != -1)) { + $cachefile=fopen($cachefilename_body.$bodynum,"r"); + $message->body[$bodynum]= + fread($cachefile,filesize($cachefilename_body.$bodynum)); + fclose($cachefile); + } + } + if ((!isset($message->header)) || + ((!isset($message->body[$bodynum])) && + ($bodynum != -1))) { + if (!isset($ns)) { + $ns=nntp_open(); + } + if ($group != "") { + fputs($ns,"GROUP ".$group."\r\n"); + $line=line_read($ns); + } + fputs($ns,'ARTICLE '.$id."\r\n"); + $line=line_read($ns); + if (substr($line,0,3) != "220") { + // requested article doesn't exist on the newsserver. Now we + // should check, if the thread stored in the spool-directory + // also doesnt't contain that article... + thread_cache_removearticle($group,$id); + return false; + } + $rawmessage=array(); + $line=line_read($ns); + while(strcmp($line,".") != 0) { + $rawmessage[]=$line; + $line=line_read($ns); + } + $message=message_parse($rawmessage); + if (ereg('^[0-9]+$',$id)) $message->header->number=$id; + // write header, body and attachments to the cache + if ((isset($cache_articles)) && ($cache_articles == true)) { + $cachefile=fopen($cachefilename_header,"w"); + if ($cachefile) { + fputs($cachefile,serialize($message->header)); + } + fclose($cachefile); + for ($i=0; $iheader->content_type); $i++) { + if (isset($message->body[$i])) { + $cachefile=fopen($cachefilename_body.$i,"w"); + fwrite($cachefile,$message->body[$i]); + fclose($cachefile); + } + } + } + } + return $message; +} + +function textwrap($text, $wrap=80, $break="\n",$maxlen=false){ + $len = strlen($text); + if ($len > $wrap) { + $h = ''; // massaged text + $lastWhite = 0; // position of last whitespace char + $lastChar = 0; // position of last char + $lastBreak = 0; // position of last break + // while there is text to process + while ($lastChar < $len && (($maxlen==false) || (strlen($h)<$maxlen))) { + $char = substr($text, $lastChar, 1); // get the next character + // if we are beyond the wrap boundry and there is a place to break + if (($lastChar - $lastBreak > $wrap) && ($lastWhite > $lastBreak)) { + $h .= substr($text, $lastBreak, ($lastWhite - $lastBreak)) . $break; + $lastChar = $lastWhite + 1; + $lastBreak = $lastChar; + } + // You may wish to include other characters as valid whitespace... + if ($char == ' ' || $char == chr(13) || $char == chr(10)) { + $lastWhite = $lastChar; // note the position of the last whitespace + } + $lastChar = $lastChar + 1; // advance the last character position by one + } + $h .= substr($text, $lastBreak); // build line + } else { + $h = $text; // in this case everything can fit on one line + } + return $h; +} +/* + * Displays a (Sub)-Thread. Is used in article.php + * + * $id: Message-ID (not number!) of an article in the thread + * $group: name of the newsgroup + */ +function message_thread($id,$group,$thread,$highlightids=false) { + $current=$id; + // set the highlightid, if not set + if(!$highlightids) + $highlightids=array($current); + flush(); + // find the first article in the subthread of $id + while(isset($thread[$id]->references)) { + foreach($thread[$id]->references as $reference) { + if((trim($reference)!='') && (isset($thread[$reference]))) { + $id=$reference; + continue 2; + } + } + break; + } + $liste=array(); + $liste[]=$id; + $tmp=0; + thread_show_head(); + echo thread_show_recursive($thread,$liste,1,"",$group,0,100,$tmp,$highlightids); + thread_show_tail(); +} + +/* + * Print the header of a message to the webpage + * + * $head: the header of the message as an headerType + * $group: the name of the newsgroup, is needed for the links to post.php3 + * and the header. + */ +function show_header($head,$group) { + global $article_show,$text_header,$file_article,$attachment_show; + global $file_attachment,$anonym_address; + echo '
'; + if ($article_show["Subject"]) echo $text_header["subject"].htmlspecialchars($head->subject)."
"; + if ($article_show["From"]) { + echo $text_header["from"]; + if($head->from==$anonym_address) { + // this is the anonymous address, so only show the name + echo htmlspecialchars($head->name); + } else { + if($article_show["From_link"]) + echo ''; + if(isset($article_show["From_rewrite"])) + echo eregi_replace($article_show["From_rewrite"][0], + $article_show["From_rewrite"][1], + htmlspecialchars($head->from)); + else + echo htmlspecialchars($head->from); + if($article_show["From_link"]) + echo ''; + if ($head->name != "") echo ' ('.htmlspecialchars($head->name).')'; + } + echo "
"; + } + if ($article_show["Newsgroups"]) + echo $text_header["newsgroups"].htmlspecialchars(str_replace(',',', ',$head->newsgroups))."
\n"; + if (isset($head->followup) && ($article_show["Followup"]) && ($head->followup != "")) + echo $text_header["followup"].htmlspecialchars($head->followup)."
\n"; + if ((isset($head->organization)) && ($article_show["Organization"]) && + ($head->organization != "")) + echo $text_header["organization"]. + html_parse(htmlspecialchars($head->organization))."
\n"; + if ($article_show["Date"]) + echo $text_header["date"].date($text_header["date_format"],$head->date)."
\n"; + if ($article_show["Message-ID"]) + echo $text_header["message-id"].htmlspecialchars($head->id)."
\n"; + if (($article_show["References"]) && (isset($head->references[0]))) { + echo $text_header["references"]; + for ($i=0; $i<=count($head->references)-1; $i++) { + $ref=$head->references[$i]; + echo ' '.''.($i+1).''; + } + echo "
"; + } + if (isset($head->user_agent)) { + if ((isset($article_show["User-Agent"])) && + ($article_show["User-Agent"])) { + echo $text_header["user-agent"].htmlspecialchars($head->user_agent)."
\n"; + } else { + echo "\n"; + } + } + if ((isset($attachment_show)) && ($attachment_show==true) && + (isset($head->content_type[1]))) { + echo $text_header["attachments"]; + for ($i=1; $icontent_type); $i++) { + echo ''. + $head->content_type_name[$i].' ('. + $head->content_type[$i].')'; + if ($icontent_type)-1) echo ', '; + } + } + echo '
'; +} + +/* + * decodes a body. Splits the content of $body into an array of several + * lines, respecting the special decoding issues of format=flowed + * articles. Each returned line consists of two fields: text and + * the quote depth (depth) + */ +function decode_textbody($body,$format="fixed") { + $body=split("\n",$body); + $nbody=array(); + $depth=0; + $paragraph=""; // empty paragraph + $lastline=""; + for($i=0; $i') { + $ndepth++; + } else { + if(($body[$i][$j]!=' ') || ($body[$i][$j-1]==' ') || ($j==0)) { + break; + } + } + } + // generate a new paragraph? + if(($i>0) && (($ndepth!=$depth) || $format!="flowed" || + ($paragraph[strlen($paragraph)-1]!=' ')) || ($i==count($body))) { + $tmp->text=$lastline=$paragraph; + $tmp->depth=$depth; + $paragraph=""; + if(phpversion()>=5) + $nbody[]=clone($tmp); + else + $nbody[]=$tmp; + } + if($body[$i]=="-- " && $format=="flowed") $body[$i]="--"; + $paragraph.=substr($body[$i],$tdepth); + $depth=$ndepth; + } + return $nbody; +} + +/* + * replaces multiple spaces in texts by  es and convert special-chars + * to their entities + */ +function text2html($text) { + return eregi_replace("^ "," ", + str_replace(" ","  ", + str_replace(" ","  ", + str_replace("\n","
", + htmlspecialchars($text))))); +} + + +/* + * print an article to the webpage + * + * $group: The name of the newsgroup + * $id: the ID of the article inside the group or the message-id + * $attachment: The number of the attachment of the article. + * 0 means the normal textbody. + */ +function message_show($group,$id,$attachment=0,$article_data=false,$maxlen=false) { + global $file_article,$file_article_full; + global $text_header,$text_article,$article_showthread; + global $block_xnoarchive,$article_graphicquotes; + if ($article_data == false) + $article_data=message_read($id,$attachment,$group); + $head=$article_data->header; + $body=$article_data->body[$attachment]; + if ($head) { + if (($block_xnoarchive) && (isset($head->xnoarchive)) && + ($head->xnoarchive=="yes")) { + echo $text_article["block-xnoarchive"]; + } else + if (($head->content_type[$attachment]=="text/plain") && + ($attachment==0)) { + show_header($head,$group); + $body=decode_textbody($body, + $article_data->header->content_type_format[$attachment]); + $depth=0; + echo '
'; + $currentlen=0; // needed if $maxlen is set + for ($i=0; $i<=count($body) && + (($currentlen<$maxlen) || ($maxlen==false)); $i++) { + // HTMLized Quotings instead of boring > ? + if($article_graphicquotes) { + // HTMLized Quotings + for($j=$depth; $j<$body[$i]->depth; $j++) + echo '
'; + for($j=$body[$i]->depth; $j<$depth; $j++) + echo '
'; + $t=html_parse(text2html($body[$i]->text)).'
'; + echo $t; + $currentlen+=strlen($t); + echo "\n"; + $depth=$body[$i]->depth; + } else { + // Boring old Quotings with > + if($body[$i]->depth==0) { + if(trim($body[$i]->text)=='') + $t="
\n"; + else + $t=html_parse(text2html($body[$i]->text))."
\n"; + } else { + $t=''.str_repeat('>',$body[$i]->depth).' '. + html_parse(text2html( + textwrap($body[$i]->text,72-$body[$i]->depth, + "\n".str_repeat('>',$body[$i]->depth).' '))). + "
\n"; + } + echo $t; + $currentlen+=strlen($t); + } + } + echo '
'; + if($maxlen!=false && $currentlen>=$maxlen) { + echo '
'.$text_article["full_article"].''; + } + } else { + echo $body; + } + } +} + +/* + * Shows the little menu on article-flat.php where you can select the + * different pages with the articles on it + */ +function articleflat_pageselect($group,$id,$article_count,$first) { + global $articleflat_articles_per_page,$file_article,$file_framethread,$name; + global $text_thread,$thread_show; + $pages=ceil($article_count / $articleflat_articles_per_page); + $return=""; + if ($article_count > $articleflat_articles_per_page) + $return.= $text_thread["pages"]; + for ($i = 0; $i < $pages; $i++) { + if ($first != $i*$articleflat_articles_per_page+1) + $return.= ''; + else + $return.= ''; + $return.= $i+1; + if ($i == $pages-1) { + // $return.= $article_count; + } + if ($first != $i*$articleflat_articles_per_page+1) + $return.= ''; + else + $return.= ''; + } + return $return; +} diff --git a/newsgroup/lib/post.inc.php b/newsgroup/lib/post.inc.php new file mode 100755 index 0000000..c322cb6 --- /dev/null +++ b/newsgroup/lib/post.inc.php @@ -0,0 +1,195 @@ +', '=3F', '@', 'A', + 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', + 'T', 'U', 'V', 'W', 'X', 'Y', + 'Z', '[', '\\', ']', '^', '=5F', + '', 'a', 'b', 'c', 'd', 'e', + 'f', 'g', 'h', 'i', 'j', 'k', + 'l', 'm', 'n', 'o', 'p', 'q', + 'r', 's', 't', 'u', 'v', 'w', + 'x', 'y', 'z', '{', '|', '}', + '~', '=7F', '=80', '=81', '=82', '=83', + '=84', '=85', '=86', '=87', '=88', '=89', + '=8A', '=8B', '=8C', '=8D', '=8E', '=8F', + '=90', '=91', '=92', '=93', '=94', '=95', + '=96', '=97', '=98', '=99', '=9A', '=9B', + '=9C', '=9D', '=9E', '=9F', '=A0', '=A1', + '=A2', '=A3', '=A4', '=A5', '=A6', '=A7', + '=A8', '=A9', '=AA', '=AB', '=AC', '=AD', + '=AE', '=AF', '=B0', '=B1', '=B2', '=B3', + '=B4', '=B5', '=B6', '=B7', '=B8', '=B9', + '=BA', '=BB', '=BC', '=BD', '=BE', '=BF', + '=C0', '=C1', '=C2', '=C3', '=C4', '=C5', + '=C6', '=C7', '=C8', '=C9', '=CA', '=CB', + '=CC', '=CD', '=CE', '=CF', '=D0', '=D1', + '=D2', '=D3', '=D4', '=D5', '=D6', '=D7', + '=D8', '=D9', '=DA', '=DB', '=DC', '=DD', + '=DE', '=DF', '=E0', '=E1', '=E2', '=E3', + '=E4', '=E5', '=E6', '=E7', '=E8', '=E9', + '=EA', '=EB', '=EC', '=ED', '=EE', '=EF', + '=F0', '=F1', '=F2', '=F3', '=F4', '=F5', + '=F6', '=F7', '=F8', '=F9', '=FA', '=FB', + '=FC', '=FD', '=FE', '=FF'); + // are there "forbidden" characters in the string? + for($i=0; $i0 && $line[$from] != ' '; $from--); + if($from>0) $from++; + for(;$to'; + break; + default: + return false; + break; + } +} + +/* + * Post an article to a newsgroup + * + * $subject: The Subject of the article + * $from: The authors name and email of the article + * $newsgroups: The groups to post to + * $ref: The references of the article + * $body: The article itself + */ +function message_post($subject,$from,$newsgroups,$ref,$body) { + global $server,$port,$send_poster_host,$organization,$text_error; + global $file_footer,$www_charset,$spooldir; + global $msgid_generate,$msgid_fqdn; + 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."; format=flowed\r\n"); + fputs($ns,"Content-Transfer-Encoding: 8bit\r\n"); + fputs($ns,"User-Agent: NewsPortal/0.36 (http://florian-amrhein.de/newsportal)\r\n"); + if ($send_poster_host) + @fputs($ns,'X-HTTP-Posting-Host: '.gethostbyaddr(getenv("REMOTE_ADDR"))."\r\n"); + if (($ref!=false) && (count($ref)>0)) { + // strip references + if(strlen(implode(" ",$ref))>900) { + $ref_first=array_shift($ref); + do { + $ref=array_slice($ref,1); + } while(strlen(implode(" ",$ref))>800); + array_unshift($ref,$ref_first); + } + fputs($ns,'References: '.implode(" ",$ref)."\r\n"); + } + if (isset($organization)) + fputs($ns,'Organization: '.quoted_printable_encode($organization)."\r\n"); + if ((isset($file_footer)) && ($file_footer!="")) { + $footerfile=fopen($file_footer,"r"); + $body.="\n".fread($footerfile,filesize($file_footer)); + fclose($footerfile); + } + if($msgid=generate_msgid( + $subject.",".$from.",".$newsgroups.",".$ref.",".$body)) + fputs($ns,'Message-ID: '.$msgid."\r\n"); + $body=str_replace("\n.\r","\n..\r",$body); + $body=str_replace("\r",'',$body); + $b=split("\n",$body); + $body=""; + for ($i=0; $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"]; + } + // let thread.php ignore the cache for this group, so this new + // article will be visible instantly + $cachefile=$spooldir.'/'.$newsgroups.'-cache.txt'; + @unlink($cachefile); + return $message; +} +?> \ No newline at end of file diff --git a/newsgroup/lib/thread.inc.php b/newsgroup/lib/thread.inc.php new file mode 100755 index 0000000..78c8e42 --- /dev/null +++ b/newsgroup/lib/thread.inc.php @@ -0,0 +1,1030 @@ + $articles_per_page) + echo $text_thread["pages"]; + for ($i = 0; $i < $pages; $i++) { + // echo '['; + if ($first != $i*$articles_per_page+1) + echo ''; + else + ; //echo ''; + // echo ($i*$articles_per_page+1).'-'; + 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 ''; + else + ; //echo ''; + // 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; + $filename=$spooldir."/".$group."-data.dat"; + if (!file_exists($filename)) return false; + if ($compress_spoolfiles) { + $file=gzopen("$spooldir/$group-data.dat","r"); + $headers=unserialize(gzread($file,1000000)); + gzclose($file); + } else { + $file=fopen($filename,"r"); + $headers=unserialize(fread($file,filesize($filename))); + fclose($file); + } + 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; + if ($compress_spoolfiles) { + $file=gzopen("$spooldir/$group-data.dat","w"); + gzputs($file,serialize($headers)); + gzclose($file); + } else { + $file=fopen("$spooldir/$group-data.dat","w"); + if($file===false) { + die('The spool-directory is not writeable. Please change the user '. + 'permissions to give the webserver write-access to it.'); + } + fputs($file,serialize($headers)); + fclose($file); + } +} + +/* + * 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) { + $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) { + $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]); + } + } + 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=split("\t",$line,count($overviewfmt)-1); + $over=split("\t",$line); + //$article=new headerType; + for ($i=0; $iisReply=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]),"nirgendwo"); + $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]); + } + } + $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,$maxarticles,$maxfetch,$initialfetch,$maxarticles_extra; + global $text_error,$text_thread,$compress_spoolfiles,$server; + global $www_charset,$iconv_enable,$thread_show,$thread_sort_order; + $idstring="0.36,".$server.",".$compress_spoolfiles.",".$maxarticles.",". + $maxarticles_extra.",".$maxfetch.",".$initialfetch.",". + $www_charset.','.$iconv_enable.','.$thread_show["replies"]; + $overviewformat=thread_overview_read($ns); + $spoolfilename=$spooldir."/".$groupname."-data.dat"; + 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 + $spoolopenmodus="n"; + // if the group-info file doesn't exist: create it + if (!((file_exists($infofilename)) && (file_exists($spoolfilename)) && + (filesize($infofilename)>0) && (filesize($spoolfilename)>0))) { + $spoolopenmodus="w"; + } else { + $infofile=fopen($infofilename,"r"); + $oldid=fgets($infofile,100); + if (trim($oldid) != $idstring) { + echo "\n"; + $spoolopenmodus="w"; + } + $oldgroupinfo=explode(" ",trim(fgets($infofile,200))); + fclose($infofile); + if ($groupinfo[3] < $oldgroupinfo[1]) { + $spoolopenmodus="w"; + } + if ($maxarticles == 0) { + if ($groupinfo[2] != $oldgroupinfo[0]) $spoolopenmodus="w"; + } else { + if ($groupinfo[2] > $oldgroupinfo[0]) $spoolopenmodus="w"; + } + // if the high watermark increased, add articles to the existing spool + if (($spoolopenmodus == "n") && ($groupinfo[3] > $oldgroupinfo[1])) + $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") $headers=thread_cache_load($groupname); + // read articles from the newsserver + if ($spoolopenmodus != "n") { + // order the article overviews from the newsserver + 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."/".urlencode($groupname)."-lastarticleinfo.dat","w"); + $lastarticleinfo->from=$article->from; + $lastarticleinfo->date=$article->date; + $lastarticleinfo->name=$article->name; + fputs($infofile,serialize($lastarticleinfo)); + fclose($infofile); + } + // remove the old spoolfile + if (file_exists($spoolfilename)) unlink($spoolfilename); + if ((isset($headers)) && (count($headers)>0)) { + //$infofile=fopen($infofilename,"w"); + //if ($spoolopenmodus=="a") $firstarticle=$oldgroupinfo[0]; + //fputs($infofile,$idstring."\n"); + //fputs($infofile,$firstarticle." ".$lastarticle."\r\n"); + //fclose($infofile); + 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=eregi_replace('^(.*)_(.*)\.(.*)$','\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); + } + } + if(isset($headers)) + return $headers; + return false; + //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; + 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($cache_thread>0) { + if((file_exists($cachefile)) && + (filemtime($cachefile)+$cache_thread>time())) { + // 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 + $fp_cachefile=@fopen($cachefile,"w"); + if($fp_cachefile!==false) { + fputs($fp_cachefile,""); + fclose($fp_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=eregi_replace('^(odp:|aw:|re:|re\[2\]:| )+','',$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 ; $oisReply) { + $re="Re: "; + } else { + $re=""; + } + // is the current article to be highlighted? + if(($highlightids) && + ((in_array($c->id,$highlightids)) || + (in_array($c->number,$highlightids)))) + $highlight=true; + else + $highlight=false; + if($highlight) + $return=''; + else { + $return='number).'">'; + } + $return.=$re.htmlspecialchars(substr(trim($c->subject),0,$thread_maxSubject)); + if($highlight) + $return.=''; + else + $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; + $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) { + global $thread_show,$anonym_address; + // 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); +} + +/* + * 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) { + global $thread_treestyle; + global $thread_show,$imgdir; + global $file_article,$thread_maxSubject; + global $age_count,$age_time,$age_color; + global $frame_article; + $output=""; + if ($thread_treestyle==3) $output.= "\n
    \n"; + for ($i = 0 ; $i= $article_first) && + ($article_count <= $article_last))) { + switch ($thread_treestyle) { + case 0: // simple list + $output.= ''; + if ($thread_show["date"]) $output.= thread_format_date($c)." "; + if ($thread_show["subject"]) $output.= thread_format_subject($c,$group)." "; + if ($thread_show["author"]) $output.= "(".thread_format_author($c).")"; + $output.= ''; + $output.= "
    \n"; + break; + case 1: // html-auflistung, kein baum + $output.= '
  • '; + if ($thread_show["date"]) + $output.= thread_format_date($c).' '; + if ($thread_show["subject"]) + $output.= thread_format_subject($c,$group,$highlight).' '; + if ($thread_show["author"]) + $output.= "(".thread_format_author($c).")"; + $output.= '
  • '; + break; + case 2: // table + $output.= ''; + if ($thread_show["date"]) { + $output.= ''. + thread_format_date($c).' '; + } + if ($thread_show["subject"]) { + $output.= ''. + ''. + thread_format_subject($c,$group,$highlight). + ''; + } + if ($thread_show["author"]) { + $output.= ''. + ''. + ''.thread_format_author($c). + ''; + } + $output.= "\n"; + break; + case 3: // html-tree + $output.= '
  • '; + if ($thread_show["date"]) + $output.= thread_format_date($c)." "; + if ($thread_show["subject"]) + $output.= thread_format_subject($c,$group,$highlight)." "; + if ($thread_show["author"]) + $output.= "(".thread_format_author($c).")"; + $output.= ""; + break; + case 4: // thread + $output.= ''; + if ($thread_show["date"]) + $output.= thread_format_date($c)." "; + $output.= formatTreeText($newtree)." "; + if ($thread_show["subject"]) + $output.= thread_format_subject($c,$group,$highlight)." "; + if ($thread_show["author"]) + $output.= "(".thread_format_author($c).")"; + $output.= '
    '; + break; + case 5: // thread, graphic + $output.= ''; + if ($thread_show["date"]) + $output.= ''; + $output.= ''; + if ($thread_show["subject"]) + $output.= ''; + $output.= "
    '. + ''. + thread_format_date($c).' '. + thread_show_treegraphic($newtree).''. + ' '. + thread_format_subject($c,$group,$highlight)." "; + if ($thread_show["author"]) + $output.= '('.thread_format_author($c).')
    "; + break; + case 6: // thread, table + $output.= ""; + if ($thread_show["date"]) + $output.= ''. + ''. + thread_format_date($c).' '; + $output.= ''. + ''. + formatTreeText($newtree)." "; + if ($thread_show["subject"]) { + $output.= thread_format_subject($c,$group,$highlight).""; + $output.= ""; + } + if ($thread_show["author"]) + $output.= ''. + ''. + thread_format_author($c).''; + $output.= ""; + break; + case 7: // thread, table, graphic + $output.= ''; + if ($thread_show["date"]) + $output.= ''. + ''. + thread_format_date($c)." ". + ''; + $output.= ''; + $output.= thread_show_treegraphic($newtree); + if ($thread_show["subject"]) + $output.= ' '. + thread_format_subject($c,$group,$highlight).''; + $output.=''; + if($thread_show["threadsize"]) + $output.= "".$c->threadsize.''; + if ($thread_show["subject"]) $output.= ""; + if ($thread_show["author"]) + $output.= ''. + ''. + thread_format_author($c).''; + $output.= ""; + break; + } + } + if ((isset($c->answers[0])) && (count($c->answers)>0) && + ($article_count<=$article_last)) { + if ($thread_treestyle >= 4) { + if (substr($newtree,$depth-2,1) == "+") + $newtree=str_change($newtree,$depth-2,"i"); + $newtree=str_change($newtree,$depth-1,"+"); + $newtree=strtr($newtree,"`","."); + } + if (!isset($newtree)) $newtree=""; + if($thread_show["replies"]) { + $output.=thread_show_recursive($headers,$c->answers,$depth+1,$newtree."",$group, + $article_first,$article_last,$article_count,$highlight); + } + } + flush(); + } + if ($thread_treestyle==3) $output.= "
"; + return $output; +} + + +/* + * Displays the Head (table tags, headlines etc.) of a thread + */ +function thread_show_head() { + 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"]) { + echo ''; + echo '\n"; + } + echo "\n"; + } else { + if ($thread_treestyle==1) echo "
    \n"; + } +} + +/* + * Displays the tail (closing table tags, headlines etc.) of a thread + */ +function thread_show_tail() { + global $thread_show, $thread_showTable; + global $text_thread,$thread_treestyle; + if (($thread_treestyle==2) || ($thread_treestyle==6) || + ($thread_treestyle==7)) { + echo "
'.$text_thread["date"]." '. + $text_thread["subject"]."'. + $text_thread["threadsize"]."  '.$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) { + 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); + } + // 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(); + 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)); + } + } + 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; +} + +?> \ No newline at end of file diff --git a/newsgroup/lib/types.inc.php b/newsgroup/lib/types.inc.php new file mode 100755 index 0000000..df049d8 --- /dev/null +++ b/newsgroup/lib/types.inc.php @@ -0,0 +1,79 @@ +0) + var $content_type_charset; // like content_type + var $content_type_name; // array of the names of the attachments + var $content_type_boundary; // The boundary of an multipart-article. + var $content_type_format; // array, is the body in flowed format? + var $answers; // which articles are followups of this article? + var $isAnswer; // is the article an answer to an other article? + var $username; + var $user_agent; + var $isReply; // has this article "Re: " at the beginning of the subject? + var $threadsize; // number of articles in this thread +} +?> \ No newline at end of file diff --git a/newsgroup/lib/validator.inc b/newsgroup/lib/validator.inc new file mode 100755 index 0000000..efcd993 --- /dev/null +++ b/newsgroup/lib/validator.inc @@ -0,0 +1,243 @@ +fields as $field) { + switch($field->typ) { + case "text": + case "textarea": + case "checkbox": + case "radiobutton": + // Basteln wegen scheiss PHP + $valtmp=$field->validator; + // feld_leer*feld_darf_leer + + // nicht_feld_leer*(testfkt.ex.*testfkt + + // nicht_testfkt.ex) + if( + ( + ( + (!isset($_REQUEST[$field->name])) || + (trim($_REQUEST[$field->name])=="") + ) && + (isset($field->empty)) && + ($field->empty==true) + ) || ( + (isset($_REQUEST[$field->name])) && + (trim($_REQUEST[$field->name])!="") && + ( + ($field->validator==false) || + ($field->validator!=false) && + (($errmsg=$valtmp($_REQUEST[$field->name]))===true) + ) + ) + ) { + // Feld leer und darf leer sein, oder + // feld voll und (testfunktion existiert und testfkt.=true + // oder testfunktion existiert nicht) + $this->fields[$field->name]->error=false; + } else { + // Feld leer und darf nicht leer sein, oder + // Feld voll und testfkt.ex und testfunktion=false + $errors=true; + $this->fields[$field->name]->error=true; + if(isset($errmsg)) { + $this->fields[$field->name]->errormessage=$errmsg; + unset($errmsg); + } + } + break; + case "pulldown": + case "check-text": + case "radio-text": + if((isset($field->empty)) && (!$field->empty) && + ((!isset($_REQUEST[$field->name])) || + (trim($_REQUEST[$field->name])=="") || + (($_REQUEST[$field->name]=="_frei") && + ((!isset($_REQUEST[$field->name."_frei"])) || + (trim($_REQUEST[$field->name."_frei"])=="") + ) + ) + ) + ) { + $errors=true; + $this->fields[$field->name]->error=true; + } else { + $this->fields[$field->name]->error=false; + } + break; + } +// echo '

eval: '.$field->name.': '.$_REQUEST[$field->name].'

'; + } + return !$errors; + } + + + /* + * liefert true, falls $name fehlerhaft ausgefüllt wurde + */ + function is_error($name) { + return $this->fields[$name]->error; + } + + /* + * Liefert die individuelle Fehlermeldung, falls $name fehlerhaft + * ausgefüllt wurde. Falls keine Meldung vorliegt, wird false + * geliefert. + */ + function geterrormessage($name) { + if(isset($this->fields[$name]->errormessage)) + return $this->fields[$name]->errormessage; + else + return false; + } + + /* + * Zeigt gegebenenfalls eine Fehlermeldung an, falls $name nicht + * korrekt ausgefüllt wurde + */ + function show_error($name) { + if($this->is_error($name)) { + echo "

fehler

"; + } + } + + /* + * Zeigt den Titel zu einem Feld an. Ist das zugehörige Feld fehlerhaft + * ausgefüllt worden, wird es (z.B. farblich) markiert. + * + * $name: Name des Feldes + * $text: auszugebener Text + */ + function show_title($name,$text) { + if($this->is_error($name)) + echo ''.$text.''; + else + echo $text; + } + + /* + * Zeigt die Daten an, die der Benutzer in das Formular, ob richtig oder + * falsch ist egal, eingegeben hatte. + */ + function show_value($name) { + echo stripslashes($_REQUEST[$name]); + } + + /* + * Liefert Variablenwerte ohne vorherige Umkodierung/Zusammenfassung + * zurück + */ + function value($name) { + if(is_array($_REQUEST[$name])) { + $a=$_REQUEST[$name]; + return $a; + } + return stripslashes($_REQUEST[$name]); + } + + /* Liefert Variablenwerte mit vorheriger Umkodierung/Zusammenfassung + * zurück. Vor allem wichtig bei Typ check-text und radio-text, wo + * der eigentliche Inhalt über mehrere Variablen verteilt ist, bzw. + * teilweise gar nicht zum Zuge kommt (freies Textfeld ausgefüllt, + * aber nicht angeklickt) + */ + function get_value($name) { + if(is_array($_REQUEST[$name])) { + $a=$_REQUEST[$name]; + // Freies Textfeld? + if(in_array("_frei",$a)) { + if((isset($_REQUEST[$name.'_frei'])) && + ($_REQUEST[$name.'_frei']!="")) + $a[]=$_REQUEST[$name.'_frei']; + unset($a[$name.'_frei']); + } + + return $a; + } else if(($_REQUEST[$name]=='_frei') && + ($this->fields[$name]->typ=='radio-text')) { + return stripslashes($_REQUEST[$name.'_frei']); + } else + return stripslashes($_REQUEST[$name]); + } + + /* + * gibt einfach nur " checked" aus, wenn $name den wert $value enthält. + * Nötig für das Vorselektieren von Knöpfen + */ + function show_checked($name,$value) { + global $fields; + if(($this->fields[$name]->typ!="checkbox") && + ($this->fields[$name]->typ!="check-text")) { + if($this->value($name)==$value) { + if($this->fields[$name]->typ=="pulldown") + echo ' selected'; + else + echo ' checked'; + } + } else { + if(in_array($value,$this->value($name))) + echo ' checked'; + } + } + + function show_selected($name,$value) { + global $fields; + if(($this->fields[$name]->typ!="checkbox") && + ($this->fields[$name]->typ!="check-text")) { + if($this->value($name)==$value) { + echo ' selected'; + } + } + } + + + /* + * Registriert eine Variable als zum Formular gehörend + * + * $name: Name der Variablen + * $typ: Art der Eingabe: + * - text: Textfeld mit einfacher freier Eingabe + * - textarea: Mehrzeiliger Text mit freier Eingabe + * - checkbox: Ankreuzfelder, mehrere gleichzeitig + * - radiobutton: Ankreuzfelder, nur eins gleichzeitig + * - pulldown: Pulldown-Menu, nut eins gleichzeitig + * - check-text: Ankreuzfelder+Textfeld, mehrere gleichzeitig + * - radio-text: Ankreuzfelder+Textfeld, maximal eins + * $empty: Darf das entsprechende Feld leer gelassen werden? + * bzw. mindestens kein angekreuzt bzw. ausgefüllt? + * $validator: Information, wie der Inhalt auf Korrektheit geprüft + * werden soll + * $errmsg: Fehlermeldung, die bei erkanntem Fehler ausgegeben + * werden soll + */ + function register($name,$typ,$empty=true,$validator=false,$errmsg=false) { + $var->name=$name; + $var->typ=$typ; + $var->empty=$empty; + $var->validator=$validator; + $var->errmsg=$errmsg; + $this->fields[$name]=$var; + } + + /* + * der Konstruktor + */ + function formvalidate() { + } +} +?>