"; if ($from == "" || $from == "Anonymous") $from = "Anonymous "; // // 'sanitize_email()' - Convert an email address to something a SPAMbot // can't read... // function // O - Sanitized email sanitize_email($email, // I - Email address $html = 1) // I - HTML format? { $nemail = ""; $len = strlen($email); for ($i = 0; $i < $len; $i ++) { switch ($email[$i]) { case '@' : if ($i > 0) $i = $len; else if ($html) $nemail .= " at "; else $nemail .= " at "; break; case '<' : if ($i > 0) $i = $len; break; case '>' : break; case '&' ; $nemail .= "&"; break; default : $nemail .= $email[$i]; break; } } return (trim($nemail)); } // // 'format_date()' - Format a RFC 2822 date string. // function // O - Date/time in human format format_date($rfc2822) // I - Date/time in RFC 2822 format { if (($time = strtotime($rfc2822)) < 0) $date = htmlspecialchars($rfc2822, ENT_QUOTES); else { $diff = abs(time() - $time); if ($diff < 604800) $date = date("H:i D", $time); else if ($diff < 31536000) $date = date("H:i M d", $time); else $date = date("M d, Y", $time); } return ($date); } // // 'nntp_header()' - Show the standard header and nav links. // function nntp_header($title, // I - Title $links) // I - Links { html_header($title); html_start_links(TRUE); html_links($links); html_end_links(); } // // 'nntp_close()' - Close a news server thing... // function nntp_close($stream) // I - Socket stream { nntp_command($stream, "QUIT", 205); fclose($stream); } // // 'nntp_command()' - Send a command and get the response... // function // O - NNTP response nntp_command($stream, // I - Socket stream $command = "QUIT", // I - NNTP command $expect = 200) // I - Expected status { // print("

nntp_command(stream=$stream, command='$command', expect=$expect)

\n"); fwrite($stream, "$command\r\n"); $status = fgets($stream, 1024); // print("

status='$status'

\n"); // if ((int)$status != $expect) // { // print("

Error: $status

\n"); // return (NULL); // } // else return ($status); } // // 'nntp_connect()' - Connect to the news server. // function // O - Socket stream nntp_connect() { global $NNTPSERVER, $NNTPPORT; $errno = 0; $errstr = ""; $stream = fsockopen($NNTPSERVER, $NNTPPORT, $errno, $errstr); if ($stream) { if ($line = fgets($stream, 1024)) { if ((int)$line != 200) { print("

Error: $line

\n"); fclose($stream); return (FALSE); } } else { print("

Error: No response from NNTP server!

\n"); fclose($stream); return (FALSE); } } else print("

Error: $errstr ($errno)

\n"); return ($stream); } // // 'nntp_error()' - Show an error message. // function nntp_error($text, // I - Human-readable message $status, // I - NNTP status message $group = "") // I - Current group, if any { $links = array(); $links["All Forums"] = "forums.php"; if ($group != "") $links["Back to $group"] = "forums.php?g$group"; nntp_header("Error", $links); print("

$text

\n" ."
$status
\n"); html_footer(); } // // 'nntp_search()' - Do a header search... // function // O - Matching message headers... nntp_search($stream, // I - Socket stream $group, // I - NNTP group $search, // I - Search text $threaded = TRUE) // I - Thread messages? { // print("

nntp_search(stream=$stream, group='$group', search='$search'

\n"); // Get the start and end messages in the group... $status = nntp_command($stream, "GROUP $group", 211); if ((int)$status != 211) { nntp_error("We were unable to open the forum '$group' for the following " ."reason:", $status, $group); return (NULL); } // Read the messages in the group... $fields = explode(" ", $status); $status = nntp_command($stream, "XOVER $fields[2]-$fields[3]", 224); if ((int)$status != 224) { nntp_error("We were unable to search the forum '$group' for the following " ."reason:", $status, $group); return (NULL); } $words = html_search_words($search); $num_matches = 0; $matches = NULL; while ($line = fgets($stream, 1024)) { $line = rtrim($line); if ($line == ".") break; // print("
" . htmlspecialchars($line) . "
\n"); if ($search == "") { // Return all matches... $matches[$num_matches] = $line; $num_matches ++; } else { // Search for words... reset($words); $fields = explode("\t", $line); foreach ($words as $word) { if (stristr($fields[1], $word) || stristr($fields[2], $word)) { $matches[$num_matches] = $line; $num_matches ++; break; } } } } if ($threaded) { // Thread the articles... $threads = array(); $parents = array(); for ($i = 0; $i < sizeof($matches); $i ++) { $fields = explode("\t", $matches[$i]); $subject = eregi_replace("(re:|\\[[a-z]+\\.[a-z]+\\]) ", "", $fields[1]); if (array_key_exists($subject, $parents)) $threads[$i] = sprintf("%06d%06d", $parents[$subject], $i); else { $parents["$subject"] = $i; $threads[$i] = sprintf("%06d%06d", $i, $i); } } array_multisort($threads, SORT_NUMERIC, $matches); } // Return the matches... return ($matches); } // // 'show_prevnext_page()' - Show the prev/next links for the messages list... // function show_prevnext_page($group, // I - Group $group_filter, // I - Group filter $start, // I - Start message $end, // I - End message $count, // I - Number of messages $threaded) // I - Thread messages? { global $PHP_SELF, $PAGE_MAX, $options; print("

\n" ."\n" ."
"); if ($start > 1) { $i = $start - $PAGE_MAX; if ($i < 1) $i = 1; $j = $i + $PAGE_MAX - 1; if ($j > $count) $j = $count; html_start_links(); html_link("Show Messages $i - $j", "$PHP_SELF?s$i+g$group$options"); html_end_links(); } print(""); html_start_links(); if (!ereg(".*\.announce", $group) && !ereg(".*\.commit", $group)) html_link("New Message", "$PHP_SELF?s$start+g$group+n$options"); if ($threaded) html_link("Sort by Date", "$PHP_SELF?s$start+g$group+T0" . substr($options, 3)); else html_link("Sort by Thread", "$PHP_SELF?s$start+g$group+T1" . substr($options, 3)); html_end_links(); print(""); if ($end < $count) { $i = $start + $PAGE_MAX; $j = $i + $PAGE_MAX - 1; if ($j > $count) $j = $count; html_start_links(); html_link("Show Messages $i - $j", "$PHP_SELF?s$i+g$group$options"); html_end_links(); } print("

\n"); } // // 'show_messages()' - Show messages in the named group... // function show_messages($group, // I - Group $group_filter, // I - Group filter $start, // I - Start message $search, // I - Search string $threaded) // I - Threaded view? { global $PHP_SELF, $PAGE_MAX, $_COOKIE, $options; // Figure out which messages to show... $error = ""; $stream = nntp_connect(); $matches = nntp_search($stream, $group, $search, $threaded); nntp_close($stream); if (!$matches) { $count = 0; if ($search == "") $error = "No messages in group."; else $error = "No matches found for '" . htmlspecialchars($search, ENT_QUOTES) . "'..."; } else $count = count($matches); if ($start == 0) { if ($search == "") { $cookie = str_replace(".", "_", $group); if (array_key_exists($cookie, $_COOKIE)) { $msgnum = (int)$_COOKIE[$cookie]; for ($i = 0; $i < $count; $i ++) { $fields = explode("\t", $matches[$i]); if ((int)$fields[0] == $msgnum) break; } $start = $i + 1; } else $start = $count - $PAGE_MAX + 1; } else $start = 1; } if ($start > ($count - $PAGE_MAX + 1)) $start = $count - $PAGE_MAX + 1; if ($start < 1) $start = 1; $end = $start + $PAGE_MAX - 1; if ($end > $count) $end = $count; // Show the standard header... nntp_header("$group ($start - $end of $count)", array("All Forums" => "forums.php?g$options")); $temp = htmlspecialchars($search, ENT_QUOTES); print("
\n" ."

Search Words:

\n" ."
\n"); if ($error != "") print("

$error

\n"); else { show_prevnext_page($group, $group_filter, $start, $end, $count, $threaded); html_start_table(array("Subject", "Author", "Date/Time")); for ($i = $start; $i <= $end; $i ++) { $fields = explode("\t", $matches[$i - 1]); $subject = htmlspecialchars(eregi_replace("\\[[a-z]+\\.[a-z]+\\] ", "", $fields[1]), ENT_QUOTES); $author = sanitize_email($fields[2]); $date = format_date($fields[3]); if ($subject == "") $subject = "(No Subject)"; html_start_row(); print("" ."$subject" ."  $author  " ."$date"); html_end_row(); } html_end_table(); show_prevnext_page($group, $group_filter, $start, $end, $count, $threaded); } html_footer(); } // // 'show_groups()' - Show groups... // function show_groups($group_filter, // I - Group filter $search) // I - Search string { global $PHP_SELF, $_COOKIE, $options; nntp_header("Forums", array("All Forums" => "forums.php?g$options")); // Figure out which messages to show... $stream = nntp_connect(); // Search stuff... print("
\n" ."

Search Words:

\n" ."
\n"); // Show the standard header... html_start_table(array("Forum", "Messages", "")); $status = nntp_command($stream, "LIST", 215); $num_groups = 0; $groups = array(); if ((int)$status == 215) { while ($line = fgets($stream, 1024)) { $line = rtrim($line); if ($line == ".") break; $fields = explode(" ", $line); $groups[$num_groups] = $fields[0]; $num_groups ++; } } sort($groups); while (list($key, $group) = each($groups)) { if (ereg("(linuxprinting|private)\\..*", $group)) continue; if ($group_filter && !ereg("${group_filter}\\.*", $group)) continue; $status = nntp_command($stream, "GROUP $group", 211); if ((int)$status != 211) continue; $fields = explode(" ", $status); $total = (int)$fields[1]; if ($search != "") { $matches = nntp_search($stream, $group, $search); $mcount = count($matches); $count = "$total total, $mcount match"; } else { $cookie = str_replace(".", "_", $group); if (array_key_exists($cookie, $_COOKIE)) { $newcount = (int)$fields[3] - (int)$_COOKIE[$cookie]; $count = "$total total, $newcount unread"; } else $count = "$total total"; } html_start_row(); print("$group" ."$count"); if ($search != "") print("/$total"); print(""); html_start_links(); html_link("View", "$PHP_SELF?g$group$options"); if (!ereg(".*\.announce", $group) && !ereg(".*\.commit", $group)) html_link("New Message", "$PHP_SELF?g$group+n$options"); html_end_links(); print(""); html_end_row(); } html_start_row("header"); print("Forums and Mailing Lists"); html_end_row(); html_start_row(); print("" ."

Point your news reader at " ."news.easysw.com to view these forums directly.

\n" ."

Go to " ."http://lists.easysw.com/mailman/listinfo " ."to subscribe to or unsubcribe from the mailing lists that mirror " ."these forums.

" .""); html_end_row(); html_end_table(); nntp_close($stream); html_footer(); } // // 'show_prevnext_msg()' - Show the prev/next links for the messages list... // function show_prevnext_msg($group, // I - Group $group_filter, // I - Group filter $start, // I - Start message $count, // I - Number of messages $msg, // I - Current message $threaded) // I - Thread messages? { global $PHP_SELF, $options; print("

\n" ."\n" ."
"); if ($msg > 1) { $i = $msg - 1; html_start_links(); html_link("Previous Message", "$PHP_SELF?s$start+g$group+v$i$options"); html_end_links(); } print(""); if (!ereg(".*\.announce", $group) && !ereg(".*\.commit", $group)) { html_start_links(); html_link("New Message", "$PHP_SELF?s$start+g$group+n$options"); html_link("Reply", "$PHP_SELF?s$start+g$group+r$msg$options"); html_end_links(); } print(""); if ($msg < $count) { $i = $msg + 1; html_start_links(); html_link("Next Message", "$PHP_SELF?s$start+g$group+v$i$options"); html_end_links(); } print("

\n"); } // // 'show_message()' - Show a single message... // function show_message($group, // I - Group $group_filter, // I - Group filter $start, // I - Start message $msg, // I - Current message $search, // I - Search string $threaded) // I - Thread messages? { global $PHP_SELF, $_COOKIE, $options; // print("\n"); // Figure out which messages to show... $stream = nntp_connect(); $matches = nntp_search($stream, $group, $search, $threaded); $count = count($matches); if ($msg[0] == ':') { // Lookup a specific message ID... $msg = (int)substr($msg, 1); for ($i = 0; $i < $count; $i ++) { $fields = explode("\t", $matches[$i]); if ($msg == $fields[0]) break; } if ($i >= $count) { nntp_error("We were unable to show the requested message for the following " ."reason:", "The message number ($msg) is out of range.", $group); nntp_close($stream); return; } $msg = $i; } else { // Lookup index into search... if ($msg < 1 || $msg > $count) { nntp_error("We were unable to show the requested message for the following " ."reason:", "The message number is out of range.", $group); nntp_close($stream); return; } $fields = explode("\t", $matches[$msg - 1]); } // print("\n"); $msgnum = (int)$fields[0]; $subject = htmlspecialchars(eregi_replace("\\[[a-z]+\\.[a-z]+\\] ", "", $fields[1]), ENT_QUOTES); $author = sanitize_email($fields[2]); $date = format_date($fields[3]); if ($subject == "") $subject = "(No Subject)"; // Save last message read... $cookie = str_replace(".", "_", $group); if ($search == "" && (!array_key_exists($group, $_COOKIE) || (int)$_COOKIE[$cookie] < $msgnum)) setcookie($cookie, $msgnum, time() + 90 * 86400, "/"); $status = nntp_command($stream, "BODY $msgnum", 222); if ((int)$status != 222) { nntp_close($stream); nntp_error("We were unable to show the requested message for the following " ."reason:", $status, $group); return (NULL); } $body = ""; while ($line = fgets($stream, 1024)) { $line = rtrim($line); if ($line == ".") break; $body = $body . $line . "\n"; } nntp_close($stream); $body = quote_text($body); nntp_header("$subject", array("All Forums" => "forums.php?g$options", "Back to $group" => "forums.php?g$group+s$start$options")); show_prevnext_msg($group, $group_filter, $start, $count, $msg, $threaded); html_start_table(array($subject, $author, $date), "", "", TRUE); html_start_row(); print("$body
\n" ."[ Direct Link" ." to Message ]"); html_end_row(); html_end_table(); show_prevnext_msg($group, $group_filter, $start, $count, $msg, $threaded); html_footer(); } // // 'post_message()' - Post a message... // function post_message($group, // I - Group $group_filter, // I - Group filter $start, // I - Start message $msg, // I - Current message $search, // I - Search string $threaded) // I - Thread messages? { global $LOGIN_USER, $PHP_SELF, $PROJECT_URL, $_POST, $options; // Get form data... if (array_key_exists("FROM", $_POST)) $from = $_POST["FROM"]; else $from = ""; if (array_key_exists("SUBJECT", $_POST)) $subject = $_POST["SUBJECT"]; else $subject = ""; if (array_key_exists("BODY", $_POST)) $body = $_POST["BODY"]; else $body = ""; // Validate form data... if (!validate_email($from) || $subject == "" || $body == "") { new_message($group, $group_filter, $start, $from, $subject, $body); return; } // Connect to the news server and get the reply-to message ID... $stream = nntp_connect(); if (!$stream) { return; } $id = ""; if ($msg > 0) { $matches = nntp_search($stream, $group, $search, $threaded); $count = count($matches); if ($msg <= $count) { $fields = explode("\t", $matches[$msg - 1]); $id = $fields[4]; } } // Create the message body... $message = "From: $from\r\n" ."Subject: $subject\r\n" ."Newsgroups: $group\r\n"; if ($id != "") $message .= "In-Reply-To: $id\r\n"; $message .= "X-Login-Name: $LOGIN_USER\r\n" ."X-Site-URL: $PROJECT_URL\r\n" ."\r\n"; $lines = explode("\n", $body); $count = count($lines); for ($i = 0; $i < $count; $i ++) { $line = rtrim($lines[$i]); if ($line == ".") $message .= ". \r\n"; else $message .= "$line\r\n"; } // Run the message by spamc to see if it thinks the message is // spam... $p = popen("spamc -c >/dev/null", "w"); if ($p) { fwrite($p, $message); if (pclose($p)) { // Message is spam... nntp_header("$group Error", array("All Forums" => "forums.php?g$options", "Back to $group" => "forums.php?g$group+s$start$options")); print("

Your message could not be posted for the following reason:

\n" ."
The anti-spam filters determined that your message " ."is most likely an unsolicited commercial message that is not " ."allowed on this group. If this is not the case, please press your " ."browser's Back button and check that the message does " ."not contain common spam phrases like 'an offer for you' and so " ."forth.
\n"); html_footer(); return; } } // Post the message... $status = nntp_command($stream, "POST", 340); if ((int)$status != 340) { nntp_close($stream); nntp_error("We were unable to post the requested message for the following " ."reason:", $status, $group); return; } fwrite($stream, $message); // Get the posting status... $status = nntp_command($stream, ".", 240); if ((int)$status == 240) { if ($msg == 0) header("Location: $PHP_SELF?s$start+g$group$options"); else header("Location: $PHP_SELF?s$start+g$group+v$msg$options"); } else nntp_error("We were unable to post the requested message for the following " ."reason:", $status, $group); nntp_close($stream); } // // 'reply_message()' - Reply to a message... // function reply_message($group, // I - Group to reply to $group_filter, // I - Group filter $start, // I - First message in the display $msg, // I - Message to reply to $search, // I - Search string $threaded, // I - Thread messages? $sender) // I - Sender address { // Figure out which messages to show... $stream = nntp_connect(); $matches = nntp_search($stream, $group, $search, $threaded); $count = count($matches); if ($msg < 1 || $msg > $count) { nntp_close($stream); return; } $fields = explode("\t", $matches[$msg - 1]); $msgnum = (int)$fields[0]; $subject = eregi_replace("\\[[a-z]+\\.[a-z]+\\] ", "", $fields[1]); $author = sanitize_email($fields[2]); $date = htmlspecialchars($fields[3], ENT_QUOTES); if (strncasecmp($subject, "re:", 3)) $subject = "Re: " . $subject; $status = nntp_command($stream, "BODY $msgnum", 222); if ((int)$status != 222) { nntp_close($stream); nntp_error("We were unable to reply to the requested message for the following " ."reason:", $status, $group); return; } $body = ""; while ($line = fgets($stream, 1024)) { $line = rtrim($line); if ($line == ".") break; $body = $body . "> " . $line . "\n"; } nntp_close($stream); new_message($group, $group_filter, $start, $subject, $sender, $body); } // // 'new_message()' - Post a new message... // function new_message($group, // I - Group to post to $group_filter, // I - Group filter $start, // I - First message $subject, // I - Subject of message $sender, // I - Sender address $body) // I - Message body { global $PHP_SELF, $NNTPSPEC, $options; $subject = htmlspecialchars($subject, ENT_QUOTES); $sender = htmlspecialchars($sender, ENT_QUOTES); $body = htmlspecialchars($body, ENT_QUOTES); nntp_header("Post Message to $group", array("All Forums" => "forums.php?g$options", "Back to $group" => "forums.php?g$group+s$start$options")); print("

Post Message to $group

"); print("
\n"); print("
\n"); print("" ."\n"); print("" ."\n"); print("" ."\n"); print("" ."\n"); print("
Subject:
From:
Body:
\n"); print("
\n"); html_footer(); } // Parse command-line options... $start = 0; $group = ""; $op = 'l'; $msg = ""; $groups = "minixml"; if (array_key_exists("THREADED", $_COOKIE)) $threaded = $_COOKIE["THREADED"] != 0; else $threaded = FALSE; if (array_key_exists("SEARCH", $_POST)) $search = $_POST["SEARCH"]; else $search = ""; for ($i = 0; $i < $argc; $i ++) { switch ($argv[$i][0]) { case 'g' : $group = substr($argv[$i], 1); break; case 'G' : $groups = substr($argv[$i], 1); break; case 'n' : case 'p' : case 'r' : $op = $argv[$i][0]; $msg = (int)substr($argv[$i], 1); break; case 'v' : $op = $argv[$i][0]; $msg = substr($argv[$i], 1); break; case 's' : $start = (int)substr($argv[$i], 1); break; case 'T' : // Set threading $threaded = (int)substr($argv[$i], 1); break; case 'Q' : // Set search text $search = urldecode(substr($argv[$i], 1)); $i ++; while ($i < $argc) { $search .= urldecode(" $argv[$i]"); $i ++; } break; } } setcookie("THREADED", $threaded, time() + 90 * 86400, "/"); if ($search != "") $options = "+T$threaded+Q" . urlencode($search); else $options = "+T$threaded"; // Now handle the request... switch ($op) { case 'l' : // List if ($group) show_messages($group, $groups, $start, $search, $threaded); else show_groups($groups, $search); break; case 'n' : // New message if ($LOGIN_USER == "") { $options = str_replace("+", "%2B", "+g" . urlencode($group) . $options); header("Location: login.php?PAGE=$PHP_SELF?n$options"); return; } new_message($group, $groups, $start, "", $from, ""); break; case 'p' : // Post message if ($LOGIN_USER == "") { $options = str_replace("+", "%2B", "+g" . urlencode($group) . $options); header("Location: login.php?PAGE=$PHP_SELF?l$options"); return; } if (ereg(".*\.announce", $group) || ereg(".*\.commit", $group)) { nntp_header("Forum Posting Error", array("All Forums" => "forums.php?g$options", "Back to $group" => "forums.php?g$group+s$start$options")); print("

We are sorry, but we could not post your message for the " ."following reason:\n" ."

Forum $group is read-only.
\n"); html_footer(); } else post_message($group, $groups, $start, $msg, $search, $threaded); break; case 'r' : // Reply message if ($LOGIN_USER == "") { $options = str_replace("+", "%2B", "+g" . urlencode($group) . $options); header("Location: login.php?PAGE=$PHP_SELF?r$msg$options"); return; } reply_message($group, $groups, $start, $msg, $search, $threaded, $from); break; case 'v' : // View message show_message($group, $groups, $start, $msg, $search, $threaded); break; } // // End of "$Id$". // ?>